The EOI handler of MSI/MSI-X interrupts for P8 (PHB3) need additional steps to handle the P/Q bits in IVE before EOIing the corresponding interrupt. The patch changes the EOI handler to cover that.
Signed-off-by: Gavin Shan <sha...@linux.vnet.ibm.com> --- arch/powerpc/include/asm/xics.h | 3 ++ arch/powerpc/platforms/powernv/pci-ioda.c | 33 +++++++++++++++++++++++++++++ arch/powerpc/platforms/powernv/pci.c | 19 ++++++++++++++++ arch/powerpc/platforms/powernv/pci.h | 1 + arch/powerpc/sysdev/xics/icp-native.c | 27 ++++++++++++++++++++++- 5 files changed, 82 insertions(+), 1 deletions(-) diff --git a/arch/powerpc/include/asm/xics.h b/arch/powerpc/include/asm/xics.h index 4ae9a09..c4b364b 100644 --- a/arch/powerpc/include/asm/xics.h +++ b/arch/powerpc/include/asm/xics.h @@ -72,6 +72,9 @@ extern int ics_opal_init(void); static inline int ics_opal_init(void) { return -ENODEV; } #endif +/* Extra EOI handler for PHB3 */ +extern int pnv_pci_msi_eoi(unsigned int hw_irq); + /* ICS instance, hooked up to chip_data of an irq */ struct ics { struct list_head link; diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 0c15870..8ec77a7 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -646,6 +646,37 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, return 0; } +static int pnv_pci_ioda_msi_eoi(struct pnv_phb *phb, unsigned int hw_irq) +{ + u8 p_bit = 1, q_bit = 1; + long rc; + + while (p_bit || q_bit) { + rc = opal_pci_get_xive_reissue(phb->opal_id, + hw_irq - phb->msi_base, &p_bit, &q_bit); + if (rc) { + pr_warning("%s: Failed to get P/Q bits of IRQ#%d " + "on PHB#%d, rc=%ld\n", __func__, hw_irq, + phb->hose->global_number, rc); + return -EIO; + } + if (!p_bit && !q_bit) + break; + + rc = opal_pci_set_xive_reissue(phb->opal_id, + hw_irq - phb->msi_base, p_bit, q_bit); + if (rc) { + pr_warning("%s: Failed to clear P/Q (%01d/%01d) of " + "IRQ#%d on PHB#%d, rc=%ld\n", __func__, + p_bit, q_bit, hw_irq, + phb->hose->global_number, rc); + return -EIO; + } + } + + return 0; +} + static void pnv_pci_init_ioda_msis(struct pnv_phb *phb) { unsigned int count; @@ -667,6 +698,8 @@ static void pnv_pci_init_ioda_msis(struct pnv_phb *phb) } phb->msi_setup = pnv_pci_ioda_msi_setup; + if (phb->type == PNV_PHB_IODA2) + phb->msi_eoi = pnv_pci_ioda_msi_eoi; phb->msi32_support = 1; pr_info(" Allocated bitmap for %d MSIs (base IRQ 0x%x)\n", count, phb->msi_base); diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 83514dc..1a03f42 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -115,6 +115,25 @@ static void pnv_teardown_msi_irqs(struct pci_dev *pdev) irq_dispose_mapping(entry->irq); } } + +int pnv_pci_msi_eoi(unsigned int hw_irq) +{ + struct pci_controller *hose, *tmp; + struct pnv_phb *phb = NULL; + + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { + phb = hose->private_data; + if (hw_irq >= phb->msi_base && + hw_irq < phb->msi_base + phb->msi_bmp.irq_count) { + if (!phb->msi_eoi) + return -EEXIST; + return phb->msi_eoi(phb, hw_irq); + } + } + + /* For LSI interrupts, we needn't do it */ + return 0; +} #endif /* CONFIG_PCI_MSI */ static void pnv_pci_dump_p7ioc_diag_data(struct pnv_phb *phb) diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index c048c29..c6690b3 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -81,6 +81,7 @@ struct pnv_phb { int (*msi_setup)(struct pnv_phb *phb, struct pci_dev *dev, unsigned int hwirq, unsigned int is_64, struct msi_msg *msg); + int (*msi_eoi)(struct pnv_phb *phb, unsigned int hw_irq); void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev); void (*fixup_phb)(struct pci_controller *hose); u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn); diff --git a/arch/powerpc/sysdev/xics/icp-native.c b/arch/powerpc/sysdev/xics/icp-native.c index 48861d3..38dd2b1 100644 --- a/arch/powerpc/sysdev/xics/icp-native.c +++ b/arch/powerpc/sysdev/xics/icp-native.c @@ -89,6 +89,22 @@ static void icp_native_eoi(struct irq_data *d) icp_native_set_xirr((xics_pop_cppr() << 24) | hw_irq); } +static void icp_p8_native_eoi(struct irq_data *d) +{ + unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); + int ret; + + /* Let firmware handle P/Q bits */ + if (hw_irq != XICS_IPI) { + ret = pnv_pci_msi_eoi(hw_irq); + WARN_ON_ONCE(ret); + } + + /* EOI on ICP */ + iosync(); + icp_native_set_xirr((xics_pop_cppr() << 24) | hw_irq); +} + static void icp_native_teardown_cpu(void) { int cpu = smp_processor_id(); @@ -264,7 +280,7 @@ static int __init icp_native_init_one_node(struct device_node *np, return 0; } -static const struct icp_ops icp_native_ops = { +static struct icp_ops icp_native_ops = { .get_irq = icp_native_get_irq, .eoi = icp_native_eoi, .set_priority = icp_native_set_cpu_priority, @@ -296,6 +312,15 @@ int __init icp_native_init(void) if (found == 0) return -ENODEV; + /* Change the EOI handler for P8 */ +#ifdef CONFIG_POWERNV_MSI + np = of_find_compatible_node(NULL, NULL, "ibm,power8-xicp"); + if (np) { + icp_native_ops.eoi = icp_p8_native_eoi; + of_node_put(np); + } +#endif + icp_ops = &icp_native_ops; return 0; -- 1.7.5.4 _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev