昇腾社区首页
中文
注册

修改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 },