From: SangeethaRao <sangeetha....@lsi.com>

Signed-off-by: SangeethaRao <sangeetha....@lsi.com>
---
 arch/powerpc/boot/dts/acp35xx.dts |  34 +++-
 arch/powerpc/sysdev/Kconfig       |  11 +-
 arch/powerpc/sysdev/Makefile      |   5 +-
 arch/powerpc/sysdev/lsi_msi.c     | 389 ++++++++++++++++++++++++++++++++++++++
 arch/powerpc/sysdev/lsi_msi.h     |  21 ++
 arch/powerpc/sysdev/lsi_pci.c     |  52 +++--
 6 files changed, 488 insertions(+), 24 deletions(-)
 create mode 100644 arch/powerpc/sysdev/lsi_msi.c
 create mode 100644 arch/powerpc/sysdev/lsi_msi.h

diff --git a/arch/powerpc/boot/dts/acp35xx.dts 
b/arch/powerpc/boot/dts/acp35xx.dts
index ec4fc75..1a5a33e 100644
--- a/arch/powerpc/boot/dts/acp35xx.dts
+++ b/arch/powerpc/boot/dts/acp35xx.dts
@@ -307,6 +307,39 @@
                                sbb-reg = <0x00500000 0x8000>;
                                tzc-reg = <0x00541000 0x1000>;
                        };
+                       MSI0: msi0 {
+                               compatible = "lsi,msi";
+                               device_type = "lsi_msi";
+                               status = "ok";
+                               enabled = <0x1>;
+                               port = <0>;
+                               #address-cells = <2>;
+                               #size-cells = <1>;
+                               /* config space access MPAGE7 registers*/
+                               reg = <0x580000 0x2000>;
+                       };
+                       MSI1: msi1 {
+                               compatible = "lsi,msi";
+                               device_type = "lsi_msi";
+                               status = "ok";
+                               enabled = <0x1>;
+                               port = <1>;
+                               #address-cells = <2>;
+                               #size-cells = <1>;
+                               /* config space access MPAGE7 registers*/
+                               reg = <0x588000 0x2000>;
+                       };
+                       MSI2: msi2 {
+                               compatible = "lsi,msi";
+                               device_type = "lsi_msi";
+                               status = "ok";
+                               enabled = <0x1>;
+                               port = <2>;
+                               #address-cells = <2>;
+                               #size-cells = <1>;
+                               /* config space access MPAGE7 registers*/
+                               reg = <0x590000 0x2000>;
+                       };
                };
        };
 
@@ -361,7 +394,6 @@
                         0000 0 0 4 &MPIC 52
                 >;
         };
-
         PCIE1: pei1 {
                 compatible = "lsi,plb-pciex";
                 device_type = "pci";
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
index 1ea267b..ded32ef 100644
--- a/arch/powerpc/sysdev/Kconfig
+++ b/arch/powerpc/sysdev/Kconfig
@@ -3,10 +3,17 @@
 #
 
 config LSI_PPC_PCI
-       bool "LSI ACP34XX PCIe support"
+       bool "LSI ACP34XX/AXM35XX PCIe support"
        default y
         help
-          PCIe driver support for LSI's ACP34xx
+          PCIe driver support for LSI's ACP34xx/AXM35xx
+
+config LSI_PPC_PCI_MSI
+       bool "LSI ACP34XX/AXM35XX PCIe MSI support"
+       depends on PCI_MSI
+       default y
+        help
+          PCIe MSI driver support for LSI's ACP34xx/AXM35xx
 
 config PPC4xx_PCI_EXPRESS
        bool
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index b46b432..63dea41 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -1,4 +1,4 @@
-subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
+#subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
 
 ccflags-$(CONFIG_PPC64)                := -mno-minimal-toc
 
@@ -41,6 +41,7 @@ obj-$(CONFIG_4xx_SOC)         += ppc4xx_soc.o
 obj-$(CONFIG_XILINX_VIRTEX)    += xilinx_intc.o
 obj-$(CONFIG_XILINX_PCI)       += xilinx_pci.o
 obj-$(CONFIG_LSI_PPC_PCI)      += lsi_pci.o
+obj-$(CONFIG_LSI_PPC_PCI_MSI)  += lsi_msi.o
 obj-$(CONFIG_OF_RTC)           += of_rtc.o
 ifeq ($(CONFIG_PCI),y)
 ifeq ($(CONFIG_LSI_PCI),n)
@@ -67,7 +68,7 @@ endif
 
 obj-$(CONFIG_PPC_SCOM)         += scom.o
 
-subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
+#subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
 
 obj-$(CONFIG_PPC_XICS)         += xics/
 
diff --git a/arch/powerpc/sysdev/lsi_msi.c b/arch/powerpc/sysdev/lsi_msi.c
new file mode 100644
index 0000000..f89ba7a
--- /dev/null
+++ b/arch/powerpc/sysdev/lsi_msi.c
@@ -0,0 +1,389 @@
+/*
+* LSI specific PCI-Express MSI support;
+*/
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/bootmem.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+
+#include <linux/io.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
+#include <mm/mmu_decl.h>
+
+#include "../../drivers/misc/lsi-ncr.h"
+#include "lsi_msi.h"
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/bitmap.h>
+#include <linux/msi.h>
+#include <linux/of_platform.h>
+#include <asm/prom.h>
+#include <asm/hw_irq.h>
+#include <asm/ppc-pci.h>
+
+#define MAX_NUMBER_PCI_SLOTS 3
+static struct acp_pci_msi *acp_pci_msi[MAX_NUMBER_PCI_SLOTS];
+
+static void acp_pci_msi_end_irq(struct irq_data *d)
+{
+}
+
+static struct irq_chip acp_pci_msi_chip = {
+       .irq_mask       = mask_msi_irq,
+       .irq_unmask     = unmask_msi_irq,
+       .irq_ack        = acp_pci_msi_end_irq,
+       .name   = " ACP-MSI  ",
+};
+
+static int acp_pci_msi_host_map(struct irq_domain *h, unsigned int virq,
+       irq_hw_number_t hw)
+{
+       struct irq_chip *chip = &acp_pci_msi_chip;
+       irq_set_status_flags(virq, IRQ_TYPE_EDGE_FALLING);
+       irq_set_chip_and_handler(virq, chip, handle_edge_irq);
+       return 0;
+}
+
+static struct irq_domain_ops acp_pci_msi_host_ops = {
+       .map = acp_pci_msi_host_map,
+};
+
+static void acp_pci_msi_free_hwirqs(struct acp_pci_msi *msi, int offset,
+       int num)
+{
+       unsigned long flags;
+       int order = get_count_order(num);
+
+       pr_debug("%s: freeing 0x%x (2^%d) at offset 0x%x\n",
+               __func__, num, order, offset);
+
+       spin_lock_irqsave(&msi->bitmap_lock, flags);
+       bitmap_release_region(msi->acp_pci_msi_bitmap, offset, order);
+       spin_unlock_irqrestore(&msi->bitmap_lock, flags);
+}
+
+static int acp_pci_msi_free_dt_hwirqs(struct acp_pci_msi *msi_data)
+{
+       bitmap_allocate_region(msi_data->acp_pci_msi_bitmap, 0,
+               get_count_order(NR_MSI_IRQS));
+
+       acp_pci_msi_free_hwirqs(msi_data, 0, 0x100);
+       return 0;
+}
+
+static int acp_pci_msi_init_allocator(struct acp_pci_msi *msi_data)
+{
+       int rc;
+       int size = BITS_TO_LONGS(NR_MSI_IRQS) * sizeof(u32);
+
+       msi_data->acp_pci_msi_bitmap = kzalloc(size, GFP_KERNEL);
+       if (msi_data->acp_pci_msi_bitmap == NULL) {
+               pr_debug("%s: out of memory at line %d\n",
+                       __func__, __LINE__);
+               return -ENOMEM;
+       }
+
+       rc = acp_pci_msi_free_dt_hwirqs(msi_data);
+       if (rc)
+               goto out_free;
+
+       return 0;
+out_free:
+       kfree(msi_data->acp_pci_msi_bitmap);
+       msi_data->acp_pci_msi_bitmap = NULL;
+       return rc;
+}
+
+static int acp_pci_msi_hw_init(struct acp_pci_msi *msi,
+       struct platform_device *pdev)
+{
+       u32 int_enable;
+       u32 msg_phys_adrs;
+       int rc, i;
+
+       if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) != 0) {
+               if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)) != 0) {
+                       dev_err(&pdev->dev, "No memory for MSI table\n");
+                       rc = -ENOMEM;
+                       return rc;
+               } else {
+                       dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+               }
+       } else {
+               dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
+       }
+
+       msi->msg_buf = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
+               &msi->msg_buf_dma, GFP_KERNEL);
+       if (!msi->msg_buf) {
+               pr_debug("%s: out of memory at line %d\n", __func__, __LINE__);
+               rc = -ENOMEM;
+               return rc;
+       }
+
+       msi->msi_addr_hi = 0;
+       /* Align address to 1K */
+       if (msi->msg_buf_dma & 0x3f)
+               msi->msg_buf_dma = (msi->msg_buf_dma + 0x400) & 0xfffffc00;
+       msi->msi_addr_lo = msi->msg_buf_dma;
+
+       /* Set MSI buffer address. */
+       msg_phys_adrs = msi->msg_buf_dma & 0x0fffffff;
+
+       pr_debug("%s: MSI buffer address = x%x\n", __func__, msg_phys_adrs);
+       printk(KERN_INFO "%s: MSI buffer address = x%x\n", __func__,
+               msg_phys_adrs);
+       out_le32(msi->utl_base + 0x1190, (msg_phys_adrs) >> 10);
+
+       /* Enable MSI interrupts. */
+       int_enable = in_le32(msi->utl_base + 0x10C4);
+       int_enable |= (1 << 31);
+       out_le32(msi->utl_base + 0x10C4, int_enable);
+       out_le32(msi->utl_base + 0x1234, 0xffff);
+       for (i = 0; i < 16; i++)
+               out_le32(msi->utl_base + 0x1240 + (i*0xc), 0xffff);
+
+       return 0;
+}
+
+static int acp_pci_msi_setup_irqs(struct pci_dev *pdev, int nvec, int type);
+static void acp_pci_msi_teardown_irqs(struct pci_dev *pdev);
+static int acp_pci_msi_check_device(struct pci_dev *pdev, int nvec, int type);
+
+static int acp_pci_msi_init(struct platform_device *pdev, int port)
+{
+       int rc;
+       struct acp_pci_msi *msi;
+
+       msi = acp_pci_msi[port];
+
+       msi->irqhost = irq_domain_add_linear(msi->node,
+               NR_MSI_IRQS, &acp_pci_msi_host_ops, 0);
+
+       if (msi->irqhost == NULL) {
+               pr_debug("%s: out of memory at line %d\n",
+                       __func__, __LINE__);
+               rc = -ENOMEM;
+               goto error_out;
+       }
+
+       rc = acp_pci_msi_init_allocator(msi);
+       if (rc)
+               goto error_out;
+
+       rc = acp_pci_msi_hw_init(msi, pdev);
+       if (rc != 0)
+               goto error_out;
+
+       spin_lock_init(&msi->bitmap_lock);
+       acp_pci_msi[port] = msi;
+       ppc_md.setup_msi_irqs = acp_pci_msi_setup_irqs;
+       ppc_md.teardown_msi_irqs = acp_pci_msi_teardown_irqs;
+       ppc_md.msi_check_device = acp_pci_msi_check_device;
+
+       return 0;
+
+error_out:
+       kfree(msi);
+       return rc;
+}
+
+static int __devinit lsi_msi_probe(struct platform_device *pdev)
+{
+
+       const u32 *pval;
+       int rc = 0;
+       struct device_node *np = pdev->dev.of_node;
+       struct acp_pci_msi *pci_msi;
+       u32 int_enable;
+
+       pci_msi = kzalloc(sizeof(struct acp_pci_msi), GFP_KERNEL);
+       if (!pci_msi) {
+               rc = -ENOMEM;
+               return -1;
+       }
+
+       pci_msi->node = of_node_get(np);
+
+       /* Get the port number from the device-tree */
+       pval = of_get_property(np, "port", NULL);
+       if (pval == NULL) {
+               printk(KERN_ERR "PCIE: Can't find port number for %s\n",
+                       np->full_name);
+               return -1;
+       }
+       pci_msi->port = *pval;
+
+       pci_msi->utl_base = of_iomap(np, 0);
+       if (!pci_msi->utl_base) {
+               printk(KERN_ERR "No memory for utl_base\n");
+               return -1;
+       }
+       int_enable = in_le32(pci_msi->utl_base + 0x1004);
+
+       acp_pci_msi[pci_msi->port] = pci_msi;
+
+       acp_pci_msi_init(pdev, pci_msi->port);
+
+       return 0;
+}
+
+static int lsi_msi_remove(struct platform_device *pdev)
+{
+       platform_device_unregister(pdev);
+       return 0;
+}
+
+int acp_pci_msi_irq_handle(int port)
+{
+       struct acp_pci_msi *msi = acp_pci_msi[port];
+       u32 *msg = (u32 *) msi->msg_buf;
+       int rc = -1;
+       int i;
+
+       /*
+        * Check each message slot.  If the slot is not zero,
+        * then handle the associated virq.
+        */
+       for (i = 0; i < 256; i++) {
+               if (msg[i] != 0) {
+                       pr_debug("%s msg[%d] = x%x\n", __func__, i, msg[i]);
+                       msg[i] = 0;
+                       generic_handle_irq(i);
+                       rc = 0;
+               }
+       }
+       /* Clear MSI interrupts */
+       out_le32(msi->utl_base + 0x1230, 0xffff);
+       for (i = 0; i < 16; i++)
+               out_le32(msi->utl_base + 0x123c + (i*0xc), 0xffff);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(acp_pci_msi_irq_handle);
+
+static irq_hw_number_t acp_pci_msi_alloc_hwirqs(struct acp_pci_msi *msi,
+       int num)
+{
+       unsigned long flags;
+       int order = get_count_order(num);
+       int offset;
+
+       spin_lock_irqsave(&msi->bitmap_lock, flags);
+       offset = bitmap_find_free_region(msi->acp_pci_msi_bitmap,
+               NR_MSI_IRQS, order);
+
+       spin_unlock_irqrestore(&msi->bitmap_lock, flags);
+
+       pr_debug("%s: allocated 0x%x (2^%d) at offset 0x%x\n",
+               __func__, num, order, offset);
+
+       return offset;
+}
+
+static int acp_pci_msi_check_device(struct pci_dev *pdev, int nvec, int type)
+{
+       int rc = 0;
+       return rc;
+}
+
+static void acp_pci_msi_teardown_irqs(struct pci_dev *pdev)
+{
+       struct msi_desc *entry;
+       struct pci_bus *bus = pdev->bus;
+       struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
+       struct acp_pci_msi *msi_data = acp_pci_msi[hose->indirect_type];
+
+       list_for_each_entry(entry, &pdev->msi_list, list) {
+               if (entry->irq == NO_IRQ)
+                       continue;
+               irq_set_msi_desc(entry->irq, NULL);
+               acp_pci_msi_free_hwirqs(msi_data, virq_to_hw(entry->irq), 1);
+               irq_dispose_mapping(entry->irq);
+       }
+       return;
+}
+
+static int acp_pci_msi_setup_irqs(struct pci_dev *pdev, int nvec, int type)
+{
+       irq_hw_number_t hwirq;
+       int rc;
+       unsigned int virq;
+       struct msi_desc *entry;
+       struct msi_msg msg;
+       struct pci_bus *bus = pdev->bus;
+       struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
+       struct acp_pci_msi *msi = acp_pci_msi[hose->indirect_type];
+
+       list_for_each_entry(entry, &pdev->msi_list, list) {
+               hwirq = acp_pci_msi_alloc_hwirqs(msi, 1);
+               if (hwirq < 0) {
+                       rc = hwirq;
+                       pr_debug("%s: fail allocating msi interrupt\n",
+                               __func__);
+                       goto out_free;
+               }
+
+               virq = irq_create_mapping(msi->irqhost, hwirq);
+               if ((virq == NO_IRQ) || (virq >= NR_MSI_IRQS)) {
+                       pr_debug("%s: fail mapping hwirq 0x%lx\n",
+                               __func__, hwirq);
+                       acp_pci_msi_free_hwirqs(msi, hwirq, 1);
+                       rc = -ENOSPC;
+                       goto out_free;
+               }
+
+               pr_debug("%s: hwirq = %d, virq = %d entry = %p\n", __func__,
+                       (int) hwirq, virq, entry);
+               printk(KERN_INFO "%s: hwirq = %d, virq = %d entry = %p\n",
+                       __func__,
+                       (int) hwirq, virq, entry);
+               irq_set_msi_desc(virq, entry);
+
+               msg.address_hi = msi->msi_addr_hi;
+               msg.address_lo = msi->msi_addr_lo + (virq * sizeof(u32));
+               msg.data = MSI_MSG_DATA;
+               pr_debug("%s: msi_msg: hi = x%x, lo = x%x, data = x%x\n",
+                       __func__, msg.address_hi, msg.address_lo, msg.data);
+               printk(KERN_INFO "%s: msi_msg: hi = x%x, lo = x%x, data = 
x%x\n",
+                       __func__, msg.address_hi, msg.address_lo, msg.data);
+               write_msi_msg(virq, &msg);
+       }
+       return 0;
+
+out_free:
+       return rc;
+}
+
+static const struct of_device_id lsi_msi_match[] = {
+       {.compatible = "lsi,msi"},
+       {}
+};
+
+MODULE_DEVICE_TABLE(of, lsi_msi_match);
+
+static struct platform_driver lsi_msi_driver = {
+       .driver = {
+               .name = "lsi_msi",
+               .owner = THIS_MODULE,
+               .of_match_table = lsi_msi_match,
+               },
+       .probe = lsi_msi_probe,
+       .remove = lsi_msi_remove,
+};
+
+module_platform_driver(lsi_msi_driver);
+MODULE_AUTHOR("Sangeetha Rao <sangeetha....@avagotech.com>");
+MODULE_DESCRIPTION("LSI/Avago PCIe MSI Driver");
+
diff --git a/arch/powerpc/sysdev/lsi_msi.h b/arch/powerpc/sysdev/lsi_msi.h
new file mode 100644
index 0000000..25e3b83
--- /dev/null
+++ b/arch/powerpc/sysdev/lsi_msi.h
@@ -0,0 +1,21 @@
+/*
+* LSI specific PCI-Express support;
+*/
+
+#define NR_MSI_IRQS (256)
+#define MSI_MSG_DATA (0xcece)
+
+struct acp_pci_msi {
+       struct irq_domain *irqhost;
+       u32 msi_addr_lo;
+       u32 msi_addr_hi;
+       unsigned long *acp_pci_msi_bitmap;
+       spinlock_t bitmap_lock;
+       void *msg_buf;
+       void __iomem            *utl_base;
+       struct device_node *node;
+       dma_addr_t msg_buf_dma;
+       int port;
+};
+int acp_pci_msi_irq_handle(int port);
+
diff --git a/arch/powerpc/sysdev/lsi_pci.c b/arch/powerpc/sysdev/lsi_pci.c
index 1236fdc..417488a 100644
--- a/arch/powerpc/sysdev/lsi_pci.c
+++ b/arch/powerpc/sysdev/lsi_pci.c
@@ -20,15 +20,41 @@
 #include <mm/mmu_decl.h>
 
 #include "ppc4xx_pci.h"
+#include "lsi_msi.h"
 #include "../../../drivers/misc/lsi-ncr.h"
 
 #include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/bitmap.h>
+#include <linux/msi.h>
+#include <linux/of_platform.h>
+#include <asm/prom.h>
+#include <asm/hw_irq.h>
+#include <asm/ppc-pci.h>
 
 static int acp_plx;
 
 #undef PRINT_CONFIG_ACCESSES
 /*#define PRINT_CONFIG_ACCESSES*/
 
+struct pciex_port {
+       struct pci_controller   *hose;
+       struct device_node      *node;
+       unsigned int            index;
+       int                     endpoint;
+       int                     link;
+       int                     has_ibpre;
+       unsigned int            sdr_base;
+       dcr_host_t              dcrs;
+       struct resource         cfg_space;
+       struct resource         utl_regs;
+       void __iomem            *utl_base;
+       int     acpChipType;
+};
+
+static struct pciex_port *acp_pciex_ports;
+static unsigned int acp_pciex_port_count;
+
 static int dma_offset_set;
 static u32 last_mpage;
 static u32 last_port;
@@ -188,24 +214,6 @@ out:
 
 #define MAX_PCIE_BUS_MAPPED    0x40
 
-struct pciex_port {
-       struct pci_controller   *hose;
-       struct device_node      *node;
-       unsigned int            index;
-       int                     endpoint;
-       int                     link;
-       int                     has_ibpre;
-       unsigned int            sdr_base;
-       dcr_host_t              dcrs;
-       struct resource         cfg_space;
-       struct resource         utl_regs;
-       void __iomem            *utl_base;
-       int     acpChipType;
-};
-
-static struct pciex_port *acp_pciex_ports;
-static unsigned int acp_pciex_port_count;
-
 struct pciex_hwops {
        bool want_sdr;
        int (*core_init)(struct device_node *np);
@@ -854,8 +862,14 @@ acp_pcie_isr(int irq, void *arg)
 
        /* read the PEI interrupt status register */
        intr_status = in_le32(mbase + 0x10c0);
-       in_le32(mbase + 0x10c4);
 
+       if (intr_status & 0x80000000) {
+               printk(KERN_INFO "Received MSI interrupt\n");
+               acp_pci_msi_irq_handle(hose->indirect_type);
+               /* Clear MSI interrupt */
+               out_le32(mbase + 0x10c0, 0x80000000);
+               return IRQ_HANDLED;
+       }
        /* check if this is a PCIe message from an external device */
        if (intr_status & 0x00000010) {
                externalPciIntr = 1;
-- 
1.8.1.4

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

Reply via email to