[PATCH 3/3] powerpc/powernv: Patch MSI EOI handler on P8
Gavin Shan
shangw at linux.vnet.ibm.com
Fri Apr 19 19:32:45 EST 2013
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 <shangw at 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
More information about the Linuxppc-dev
mailing list