pci-hotplug/pnv_php: Add a reset_slot() callback
authorOliver O'Halloran <oohall@gmail.com>
Tue, 3 Sep 2019 10:15:59 +0000 (20:15 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Thu, 5 Sep 2019 04:22:39 +0000 (14:22 +1000)
When performing EEH recovery of devices in a hotplug slot we need to use
the slot driver's ->reset_slot() callback to prevent spurious hotplug
events due to spurious DLActive and PresDet change interrupts. Add a
reset_slot() callback to pnv_php so we can handle recovery of devices
in pnv_php managed slots.

Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20190903101605.2890-9-oohall@gmail.com
drivers/pci/hotplug/pnv_php.c

index 6758fd7c382e351600aa8f5cdd35be2b9544ae3c..b0e243dabf77356b183fdbcb593d9b673b4625de 100644 (file)
@@ -511,6 +511,37 @@ scan:
        return 0;
 }
 
+static int pnv_php_reset_slot(struct hotplug_slot *slot, int probe)
+{
+       struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
+       struct pci_dev *bridge = php_slot->pdev;
+       uint16_t sts;
+
+       /*
+        * The CAPI folks want pnv_php to drive OpenCAPI slots
+        * which don't have a bridge. Only claim to support
+        * reset_slot() if we have a bridge device (for now...)
+        */
+       if (probe)
+               return !bridge;
+
+       /* mask our interrupt while resetting the bridge */
+       if (php_slot->irq > 0)
+               disable_irq(php_slot->irq);
+
+       pci_bridge_secondary_bus_reset(bridge);
+
+       /* clear any state changes that happened due to the reset */
+       pcie_capability_read_word(php_slot->pdev, PCI_EXP_SLTSTA, &sts);
+       sts &= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
+       pcie_capability_write_word(php_slot->pdev, PCI_EXP_SLTSTA, sts);
+
+       if (php_slot->irq > 0)
+               enable_irq(php_slot->irq);
+
+       return 0;
+}
+
 static int pnv_php_enable_slot(struct hotplug_slot *slot)
 {
        struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
@@ -548,6 +579,7 @@ static const struct hotplug_slot_ops php_slot_ops = {
        .set_attention_status   = pnv_php_set_attention_state,
        .enable_slot            = pnv_php_enable_slot,
        .disable_slot           = pnv_php_disable_slot,
+       .reset_slot             = pnv_php_reset_slot,
 };
 
 static void pnv_php_release(struct pnv_php_slot *php_slot)
@@ -721,6 +753,12 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data)
        pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts);
        sts &= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
        pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts);
+
+       pci_dbg(pdev, "PCI slot [%s]: HP int! DLAct: %d, PresDet: %d\n",
+                       php_slot->name,
+                       !!(sts & PCI_EXP_SLTSTA_DLLSC),
+                       !!(sts & PCI_EXP_SLTSTA_PDC));
+
        if (sts & PCI_EXP_SLTSTA_DLLSC) {
                pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts);
                added = !!(lsts & PCI_EXP_LNKSTA_DLLLA);
@@ -735,6 +773,7 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data)
 
                added = !!(presence == OPAL_PCI_SLOT_PRESENT);
        } else {
+               pci_dbg(pdev, "PCI slot [%s]: Spurious IRQ?\n", php_slot->name);
                return IRQ_NONE;
        }