diff mbox

UBUNTU: SAUCE: Add MSI/MSI-X driver for APM PCI bus

Message ID 1399977551-5384-1-git-send-email-ike.pan@canonical.com
State New
Headers show

Commit Message

Ike Panhc May 13, 2014, 10:39 a.m. UTC
BugLink: http://launchpad.net/bugs/1318977

Signed-off-by: Ike Panhc <ike.pan@canonical.com>
---
 arch/arm64/boot/dts/apm-storm.dtsi  |  92 ++++--
 arch/arm64/include/asm/msi_bitmap.h |  36 +++
 arch/arm64/kernel/Makefile          |   1 +
 arch/arm64/kernel/msi_bitmap.c      | 135 +++++++++
 drivers/pci/host/Kconfig            |   4 +
 drivers/pci/host/Makefile           |   1 +
 drivers/pci/host/pci-xgene-msi.c    | 554 ++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c        | 291 +++++++++++++++++--
 8 files changed, 1070 insertions(+), 44 deletions(-)
 create mode 100644 arch/arm64/include/asm/msi_bitmap.h
 create mode 100644 arch/arm64/kernel/msi_bitmap.c
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
diff mbox

Patch

diff --git a/arch/arm64/boot/dts/apm-storm.dtsi b/arch/arm64/boot/dts/apm-storm.dtsi
index aae496f..73fb912 100644
--- a/arch/arm64/boot/dts/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm-storm.dtsi
@@ -341,11 +341,15 @@ 
 			#size-cells = <2>;
 			#address-cells = <3>;
 			reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
-				0xe0 0xd0000000 0x0 0x00200000>; /* PCI config space */
-			reg-names = "csr", "cfg";
-			ranges = <0x01000000 0x00 0x00000000 0xe0 0x00000000 0x00 0x00010000   /* io */
-				  0x02000000 0x00 0x10000000 0xe0 0x10000000 0x00 0x80000000>; /* mem */
-			dma-ranges = <0x42000000 0x40 0x00000000 0x40 0x00000000 0x40 0x00000000>;
+				0xe0 0xd0000000 0x0 0x00200000 /* PCI config space */
+				0x00 0x79e00000 0x0 0x2000000 /* MSI Generation used only for msi test*/
+				0x00 0x79000000 0x0 0x800000>; /* MSI Termination used only for msi test*/
+			reg-names = "csr", "cfg", "msi_gen", "msi_term";
+			ep-mem-size = <0x100000>; /* Used only for msi test */
+			ranges = <0x01000000 0x00 0x10000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+				  0x02000000 0x00 0x30000000 0xe0 0x30000000 0x00 0x80000000>; /* mem */
+			ib-ranges = <0x02000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 /* BAR 0 */
+				     0x02000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; /* IBAR 3 */
 			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
@@ -362,11 +366,15 @@ 
 			#size-cells = <2>;
 			#address-cells = <3>;
 			reg = < 0x00 0x1f2c0000 0x0 0x00010000   /* Controller registers */
-				0xd0 0xd0000000 0x0 0x00200000>; /* PCI config space */
-			reg-names = "csr", "cfg";
-			ranges = <0x01000000 0x0 0x00000000 0xd0 0x00000000 0x00 0x00010000   /* io  */
-				  0x02000000 0x0 0x10000000 0xd0 0x10000000 0x00 0x80000000>; /* mem */
-			dma-ranges = <0x42000000 0x40 0x00000000 0x40 0x00000000 0x40 0x00000000>;
+				0xd0 0xd0000000 0x0 0x00200000 /* PCI config space */
+				0x00 0x79e00000 0x0 0x2000000 /* MSI Generation used only for msi test*/
+				0x00 0x79000000 0x0 0x800000>; /* MSI Termination used only for msi test*/
+			reg-names = "csr", "cfg", "msi_gen", "msi_term";
+			ep-mem-size = <0x200000>; /* Used only for msi test */
+			ranges = <0x01000000 0x0 0x10000000 0xd0 0x10000000 0x00 0x00010000   /* io  */
+				  0x02000000 0x0 0x30000000 0xd0 0x30000000 0x00 0x80000000>; /* mem */
+			ib-ranges = <0x02000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 /* BAR 0 */
+				     0x02000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; /* IBAR 3 */
 			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
@@ -383,11 +391,15 @@ 
 			#size-cells = <2>;
 			#address-cells = <3>;
 			reg =  < 0x00 0x1f2d0000 0x0 0x00010000   /* Controller registers */
-				 0x90 0xd0000000 0x0 0x00200000>; /* PCI config space */
-			reg-names = "csr", "cfg";
-			ranges = <0x01000000 0x0 0x00000000 0x90 0x00000000 0x0 0x00010000   /* io  */
-				  0x02000000 0x0 0x10000000 0x90 0x10000000 0x0 0x80000000>; /* mem */
-			dma-ranges = <0x42000000 0x40 0x00000000 0x40 0x00000000 0x40 0x00000000>;
+				 0x90 0xd0000000 0x0 0x00200000 /* PCI config space */
+				 0x00 0x79e00000 0x0 0x2000000 /* MSI Generation used only for msi test*/
+				 0x00 0x79000000 0x0 0x800000>; /* MSI Termination used only for msi test*/
+			reg-names = "csr", "cfg", "msi_gen", "msi_term";
+			ep-mem-size = <0x100000>; /* Used only for msi test */
+			ranges = <0x01000000 0x0 0x10000000 0x90 0x10000000 0x0 0x00010000   /* io  */
+				  0x02000000 0x0 0x30000000 0x90 0x30000000 0x0 0x80000000>; /* mem */
+			ib-ranges = <0x02000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 /* BAR 0 */
+				     0x02000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; /* IBAR 3 */
 			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
@@ -404,11 +416,15 @@ 
 			#size-cells = <2>;
 			#address-cells = <3>;
 			reg = < 0x00 0x1f500000 0x0 0x00010000   /* Controller registers */
-				0xa0 0xd0000000 0x0 0x00200000>; /* PCI config space */
-			reg-names = "csr", "cfg";
-			ranges = <0x01000000 0x0 0x00000000 0xa0 0x00000000 0x0 0x00010000  /* mem */
-				  0x02000000 0x0 0x10000000 0xa0 0x10000000 0x0 0x80000000>; /* io  */
-			dma-ranges = <0x42000000 0x40 0x00000000 0x40 0x00000000 0x40 0x00000000>;
+				0xa0 0xd0000000 0x0 0x00200000   /* PCI config space */
+				0x00 0x79e00000 0x0 0x2000000 /* MSI Generation used only for msi test*/
+				0x00 0x79000000 0x0 0x800000>; /* MSI Termination used only for msi test*/
+			reg-names = "csr", "cfg", "msi_gen", "msi_term";
+			ep-mem-size = <0x100000>; /* Used only for msi test */
+			ranges = <0x01000000 0x0 0x10000000 0xa0 0x10000000 0x0 0x00010000  /* io */
+				  0x02000000 0x0 0x30000000 0xa0 0x30000000 0x0 0x80000000>; /* mem  */
+			ib-ranges = <0x02000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 /* BAR 0 */
+				     0x02000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; /* IBAR 3 */
 			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
@@ -425,11 +441,15 @@ 
 			#size-cells = <2>;
 			#address-cells = <3>;
 			reg = < 0x00 0x1f510000 0x0 0x00010000   /* Controller registers */
-				0xc0 0xd0000000 0x0 0x00200000>; /* PCI config space */
-			reg-names = "csr", "cfg";
-			ranges = <0x01000000 0x0 0x00000000 0xc0 0x00000000 0x0 0x00010000   /* io  */
-				  0x02000000 0x0 0x10000000 0xc0 0x10000000 0x0 0x80000000>; /* mem */
-			dma-ranges = <0x42000000 0x40 0x00000000 0x40 0x00000000 0x40 0x00000000>;
+				0xc0 0xd0000000 0x0 0x00200000   /* PCI config space */
+				0x00 0x79e00000 0x0 0x2000000 /* MSI Generation used only for msi test*/
+				0x00 0x79000000 0x0 0x800000>; /* MSI Termination used only for msi test*/
+			reg-names = "csr", "cfg", "msi_gen", "msi_term";
+			ep-mem-size = <0x100000>; /* Used only for msi test */
+			ranges = <0x01000000 0x0 0x10000000 0xc0 0x10000000 0x0 0x00010000   /* io  */
+				  0x02000000 0x0 0x30000000 0xc0 0x30000000 0x0 0x80000000>; /* mem */
+			ib-ranges = <0x02000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 /* BAR 0 */
+				     0x02000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; /* IBAR 3 */
 			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
@@ -438,6 +458,28 @@ 
 			clocks = <&pcie4clk 0>;
 		};
 
+		msi: msi@79000000 {
+			compatible = "apm,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>;
+		};
+
                 rtc: rtc@10510000 {
                         device_type = "rtc";
                         compatible = "apm,xgene-rtc";
diff --git a/arch/arm64/include/asm/msi_bitmap.h b/arch/arm64/include/asm/msi_bitmap.h
new file mode 100644
index 0000000..dfaa256
--- /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 9e9c208..69900a2 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -20,6 +20,7 @@  arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
 arm64-obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
 
 obj-$(CONFIG_PCI)			+= pcibios.o
+obj-$(CONFIG_PCI_MSI)                   += msi_bitmap.o
 obj-y					+= $(arm64-obj-y) vdso/
 obj-m					+= $(arm64-obj-m)
 head-y					:= head.o
diff --git a/arch/arm64/kernel/msi_bitmap.c b/arch/arm64/kernel/msi_bitmap.c
new file mode 100644
index 0000000..107226e
--- /dev/null
+++ b/arch/arm64/kernel/msi_bitmap.c
@@ -0,0 +1,135 @@ 
+/*
+ * 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 19ce97d..a7d746a 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -33,11 +33,15 @@  config PCI_RCAR_GEN2
 	  There are 3 internal PCI controllers available with a single
 	  built-in EHCI/OHCI host controller present on each one.
 
+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 34c7c36..ddac195 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -5,3 +5,4 @@  obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
 obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
 obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 0000000..bc063c3
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,554 @@ 
+/*
+ * APM MSI Driver
+ *
+ * Copyright (c) 2010, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.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/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 <asm/hw_irq.h>
+#include <linux/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 APM_PIC_IP_MASK		0x0000000F
+#define APM_PIC_IP_GIC		0x00000001
+
+/* PCIe MSI Index Registers */
+#define MSI0IR0		0x000000
+#define MSIFIR7		0x7F0000
+
+/* PCIe MSI Interrupt Register */
+#define MSI1INT0	0x800000
+#define MSI1INTF	0x8F0000
+
+#ifdef APM_MSI_DEBUG
+# define msi_dbg	pr_info
+#else
+# define msi_dbg(x, ...)
+#endif
+
+struct apm_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));
+}
+#define pci_io_offset(s)		(s & 0x00000000)
+#else
+#define xgene_pcie_get_iof_addr(addr)	addr
+#define pci_io_offset(s)		(s & 0xff00000000)
+#endif
+#define MSI_DRIVER_VERSION "0.1"
+
+LIST_HEAD(msi_head);
+
+struct apm_msi_feature {
+	u32 apm_pic_ip;
+	u32 msiir_offset; /* Offset of MSIIR, relative to start of MSIR bank */
+};
+
+struct apm_msi_cascade_data {
+	struct apm_msi *msi_data;
+	int index;
+};
+
+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 apm_msi_intr_read(phys_addr_t __iomem *base, unsigned int reg)
+{
+	u32 irq_reg = MSI1INT0 + (reg << 16);
+	msi_dbg("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 apm_msi_read(phys_addr_t __iomem *base, unsigned int group,
+			       unsigned int reg)
+{
+	u32 irq_reg = MSI0IR0 + (group << 19) + (reg << 16);
+	msi_dbg("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 apm_msi_end_irq(struct irq_data *d)
+{
+}
+
+static struct irq_chip apm_msi_chip = {
+	.irq_mask	= mask_msi_irq,
+	.irq_unmask	= unmask_msi_irq,
+	.irq_ack	= apm_msi_end_irq,
+	.name		= "apm-msi",
+};
+
+static int apm_msi_host_map(struct irq_domain *h, unsigned int virq,
+			    irq_hw_number_t hw)
+{
+	struct apm_msi *msi_data = h->host_data;
+	struct irq_chip *chip = &apm_msi_chip;
+
+	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 apm_msi_host_ops = {
+	.map = apm_msi_host_map,
+};
+
+static int apm_msi_init_allocator(struct apm_msi *msi_data)
+{
+	int rc;
+
+	rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS,
+			      msi_data->irqhost->of_node);
+	if (rc)
+		return rc;
+
+#if 0
+	/* FIXME */
+	rc = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap);
+	if (rc < 0) {
+		msi_bitmap_free(&msi_data->bitmap);
+		return rc;
+	}
+#endif
+
+	return 0;
+}
+
+int arch_msi_check_device(struct pci_dev *pdev, int nvec, int type)
+{
+	msi_dbg("ENTER %s\n", __func__);
+	return 0;
+}
+
+void arch_teardown_msi_irqs(struct pci_dev *pdev)
+{
+	struct msi_desc *entry;
+	struct apm_msi *msi_data;
+
+	msi_dbg("ENTER %s\n", __func__);
+	list_for_each_entry(entry, &pdev->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);
+	}
+
+	return;
+}
+
+static void apm_compose_msi_msg(struct pci_dev *pdev, int hwirq,
+				struct msi_msg *msg,
+				struct apm_msi *msi_data)
+{
+	int reg_set, group;
+
+	group = hwirq % NR_MSI_REG;
+	reg_set = hwirq / (NR_MSI_REG * IRQS_PER_MSI_INDEX);
+
+	msi_dbg("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;
+	msi_dbg("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 = pci_device_to_OF_node(pdev);
+	phandle phandle = 0;
+	int rc, hwirq = -ENOMEM;
+	unsigned int virq;
+	struct msi_desc *entry;
+	struct msi_msg msg;
+	struct apm_msi *msi_data;
+	u64 gic_irq;
+
+	msi_dbg("ENTER %s - nvec = %d, type = %d\n", __func__, nvec, type);
+	/*
+	 * If the PCI node has an apm,msi property, then we need to use it
+	 * to find the specific MSI.
+	 */
+	np = of_parse_phandle(np, "apm,msi", 0);
+	if (np) {
+		if (of_device_is_compatible(np, "apm,gic-msi-cascade"))
+			phandle = np->phandle;
+		else {
+			dev_err(&pdev->dev,
+				"node %s has an invalid apm,msi phandle %u\n",
+				np->full_name, np->phandle);
+			return -EINVAL;
+		}
+	} else {
+		dev_info(&pdev->dev, "Found no apm,msi phandle\n");
+	}
+
+	list_for_each_entry(entry, &pdev->msi_list, list) {
+		msi_dbg("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;
+			msi_dbg("apm_msi : %p\n", msi_data);
+			hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
+			if (hwirq >= 0)
+				break;
+		}
+
+		if (hwirq < 0) {
+			rc = hwirq;
+			dev_err(&pdev->dev, "could not allocate MSI interrupt\n");
+			goto out_free;
+		}
+
+		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 out_free;
+		}
+		gic_irq = msi_data->msi_virqs[hwirq % NR_MSI_REG];
+		msi_dbg("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);
+		apm_compose_msi_msg(pdev, hwirq, &msg, msi_data);
+		irq_set_handler_data(virq, (void *)gic_irq);
+		write_msi_msg(virq, &msg);
+	}
+	return 0;
+
+out_free:
+	/* free by the caller of this function */
+	msi_dbg("EXIT with error %d\n", rc);
+	return rc;
+}
+
+static void apm_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);
+	unsigned int cascade_irq;
+	struct apm_msi *msi_data;
+	int msir_index = -1;
+	u32 msir_value = 0;
+	u32 intr_index = 0;
+	u32 msi_intr_reg_value = 0;
+	u32 msi_intr_reg;
+	struct apm_msi_cascade_data *cascade_data;
+
+	msi_dbg("\nENTER %s, irq=%u\n", __func__, irq);
+	cascade_data = irq_get_handler_data(irq);
+	msi_data = cascade_data->msi_data;
+	msi_dbg("apm_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;
+	msi_dbg("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 & APM_PIC_IP_MASK) {
+	case APM_PIC_IP_GIC:
+		msi_intr_reg_value = apm_msi_intr_read(msi_data->msi_regs,
+						       msi_intr_reg);
+		msi_dbg("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;
+		msi_dbg("msir_index : %d\n", msir_index);
+		msir_value = apm_msi_read(msi_data->msi_regs, msi_intr_reg,
+				msir_index);
+		while (msir_value) {
+			intr_index = ffs(msir_value) - 1;
+			msi_dbg("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);
+			msi_dbg("cascade_irq : %d\n", cascade_irq);
+			if (cascade_irq != 0)
+				generic_handle_irq(cascade_irq);
+			msir_value &= ~(1 << intr_index);
+			msi_dbg("msir_value : 0x%08x\n", msir_value);
+		}
+		msi_intr_reg_value &= ~(1 << msir_index);
+		msi_dbg("msi_intr_reg_value : 0x%08x\n", msi_intr_reg_value);
+	}
+	irqd_clr_chained_irq_inprogress(idata);
+
+	switch (msi_data->feature & APM_PIC_IP_MASK) {
+	case APM_PIC_IP_GIC:
+		chip->irq_eoi(idata);
+		break;
+	}
+unlock:
+	raw_spin_unlock(&desc->lock);
+	msi_dbg("EXIT\n");
+}
+
+static int apm_of_msi_remove(struct platform_device *ofdev)
+{
+	struct apm_msi *msi = platform_get_drvdata(ofdev);
+	int virq, i;
+	struct apm_msi_cascade_data *cascade_data;
+
+	msi_dbg("ENTER %s\n", __func__);
+	if (msi->list.prev != NULL)
+		list_del(&msi->list);
+	for (i = 0; i < NR_MSI_REG; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0) {
+			cascade_data = irq_get_handler_data(virq);
+			kfree(cascade_data);
+			irq_dispose_mapping(virq);
+		}
+	}
+	if (msi->bitmap.bitmap)
+		msi_bitmap_free(&msi->bitmap);
+
+	iounmap(msi->msi_regs);
+	kfree(msi);
+
+	return 0;
+}
+
+static int apm_msi_setup_hwirq(struct apm_msi *msi,
+				struct platform_device *dev,
+				int offset, int irq_index)
+{
+	struct apm_msi_cascade_data *cascade_data = NULL;
+	int virt_msir;
+	cpumask_var_t mask;
+
+	virt_msir = irq_of_parse_and_map(dev->dev.of_node, irq_index);
+	if (virt_msir == 0) {
+		dev_err(&dev->dev, "%s: Cannot translate IRQ index %d\n",
+			__func__, irq_index);
+		return 0;
+	}
+	msi_dbg("mapped phys irq %d\n", virt_msir);
+
+	cascade_data = kzalloc(sizeof(struct apm_msi_cascade_data), GFP_KERNEL);
+	if (!cascade_data) {
+		dev_err(&dev->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, apm_msi_cascade);
+
+	return 0;
+}
+
+static const struct of_device_id apm_of_msi_ids[];
+static int apm_of_msi_probe(struct platform_device *dev)
+{
+	const struct of_device_id *match;
+	struct apm_msi *msi;
+	struct resource res;
+	phys_addr_t msiir_offset;
+	int err, j, irq_index, count;
+	int rc;
+	const struct apm_msi_feature *features;
+	u32 offset;
+	u32 p[] = { 0, NR_MSI_IRQS};
+
+	match = of_match_device(apm_of_msi_ids, &dev->dev);
+	if (!match)
+		return -EINVAL;
+	features = match->data;
+
+	msi = kzalloc(sizeof(struct apm_msi), GFP_KERNEL);
+	if (!msi) {
+		dev_err(&dev->dev, "No memory for MSI structure\n");
+		return -ENOMEM;
+	}
+	msi_dbg("apm_msi : %p\n", msi);
+	platform_set_drvdata(dev, msi);
+
+	msi->irqhost = irq_domain_add_linear(dev->dev.of_node,
+				      NR_MSI_IRQS, &apm_msi_host_ops, msi);
+
+	if (msi->irqhost == NULL) {
+		dev_err(&dev->dev, "No memory for MSI irqhost\n");
+		err = -ENOMEM;
+		goto error_out;
+	}
+
+	err = of_address_to_resource(dev->dev.of_node, 0, &res);
+	if (err) {
+		dev_err(&dev->dev, "invalid resource for node %s\n",
+				dev->dev.of_node->full_name);
+		goto error_out;
+	}
+
+	msi->msi_regs = ioremap_nocache(res.start, resource_size(&res));
+	if (!msi->msi_regs) {
+		err = -ENOMEM;
+		dev_err(&dev->dev, "could not map node %s\n",
+				dev->dev.of_node->full_name);
+		goto error_out;
+	}
+	msi_dbg("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 = features->msiir_offset + (msiir_offset & 0xfffff);
+	msi->msi_addr_hi = upper_32_bits(res.start);
+	msi->msi_addr_lo = features->msiir_offset + (msiir_offset & 0xffffffff);
+	msi->feature = features->apm_pic_ip;
+	msi->phandle = dev->dev.of_node->phandle;
+
+	rc = apm_msi_init_allocator(msi);
+	if (rc) {
+		dev_err(&dev->dev, "Error allocating MSI bitmap\n");
+		goto error_out;
+	}
+
+	rc = of_property_read_u32_array(dev->dev.of_node,
+					"msi-available-ranges", p, 2);
+	if (rc) {
+		dev_err(&dev->dev, "Error getting MSI ranges\n");
+		goto error_out;
+	}
+
+	msi_dbg("p[0] = 0x%x p[1] = 0x%x\n", p[0], p[1]);
+	if (p[0] % IRQS_PER_MSI_REG || p[1] % IRQS_PER_MSI_REG) {
+		pr_warn("%s: %s: msi available range of "
+				"%u at %u is not IRQ-aligned\n",
+				__func__, dev->dev.of_node->full_name,
+				p[1], p[0]);
+		err = -EINVAL;
+		goto error_out;
+	}
+
+	offset = p[0] / IRQS_PER_MSI_REG;
+	count  = p[1] / IRQS_PER_MSI_REG;
+	msi_dbg("offset = %d count = %d\n", offset, count);
+
+	for (irq_index = 0, j = 0; j < count; j++, irq_index++) {
+		err = apm_msi_setup_hwirq(msi, dev, offset + j, irq_index);
+		if (err)
+			goto error_out;
+	}
+
+	list_add_tail(&msi->list, &msi_head);
+	pr_info("APM88xxxx: PCIe MSI driver v%s\n", MSI_DRIVER_VERSION);
+
+	msi_dbg("EXIT\n");
+	return 0;
+error_out:
+	apm_of_msi_remove(dev);
+	return err;
+}
+
+static const struct apm_msi_feature gic_msi_feature = {
+	.apm_pic_ip = APM_PIC_IP_GIC,
+	.msiir_offset = 0,
+};
+
+static const struct of_device_id apm_of_msi_ids[] = {
+	{
+		.compatible = "apm,gic-msi",
+		.data = (void *)&gic_msi_feature,
+	},
+	{}
+};
+
+static struct platform_driver apm_of_msi_driver = {
+	.driver = {
+		.name = "apm-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = apm_of_msi_ids,
+	},
+	.probe = apm_of_msi_probe,
+	.remove = apm_of_msi_remove,
+};
+
+static __init int apm_of_msi_init(void)
+{
+	return platform_driver_register(&apm_of_msi_driver);
+}
+
+subsys_initcall_sync(apm_of_msi_init);
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 03be990..562a0d5 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -16,6 +16,7 @@ 
  * GNU General Public License for more details.
  *
  */
+#include <linux/version.h>
 #include <linux/clk-private.h>
 #include <linux/delay.h>
 #include <linux/io.h>
@@ -25,11 +26,13 @@ 
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/interrupt.h>
 #include <linux/of_pci.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
-#include <asm/pcibios.h>
+#include <linux/proc_fs.h>
+#include <linux/io.h>
 
 #define PCIECORE_LTSSM			0x4c
 #define PCIECORE_CTLANDSTATUS		0x50
@@ -53,6 +56,7 @@ 
 #define BRIDGE_CFG_4			0x2010
 #define BRIDGE_CFG_32			0x2030
 #define BRIDGE_CFG_14			0x2038
+#define BRIDGE_CTRL_0			0x2200
 #define BRIDGE_CTRL_1			0x2204
 #define BRIDGE_CTRL_2			0x2208
 #define BRIDGE_CTRL_5			0x2214
@@ -60,6 +64,16 @@ 
 #define MEM_RAM_SHUTDOWN                0xd070
 #define BLOCK_MEM_RDY                   0xd074
 
+#define CFG_CONSTANTS_479_448		0x2038
+#define SWITCH_PORT_MODE_MASK		0x00000800
+#define MSIX_CAP_DISABLE_MASK		0x00020000
+#define MSI_CAP_DISABLE_MASK		0x00010000
+#define CFG_CONTROL_191_160		0x2214
+#define CFG_CONTROL_447_416		0x2234
+#define CFG_CONSTANTS_31_00		0x2000
+#define SLOT_IMPLEMENTED_MASK		0x04000000
+#define CFG_CONSTANTS_63_32		0x2004
+#define CFG_CONSTANTS_159_128		0x2010
 #define DEVICE_PORT_TYPE_MASK		0x03c00000
 #define PM_FORCE_RP_MODE_MASK		0x00000400
 #define SWITCH_PORT_MODE_MASK		0x00000800
@@ -74,18 +88,30 @@ 
 #define EN_COHERENCY			0xF0000000
 #define EN_REG				0x00000001
 #define OB_LO_IO			0x00000002
-#define XGENE_PCIE_VENDORID		0xE008
+#define XGENE_PCIE_VENDORID		0x10E8
 #define XGENE_PCIE_DEVICEID		0xE004
 #define XGENE_PCIE_TIMEOUT		(500*1000) /* us */
 #define XGENE_LTSSM_DETECT_WAIT		20
 #define XGENE_LTSSM_L0_WAIT		4
 #define SZ_1T				(SZ_1G*1024ULL)
+#define AXI_EP_DMA_ACCESS		0x20000
+#define XGENE_PCIE_EP_MEM_SIZE		0x100000
+#define PTYPE_ENDPOINT			0
+#define PTYPE_ROOT_PORT			1
 
 struct xgene_res_cfg {
 	struct resource		res;
 	u64			pci_addr;
 };
 
+struct xgene_pcie_ep_info {
+	void __iomem		*reg_virt;/* maps to outbound space of RC */
+	dma_addr_t		reg_phys;/* Physical address of reg space */
+	struct proc_dir_entry	*mem;		/* dump ep mem space */
+	void __iomem		*msi_gen_base;
+	void __iomem		*msi_term_base;
+};
+
 struct xgene_pcie_port {
 	struct device_node	*node;
 	struct device		*dev;
@@ -96,6 +122,9 @@  struct xgene_pcie_port {
 	void __iomem		*csr_base;
 	void __iomem		*cfg_base;
 	u8			link_up;
+	u8			type;
+	u8			dma;
+	struct xgene_pcie_ep_info	ep_info;
 };
 
 static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
@@ -209,17 +238,40 @@  static inline void xgene_pcie_cfg_in8(void __iomem *addr, u8 *val)
 	}
 }
 
+int pcibios_prep_pcie_dma(struct pci_bus *bus)
+{
+	struct xgene_pcie_port *port = xgene_pcie_bus_to_port(bus);
+
+	if (port->dma)
+		return -EBUSY;
+	port->dma = 1;
+	return 0;
+}
+EXPORT_SYMBOL(pcibios_prep_pcie_dma);
+
+void pcibios_cleanup_pcie_dma(struct pci_bus *bus)
+{
+	struct xgene_pcie_port *port = xgene_pcie_bus_to_port(bus);
+	port->dma = 0;
+}
+EXPORT_SYMBOL(pcibios_cleanup_pcie_dma);
+
+#endif
 /* When the address bit [17:16] is 2'b01, the Configuration access will be
  * treated as Type 1 and it will be forwarded to external PCIe device.
  */
 static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
 {
 	struct xgene_pcie_port *port = xgene_pcie_bus_to_port(bus);
+	phys_addr_t addr = (phys_addr_t) port->cfg_base;
 
 	if (bus->number >= (bus->primary + 1))
-		return port->cfg_base + AXI_EP_CFG_ACCESS;
+		addr |= AXI_EP_CFG_ACCESS;
 
-	return port->cfg_base;
+	if (port->dma)
+		addr |= AXI_EP_DMA_ACCESS;
+
+	return (void *)addr;
 }
 
 /* For Configuration request, RTDID register is used as Bus Number,
@@ -306,9 +358,9 @@  static void xgene_pcie_program_core(void __iomem *csr_base)
 {
 	u32 val;
 
-	val = readl(csr_base + BRIDGE_CFG_0);
+	val = readl(csr_base + BRIDGE_CTRL_0);
 	val |= AER_OPTIONAL_ERROR_EN;
-	writel(val, csr_base + BRIDGE_CFG_0);
+	writel(val, csr_base + BRIDGE_CTRL_0);
 	writel(0x0, csr_base + INTXSTATUSMASK);
 	val = readl(csr_base + BRIDGE_CTRL_1);
 	val = (val & ~0xffff) | XGENE_PCIE_DEV_CTRL;
@@ -472,12 +524,12 @@  static int xgene_pcie_map_reg(struct xgene_pcie_port *port,
 	struct resource *res;
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
-	port->csr_base = devm_ioremap_resource(port->dev, res);
+	port->csr_base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(port->csr_base))
 		return PTR_ERR(port->csr_base);
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
-	port->cfg_base = devm_ioremap_resource(port->dev, res);
+	port->cfg_base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(port->cfg_base))
 		return PTR_ERR(port->cfg_base);
 	*cfg_addr = res->start;
@@ -567,7 +619,8 @@  static int xgene_pcie_parse_map_ranges(struct xgene_pcie_port *port,
 			return -EINVAL;
 		}
 	}
-	xgene_pcie_setup_cfg_reg(port->csr_base, cfg_addr);
+	if (port->type == PTYPE_ROOT_PORT)
+		xgene_pcie_setup_cfg_reg(port->csr_base, cfg_addr);
 	return 0;
 }
 
@@ -665,7 +718,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;
 
@@ -682,17 +735,21 @@  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);
 	}
+	if (!(ib_reg_mask &  (1 << 1)))
+		writel(0x0, port->csr_base + IR2MSK);
+	if (!(ib_reg_mask & (1 << 2)))
+		writel(0x0, port->csr_base + IR3MSKL);
 	return 0;
 }
 
@@ -701,6 +758,8 @@  static int xgene_pcie_setup(int nr, struct pci_sys_data *sys)
 	struct xgene_pcie_port *pp = sys->private_data;
 	struct resource *io = &pp->realio;
 
+	if (pp->type == PTYPE_ENDPOINT)
+		return 0;
 	io->start = sys->domain * SZ_64K;
 	io->end = io->start + SZ_64K;
 	io->flags = pp->io.res.flags;
@@ -714,7 +773,184 @@  static int xgene_pcie_setup(int nr, struct pci_sys_data *sys)
 	return 1;
 }
 
-static int xgene_pcie_probe_bridge(struct platform_device *pdev)
+/*
+ * This test requires user to manually change dtbs for
+ * EP port. Following are changes required in DTB
+ * 1. disable MSI node. Add status = "disabled" in MSI node
+ * 2. add 'interrupts = <0x0 0x10 0x4>' in PCIe Port node made as EP
+ * */
+
+static irqreturn_t xgene_misc_isr(int irq, void *data)
+{
+	struct xgene_pcie_port *port = (struct xgene_pcie_port *) data;
+	struct xgene_pcie_ep_info *ep = &port->ep_info;
+	u32 val;
+
+	val = readl(ep->msi_term_base);
+	pr_debug("addr = 0x%llx, data = 0x%x\n", (u64)ep->msi_term_base, val);
+	return IRQ_HANDLED;
+}
+
+static ssize_t ep_read_mem(struct file *file, char __user *buf,
+			    size_t count, loff_t *data)
+{
+	struct xgene_pcie_ep_info *ep = PDE_DATA(file_inode(file));
+	void __iomem *mem = ep->reg_virt;
+	u32 val;
+	int i;
+
+	pr_debug("dump mem - virt = %p , phys = 0x%llx\n", mem, ep->reg_phys);
+	for (i = 0; i < 0x10; i++) {
+		val = readl(mem + (i << 2));
+		pr_debug("[%d] = 0x%08x\n", i, val);
+	}
+	return 0;
+}
+
+static ssize_t ep_write_mem(struct file *file, const char __user *buf,
+			    size_t count, loff_t *data)
+{
+	struct xgene_pcie_ep_info *ep = PDE_DATA(file_inode(file));
+	void __iomem *mem = ep->reg_virt;
+	int i;
+
+	pr_debug("clear mem - virt = %p , phys = 0x%llx\n", mem, ep->reg_phys);
+	for (i = 0; i < 0x10; i++)
+		writel(0x0, mem + (i << 2));
+	return count;
+}
+
+static ssize_t ep_gen_irq(struct file *file, const char __user *buf,
+			    size_t count, loff_t *data)
+{
+	struct xgene_pcie_ep_info *ep = PDE_DATA(file_inode(file));
+	void __iomem *base = ep->msi_gen_base;
+	u32 val;
+
+	writel(1, base); /* enable MSI mode */
+	val = readl(base);
+	writel(0, base + 4); /* enable MSI sources */
+	val = readl(base + 4);
+	val = readl(base + 0x100000); /* INT status */
+	pr_info("**** Generate MSI ******\n");
+	writel(1, base + 0x200000); /* signal message interrupt pending */
+	val = readl(base + 0x200000);
+	writel(1, base + 0xa00000); /* clear int status */
+	val = readl(base + 0xa00000);
+	return count;
+}
+
+static const struct file_operations ep_fops = {
+		.owner = THIS_MODULE,
+		.write = ep_write_mem,
+		.read  = ep_read_mem,
+};
+
+static const struct file_operations ep_irq_fops = {
+		.owner = THIS_MODULE,
+		.write = ep_gen_irq,
+};
+static int xgene_pcie_alloc_ep_mem(struct platform_device *pdev,
+		struct xgene_pcie_port *port)
+{
+	struct device_node *np = of_node_get(pdev->dev.of_node);
+	struct xgene_pcie_ep_info *ep = &port->ep_info;
+	struct proc_dir_entry *entry;
+	struct resource *res;
+	u32 memsize = 0;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "msi_gen");
+	ep->msi_gen_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ep->msi_gen_base))
+		return PTR_ERR(ep->msi_gen_base);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "msi_term");
+	ep->msi_term_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ep->msi_term_base))
+		return PTR_ERR(ep->msi_term_base);
+
+	if (of_property_read_u32(np, "ep-mem-size", &memsize))
+		memsize = XGENE_PCIE_EP_MEM_SIZE;
+
+	ep->mem = proc_mkdir("xgene_pcie_ep", NULL);
+	if (!ep->mem)
+		return -ENOMEM;
+	entry = proc_create_data("mem", S_IRWXUGO, ep->mem, &ep_fops, ep);
+	if (!entry)
+		return -ENOMEM;
+	dev_info(port->dev, "xgene ep - /proc/xgene_pcie_ep/mem created!");
+
+	entry = proc_create_data("irq", S_IRWXUGO, ep->mem, &ep_irq_fops, ep);
+	if (!entry)
+		return -ENOMEM;
+	dev_info(port->dev, "xgene ep - /proc/xgene_pcie_ep/irq created!");
+
+	ep->reg_virt = dma_alloc_coherent(port->dev, memsize, &ep->reg_phys,
+			GFP_KERNEL);
+	if (ep->reg_virt == NULL)
+		return -ENOMEM;
+
+	dev_info(port->dev, "EP: Reg Space - %p Phys - 0x%llx Size - 0x%x\n",
+		 ep->reg_virt, (u64) ep->reg_phys,
+		 memsize);
+
+	return 0;
+}
+
+static int xgene_pcie_setup_endpoint(struct platform_device *pdev,
+		struct xgene_pcie_port *port)
+{
+	void *csr_base = port->csr_base;
+	u32 val;
+	u32 mask_addr;
+	u64 size, pci_addr;
+	int irq;
+	u32 flags = PCI_BASE_ADDRESS_MEM_PREFETCH |
+		PCI_BASE_ADDRESS_MEM_TYPE_64;
+	struct xgene_pcie_ep_info *ep = &port->ep_info;
+	void *pim_addr = port->csr_base + PIM1_1L;
+
+	val = readl(csr_base + CFG_CONSTANTS_479_448);
+	val &= ~SWITCH_PORT_MODE_MASK;
+	val &= ~PM_FORCE_RP_MODE_MASK;
+	writel(val, csr_base + CFG_CONSTANTS_479_448);
+
+	val = readl(csr_base + CFG_CONTROL_191_160);
+	val &= ~DEVICE_PORT_TYPE_MASK;
+	val &= ~SLOT_IMPLEMENTED_MASK;
+	writel(val, csr_base + CFG_CONTROL_191_160);
+
+	val = readl(csr_base + CFG_CONSTANTS_31_00);
+	val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID;
+	writel(val, csr_base + CFG_CONSTANTS_31_00);
+
+	val = readl(csr_base + CFG_CONSTANTS_63_32);
+	val &= ~CLASS_CODE_MASK;
+	val |= PCI_CLASS_BRIDGE_OTHER << 16;
+	writel(val, csr_base + CFG_CONSTANTS_63_32);
+
+	val = readl(csr_base + CFG_CONTROL_447_416);
+	val &= ~(MSI_CAP_DISABLE_MASK | MSIX_CAP_DISABLE_MASK);
+	writel(val, csr_base + CFG_CONTROL_447_416);
+
+	mask_addr = CFG_CONSTANTS_159_128;
+	size = (u64) ~(XGENE_PCIE_EP_MEM_SIZE - 1);
+	xgene_pcie_set_ib_mask(port, mask_addr,
+			XGENE_PCIE_EP_MEM_SIZE, flags);
+
+	if (xgene_pcie_alloc_ep_mem(pdev, port))
+		return -ENOMEM;
+
+	pci_addr = ep->reg_phys;
+	xgene_pcie_setup_pims(pim_addr, pci_addr, size);
+	irq = platform_get_irq(pdev, 0);
+	if (irq > 0)
+		BUG_ON(request_irq(irq, xgene_misc_isr, IRQF_SHARED,
+					"pcie-misc", (void *)port));
+	return 0;
+}
+
+static int __init xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *np = of_node_get(pdev->dev.of_node);
 	struct xgene_pcie_port *port;
@@ -722,6 +958,7 @@  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	u32 lanes = 0, speed = 0;
 	u64 cfg_addr = 0;
 	static u32 domain;
+	const u8 *val;
 	int ret;
 
 	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
@@ -730,6 +967,12 @@  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	port->node = np;
 	port->dev = &pdev->dev;
 
+	val = of_get_property(np, "device_type", NULL);
+	if (val && !strcmp(val, "ep"))
+		port->type = PTYPE_ENDPOINT;
+	else
+		port->type = PTYPE_ROOT_PORT;
+
 	ret = xgene_pcie_map_reg(port, pdev, &cfg_addr);
 	if (ret)
 		return ret;
@@ -738,20 +981,31 @@  static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (ret)
 		goto skip;
 	xgene_pcie_program_core(port->csr_base);
+	if (port->type == PTYPE_ENDPOINT) {
+		ret = xgene_pcie_setup_endpoint(pdev, port);
+		if (ret)
+			return ret;
+		goto skip;
+	}
 	xgene_pcie_setup_root_complex(port);
 	ret = xgene_pcie_parse_map_ranges(port, cfg_addr);
 	if (ret)
 		goto skip;
+
 	ret = xgene_pcie_parse_map_dma_ranges(port);
 	if (ret)
 		goto skip;
+
 	xgene_pcie_poll_linkup(port, &lanes, &speed);
 skip:
-	if (!port->link_up)
-		dev_info(port->dev, "(rc) link down\n");
+	if (port->type == PTYPE_ENDPOINT)
+		dev_info(port->dev, "port: (EP)\n");
 	else
-		dev_info(port->dev, "(rc) x%d gen-%d link up\n",
-				lanes, speed + 1);
+		if (!port->link_up)
+			dev_info(port->dev, "(rc) link down\n");
+		else
+			dev_info(port->dev, "(rc) x%d gen-%d link up\n",
+					lanes, speed + 1);
 	platform_set_drvdata(pdev, port);
 	memset(&xgene_pcie_hw, 0, sizeof(xgene_pcie_hw));
 	xgene_pcie_hw.domain = domain++;
@@ -763,8 +1017,7 @@  skip:
 	pci_common_init(&xgene_pcie_hw);
 	return 0;
 }
-
-static const struct of_device_id xgene_pcie_match_table[] = {
+static const struct of_device_id xgene_pcie_match_table[] __initconst = {
 	{.compatible = "apm,xgene-pcie",},
 	{},
 };