forked from Minki/linux
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: 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>
This commit is contained in:
parent
d0c424971f
commit
454593e54c
@ -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
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user