summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTanmay Inamdar <tinamdar@apm.com>2014-05-23 20:54:28 +0800
committerDaniel Silverstone <daniel.silverstone@codethink.co.uk>2015-01-27 09:54:59 +0000
commit1572e6825565af3c3ea2230f1d52f20bd0e23b8b (patch)
tree86c506c428d04ed7e33e596f47cd145bd76c8c1f
parent470825c878693ff4fc65a0b36bc55d94e7803fe6 (diff)
downloadlinux-1572e6825565af3c3ea2230f1d52f20bd0e23b8b.tar.gz
UBUNTU: SAUCE: Add MSI/MSI-X driver for APM PCI bus
BugLink: http://launchpad.net/bugs/1318977 Signed-off-by: Tanmay Inamdar <tinamdar@apm.com> Signed-off-by: Ike Panhc <ike.pan@canonical.com> Signed-off-by: Tim Gardner <tim.gardner@canonical.com> Signed-off-by: Brad Figg <brad.figg@canonical.com> Conflicts: drivers/pci/host/Kconfig drivers/pci/host/pci-xgene.c Conflicts: arch/arm64/kernel/Makefile drivers/pci/host/Makefile
-rw-r--r--arch/arm64/boot/dts/apm/apm-storm.dtsi42
-rw-r--r--arch/arm64/include/asm/msi_bitmap.h36
-rw-r--r--arch/arm64/kernel/Makefile1
-rw-r--r--arch/arm64/kernel/msi_bitmap.c134
-rw-r--r--drivers/pci/host/Kconfig4
-rw-r--r--drivers/pci/host/Makefile1
-rw-r--r--drivers/pci/host/pci-xgene-msi.c593
-rw-r--r--drivers/pci/host/pci-xgene.c7
8 files changed, 804 insertions, 14 deletions
diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index 7bdbd8734cd0..cb795a36966a 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -354,6 +354,28 @@
};
};
+ msi: msi@79000000 {
+ compatible = "xgene,gic-msi";
+ reg = <0x00 0x79000000 0x0 0x900000>;
+ msi-available-ranges = <0x0 0x1000>;
+ interrupts = < 0x0 0x10 0x4
+ 0x0 0x11 0x4
+ 0x0 0x12 0x4
+ 0x0 0x13 0x4
+ 0x0 0x14 0x4
+ 0x0 0x15 0x4
+ 0x0 0x16 0x4
+ 0x0 0x17 0x4
+ 0x0 0x18 0x4
+ 0x0 0x19 0x4
+ 0x0 0x1a 0x4
+ 0x0 0x1b 0x4
+ 0x0 0x1c 0x4
+ 0x0 0x1d 0x4
+ 0x0 0x1e 0x4
+ 0x0 0x1f 0x4>;
+ };
+
pcie0: pcie@1f2b0000 {
status = "disabled";
device_type = "pci";
@@ -366,8 +388,8 @@
reg-names = "csr", "cfg";
ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */
0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
- dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
- 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+ ib-ranges = <0x42000000 0x40 0x00000000 0x40 0x00000000 0x40 0x00000000 /* dma */
+ 0x00000000 0x0 0x79000000 0x00 0x79000000 0x0 0x00800000>; /* msi */
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
@@ -389,8 +411,8 @@
reg-names = "csr", "cfg";
ranges = <0x01000000 0x0 0x00000000 0xd0 0x10000000 0x00 0x00010000 /* io */
0x02000000 0x0 0x80000000 0xd1 0x80000000 0x00 0x80000000>; /* mem */
- dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
- 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+ ib-ranges = <0x42000000 0x40 0x00000000 0x40 0x00000000 0x40 0x00000000 /* dma */
+ 0x00000000 0x0 0x79000000 0x00 0x79000000 0x0 0x00800000>; /* msi */
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc8 0x1
0x0 0x0 0x0 0x2 &gic 0x0 0xc9 0x1
@@ -412,8 +434,8 @@
reg-names = "csr", "cfg";
ranges = <0x01000000 0x0 0x00000000 0x90 0x10000000 0x0 0x00010000 /* io */
0x02000000 0x0 0x80000000 0x91 0x80000000 0x0 0x80000000>; /* mem */
- dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
- 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+ ib-ranges = <0x42000000 0x40 0x00000000 0x40 0x00000000 0x40 0x00000000 /* dma */
+ 0x00000000 0x0 0x79000000 0x00 0x79000000 0x0 0x00800000>; /* msi */
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xce 0x1
0x0 0x0 0x0 0x2 &gic 0x0 0xcf 0x1
@@ -435,8 +457,8 @@
reg-names = "csr", "cfg";
ranges = <0x01000000 0x0 0x00000000 0xa0 0x10000000 0x0 0x00010000 /* io */
0x02000000 0x0 0x80000000 0xa1 0x80000000 0x0 0x80000000>; /* mem */
- dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
- 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+ ib-ranges = <0x42000000 0x40 0x00000000 0x40 0x00000000 0x40 0x00000000 /* dma */
+ 0x00000000 0x0 0x79000000 0x00 0x79000000 0x0 0x00800000>; /* msi */
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xd4 0x1
0x0 0x0 0x0 0x2 &gic 0x0 0xd5 0x1
@@ -458,8 +480,8 @@
reg-names = "csr", "cfg";
ranges = <0x01000000 0x0 0x00000000 0xc0 0x10000000 0x0 0x00010000 /* io */
0x02000000 0x0 0x80000000 0xc1 0x80000000 0x0 0x80000000>; /* mem */
- dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
- 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+ ib-ranges = <0x42000000 0x40 0x00000000 0x40 0x00000000 0x40 0x00000000 /* dma */
+ 0x00000000 0x0 0x79000000 0x00 0x79000000 0x0 0x00800000>; /* msi */
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xda 0x1
0x0 0x0 0x0 0x2 &gic 0x0 0xdb 0x1
diff --git a/arch/arm64/include/asm/msi_bitmap.h b/arch/arm64/include/asm/msi_bitmap.h
new file mode 100644
index 000000000000..dfaa25601c88
--- /dev/null
+++ b/arch/arm64/include/asm/msi_bitmap.h
@@ -0,0 +1,36 @@
+#ifndef MSI_BITMAP_H
+#define MSI_BITMAP_H
+
+/*
+ * Copyright 2008, Michael Ellerman, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * Borrowed from powerpc arch
+ */
+
+#include <linux/of.h>
+#include <asm/irq.h>
+
+struct msi_bitmap {
+ struct device_node *of_node;
+ unsigned long *bitmap;
+ spinlock_t lock;
+ unsigned int irq_count;
+};
+
+int msi_bitmap_alloc_hwirqs(struct msi_bitmap *bmp, int num);
+void msi_bitmap_free_hwirqs(struct msi_bitmap *bmp, unsigned int offset,
+ unsigned int num);
+void msi_bitmap_reserve_hwirq(struct msi_bitmap *bmp, unsigned int hwirq);
+
+int msi_bitmap_reserve_dt_hwirqs(struct msi_bitmap *bmp);
+
+int msi_bitmap_alloc(struct msi_bitmap *bmp, unsigned int irq_count,
+ struct device_node *of_node);
+void msi_bitmap_free(struct msi_bitmap *bmp);
+
+#endif /* MSI_BITMAP_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index eaa77ed7766a..ff7c96fc72e9 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -33,6 +33,7 @@ arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
arm64-obj-$(CONFIG_KGDB) += kgdb.o
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
arm64-obj-$(CONFIG_PCI) += pci.o
+arm64-obj-$(CONFIG_PCI_MSI) += msi_bitmap.o
arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
obj-y += $(arm64-obj-y) vdso/
diff --git a/arch/arm64/kernel/msi_bitmap.c b/arch/arm64/kernel/msi_bitmap.c
new file mode 100644
index 000000000000..5d851fe42cb4
--- /dev/null
+++ b/arch/arm64/kernel/msi_bitmap.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2006-2008, Michael Ellerman, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * Borrowed from powerpc arch
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <asm/msi_bitmap.h>
+#include <asm/setup.h>
+
+int msi_bitmap_alloc_hwirqs(struct msi_bitmap *bmp, int num)
+{
+ unsigned long flags;
+ int offset, order = get_count_order(num);
+
+ spin_lock_irqsave(&bmp->lock, flags);
+ /*
+ * This is fast, but stricter than we need. We might want to add
+ * a fallback routine which does a linear search with no alignment.
+ */
+ offset = bitmap_find_free_region(bmp->bitmap, bmp->irq_count, order);
+ spin_unlock_irqrestore(&bmp->lock, flags);
+
+ pr_debug("msi_bitmap: allocated 0x%x (2^%d) at offset 0x%x\n",
+ num, order, offset);
+
+ return offset;
+}
+
+void msi_bitmap_free_hwirqs(struct msi_bitmap *bmp, unsigned int offset,
+ unsigned int num)
+{
+ unsigned long flags;
+ int order = get_count_order(num);
+
+ pr_debug("msi_bitmap: freeing 0x%x (2^%d) at offset 0x%x\n",
+ num, order, offset);
+
+ spin_lock_irqsave(&bmp->lock, flags);
+ bitmap_release_region(bmp->bitmap, offset, order);
+ spin_unlock_irqrestore(&bmp->lock, flags);
+}
+
+void msi_bitmap_reserve_hwirq(struct msi_bitmap *bmp, unsigned int hwirq)
+{
+ unsigned long flags;
+
+ pr_debug("msi_bitmap: reserving hwirq 0x%x\n", hwirq);
+
+ spin_lock_irqsave(&bmp->lock, flags);
+ bitmap_allocate_region(bmp->bitmap, hwirq, 0);
+ spin_unlock_irqrestore(&bmp->lock, flags);
+}
+
+/**
+ * msi_bitmap_reserve_dt_hwirqs - Reserve irqs specified in the device tree.
+ * @bmp: pointer to the MSI bitmap.
+ *
+ * Looks in the device tree to see if there is a property specifying which
+ * irqs can be used for MSI. If found those irqs reserved in the device tree
+ * are reserved in the bitmap.
+ *
+ * Returns 0 for success, < 0 if there was an error, and > 0 if no property
+ * was found in the device tree.
+ **/
+int msi_bitmap_reserve_dt_hwirqs(struct msi_bitmap *bmp)
+{
+ int i, j, len = 2;
+ u32 array[2];
+ u32 *p = array;
+ int ret;
+
+ if (!bmp->of_node)
+ return 1;
+
+ ret = of_property_read_u32_array(bmp->of_node,
+ "msi-available-ranges", p, len);
+ if (ret)
+ return ret;
+
+ bitmap_allocate_region(bmp->bitmap, 0, get_count_order(bmp->irq_count));
+
+ spin_lock(&bmp->lock);
+
+ /* Format is: (<u32 start> <u32 count>)+ */
+ len /= 2 * sizeof(u32);
+ for (i = 0; i < len; i++, p += 2) {
+ for (j = 0; j < *(p + 1); j++)
+ bitmap_release_region(bmp->bitmap, *p + j, 0);
+ }
+
+ spin_unlock(&bmp->lock);
+
+ return 0;
+}
+
+int msi_bitmap_alloc(struct msi_bitmap *bmp, unsigned int irq_count,
+ struct device_node *of_node)
+{
+ int size;
+
+ if (!irq_count)
+ return -EINVAL;
+
+ size = BITS_TO_LONGS(irq_count) * sizeof(long);
+ pr_debug("msi_bitmap: allocator bitmap size is 0x%x bytes\n", size);
+
+ bmp->bitmap = kzalloc(size, GFP_KERNEL);
+ if (!bmp->bitmap) {
+ pr_debug("msi_bitmap: ENOMEM allocating allocator bitmap!\n");
+ return -ENOMEM;
+ }
+
+ /* We zalloc'ed the bitmap, so all irqs are free by default */
+ spin_lock_init(&bmp->lock);
+ bmp->of_node = of_node_get(of_node);
+ bmp->irq_count = irq_count;
+
+ return 0;
+}
+
+void msi_bitmap_free(struct msi_bitmap *bmp)
+{
+ /* we can't free the bitmap we don't know if it's bootmem etc. */
+ of_node_put(bmp->of_node);
+ bmp->bitmap = NULL;
+}
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index c4b6568e486d..650fd1dd8b65 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -84,11 +84,15 @@ config PCIE_XILINX
Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
Host Bridge driver.
+config PCI_XGENE_MSI
+ bool
+
config PCI_XGENE
bool "X-Gene PCIe controller"
depends on ARCH_XGENE
depends on OF
select PCIEPORTBUS
+ select PCI_XGENE_MSI if PCI_MSI
help
Say Y here if you want internal PCI support on APM X-Gene SoC.
There are 5 internal PCIe ports available. Each port is GEN3 capable
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 44c26998027f..c261cf733356 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,4 +11,5 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 000000000000..f21c4590d4cc
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,593 @@
+/*
+ * XGene MSI Driver
+ *
+ * Copyright (c) 2010, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ * Tuan Phan <tphan@apm.com>
+ * Jim Hull <jim.hull@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/bootmem.h>
+#include <linux/msi.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/acpi.h>
+#include <linux/efi.h>
+#include <asm/hw_irq.h>
+#include <asm/io.h>
+#include <asm/msi_bitmap.h>
+
+#define NR_MSI_REG 16
+#define IRQS_PER_MSI_INDEX 32
+#define IRQS_PER_MSI_REG 256
+#define NR_MSI_IRQS (NR_MSI_REG * IRQS_PER_MSI_REG)
+
+#define XGENE_PIC_IP_MASK 0x0000000F
+#define XGENE_PIC_IP_GIC 0x00000001
+
+/* PCIe MSI Index Registers */
+#define MSI0IR0 0x000000
+#define MSIFIR7 0x7F0000
+
+/* PCIe MSI Interrupt Register */
+#define MSI1INT0 0x800000
+#define MSI1INTF 0x8F0000
+
+struct xgene_msi {
+ struct irq_domain *irqhost;
+ unsigned long cascade_irq;
+ u32 msiir_offset;
+ u32 msi_addr_lo;
+ u32 msi_addr_hi;
+ void __iomem *msi_regs;
+ u32 feature;
+ int msi_virqs[NR_MSI_REG];
+ struct msi_bitmap bitmap;
+ struct list_head list; /* support multiple MSI banks */
+ phandle phandle;
+};
+
+#ifdef CONFIG_ARCH_MSLIM
+static inline u64 xgene_pcie_get_iof_addr(u64 addr)
+{
+ return mslim_pa_to_iof_axi(lower_32_bits(addr));
+}
+#else
+#define xgene_pcie_get_iof_addr(addr) addr
+#endif
+
+#define MSI_DRIVER_VERSION "0.1"
+
+struct xgene_msi_feature {
+ u32 xgene_pic_ip;
+ u32 msiir_offset; /* Offset of MSIIR, relative to start of MSIR bank */
+};
+
+struct xgene_msi_cascade_data {
+ struct xgene_msi *msi_data;
+ int index;
+};
+
+LIST_HEAD(msi_head);
+
+static const struct xgene_msi_feature gic_msi_feature = {
+ .xgene_pic_ip = XGENE_PIC_IP_GIC,
+ .msiir_offset = 0,
+};
+
+irq_hw_number_t virq_to_hw(unsigned int virq)
+{
+ struct irq_data *irq_data = irq_get_irq_data(virq);
+ return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
+}
+
+static inline u32 xgene_msi_intr_read(phys_addr_t __iomem *base,
+ unsigned int reg)
+{
+ u32 irq_reg = MSI1INT0 + (reg << 16);
+ pr_debug("base = %p irq_reg = 0x%x , reg = 0x%x\n", base, irq_reg, reg);
+ return readl((void *)((phys_addr_t) base + irq_reg));
+}
+
+static inline u32 xgene_msi_read(phys_addr_t __iomem *base, unsigned int group,
+ unsigned int reg)
+{
+ u32 irq_reg = MSI0IR0 + (group << 19) + (reg << 16);
+ pr_debug("base %p irq_reg 0x%x, group 0x%x, reg 0x%x\n",
+ base, irq_reg, group, reg);
+ return readl((void *)((phys_addr_t) base + irq_reg));
+}
+
+/*
+ * We do not need this actually. The MSIR register has been read once
+ * in the cascade interrupt. So, this MSI interrupt has been acked
+*/
+static void xgene_msi_end_irq(struct irq_data *d)
+{
+}
+
+#ifdef CONFIG_SMP
+static int xgene_msi_set_affinity(struct irq_data *d,
+ const struct cpumask *mask_val, bool force)
+{
+ u64 virt_msir;
+
+ virt_msir = (u64)irq_get_handler_data(d->irq);
+ return irq_set_affinity(virt_msir, mask_val);
+}
+#endif
+
+static struct irq_chip xgene_msi_chip = {
+ .irq_mask = mask_msi_irq,
+ .irq_unmask = unmask_msi_irq,
+ .irq_ack = xgene_msi_end_irq,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = xgene_msi_set_affinity,
+#endif
+ .name = "xgene-msi",
+};
+
+static int xgene_msi_host_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct xgene_msi *msi_data = h->host_data;
+ struct irq_chip *chip = &xgene_msi_chip;
+
+ pr_debug("\nENTER %s, virq=%u\n", __func__, virq);
+ irq_set_status_flags(virq, IRQ_TYPE_LEVEL_HIGH);
+ irq_set_chip_data(virq, msi_data);
+ irq_set_chip_and_handler(virq, chip, handle_level_irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops xgene_msi_host_ops = {
+ .map = xgene_msi_host_map,
+};
+
+static int xgene_msi_init_allocator(struct xgene_msi *msi_data)
+{
+#ifdef CONFIG_ACPI
+ if (efi_enabled(EFI_BOOT))
+ return msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS, NULL);
+ else
+#endif
+ return msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS,
+ msi_data->irqhost->of_node);
+}
+
+int arch_msi_check_device(struct pci_dev *dev, int nvec, int type)
+{
+ pr_debug("ENTER %s\n", __func__);
+ return 0;
+}
+
+void arch_teardown_msi_irqs(struct pci_dev *dev)
+{
+ struct msi_desc *entry;
+ struct xgene_msi *msi_data;
+
+ pr_debug("ENTER %s\n", __func__);
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ if (entry->irq == 0)
+ continue;
+
+ msi_data = irq_get_chip_data(entry->irq);
+ irq_set_msi_desc(entry->irq, NULL);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap,
+ virq_to_hw(entry->irq), 1);
+ irq_dispose_mapping(entry->irq);
+ }
+}
+
+static void xgene_compose_msi_msg(struct pci_dev *dev, int hwirq,
+ struct msi_msg *msg,
+ struct xgene_msi *msi_data)
+{
+ int reg_set, group;
+
+ group = hwirq % NR_MSI_REG;
+ reg_set = hwirq / (NR_MSI_REG * IRQS_PER_MSI_INDEX);
+
+ pr_debug("group = %d, reg_set : 0x%x\n", group, reg_set);
+ msg->address_lo = msi_data->msi_addr_lo +
+ (((8 * group) + reg_set) << 16);
+ msg->address_hi = msi_data->msi_addr_hi;
+ msg->data = (hwirq / NR_MSI_REG) % IRQS_PER_MSI_INDEX;
+ pr_debug("addr : 0x%08x, data : 0x%x\n", msg->address_lo, msg->data);
+}
+
+int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
+{
+ struct device_node *np;
+ struct msi_desc *entry;
+ struct msi_msg msg;
+ u64 gic_irq;
+ unsigned int virq;
+ struct xgene_msi *msi_data;
+ phandle phandle = 0;
+ int rc = 0;
+ int hwirq = -1;
+
+ pr_debug("ENTER %s - nvec = %d, type = %d\n", __func__, nvec, type);
+#ifdef CONFIG_ACPI
+ if (!efi_enabled(EFI_BOOT))
+#endif
+ {
+ np = pci_device_to_OF_node(pdev);
+ /*
+ * If the PCI node has an xgene,msi property,
+ * then we need to use it to find the specific MSI.
+ */
+ np = of_parse_phandle(np, "xgene,msi", 0);
+ if (np) {
+ if (of_device_is_compatible(np,
+ "xgene,gic-msi-cascade"))
+ phandle = np->phandle;
+ else {
+ dev_err(&pdev->dev,
+ "node %s has an invalid xgene,msi phandle %u\n",
+ np->full_name, np->phandle);
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else
+ dev_info(&pdev->dev, "Found no xgene,msi phandle\n");
+ }
+
+ list_for_each_entry(entry, &pdev->msi_list, list) {
+ pr_debug("Loop over MSI devices\n");
+ /*
+ * Loop over all the MSI devices until we find one that has an
+ * available interrupt.
+ */
+ list_for_each_entry(msi_data, &msi_head, list) {
+ if (phandle && (phandle != msi_data->phandle))
+ continue;
+ pr_debug("Xgene msi pointer : %p\n", msi_data);
+ hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
+ break;
+ }
+
+ if (hwirq < 0) {
+ dev_err(&pdev->dev,
+ "could not allocate MSI interrupt\n");
+ rc = -ENOSPC;
+ goto exit;
+ }
+
+ virq = irq_create_mapping(msi_data->irqhost, hwirq);
+ if (virq == 0) {
+ dev_err(&pdev->dev, "fail mapping hwirq %i\n", hwirq);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap, hwirq, 1);
+ rc = -ENOSPC;
+ goto exit;
+ }
+
+ gic_irq = msi_data->msi_virqs[hwirq % NR_MSI_REG];
+ pr_debug("Created Mapping HWIRQ %d on GIC IRQ %llu "
+ "TO VIRQ %d\n",
+ hwirq, gic_irq, virq);
+ /* chip_data is msi_data via host->hostdata in host->map() */
+ irq_set_msi_desc(virq, entry);
+ xgene_compose_msi_msg(pdev, hwirq, &msg, msi_data);
+ irq_set_handler_data(virq, (void *)gic_irq);
+ write_msi_msg(virq, &msg);
+ }
+
+exit:
+ return rc;
+}
+
+static void xgene_msi_cascade(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_data *idata = irq_desc_get_irq_data(desc);
+ struct xgene_msi_cascade_data *cascade_data;
+ struct xgene_msi *msi_data;
+ unsigned int cascade_irq;
+ int msir_index = -1;
+ u32 msir_value = 0;
+ u32 intr_index = 0;
+ u32 msi_intr_reg_value = 0;
+ u32 msi_intr_reg;
+
+ pr_debug("\nENTER %s, irq=%u\n", __func__, irq);
+ cascade_data = irq_get_handler_data(irq);
+ msi_data = cascade_data->msi_data;
+ pr_debug("xgene_msi : 0x%p, irq = %u\n", msi_data, irq);
+
+ raw_spin_lock(&desc->lock);
+ if (unlikely(irqd_irq_inprogress(idata)))
+ goto unlock;
+
+ msi_intr_reg = cascade_data->index;
+ pr_debug("msi_intr_reg : %d\n", msi_intr_reg);
+
+ if (msi_intr_reg >= NR_MSI_REG)
+ cascade_irq = 0;
+
+ irqd_set_chained_irq_inprogress(idata);
+
+ switch (msi_data->feature & XGENE_PIC_IP_MASK) {
+ case XGENE_PIC_IP_GIC:
+ msi_intr_reg_value = xgene_msi_intr_read(msi_data->msi_regs,
+ msi_intr_reg);
+ pr_debug("msi_intr_reg_value : 0x%08x\n", msi_intr_reg_value);
+ break;
+ }
+
+ while (msi_intr_reg_value) {
+ msir_index = ffs(msi_intr_reg_value) - 1;
+ pr_debug("msir_index : %d\n", msir_index);
+ msir_value = xgene_msi_read(msi_data->msi_regs, msi_intr_reg,
+ msir_index);
+ while (msir_value) {
+ intr_index = ffs(msir_value) - 1;
+ pr_debug("intr_index : %d\n", intr_index);
+ cascade_irq = irq_linear_revmap(msi_data->irqhost,
+ msir_index * IRQS_PER_MSI_INDEX * NR_MSI_REG +
+ intr_index * NR_MSI_REG + msi_intr_reg);
+ pr_debug("cascade_irq : %d\n", cascade_irq);
+ if (cascade_irq != 0)
+ generic_handle_irq(cascade_irq);
+ msir_value &= ~(1 << intr_index);
+ pr_debug("msir_value : 0x%08x\n", msir_value);
+ }
+ msi_intr_reg_value &= ~(1 << msir_index);
+ pr_debug("msi_intr_reg_value : 0x%08x\n", msi_intr_reg_value);
+ }
+ irqd_clr_chained_irq_inprogress(idata);
+
+ switch (msi_data->feature & XGENE_PIC_IP_MASK) {
+ case XGENE_PIC_IP_GIC:
+ chip->irq_eoi(idata);
+ break;
+ }
+
+unlock:
+ raw_spin_unlock(&desc->lock);
+ pr_debug("EXIT\n");
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+ int virq, i;
+ struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+ pr_debug("ENTER %s\n", __func__);
+ for (i = 0; i < NR_MSI_REG; i++) {
+ virq = msi->msi_virqs[i];
+ if (virq != 0)
+ irq_dispose_mapping(virq);
+ }
+
+ if (msi->bitmap.bitmap)
+ msi_bitmap_free(&msi->bitmap);
+
+ return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+ struct platform_device *pdev,
+ int offset, int irq_index)
+{
+ int virt_msir;
+ cpumask_var_t mask;
+ struct xgene_msi_cascade_data *cascade_data = NULL;
+
+ virt_msir = platform_get_irq(pdev, irq_index);
+ if (virt_msir < 0) {
+ dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+ irq_index);
+ return -EINVAL;
+ }
+
+ cascade_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct xgene_msi_cascade_data), GFP_KERNEL);
+ if (!cascade_data) {
+ dev_err(&pdev->dev, "No memory for MSI cascade data\n");
+ return -ENOMEM;
+ }
+
+ if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+ cpumask_setall(mask);
+ irq_set_affinity(virt_msir, mask);
+ free_cpumask_var(mask);
+ }
+
+ msi->msi_virqs[irq_index] = virt_msir;
+ cascade_data->index = offset;
+ cascade_data->msi_data = msi;
+ irq_set_handler_data(virt_msir, cascade_data);
+ irq_set_chained_handler(virt_msir, xgene_msi_cascade);
+ pr_debug("mapped phys irq %d\n", virt_msir);
+
+ return 0;
+}
+
+static int xgene_msi_get_param(struct platform_device *pdev, const char *name,
+ u32 *buf, int count)
+{
+ int rc = 0;
+
+#ifdef CONFIG_ACPI
+ if (efi_enabled(EFI_BOOT)) {
+ struct acpi_dsm_entry entry;
+
+ if (acpi_dsm_lookup_value(ACPI_HANDLE(&pdev->dev),
+ name, 0, &entry) || !entry.value)
+ return -EINVAL;
+
+ if (count == 2)
+ sscanf(entry.value, "%d %d", &buf[0], &buf[1]);
+ else
+ rc = -EINVAL;
+
+ kfree(entry.key);
+ kfree(entry.value);
+ } else
+#endif
+ if (of_property_read_u32_array(pdev->dev.of_node, name,
+ buf, count))
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+ struct xgene_msi *msi;
+ struct resource *res;
+ phys_addr_t msiir_offset;
+ int rc, j, irq_index, count;
+ u32 offset;
+ u32 buf[] = { 0, NR_MSI_IRQS};
+
+ msi = devm_kzalloc(&pdev->dev, sizeof(struct xgene_msi), GFP_KERNEL);
+ if (!msi) {
+ dev_err(&pdev->dev, "No memory for MSI structure\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, msi);
+
+#ifdef CONFIG_ACPI
+ if (efi_enabled(EFI_BOOT))
+ msi->irqhost = irq_domain_add_linear(NULL,
+ NR_MSI_IRQS, &xgene_msi_host_ops, msi);
+ else
+#endif
+ msi->irqhost = irq_domain_add_linear(pdev->dev.of_node,
+ NR_MSI_IRQS, &xgene_msi_host_ops, msi);
+ if (msi->irqhost == NULL) {
+ dev_err(&pdev->dev, "No memory for MSI irqhost\n");
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(msi->msi_regs)) {
+ dev_err(&pdev->dev, "no reg space\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ pr_debug("mapped 0x%08llx to 0x%p Size : 0x%08llx\n", res->start,
+ msi->msi_regs, resource_size(res));
+
+ msiir_offset = lower_32_bits(xgene_pcie_get_iof_addr(res->start));
+ msi->msiir_offset = gic_msi_feature.msiir_offset +
+ (msiir_offset & 0xfffff);
+ msi->msi_addr_hi = upper_32_bits(res->start);
+ msi->msi_addr_lo = gic_msi_feature.msiir_offset +
+ (msiir_offset & 0xffffffff);
+ msi->feature = gic_msi_feature.xgene_pic_ip;
+
+#ifdef CONFIG_ACPI
+ if (efi_enabled(EFI_BOOT))
+ msi->phandle = 0;
+ else
+#endif
+ msi->phandle = pdev->dev.of_node->phandle;
+
+ rc = xgene_msi_init_allocator(msi);
+ if (rc) {
+ dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+ goto error;
+ }
+
+ rc = xgene_msi_get_param(pdev, "msi-available-ranges", buf, 2);
+ if (rc) {
+ dev_err(&pdev->dev, "Error getting MSI ranges\n");
+ goto error;
+ }
+
+ pr_debug("buf[0] = 0x%x buf[1] = 0x%x\n", buf[0], buf[1]);
+ if (buf[0] % IRQS_PER_MSI_REG || buf[1] % IRQS_PER_MSI_REG) {
+ pr_err_once("msi available range of"
+ "%u at %u is not IRQ-aligned\n",
+ buf[1], buf[0]);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ offset = buf[0] / IRQS_PER_MSI_REG;
+ count = buf[1] / IRQS_PER_MSI_REG;
+ pr_debug("offset = %d count = %d\n", offset, count);
+
+ for (irq_index = 0, j = 0; j < count; j++, irq_index++) {
+ rc = xgene_msi_setup_hwirq(msi, pdev, offset + j, irq_index);
+ if (rc)
+ goto error;
+ }
+
+ list_add_tail(&msi->list, &msi_head);
+
+ pr_info("XGene: PCIe MSI driver v%s\n", MSI_DRIVER_VERSION);
+
+ return 0;
+
+error:
+ xgene_msi_remove(pdev);
+ return rc;
+}
+
+static const struct of_device_id xgene_msi_of_ids[] = {
+ {
+ .compatible = "xgene,gic-msi",
+ },
+ {}
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_msi_acpi_ids[] = {
+ {"APMC0D0E", 0},
+ {},
+};
+#endif
+
+static struct platform_driver xgene_msi_driver = {
+ .driver = {
+ .name = "xgene-msi",
+ .owner = THIS_MODULE,
+ .of_match_table = xgene_msi_of_ids,
+#ifdef CONFIG_ACPI
+ .acpi_match_table = ACPI_PTR(xgene_msi_acpi_ids),
+#endif
+ },
+ .probe = xgene_msi_probe,
+ .remove = xgene_msi_remove,
+};
+
+static __init int xgene_msi_init(void)
+{
+ return platform_driver_register(&xgene_msi_driver);
+}
+
+subsys_initcall_sync(xgene_msi_init);
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index b1d0596457c5..998e15bad2ef 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -529,7 +529,7 @@ static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
parser->pna = of_n_addr_cells(node);
parser->np = parser->pna + na + ns;
- parser->range = of_get_property(node, "dma-ranges", &rlen);
+ parser->range = of_get_property(node, "ib-ranges", &rlen);
if (!parser->range)
return -ENOENT;
parser->end = parser->range + rlen / sizeof(__be32);
@@ -546,14 +546,13 @@ static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port)
u8 ib_reg_mask = 0;
if (pci_dma_range_parser_init(&parser, np)) {
- dev_err(dev, "missing dma-ranges property\n");
+ dev_err(dev, "missing ib-ranges property\n");
return -EINVAL;
}
- /* Get the dma-ranges from DT */
+ /* Get the ib-ranges from DT */
for_each_of_pci_range(&parser, &range) {
u64 end = range.cpu_addr + range.size - 1;
-
dev_dbg(port->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
range.flags, range.cpu_addr, end, range.pci_addr);
xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask);