]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
PCI/DPC: Wait for Root Port busy to clear
authorKeith Busch <keith.busch@intel.com>
Fri, 3 Feb 2017 21:46:13 +0000 (16:46 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 10 Feb 2017 20:36:14 +0000 (14:36 -0600)
Per PCIe r3.1, sec 6.2.10 and sec 7.13.4, on Root Ports that support "RP
Extensions for DPC",

  When the DPC Trigger Status bit is Set and the DPC RP Busy bit is Set,
  software must leave the Root Port in DPC until the DPC RP Busy bit reads
  0b.

Wait up to 1 second for the Root Port to become non-busy.

[bhelgaas: changelog, spec references]
Signed-off-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/pcie/pcie-dpc.c
include/uapi/linux/pci_regs.h

index 5a261fd4f03df1d6a051f94147011a6a4e012018..d4d70ef4a2d7a9f844a0e7e72e5a532c116d5137 100644 (file)
@@ -19,8 +19,28 @@ struct dpc_dev {
        struct pcie_device      *dev;
        struct work_struct      work;
        int                     cap_pos;
+       bool                    rp;
 };
 
+static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
+{
+       unsigned long timeout = jiffies + HZ;
+       struct pci_dev *pdev = dpc->dev->port;
+       u16 status;
+
+       pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status);
+       while (status & PCI_EXP_DPC_RP_BUSY &&
+                                       !time_after(jiffies, timeout)) {
+               msleep(10);
+               pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status);
+       }
+       if (status & PCI_EXP_DPC_RP_BUSY) {
+               dev_warn(&pdev->dev, "DPC root port still busy\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
 static void dpc_wait_link_inactive(struct pci_dev *pdev)
 {
        unsigned long timeout = jiffies + HZ;
@@ -33,7 +53,7 @@ static void dpc_wait_link_inactive(struct pci_dev *pdev)
                pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
        }
        if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
-               dev_warn(&pdev->dev, "Link state not disabled for DPC event");
+               dev_warn(&pdev->dev, "Link state not disabled for DPC event\n");
 }
 
 static void interrupt_event_handler(struct work_struct *work)
@@ -52,6 +72,8 @@ static void interrupt_event_handler(struct work_struct *work)
        pci_unlock_rescan_remove();
 
        dpc_wait_link_inactive(pdev);
+       if (dpc->rp && dpc_wait_rp_inactive(dpc))
+               return;
        pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS,
                PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
 }
@@ -115,6 +137,8 @@ static int dpc_probe(struct pcie_device *dev)
        pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
        pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
 
+       dpc->rp = (cap & PCI_EXP_DPC_CAP_RP_EXT);
+
        ctl |= PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN;
        pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
 
index 174d1147081bccf618e851a85e20ff79895bfd00..c1b94b04479593d0bb61681ea3ba51dbc02851ba 100644 (file)
 #define PCI_EXP_DPC_STATUS             8       /* DPC Status */
 #define  PCI_EXP_DPC_STATUS_TRIGGER    0x01    /* Trigger Status */
 #define  PCI_EXP_DPC_STATUS_INTERRUPT  0x08    /* Interrupt Status */
+#define  PCI_EXP_DPC_RP_BUSY           0x10    /* Root Port Busy */
 
 #define PCI_EXP_DPC_SOURCE_ID          10      /* DPC Source Identifier */