drivers/pci/hotplug: Mask PDC interrupt if required
authorGavin Shan <gwshan@linux.vnet.ibm.com>
Wed, 11 Jan 2017 00:50:08 +0000 (11:50 +1100)
committerMichael Ellerman <mpe@ellerman.id.au>
Wed, 15 Feb 2017 09:02:43 +0000 (20:02 +1100)
We're supporting surprise hotplug on PCI slots behind root port
or PCIe switch downstream ports, which don't claim the capability
in hardware register (offset: PCIe cap + PCI_EXP_SLTCAP). PEX8718
is one of the examples. For those PCI slots, the PDC (Presence
Detection Change) event isn't reliable and the underly (skiboot)
firmware has best judgement.

This masks the PDC event when skiboot requests by "ibm,slot-broken-pdc"
property in PCI slot's device-tree node.

Reported-by: Hank Chang <hankmax0000@gmail.com>
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Tested-by: Willie Liauw <williel@supermicro.com.tw>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/pnv-pci.h
drivers/pci/hotplug/pnv_php.c

index 696438f09aeab1399683cd8e504ddb317b092caf..de9681034353c661d3e0a6299c3c6b7c3573b832 100644 (file)
@@ -57,6 +57,8 @@ struct pnv_php_slot {
        uint64_t                        id;
        char                            *name;
        int                             slot_no;
+       unsigned int                    flags;
+#define PNV_PHP_FLAG_BROKEN_PDC                0x1
        struct kref                     kref;
 #define PNV_PHP_STATE_INITIALIZED      0
 #define PNV_PHP_STATE_REGISTERED       1
index 41304b313512baaa0393a2f6b3ad0afbcf57b6a2..63cd9f354b799642b9e5771cb801fe00f46489af 100644 (file)
@@ -717,7 +717,8 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data)
        if (sts & PCI_EXP_SLTSTA_DLLSC) {
                pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts);
                added = !!(lsts & PCI_EXP_LNKSTA_DLLLA);
-       } else if (sts & PCI_EXP_SLTSTA_PDC) {
+       } else if (!(php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) &&
+                  (sts & PCI_EXP_SLTSTA_PDC)) {
                ret = pnv_pci_get_presence_state(php_slot->id, &presence);
                if (ret) {
                        dev_warn(&pdev->dev, "PCI slot [%s] error %d getting presence (0x%04x), to retry the operation.\n",
@@ -768,6 +769,7 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data)
 static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
 {
        struct pci_dev *pdev = php_slot->pdev;
+       u32 broken_pdc = 0;
        u16 sts, ctrl;
        int ret;
 
@@ -779,9 +781,18 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
                return;
        }
 
+       /* Check PDC (Presence Detection Change) is broken or not */
+       ret = of_property_read_u32(php_slot->dn, "ibm,slot-broken-pdc",
+                                  &broken_pdc);
+       if (!ret && broken_pdc)
+               php_slot->flags |= PNV_PHP_FLAG_BROKEN_PDC;
+
        /* Clear pending interrupts */
        pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts);
-       sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
+       if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC)
+               sts |= PCI_EXP_SLTSTA_DLLSC;
+       else
+               sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
        pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts);
 
        /* Request the interrupt */
@@ -795,9 +806,15 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
 
        /* Enable the interrupts */
        pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl);
-       ctrl |= (PCI_EXP_SLTCTL_HPIE |
-                PCI_EXP_SLTCTL_PDCE |
-                PCI_EXP_SLTCTL_DLLSCE);
+       if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) {
+               ctrl &= ~PCI_EXP_SLTCTL_PDCE;
+               ctrl |= (PCI_EXP_SLTCTL_HPIE |
+                        PCI_EXP_SLTCTL_DLLSCE);
+       } else {
+               ctrl |= (PCI_EXP_SLTCTL_HPIE |
+                        PCI_EXP_SLTCTL_PDCE |
+                        PCI_EXP_SLTCTL_DLLSCE);
+       }
        pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl);
 
        /* The interrupt is initialized successfully when @irq is valid */