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

Reply via email to