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/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 | 33 ++++++++++++++++++++++++++++- 4 files changed, 85 insertions(+), 1 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index d8d5baa..de4a4a9 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -645,6 +645,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 bmap_size; @@ -667,6 +698,8 @@ static void pnv_pci_init_ioda_msis(struct pnv_phb *phb) return; } 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", phb->msi_count, phb->msi_base); diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index e088dc7..439dfc5 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -148,6 +148,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_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 fcd5135..4ae015b 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -101,6 +101,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..289355e 100644 --- a/arch/powerpc/sysdev/xics/icp-native.c +++ b/arch/powerpc/sysdev/xics/icp-native.c @@ -27,6 +27,10 @@ #include <asm/xics.h> #include <asm/kvm_ppc.h> +#if defined(CONFIG_PPC_POWERNV) && defined(CONFIG_PCI_MSI) +extern int pnv_pci_msi_eoi(unsigned int hw_irq); +#endif + struct icp_ipl { union { u32 word; @@ -89,6 +93,24 @@ 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 defined(CONFIG_PPC_POWERNV) && defined(CONFIG_PCI_MSI) + if (hw_irq != XICS_IPI) { + ret = pnv_pci_msi_eoi(hw_irq); + WARN_ON_ONCE(ret); + } +#endif + + /* 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 +286,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 +318,15 @@ int __init icp_native_init(void) if (found == 0) return -ENODEV; + /* Change the EOI handler for P8 */ +#if defined(CONFIG_PPC_POWERNV) && defined(CONFIG_PCI_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