From: Anders Berg <anders.b...@lsi.com>

Add support for PCIe MSI on both controllers. On AXM5516, PCIE0 has the ability
to signal MSI interrupts on 16 separate lines to the CPU cores, where as PCIE1
only has a single interrupt line that is used for legacy, status/error and MSI.
This patch adds MSI support on a controller with only one interrupt line.

Signed-off-by: Anders Berg <anders.b...@lsi.com>
---
 arch/arm/mach-axxia/pci.c | 67 +++++++++++++++++++++++++++++------------------
 1 file changed, 41 insertions(+), 26 deletions(-)

diff --git a/arch/arm/mach-axxia/pci.c b/arch/arm/mach-axxia/pci.c
index 65e2f39..297a47b 100644
--- a/arch/arm/mach-axxia/pci.c
+++ b/arch/arm/mach-axxia/pci.c
@@ -40,6 +40,7 @@
 #include <asm-generic/errno-base.h>
 #include <mach/pci.h>
 
+
 #define PCIE_CONFIG              (0x1000)
 #define PCIE_STATUS              (0x1004)
 #define PCIE_CORE_DEBUG          (0x1008)
@@ -144,6 +145,7 @@ struct axxia_pciex_port {
 #define PCIE_MAX_PORTS 2
 static struct axxia_pciex_port *axxia_pciex_ports;
 
+static void pcie_msi_dispatch(u32 group, struct axxia_pciex_port *port);
 
 static void
 fixup_axxia_pci_bridge(struct pci_dev *dev)
@@ -499,6 +501,14 @@ pcie_legacy_isr(int irq, void *arg)
                        (void) readl(port->regs + PCIE_MSG_IN_FIFO);
                /* Next handler in chain will service this interrupt */
                retval = IRQ_NONE;
+       } else if (intr_status & INT0_MSI) {
+               u32 msi_status = readl(port->regs + PCIE_MSI0_STATUS);
+               if (msi_status == 0) {
+                       retval = IRQ_NONE;
+               } else {
+                       u32 group = ffs(msi_status) - 1;
+                       pcie_msi_dispatch(group, port);
+               }
        }
 
        if (intr1_status & INT1_DOORBELL) {
@@ -521,7 +531,7 @@ pcie_legacy_isr(int irq, void *arg)
 }
 
 /*
- * pcie_msi_irq_handler
+ * MSI handler
  *
  * This is the handler for PCIE MSI service. It will decode the signalled MSI
  * using the following hierarchy of status bits. This handler is installed as a
@@ -559,38 +569,40 @@ pcie_legacy_isr(int irq, void *arg)
  *                                                   +----------+
  */
 static void
-pcie_msi_irq_handler(int irq, struct axxia_pciex_port *port)
+pcie_msi_dispatch(u32 group, struct axxia_pciex_port *port)
 {
-       void __iomem *mbase = port->regs;
-       u32 group = irq - port->irq[1];
        u32 status;
 
-       /* Check if interrupt is pending */
-       status = readl(mbase + PCIE_MSI0_STATUS);
-       if (!(status & (1<<group)))
-               return;
-
        /* Check next level interrupt status */
-       status = readl(mbase + PCIE_MSI1_STATUS(group)) & 0xffff;
+       status = readl(port->regs + PCIE_MSI1_STATUS(group)) & 0xffff;
        while (status) {
                u32 line = ffs(status) - 1;
-               status &= ~(1<<line);
+               status &= ~(1 << line);
                /* Clear interrupt on sub-level */
-               writel((1<<line), mbase + PCIE_MSI1_STATUS(group));
+               writel((1 << line), port->regs + PCIE_MSI1_STATUS(group));
                generic_handle_irq(AXXIA_MSI_FIRST + (group * 16) + line);
        }
 
        /* Clear interrupt on top-level*/
-       writel(1<<group, mbase + PCIE_MSI0_STATUS);
-
+       writel(1 << group, port->regs + PCIE_MSI0_STATUS);
 }
 
 static void
-pcie_pei0_msi_handler(unsigned int irq, struct irq_desc *desc)
+pcie_msi_irq_handler(unsigned int irq, struct irq_desc *desc)
 {
-       kstat_incr_irqs_this_cpu(irq, desc);
-       /* Handle the PCIe interrupt */
-       pcie_msi_irq_handler(irq, &axxia_pciex_ports[0]);
+       struct axxia_pciex_port *port = &axxia_pciex_ports[0];
+       u32 group = irq - port->irq[1];
+       u32 status;
+
+       /* Check if interrupt is pending */
+       status = readl(port->regs + PCIE_MSI0_STATUS);
+       if (status & (1 << group)) {
+               kstat_incr_irqs_this_cpu(irq, desc);
+               /* Handle the PCIe interrupt */
+               pcie_msi_dispatch(group, port);
+       } else {
+               handle_bad_irq(irq, desc);
+       }
        /* Signal end-of-interrupt */
        irq_desc_get_chip(desc)->irq_eoi(&desc->irq_data);
 }
@@ -656,13 +668,12 @@ static int axxia_pcie_setup(int portno, struct 
pci_sys_data *sys)
                goto fail;
        }
 
-       /* MSI interrupts for PEI0 */
-       if (sys->domain == 0) {
-               for (i = 1; i <= 16; i++) {
-                       port->irq[i] = irq_of_parse_and_map(port->node, i);
-                       irq_set_chained_handler(port->irq[i],
-                                               pcie_pei0_msi_handler);
-               }
+       /* MSI interrupts */
+       for (i = 1; i <= 16; i++) {
+               port->irq[i] = irq_of_parse_and_map(port->node, i);
+               if (!port->irq[i])
+                       break;
+               irq_set_chained_handler(port->irq[i], pcie_msi_irq_handler);
        }
 
        /* Setup as root complex */
@@ -1050,7 +1061,11 @@ again:
 
        /* Initialize IRQ descriptor */
        dynamic_irq_init(irq);
-       irq_set_msi_desc(irq, desc);
+       if (irq_set_msi_desc(irq, desc) != 0) {
+               dev_err(&pdev->dev, "Bad IRQ descriptor for IRQ%d\n", irq);
+               clear_bit(pos, msi_irq_in_use);
+               return -EINVAL;
+       }
        /* Use a simple handle for our "SW" MSI IRQs */
        irq_set_chip_and_handler(irq, &axxia_msi_chip, handle_simple_irq);
        set_irq_flags(irq, IRQF_VALID);
-- 
1.8.1.4

-- 
_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to