Commit 454593e5 authored by Gavin Shan's avatar Gavin Shan Committed by Michael Ellerman

drivers/pci/hotplug: Mask PDC interrupt if required

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: default avatarHank Chang <hankmax0000@gmail.com>
Signed-off-by: default avatarGavin Shan <gwshan@linux.vnet.ibm.com>
Tested-by: default avatarWillie Liauw <williel@supermicro.com.tw>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent d0c42497
...@@ -57,6 +57,8 @@ struct pnv_php_slot { ...@@ -57,6 +57,8 @@ struct pnv_php_slot {
uint64_t id; uint64_t id;
char *name; char *name;
int slot_no; int slot_no;
unsigned int flags;
#define PNV_PHP_FLAG_BROKEN_PDC 0x1
struct kref kref; struct kref kref;
#define PNV_PHP_STATE_INITIALIZED 0 #define PNV_PHP_STATE_INITIALIZED 0
#define PNV_PHP_STATE_REGISTERED 1 #define PNV_PHP_STATE_REGISTERED 1
......
...@@ -717,7 +717,8 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data) ...@@ -717,7 +717,8 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data)
if (sts & PCI_EXP_SLTSTA_DLLSC) { if (sts & PCI_EXP_SLTSTA_DLLSC) {
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts); pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts);
added = !!(lsts & PCI_EXP_LNKSTA_DLLLA); 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); ret = pnv_pci_get_presence_state(php_slot->id, &presence);
if (ret) { if (ret) {
dev_warn(&pdev->dev, "PCI slot [%s] error %d getting presence (0x%04x), to retry the operation.\n", 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) ...@@ -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) static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
{ {
struct pci_dev *pdev = php_slot->pdev; struct pci_dev *pdev = php_slot->pdev;
u32 broken_pdc = 0;
u16 sts, ctrl; u16 sts, ctrl;
int ret; int ret;
...@@ -779,9 +781,18 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) ...@@ -779,9 +781,18 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
return; 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 */ /* Clear pending interrupts */
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts); 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); pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts);
/* Request the interrupt */ /* Request the interrupt */
...@@ -795,9 +806,15 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) ...@@ -795,9 +806,15 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
/* Enable the interrupts */ /* Enable the interrupts */
pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl); pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl);
ctrl |= (PCI_EXP_SLTCTL_HPIE | if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) {
PCI_EXP_SLTCTL_PDCE | ctrl &= ~PCI_EXP_SLTCTL_PDCE;
PCI_EXP_SLTCTL_DLLSCE); 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); pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl);
/* The interrupt is initialized successfully when @irq is valid */ /* The interrupt is initialized successfully when @irq is valid */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment