Message ID | 1491481904-19168-1-git-send-email-tim.gardner@canonical.com |
---|---|
State | New |
Headers | show |
On 06/04/17 13:31, Tim Gardner wrote: > From: Gavin Shan <gwshan@linux.vnet.ibm.com> > > BugLink: http://bugs.launchpad.net/bugs/1680328 > > 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> > (cherry picked from commit 454593e54cac126a0f49d4b65f90a5e518c51404) > Signed-off-by: Tim Gardner <tim.gardner@canonical.com> > --- > arch/powerpc/include/asm/pnv-pci.h | 2 ++ > drivers/pci/hotplug/pnv_php.c | 27 ++++++++++++++++++++++----- > 2 files changed, 24 insertions(+), 5 deletions(-) > > diff --git a/arch/powerpc/include/asm/pnv-pci.h b/arch/powerpc/include/asm/pnv-pci.h > index 696438f..de96810 100644 > --- a/arch/powerpc/include/asm/pnv-pci.h > +++ b/arch/powerpc/include/asm/pnv-pci.h > @@ -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 > diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c > index e96973b..335a556 100644 > --- a/drivers/pci/hotplug/pnv_php.c > +++ b/drivers/pci/hotplug/pnv_php.c > @@ -723,7 +723,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", > @@ -774,6 +775,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; > > @@ -785,9 +787,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 */ > @@ -801,9 +812,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 */ > Clean upstream cherry pick. Looks sane to me. Acked-by: Colin Ian King <colin.king@canonical.com>
diff --git a/arch/powerpc/include/asm/pnv-pci.h b/arch/powerpc/include/asm/pnv-pci.h index 696438f..de96810 100644 --- a/arch/powerpc/include/asm/pnv-pci.h +++ b/arch/powerpc/include/asm/pnv-pci.h @@ -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 diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index e96973b..335a556 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c @@ -723,7 +723,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", @@ -774,6 +775,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; @@ -785,9 +787,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 */ @@ -801,9 +812,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 */