修改Linux内核中PCIe设备的复位模块
修改Linux内核中PCIe设备的复位模块以适配NPU,以Linux-5.4.0内核修改为例,在linux-source-5.4.0\drivers\pci\quirks.c文件中新增如下代码,重新编译内核并安装使用。
#include "hotplug/pciehp.h" #ifndef PCIE_RESET_READY_POLL_MS #define PCIE_RESET_READY_POLL_MS 60000 #endif #ifndef PCI_VENDOR_ID_HUAWEI #define PCI_VENDOR_ID_HUAWEI 0x19e5 #endif #define PCI_DEVICE_ID_910A3 0xd803 #define SLOT_ID_REG 0x9C static inline struct pci_dev *ctrl_dev(struct controller *ctrl) { return ctrl->pcie->port; } static int pcie_poll_cmd(struct controller *ctrl, int timeout) { struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; do { pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); if (slot_status == (u16) ~0) { ctrl_info(ctrl, "%s: no response from device\n", __func__); return 0; } if (slot_status & PCI_EXP_SLTSTA_CC) { pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); return 1; } msleep(10); timeout -= 10; } while (timeout >= 0); return 0; /* timeout */ } static void pcie_wait_cmd(struct controller *ctrl) { unsigned int msecs = pciehp_poll_mode ? 2500 : 1000; unsigned long duration = msecs_to_jiffies(msecs); unsigned long cmd_timeout = ctrl->cmd_started + duration; unsigned long now, timeout; int rc; /* * If the controller does not generate notifications for command * completions, we never need to wait between writes. */ if (NO_CMD_CMPL(ctrl)) return; if (!ctrl->cmd_busy) return; /* * Even if the command has already timed out, we want to call * pcie_poll_cmd() so it can clear PCI_EXP_SLTSTA_CC. */ now = jiffies; if (time_before_eq(cmd_timeout, now)) timeout = 1; else timeout = cmd_timeout - now; if (ctrl->slot_ctrl & PCI_EXP_SLTCTL_HPIE && ctrl->slot_ctrl & PCI_EXP_SLTCTL_CCIE) rc = wait_event_timeout(ctrl->queue, !ctrl->cmd_busy, timeout); else rc = pcie_poll_cmd(ctrl, jiffies_to_msecs(timeout)); if (!rc) ctrl_info(ctrl, "Timeout on hotplug command %#06x (issued %u msec ago)\n", ctrl->slot_ctrl, jiffies_to_msecs(jiffies - ctrl->cmd_started)); } #define CC_ERRATUM_MASK (PCI_EXP_SLTCTL_PCC | \ PCI_EXP_SLTCTL_PIC | \ PCI_EXP_SLTCTL_AIC | \ PCI_EXP_SLTCTL_EIC) static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd, u16 mask, bool wait) { struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl_orig, slot_ctrl; mutex_lock(&ctrl->ctrl_lock); /* * Always wait for any previous command that might still be in progress */ pcie_wait_cmd(ctrl); pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); if (slot_ctrl == (u16) ~0) { ctrl_info(ctrl, "%s: no response from device\n", __func__); goto out; } slot_ctrl_orig = slot_ctrl; slot_ctrl &= ~mask; slot_ctrl |= (cmd & mask); ctrl->cmd_busy = 1; smp_mb(); ctrl->slot_ctrl = slot_ctrl; pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); ctrl->cmd_started = jiffies; /* * Controllers with the Intel CF118 and similar errata advertise * Command Completed support, but they only set Command Completed * if we change the "Control" bits for power, power indicator, * attention indicator, or interlock. If we only change the * "Enable" bits, they never set the Command Completed bit. */ if (pdev->broken_cmd_compl && (slot_ctrl_orig & CC_ERRATUM_MASK) == (slot_ctrl & CC_ERRATUM_MASK)) ctrl->cmd_busy = 0; /* * Optionally wait for the hardware to be ready for a new command, * indicating completion of the above issued command. */ if (wait) pcie_wait_cmd(ctrl); out: mutex_unlock(&ctrl->ctrl_lock); } /** * pcie_write_cmd - Issue controller command * @ctrl: controller to which the command is issued * @cmd: command value written to slot control register * @mask: bitmask of slot control register to be modified */ static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) { pcie_do_write_cmd(ctrl, cmd, mask, true); } /* Same as above without waiting for the hardware to latch */ static void pcie_write_cmd_nowait(struct controller *ctrl, u16 cmd, u16 mask) { pcie_do_write_cmd(ctrl, cmd, mask, false); } static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout) { int delay = 1; u32 id; /* * After reset, the device should not silently discard config * requests, but it may still indicate that it needs more time by * responding to them with CRS completions. The Root Port will * generally synthesize ~0 data to complete the read (except when * CRS SV is enabled and the read was for the Vendor ID; in that * case it synthesizes 0x0001 data). * * Wait for the device to return a non-CRS completion. Read the * Command register instead of Vendor ID so we don't have to * contend with the CRS SV value. */ pci_read_config_dword(dev, PCI_COMMAND, &id); while (id == ~0) { if (delay > timeout) { pci_warn(dev, "not ready %dms after %s; giving up\n", delay - 1, reset_type); return -ENOTTY; } if (delay > 1000) pci_info(dev, "not ready %dms after %s; waiting\n", delay - 1, reset_type); msleep(delay); delay *= 2; pci_read_config_dword(dev, PCI_COMMAND, &id); } if (delay > 1000) pci_info(dev, "ready %dms after %s\n", delay - 1, reset_type); return 0; } /* Return 1 on successful lock, 0 on contention */ static int pci_dev_trylock(struct pci_dev *dev) { if (pci_cfg_access_trylock(dev)) { if (device_trylock(&dev->dev)) return 1; pci_cfg_access_unlock(dev); } return 0; } static void pci_dev_unlock(struct pci_dev *dev) { device_unlock(&dev->dev); pci_cfg_access_unlock(dev); } static void pci_reset_secondary_bus_910a3(struct pci_dev *master_dev, struct pci_dev *slave_dev) { u16 slave_ctrl; u16 master_ctrl; pci_read_config_word(slave_dev, PCI_BRIDGE_CONTROL, &slave_ctrl); slave_ctrl |= PCI_BRIDGE_CTL_BUS_RESET; pci_write_config_word(slave_dev, PCI_BRIDGE_CONTROL, slave_ctrl); pci_read_config_word(master_dev, PCI_BRIDGE_CONTROL, &master_ctrl); master_ctrl |= PCI_BRIDGE_CTL_BUS_RESET; pci_write_config_word(master_dev, PCI_BRIDGE_CONTROL, master_ctrl); /* * PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms. Double * this to 2ms to ensure that we meet the minimum requirement. */ msleep(2); slave_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; pci_write_config_word(slave_dev, PCI_BRIDGE_CONTROL, slave_ctrl); master_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; pci_write_config_word(master_dev, PCI_BRIDGE_CONTROL, master_ctrl); ssleep(1); } static void pcibios_reset_secondary_bus_910a3(struct pci_dev *master_dev, struct pci_dev *slave_dev) { pci_reset_secondary_bus_910a3(master_dev, slave_dev); } static int pci_bridge_secondary_bus_reset_910a3(struct pci_dev *master_dev, struct pci_dev *slave_dev) { pcibios_reset_secondary_bus_910a3(master_dev, slave_dev); return pci_dev_wait(master_dev, "bus reset", PCIE_RESET_READY_POLL_MS); } static int pci_parent_bus_reset_910a3(struct pci_dev *master_dev, struct pci_dev *slave_dev) { struct pci_dev *pdev; pci_dbg(master_dev, "[%s] master[%s] & slave[%s]\n", __func__, dev_name(&master_dev->dev), dev_name(&slave_dev->dev)); if (pci_is_root_bus(master_dev->bus) || master_dev->subordinate || !master_dev->bus->self || master_dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET) return -ENOTTY; list_for_each_entry(pdev, &master_dev->bus->devices, bus_list) if (pdev != master_dev) return -ENOTTY; if (pci_is_root_bus(slave_dev->bus) || slave_dev->subordinate || !slave_dev->bus->self || slave_dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET) return -ENOTTY; list_for_each_entry(pdev, &slave_dev->bus->devices, bus_list) if (pdev != slave_dev) return -ENOTTY; return pci_bridge_secondary_bus_reset_910a3(master_dev->bus->self, slave_dev->bus->self); } static int pciehp_reset_slot_910a3(struct hotplug_slot *hotplug_master, struct hotplug_slot *hotplug_slave) { struct controller *ctrl0 = to_ctrl(hotplug_master); struct pci_dev *pdev0 = ctrl_dev(ctrl0); struct controller *ctrl1 = to_ctrl(hotplug_slave); struct pci_dev *pdev1 = ctrl_dev(ctrl1); u16 stat_mask0 = 0, ctrl_mask0 = 0; u16 stat_mask1 = 0, ctrl_mask1 = 0; int rc; down_write(&ctrl0->reset_lock); down_write(&ctrl1->reset_lock); if (!ATTN_BUTTN(ctrl0)) { ctrl_mask0 |= PCI_EXP_SLTCTL_PDCE; stat_mask0 |= PCI_EXP_SLTSTA_PDC; } ctrl_mask0 |= PCI_EXP_SLTCTL_DLLSCE; stat_mask0 |= PCI_EXP_SLTSTA_DLLSC; if (!ATTN_BUTTN(ctrl1)) { ctrl_mask1 |= PCI_EXP_SLTCTL_PDCE; stat_mask1 |= PCI_EXP_SLTSTA_PDC; } ctrl_mask1 |= PCI_EXP_SLTCTL_DLLSCE; stat_mask1 |= PCI_EXP_SLTSTA_DLLSC; pcie_write_cmd(ctrl0, 0, ctrl_mask0); ctrl_dbg(ctrl0, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl0->pcie->port) + PCI_EXP_SLTCTL, 0); pcie_write_cmd(ctrl1, 0, ctrl_mask1); ctrl_dbg(ctrl1, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl1->pcie->port) + PCI_EXP_SLTCTL, 0); rc = pci_bridge_secondary_bus_reset_910a3(ctrl0->pcie->port, ctrl1->pcie->port); pcie_capability_write_word(pdev0, PCI_EXP_SLTSTA, stat_mask0); pcie_capability_write_word(pdev1, PCI_EXP_SLTSTA, stat_mask1); pcie_write_cmd_nowait(ctrl0, ctrl_mask0, ctrl_mask0); ctrl_dbg(ctrl0, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl0->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask0); pcie_write_cmd_nowait(ctrl1, ctrl_mask1, ctrl_mask1); ctrl_dbg(ctrl1, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl1->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask1); up_write(&ctrl0->reset_lock); up_write(&ctrl1->reset_lock); return rc; } static int pci_reset_hotplug_slot_910a3(struct hotplug_slot *hotplug_master, struct hotplug_slot *hotplug_slave) { int rc = -ENOTTY; if (!hotplug_master || !try_module_get(hotplug_master->owner) || !hotplug_slave || !try_module_get(hotplug_slave->owner)) return rc; rc = pciehp_reset_slot_910a3(hotplug_master, hotplug_slave); module_put(hotplug_master->owner); module_put(hotplug_slave->owner); return rc; } static int pci_dev_reset_slot_function_910a3(struct pci_dev *master_dev, struct pci_dev *slave_dev) { pci_dbg(master_dev, "[%s] die0[%s] & die1[%s]\n", __func__, dev_name(&master_dev->dev), dev_name(&slave_dev->dev)); if (master_dev->subordinate || !master_dev->slot || master_dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET || slave_dev->subordinate || !slave_dev->slot || slave_dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET) { pci_dbg(master_dev, "[%s] Not support hotplug function.\n", __func__); return -ENOTTY; } return pci_reset_hotplug_slot_910a3(master_dev->slot->hotplug, slave_dev->slot->hotplug); } static int pci_reset_bus_function_910a3(struct pci_dev *master_dev, struct pci_dev *slave_dev) { int rc; rc = pci_dev_reset_slot_function_910a3(master_dev, slave_dev); if (rc != -ENOTTY) return rc; return pci_parent_bus_reset_910a3(master_dev, slave_dev); } static int ascend_910a3_double_device_reset(struct pci_dev *pdev, int probe) { int ret = 0; struct pci_bus *slave_bus = NULL; struct pci_dev *slave_dev = NULL; u8 slot_id; u8 slot_id_master; struct pci_dev *dev = NULL; pci_dbg(pdev, "===>>>[%s] Entering, probe = %d\n", __func__, probe); if (probe) return 0; pci_read_config_byte(pdev, SLOT_ID_REG, &slot_id); if (slot_id >= 0x10) { pci_warn(pdev, "[%s] SLOT_ID is error[slot%d]\n", __func__, slot_id); ret = -ENOTTY; goto out; } /* slave, directly return */ if (slot_id % 2) { pci_dbg(pdev, "[%s] slave[slot%d], directly return\n", __func__, slot_id); return 0; } /* master, need to find slave */ slot_id_master = slot_id; do { dev = pci_get_device(PCI_VENDOR_ID_HUAWEI, PCI_DEVICE_ID_910A3, dev); if (NULL == dev) { break; } else if (dev == pdev) { continue; } pci_read_config_byte(dev, SLOT_ID_REG, &slot_id); if (slot_id == slot_id_master + 1) { /* find slave */ slave_dev = dev; pci_info(pdev, "[%s] Find slave(%s)[slot%d]\n", __func__, dev_name(&slave_dev->dev), slot_id); break; } } while (1); if (NULL == slave_dev) { pci_warn(pdev, "[%s] Can't find slave device, master[slot%d]\n", __func__, slot_id_master); ret = -ENOTTY; goto out; } if (!pci_dev_trylock(slave_dev)) { pci_warn(slave_dev, "[%s] pci_dev_trylock error\n", __func__); ret = -ENOTTY; goto out; } slave_bus = slave_dev->bus; ret = pci_save_state(slave_bus->self); if (ret) { pci_err(slave_bus->self, "[%s] Save slave bus device state error: %d\n", __func__, ret); goto out_lock; } ret = pci_save_state(slave_dev); if (ret) { pci_err(slave_dev, "[%s] Save slave device state error: %d\n", __func__, ret); goto out_lock; } /* bus reset */ ret = pci_reset_bus_function_910a3(pdev, slave_dev); if (ret) { pci_err(pdev, "[%s] bus reset error: %d\n", __func__, ret); } pci_restore_state(slave_bus->self); pci_restore_state(slave_dev); out_lock: pci_dev_unlock(slave_dev); out: if (slave_dev) { pci_dev_put(slave_dev); } pci_dbg(pdev, "<<<===[%s] Exiting, probe = %d\n", __func__, probe); return ret; } // 在pci_dev_reset_methods[] 全局变量的{ 0 }前面, 插入如下代码 { PCI_VENDOR_ID_HUAWEI, PCI_DEVICE_ID_910A3, ascend_910a3_double_device_reset },