[PATCH V7 02/17] SPEAr13xx: Add PCIe host controller base driver support.

Viresh Kumar viresh.kumar at st.com
Wed Mar 23 00:52:13 EDT 2011


From: Pratyush Anand <pratyush.anand at st.com>

SPEAr13xx family contains Synopsys designware PCIe version 3.30a. This
patch adds support for this PCIe module for spear platform.

Changes since V6:
- Read request size in RC'c PCIE capability is forced to 128 bytes.
- Max payload is forced to the minimum value of max payload of any of the
  device in tree.
- Request_resource for IO Space is from ioport_resource now. Earlier it was from
  iomem_resource.
- Callback for evb is made generic
- RC is programmed as virtual bridge
- Support for gen1 initilization as default option
- code optimization and modification for cfg1 rd/wr
- Adding valid VID and DID for RC
- Inbound address range increased to 0-32GB address space
- Review Comments incorporated. Main changes are:
 - Removed unnecessary define CONFIG_PCI
 - All HW addresses have been modified to __iomem *
 - unnecessary typecasting have been removed
 - all hardware address are being read by readl now

Reviewed-by: Stanley Miao <stanley.miao at windriver.com>
Signed-off-by: Pratyush Anand <pratyush.anand at st.com>
Signed-off-by: shiraz hashim <shiraz.hashim at st.com>
Signed-off-by: Viresh Kumar <viresh.kumar at st.com>
---
 arch/arm/Kconfig                                |    1 +
 arch/arm/mach-spear13xx/Makefile                |    1 +
 arch/arm/mach-spear13xx/include/mach/hardware.h |    4 +
 arch/arm/mach-spear13xx/include/mach/irqs.h     |   19 +-
 arch/arm/mach-spear13xx/include/mach/pcie.h     |  176 ++++
 arch/arm/mach-spear13xx/pcie.c                  | 1129 +++++++++++++++++++++++
 arch/arm/mach-spear13xx/spear1300_evb.c         |   38 +
 arch/arm/mach-spear13xx/spear1310_evb.c         |   38 +
 arch/arm/mach-spear13xx/spear13xx.c             |   28 +
 arch/arm/plat-spear/Kconfig                     |    2 +
 10 files changed, 1435 insertions(+), 1 deletions(-)
 create mode 100644 arch/arm/mach-spear13xx/include/mach/pcie.h
 create mode 100644 arch/arm/mach-spear13xx/pcie.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d9efe86..0aca70d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1263,6 +1263,7 @@ config PCI_HOST_ITE8152
 	select DMABOUNCE
 
 source "drivers/pci/Kconfig"
+source "drivers/pci/pcie/Kconfig"
 
 source "drivers/pcmcia/Kconfig"
 
diff --git a/arch/arm/mach-spear13xx/Makefile b/arch/arm/mach-spear13xx/Makefile
index 24bbe16..2a113b0 100644
--- a/arch/arm/mach-spear13xx/Makefile
+++ b/arch/arm/mach-spear13xx/Makefile
@@ -7,6 +7,7 @@ obj-y					+= spear13xx.o clock.o
 obj-$(CONFIG_SMP)			+= platsmp.o headsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)		+= hotplug.o
 obj-$(CONFIG_LOCAL_TIMERS)		+= localtimer.o
+obj-$(CONFIG_PCIEPORTBUS)		+= pcie.o
 
 # spear1300 specific files
 obj-$(CONFIG_MACH_SPEAR1300)		+= spear1300.o
diff --git a/arch/arm/mach-spear13xx/include/mach/hardware.h b/arch/arm/mach-spear13xx/include/mach/hardware.h
index fd8c2dc..6169d4f 100644
--- a/arch/arm/mach-spear13xx/include/mach/hardware.h
+++ b/arch/arm/mach-spear13xx/include/mach/hardware.h
@@ -28,4 +28,8 @@
 /* typesafe io address */
 #define __io_address(n)		__io(IO_ADDRESS(n))
 
+#define PCIBIOS_MIN_IO		0
+#define PCIBIOS_MIN_MEM		0
+#define pcibios_assign_all_busses()	0
+
 #endif /* __MACH_HARDWARE_H */
diff --git a/arch/arm/mach-spear13xx/include/mach/irqs.h b/arch/arm/mach-spear13xx/include/mach/irqs.h
index c4f0c9d..59bf61a 100644
--- a/arch/arm/mach-spear13xx/include/mach/irqs.h
+++ b/arch/arm/mach-spear13xx/include/mach/irqs.h
@@ -130,7 +130,24 @@
 #define SPEAR_GPIO1_INT_BASE	(SPEAR_GPIO0_INT_BASE + 8)
 #define SPEAR_GPIO_INT_END	(SPEAR_GPIO1_INT_BASE + 8)
 
-#define VIRQ_END		SPEAR_GPIO_INT_END
+/* PCIE MSI virtual irqs */
+#define SPEAR_NUM_MSI_IRQS	64
+#define SPEAR_MSI0_INT_BASE	(SPEAR_GPIO_INT_END + 0)
+#define SPEAR_MSI0_INT_END	(SPEAR_MSI0_INT_BASE + SPEAR_NUM_MSI_IRQS)
+#define SPEAR_MSI1_INT_BASE	(SPEAR_MSI0_INT_END + 0)
+#define SPEAR_MSI1_INT_END	(SPEAR_MSI1_INT_BASE + SPEAR_NUM_MSI_IRQS)
+#define SPEAR_MSI2_INT_BASE	(SPEAR_MSI1_INT_END + 0)
+#define SPEAR_MSI2_INT_END	(SPEAR_MSI2_INT_BASE + SPEAR_NUM_MSI_IRQS)
+
+#define SPEAR_NUM_INTX_IRQS	4
+#define SPEAR_INTX0_BASE	(SPEAR_MSI2_INT_END + 0)
+#define SPEAR_INTX0_END		(SPEAR_INTX0_BASE + SPEAR_NUM_INTX_IRQS)
+#define SPEAR_INTX1_BASE	(SPEAR_INTX0_END + 0)
+#define SPEAR_INTX1_END		(SPEAR_INTX1_BASE + SPEAR_NUM_INTX_IRQS)
+#define SPEAR_INTX2_BASE	(SPEAR_INTX1_END + 0)
+#define SPEAR_INTX2_END		(SPEAR_INTX2_BASE + SPEAR_NUM_INTX_IRQS)
+
+#define VIRQ_END		SPEAR_INTX2_END
 #define NR_IRQS			VIRQ_END
 
 #endif /* __MACH_IRQS_H */
diff --git a/arch/arm/mach-spear13xx/include/mach/pcie.h b/arch/arm/mach-spear13xx/include/mach/pcie.h
new file mode 100644
index 0000000..7ea10da
--- /dev/null
+++ b/arch/arm/mach-spear13xx/include/mach/pcie.h
@@ -0,0 +1,176 @@
+/*
+ * arch/arm/mach-spear13xx/include/mach/pcie.h
+ *
+ * Spear SoC PCIe handling.
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Pratyush Anand <pratyush.anand at st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_PCIE_H
+#define __MACH_PCIE_H
+
+extern int enable_pcie0_clk(void);
+
+struct pcie_port_info {
+	u8	is_host;
+	u8	is_gen1;
+};
+extern struct pcie_port_info *(*pcie_port_init)(int port);
+
+struct pcie_port {
+	u8			port;
+	u8			root_bus_nr;
+	void __iomem		*base;
+	void __iomem		*app_base;
+	void __iomem		*va_app_base;
+	void __iomem		*va_dbi_base;
+	void __iomem		*va_cfg0_base;
+	void __iomem		*va_cfg1_base;
+	spinlock_t		conf_lock;
+	char			mem_space_name[16];
+	char			io_space_name[16];
+	struct resource		res[2];
+	struct pcie_port_info	config;
+};
+
+struct pcie_app_reg {
+	u32	app_ctrl_0;		/*cr0*/
+	u32	app_ctrl_1;		/*cr1*/
+	u32	app_status_0;		/*cr2*/
+	u32	app_status_1;		/*cr3*/
+	u32	msg_status;		/*cr4*/
+	u32	msg_payload;		/*cr5*/
+	u32	int_sts;		/*cr6*/
+	u32	int_clr;		/*cr7*/
+	u32	int_mask;		/*cr8*/
+	u32	mst_bmisc;		/*cr9*/
+	u32	phy_ctrl;		/*cr10*/
+	u32	phy_status;		/*cr11*/
+	u32	cxpl_debug_info_0;	/*cr12*/
+	u32	cxpl_debug_info_1;	/*cr13*/
+	u32	ven_msg_ctrl_0;		/*cr14*/
+	u32	ven_msg_ctrl_1;		/*cr15*/
+	u32	ven_msg_data_0;		/*cr16*/
+	u32	ven_msg_data_1;		/*cr17*/
+	u32	ven_msi_0;		/*cr18*/
+	u32	ven_msi_1;		/*cr19*/
+	u32	mst_rmisc;		/*cr 20*/
+	u32	slv_awmisc;		/*cr 21*/
+	u32	slv_armisc;		/*cr 22*/
+	u32	pom0_mem_addr_start;	/*cr23*/
+	u32	pom1_mem_addr_start;	/*cr24*/
+	u32	pom_io_addr_start;	/*cr25*/
+	u32	pom_cfg0_addr_start;	/*cr26*/
+	u32	pom_cfg1_addr_start;	/*cr27*/
+	u32	in0_mem_addr_start;	/*cr28*/
+	u32	in1_mem_addr_start;	/*cr29*/
+	u32	in_io_addr_start;	/*cr30*/
+	u32	in_cfg0_addr_start;	/*cr31*/
+	u32	in_cfg1_addr_start;	/*cr32*/
+	u32	in_msg_addr_start;	/*cr33*/
+	u32	in0_mem_addr_limit;	/*cr34*/
+	u32	in1_mem_addr_limit;	/*cr35*/
+	u32	in_io_addr_limit;	/*cr36*/
+	u32	in_cfg0_addr_limit;	/*cr37*/
+	u32	in_cfg1_addr_limit;	/*cr38*/
+	u32	in_msg_addr_limit;	/*cr39*/
+	u32	mem0_addr_offset_limit;	/*cr40*/
+	u32	pim0_mem_addr_start;	/*cr41*/
+	u32	pim1_mem_addr_start;	/*cr42*/
+	u32	pim_io_addr_start;	/*cr43*/
+	u32	pim_rom_addr_start;	/*cr44*/
+};
+
+/*CR0 ID*/
+#define RX_LANE_FLIP_EN_ID	0
+#define TX_LANE_FLIP_EN_ID	1
+#define SYS_AUX_PWR_DET_ID	2
+#define APP_LTSSM_ENABLE_ID	3
+#define SYS_ATTEN_BUTTON_PRESSED_ID	4
+#define SYS_MRL_SENSOR_STATE_ID 5
+#define SYS_PWR_FAULT_DET_ID	6
+#define SYS_MRL_SENSOR_CHGED_ID 7
+#define SYS_PRE_DET_CHGED_ID	8
+#define SYS_CMD_CPLED_INT_ID	9
+#define APP_INIT_RST_0_ID	11
+#define APP_REQ_ENTR_L1_ID	12
+#define APP_READY_ENTR_L23_ID	13
+#define APP_REQ_EXIT_L1_ID	14
+#define DEVICE_TYPE_EP		(0 << 25)
+#define DEVICE_TYPE_LEP		(1 << 25)
+#define DEVICE_TYPE_RC		(4 << 25)
+#define SYS_INT_ID		29
+#define MISCTRL_EN_ID		30
+#define REG_TRANSLATION_ENABLE	31
+
+/*CR1 ID*/
+#define APPS_PM_XMT_TURNOFF_ID	2
+#define APPS_PM_XMT_PME_ID	5
+
+/*CR3 ID*/
+#define XMLH_LTSSM_STATE_ID	0
+#define XMLH_LTSSM_STATE_L0	((u32)0x11 << XMLH_LTSSM_STATE_ID)
+#define XMLH_LTSSM_STATE_MASK	((u32)0x1F << XMLH_LTSSM_STATE_ID)
+#define XMLH_LINK_UP_ID	5
+
+/*CR4 ID*/
+#define CFG_MSI_EN_ID	18
+
+/*CR6*/
+#define INTA_CTRL_INT	(1 << 7)
+#define INTB_CTRL_INT	(1 << 8)
+#define INTC_CTRL_INT	(1 << 9)
+#define INTD_CTRL_INT	(1 << 10)
+#define MSI_CTRL_INT	(1 << 26)
+
+/*CR19 ID*/
+#define VEN_MSI_REQ_ID	11
+#define VEN_MSI_FUN_NUM_ID	8
+#define VEN_MSI_TC_ID	5
+#define VEN_MSI_VECTOR_ID	0
+#define VEN_MSI_REQ_EN	((u32)0x1 << VEN_MSI_REQ_ID)
+#define VEN_MSI_FUN_NUM_MASK	((u32)0x7 << VEN_MSI_FUN_NUM_ID)
+#define VEN_MSI_TC_MASK	((u32)0x7 << VEN_MSI_TC_ID)
+#define VEN_MSI_VECTOR_MASK	((u32)0x1F << VEN_MSI_VECTOR_ID)
+
+/*CE21-22 ID*/
+/*ID definitio of ARMISC*/
+#define AXI_OP_TYPE_ID	0
+#define AXI_OP_BCM_ID	5
+#define AXI_OP_EP_ID	6
+#define AXI_OP_TD_ID	7
+#define AXI_OP_ATTRIBUTE_ID	8
+#define AXI_OP_TC_ID	10
+#define AXI_OP_MSG_CODE_ID	13
+#define AXI_OP_DBI_ACCESS_ID	21
+#define AXI_OP_TYPE_MASK	0x1F
+#define AXI_OP_TYPE_MEM_RDRW	0
+#define AXI_OP_TYPE_MEM_RDRW_LOCKED	1
+#define AXI_OP_TYPE_IO_RDRW	2
+#define AXI_OP_TYPE_CONFIG_RDRW_TYPE0	4
+#define AXI_OP_TYPE_CONFIG_RDRW_TYPE1	5
+#define AXI_OP_TYPE_MSG_REQ	16
+#define AXI_OP_TYPE_COMPLETION	10
+#define AXI_OP_TYPE_COMPLETION_LOCKED	11
+#define AXI_OP_TYPE_DBI_ELBI_ENABLE	1
+
+/* synopsis specific PCIE configuration registers*/
+#define PCIE_MSI_ADDR_LO	0x820	/* 32 bits */
+#define PCIE_MSI_ADDR_HI	0x824	/* 32 bits */
+#define PCIE_MSI_INTR0_ENABLE	0x828	/* 32 bits */
+#define PCIE_MSI_INTR0_MASK	0x82C	/* 32 bits */
+#define PCIE_MSI_INTR0_STATUS	0x830	/* 32 bits */
+
+/*BAR MASK registers*/
+#define PCIE_BAR0_MASK_REG	0x1010
+
+static inline void pcie_init(struct pcie_port_info * (*fptr)(int port))
+{
+	pcie_port_init = fptr;
+}
+#endif
diff --git a/arch/arm/mach-spear13xx/pcie.c b/arch/arm/mach-spear13xx/pcie.c
new file mode 100644
index 0000000..104dd03
--- /dev/null
+++ b/arch/arm/mach-spear13xx/pcie.c
@@ -0,0 +1,1129 @@
+/*
+ * arch/arm/mach-spear13xx/pcie.c
+ *
+ * PCIe functions for SPEAr 13xx SoCs
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Pratyush Anand <pratyush.anand at st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/msi.h>
+#include <linux/mbus.h>
+#include <linux/sched.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/pci.h>
+#include <mach/hardware.h>
+#include <mach/misc_regs.h>
+#include <mach/pcie.h>
+
+#define NUM_PCIE_PORTS	3
+
+/* Sum of all these space can maximum be 256MB*/
+#define IN0_MEM_SIZE	(200 * 1024 * 1024 - 1)
+
+/*
+ * In current implementation address translation is done using IN0 only. So IN1
+ * start address and IN0 end address has been kept same
+*/
+#define IN1_MEM_SIZE	(0 * 1024 * 1024 - 1)
+#define IN_IO_SIZE	(20 * 1024 * 1024 - 1)
+#define IN_CFG0_SIZE	(1 * 1024 * 1024 - 1)
+#define IN_CFG1_SIZE	(1 * 1024 * 1024 - 1)
+#define IN_MSG_SIZE	(1 * 1024 * 1024 - 1)
+
+#define MAX_LINK_UP_WAIT_MS	2
+
+/* Keeping 0-32GB of address range accesible for inbound transaction */
+#define INBOUND_ADDR_MASK	0x7FFFFFFF
+
+struct pcie_port_info *(*pcie_port_init)(int port);
+static struct pcie_port pcie_port[NUM_PCIE_PORTS];
+static void __iomem *pcie_base[NUM_PCIE_PORTS] = {
+	IOMEM(SPEAR13XX_PCIE0_BASE),
+	IOMEM(SPEAR13XX_PCIE1_BASE),
+	IOMEM(SPEAR13XX_PCIE2_BASE),
+};
+static void __iomem *pcie_app_base[NUM_PCIE_PORTS] = {
+	IOMEM(SPEAR13XX_PCIE0_APP_BASE),
+	IOMEM(SPEAR13XX_PCIE1_APP_BASE),
+	IOMEM(SPEAR13XX_PCIE2_APP_BASE),
+};
+
+static void enable_dbi_access(struct pcie_app_reg __iomem *app_reg)
+{
+	/* Enable DBI access */
+	writel(readl(&app_reg->slv_armisc) | (1 << AXI_OP_DBI_ACCESS_ID),
+			&app_reg->slv_armisc);
+	writel(readl(&app_reg->slv_awmisc) | (1 << AXI_OP_DBI_ACCESS_ID),
+			&app_reg->slv_awmisc);
+	wmb();
+}
+
+static void disable_dbi_access(struct pcie_app_reg __iomem *app_reg)
+{
+	/* disable DBI access */
+	wmb();
+	writel(readl(&app_reg->slv_armisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
+			&app_reg->slv_armisc);
+	writel(readl(&app_reg->slv_awmisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
+			&app_reg->slv_awmisc);
+}
+
+static void spear_dbi_read_reg(struct pcie_port *pp, int where, int size,
+		u32 *val)
+{
+	struct pcie_app_reg __iomem *app_reg = pp->va_app_base;
+	u32 va_address;
+
+	/* Enable DBI access */
+	enable_dbi_access(app_reg);
+
+	va_address = (u32)pp->va_dbi_base + (where & ~0x3);
+
+	*val = readl(va_address);
+
+	if (size == 1)
+		*val = (*val >> (8 * (where & 3))) & 0xff;
+	else if (size == 2)
+		*val = (*val >> (8 * (where & 3))) & 0xffff;
+
+	/* Disable DBI access */
+	disable_dbi_access(app_reg);
+}
+
+static void spear_dbi_write_reg(struct pcie_port *pp, int where, int size,
+		u32 val)
+{
+	struct pcie_app_reg __iomem *app_reg = pp->va_app_base;
+	u32 va_address;
+
+	/* Enable DBI access */
+	enable_dbi_access(app_reg);
+
+	va_address = (u32)pp->va_dbi_base + (where & ~0x3);
+
+	if (size == 4)
+		writel(val, va_address);
+	else if (size == 2)
+		writew(val, va_address + (where & 2));
+	else if (size == 1)
+		writeb(val, va_address + (where & 3));
+
+	/* Disable DBI access */
+	disable_dbi_access(app_reg);
+}
+
+#define PCI_FIND_CAP_TTL	48
+
+static int pci_find_own_next_cap_ttl(struct pcie_port *pp,
+		u32 pos, int cap, int *ttl)
+{
+	u32 id;
+
+	while ((*ttl)--) {
+		spear_dbi_read_reg(pp, pos, 1, &pos);
+		if (pos < 0x40)
+			break;
+		pos &= ~3;
+		spear_dbi_read_reg(pp, pos + PCI_CAP_LIST_ID, 1, &id);
+		if (id == 0xff)
+			break;
+		if (id == cap)
+			return pos;
+		pos += PCI_CAP_LIST_NEXT;
+	}
+	return 0;
+}
+
+static int pci_find_own_next_cap(struct pcie_port *pp, u32 pos, int cap)
+{
+	int ttl = PCI_FIND_CAP_TTL;
+
+	return pci_find_own_next_cap_ttl(pp, pos, cap, &ttl);
+}
+
+static int pci_find_own_cap_start(struct pcie_port *pp, u8 hdr_type)
+{
+	u32 status;
+
+	spear_dbi_read_reg(pp, PCI_STATUS, 2, &status);
+	if (!(status & PCI_STATUS_CAP_LIST))
+		return 0;
+
+	switch (hdr_type) {
+	case PCI_HEADER_TYPE_NORMAL:
+	case PCI_HEADER_TYPE_BRIDGE:
+		return PCI_CAPABILITY_LIST;
+	case PCI_HEADER_TYPE_CARDBUS:
+		return PCI_CB_CAPABILITY_LIST;
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
+/**
+ * Tell if a device supports a given PCI capability.
+ * Returns the address of the requested capability structure within the
+ * device's PCI configuration space or 0 in case the device does not
+ * support it. Possible values for @cap:
+ *
+ * %PCI_CAP_ID_PM	Power Management
+ * %PCI_CAP_ID_AGP	Accelerated Graphics Port
+ * %PCI_CAP_ID_VPD	Vital Product Data
+ * %PCI_CAP_ID_SLOTID	Slot Identification
+ * %PCI_CAP_ID_MSI	Message Signalled Interrupts
+ * %PCI_CAP_ID_CHSWP	CompactPCI HotSwap
+ * %PCI_CAP_ID_PCIX	PCI-X
+ * %PCI_CAP_ID_EXP	PCI Express
+ */
+static int pci_find_own_capability(struct pcie_port *pp, int cap)
+{
+	u32 pos;
+	u32 hdr_type;
+
+	spear_dbi_read_reg(pp, PCI_HEADER_TYPE, 1, &hdr_type);
+
+	pos = pci_find_own_cap_start(pp, hdr_type);
+	if (pos)
+		pos = pci_find_own_next_cap(pp, pos, cap);
+
+	return pos;
+}
+
+static struct pcie_port *bus_to_port(int bus)
+{
+	int i;
+
+	for (i = NUM_PCIE_PORTS - 1; i >= 0; i--) {
+		int rbus = pcie_port[i].root_bus_nr;
+		if (!(pcie_port[i].config.is_host))
+			continue;
+		if (rbus != -1 && rbus <= bus)
+			break;
+	}
+
+	return i >= 0 ? pcie_port + i : NULL;
+}
+
+#ifdef CONFIG_PCI_MSI
+static DECLARE_BITMAP(msi_irq_in_use[NUM_PCIE_PORTS], SPEAR_NUM_MSI_IRQS);
+static unsigned int spear_msi_data[NUM_PCIE_PORTS];
+
+/* MSI int handler
+ */
+static void handle_msi(struct pcie_port *pp)
+{
+	unsigned long val;
+	int i, pos;
+
+	for (i = 0; i < 8; i++) {
+		spear_dbi_read_reg(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4,
+				(u32 *)&val);
+		if (val) {
+			pos = 0;
+			while ((pos = find_next_bit(&val, 32, pos)) != 32) {
+				generic_handle_irq(SPEAR_MSI0_INT_BASE
+					+ pp->port * SPEAR_NUM_MSI_IRQS
+					+ (i * 32) + pos);
+				pos++;
+			}
+		}
+		spear_dbi_write_reg(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val);
+	}
+}
+
+static int find_valid_pos0(int port, int nvec, int pos, int *pos0)
+{
+	int flag = 1;
+	do {
+		pos = find_next_zero_bit(msi_irq_in_use[port],
+				SPEAR_NUM_MSI_IRQS, pos);
+		/*if you have reached to the end then get out from here.*/
+		if (pos == SPEAR_NUM_MSI_IRQS)
+			return -ENOSPC;
+		/* Check if this position is at correct offset.nvec is always a
+		 * power of two. pos0 must be nvec bit alligned.
+		 */
+		if (pos % nvec)
+			pos += nvec - (pos % nvec);
+		else
+			flag = 0;
+	} while (flag);
+
+	*pos0 = pos;
+	return 0;
+}
+
+static void spear13xx_msi_nop(unsigned int irq)
+{
+	return;
+}
+
+static struct irq_chip spear13xx_msi_chip = {
+	.name = "PCI-MSI",
+	.ack = spear13xx_msi_nop,
+	.irq_enable = unmask_msi_irq,
+	.irq_disable = mask_msi_irq,
+	.irq_mask = mask_msi_irq,
+	.irq_unmask = unmask_msi_irq,
+};
+
+/*
+ * Dynamic irq allocate and deallocation
+ */
+static int get_irq(int nvec, struct msi_desc *desc, int *pos)
+{
+	int res, bit, irq, pos0, pos1, i;
+	u32 val;
+	struct pcie_port *pp = bus_to_port(desc->dev->bus->number);
+
+	pos0 = find_first_zero_bit(msi_irq_in_use[pp->port],
+			SPEAR_NUM_MSI_IRQS);
+	if (pos0 % nvec) {
+		if (find_valid_pos0(pp->port, nvec, pos0, &pos0))
+			goto no_valid_irq;
+	}
+	if (nvec > 1) {
+		pos1 = find_next_bit(msi_irq_in_use[pp->port],
+				SPEAR_NUM_MSI_IRQS, pos0);
+		/* there must be nvec number of consecutive free bits */
+		while ((pos1 - pos0) < nvec) {
+			if (find_valid_pos0(pp->port, nvec, pos1, &pos0))
+				goto no_valid_irq;
+			pos1 = find_next_bit(msi_irq_in_use[pp->port],
+					SPEAR_NUM_MSI_IRQS, pos0);
+		}
+	}
+
+	irq = (SPEAR_MSI0_INT_BASE + (pp->port * SPEAR_NUM_MSI_IRQS)) + pos0;
+
+	if ((irq + nvec) > (SPEAR_MSI0_INT_END
+				+ (pp->port * SPEAR_NUM_MSI_IRQS)))
+		goto no_valid_irq;
+
+	i = 0;
+	while (i < nvec) {
+		set_bit(pos0 + i, msi_irq_in_use[pp->port]);
+		dynamic_irq_init(irq + i);
+		set_irq_msi(irq + i, desc);
+		set_irq_chip_and_handler(irq + i, &spear13xx_msi_chip,
+				handle_simple_irq);
+
+		/* Enable corresponding interrupt on MSI interrupt
+		 * controller.
+		 */
+		res = ((pos0 + i) / 32) * 12;
+		bit = (pos0 + i) % 32;
+		spear_dbi_read_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
+		val |= 1 << bit;
+		spear_dbi_write_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
+
+		i++;
+	}
+
+	*pos = pos0;
+	return irq;
+no_valid_irq:
+	*pos = pos0;
+	return -ENOSPC;
+}
+
+static void clean_irq(unsigned int irq)
+{
+	int res, bit, val, pos;
+	struct irq_desc *desc = irq_to_desc(irq);
+	struct pcie_port *pp = bus_to_port(desc->msi_desc->dev->bus->number);
+
+	pos = irq - (SPEAR_MSI0_INT_BASE + (pp->port * SPEAR_NUM_MSI_IRQS));
+
+	dynamic_irq_cleanup(irq);
+
+	clear_bit(pos, msi_irq_in_use[pp->port]);
+
+	/* Disable corresponding interrupt on MSI interrupt
+	 * controller.
+	 */
+	res = (pos / 32) * 12;
+	bit = pos % 32;
+	spear_dbi_read_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
+	val &= ~(1 << bit);
+	spear_dbi_write_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
+
+}
+
+int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
+{
+	int cvec, rvec, irq, pos;
+	struct msi_msg msg;
+	uint16_t control;
+	struct pcie_port *pp = bus_to_port(pdev->bus->number);
+
+	/*
+	 * Read the MSI config to figure out how many IRQs this device
+	 * wants.Most devices only want 1, which will give
+	 * configured_private_bits and request_private_bits equal 0.
+	 */
+	pci_read_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS,
+			&control);
+
+	/*
+	 * If the number of private bits has been configured then use
+	 * that value instead of the requested number. This gives the
+	 * driver the chance to override the number of interrupts
+	 * before calling pci_enable_msi().
+	 */
+
+	cvec = (control & PCI_MSI_FLAGS_QSIZE) >> 4;
+
+	if (cvec == 0) {
+		/* Nothing is configured, so use the hardware requested size */
+		rvec = (control & PCI_MSI_FLAGS_QMASK) >> 1;
+	} else {
+		/*
+		 * Use the number of configured bits, assuming the
+		 * driver wanted to override the hardware request
+		 * value.
+		 */
+		rvec = cvec;
+	}
+
+	/*
+	 * The PCI 2.3 spec mandates that there are at most 32
+	 * interrupts. If this device asks for more, only give it one.
+	 */
+	if (rvec > 5)
+		rvec = 0;
+
+	irq = get_irq((1 << rvec), desc, &pos);
+
+	 if (irq < 0)
+		return irq;
+
+	 /* Update the number of IRQs the device has available to it */
+	 control &= ~PCI_MSI_FLAGS_QSIZE;
+	 control |= rvec << 4;
+	 pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS,
+			 control);
+	 desc->msi_attrib.multiple = rvec;
+
+	/* An EP will modify lower 8 bits(max) of msi data while
+	 * sending any msi interrupt
+	 */
+	msg.address_hi = 0x0;
+	msg.address_lo = __virt_to_phys((u32)(&spear_msi_data[pp->port]));
+	msg.data = pos;
+	write_msi_msg(irq, &msg);
+
+	return 0;
+}
+
+void arch_teardown_msi_irq(unsigned int irq)
+{
+	clean_irq(irq);
+}
+
+static void spear13xx_msi_init(struct pcie_port *pp)
+{
+	struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->va_app_base;
+
+	spear_dbi_write_reg(pp, PCIE_MSI_ADDR_LO, 4,
+			__virt_to_phys((u32)(&spear_msi_data[pp->port])));
+	spear_dbi_write_reg(pp, PCIE_MSI_ADDR_HI, 4, 0);
+	/* Enbale MSI interrupt*/
+	writel(readl(&app_reg->int_mask) | MSI_CTRL_INT,
+			&app_reg->int_mask);
+}
+#endif
+
+static int spear13xx_pcie_link_up(void __iomem *va_app_base)
+{
+	struct pcie_app_reg __iomem *app_reg = va_app_base;
+	int ucount = 0;
+
+	do {
+		if (readl(&app_reg->app_status_1) &
+				((u32)1 << XMLH_LINK_UP_ID))
+			return 1;
+		ucount++;
+		udelay(1);
+	} while (ucount <= MAX_LINK_UP_WAIT_MS * 1000);
+
+	return 0;
+}
+
+static void spear13xx_pcie_host_init(struct pcie_port *pp)
+{
+	struct pcie_app_reg __iomem *app_reg = pp->va_app_base;
+	u32 cap, val;
+
+	/*setup registers for outbound translation */
+
+	writel(pp->base, &app_reg->in0_mem_addr_start);
+	writel(app_reg->in0_mem_addr_start + IN0_MEM_SIZE,
+			&app_reg->in0_mem_addr_limit);
+	writel(app_reg->in0_mem_addr_limit + 1, &app_reg->in1_mem_addr_start);
+	writel(app_reg->in1_mem_addr_start + IN1_MEM_SIZE,
+			&app_reg->in1_mem_addr_limit);
+	writel(app_reg->in1_mem_addr_limit + 1, &app_reg->in_io_addr_start);
+	writel(app_reg->in_io_addr_start + IN_IO_SIZE,
+			&app_reg->in_io_addr_limit);
+	writel(app_reg->in_io_addr_limit + 1, &app_reg->in_cfg0_addr_start);
+	writel(app_reg->in_cfg0_addr_start + IN_CFG0_SIZE,
+			&app_reg->in_cfg0_addr_limit);
+	writel(app_reg->in_cfg0_addr_limit + 1, &app_reg->in_cfg1_addr_start);
+	writel(app_reg->in_cfg1_addr_start + IN_CFG1_SIZE,
+			&app_reg->in_cfg1_addr_limit);
+	writel(app_reg->in_cfg1_addr_limit + 1, &app_reg->in_msg_addr_start);
+	writel(app_reg->in_msg_addr_start + IN_MSG_SIZE,
+			&app_reg->in_msg_addr_limit);
+
+	writel(app_reg->in0_mem_addr_start, &app_reg->pom0_mem_addr_start);
+	writel(app_reg->in1_mem_addr_start, &app_reg->pom1_mem_addr_start);
+	writel(app_reg->in_io_addr_start, &app_reg->pom_io_addr_start);
+
+	/*setup registers for inbound translation */
+
+	writel((u32)INBOUND_ADDR_MASK + 1, &app_reg->mem0_addr_offset_limit);
+	writel(0, &app_reg->pim0_mem_addr_start);
+	writel(0, &app_reg->pim1_mem_addr_start);
+	spear_dbi_write_reg(pp, PCIE_BAR0_MASK_REG, 4, INBOUND_ADDR_MASK);
+	spear_dbi_write_reg(pp, PCI_BASE_ADDRESS_0, 4, 0);
+
+	writel(0x0, &app_reg->pim_io_addr_start);
+	writel(0x0, &app_reg->pim_io_addr_start);
+	writel(0x0, &app_reg->pim_rom_addr_start);
+
+	cap = pci_find_own_capability(pp, PCI_CAP_ID_EXP);
+	/*this controller support only 128 bytes read size, however its
+	 * default value in capability register is 512 bytes. So force
+	 * it to 128 here */
+
+	spear_dbi_read_reg(pp, cap + PCI_EXP_DEVCTL, 4, &val);
+	val &= ~PCI_EXP_DEVCTL_READRQ;
+	spear_dbi_write_reg(pp, cap + PCI_EXP_DEVCTL, 4, val);
+
+	/*program correct class for RC*/
+	spear_dbi_read_reg(pp, PCI_CLASS_REVISION, 4, &val);
+	val &= 0xFFFF;
+	val |= (PCI_CLASS_BRIDGE_PCI << 16);
+	spear_dbi_write_reg(pp, PCI_CLASS_REVISION, 4, val);
+	/*program vid and did for RC*/
+	spear_dbi_write_reg(pp, PCI_VENDOR_ID, 2, 0x104A);
+	spear_dbi_write_reg(pp, PCI_DEVICE_ID, 2, 0xCD80);
+	/*if is_gen1 is set then handle it*/
+	if (pp->config.is_gen1) {
+		cap = pci_find_own_capability(pp, PCI_CAP_ID_EXP);
+		spear_dbi_read_reg(pp, cap + PCI_EXP_LNKCAP, 4, &val);
+		if ((val & 0xF) != 1) {
+			val &= ~((u32)0xF);
+			val |= 1;
+			spear_dbi_write_reg(pp, cap + PCI_EXP_LNKCAP, 4,
+					val);
+		}
+		spear_dbi_read_reg(pp, cap + PCI_EXP_LNKCTL2, 4, &val);
+		if ((val & 0xF) != 1) {
+			val &= ~((u32)0xF);
+			val |= 1;
+			spear_dbi_write_reg(pp, cap + PCI_EXP_LNKCTL2, 4,
+					val);
+		}
+	}
+
+	writel(DEVICE_TYPE_RC | (1 << MISCTRL_EN_ID)
+			| (1 << APP_LTSSM_ENABLE_ID)
+			| ((u32)1 << REG_TRANSLATION_ENABLE),
+			&app_reg->app_ctrl_0);
+}
+
+static void __init spear13xx_pcie_preinit(void)
+{
+	int i;
+	struct pcie_port *pp;
+	struct pcie_app_reg __iomem *app_reg;
+
+	for (i = 0; i < NUM_PCIE_PORTS; i++) {
+		pp = pcie_port + i;
+		app_reg = pp->va_app_base;
+
+		if (!(pp->config.is_host))
+			continue;
+		snprintf(pp->mem_space_name, sizeof(pp->mem_space_name),
+			"PCIe %d MEM", pp->port);
+		pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0;
+		pp->res[0].name = pp->mem_space_name;
+		pp->res[0].start = readl(&app_reg->in0_mem_addr_start);
+		pp->res[0].end = readl(&app_reg->in0_mem_addr_limit);
+		pp->res[0].flags = IORESOURCE_MEM;
+
+		snprintf(pp->io_space_name, sizeof(pp->io_space_name),
+			"PCIe %d I/O", pp->port);
+		pp->io_space_name[sizeof(pp->io_space_name) - 1] = 0;
+		pp->res[1].name = pp->io_space_name;
+		pp->res[1].start = readl(&app_reg->in_io_addr_start);
+		pp->res[1].end = readl(&app_reg->in_io_addr_limit);
+		pp->res[1].flags = IORESOURCE_IO;
+
+		if (request_resource(&iomem_resource, &pp->res[0]))
+			panic("can't allocate PCIe Mem space");
+		if (request_resource(&ioport_resource, &pp->res[1]))
+			panic("can't allocate PCIe IO space");
+	}
+}
+
+static struct hw_pci spear13xx_pci;
+
+static int pcie_get_payload(struct pci_dev *dev)
+{
+	int ret, cap;
+	u16 ctl;
+
+	cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	if (!cap)
+		return -EINVAL;
+
+	ret = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
+	if (!ret)
+		ret = 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
+
+	return ret;
+}
+
+static int pcie_set_payload(struct pci_dev *dev, int rq)
+{
+	int cap, err = -EINVAL;
+	u16 ctl, v;
+
+	if (rq < 128 || rq > 4096 || !is_power_of_2(rq))
+		goto out;
+
+	v = (ffs(rq) - 8) << 5;
+
+	cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	if (!cap)
+		goto out;
+
+	err = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
+	if (err)
+		goto out;
+
+	if ((ctl & PCI_EXP_DEVCTL_PAYLOAD) != v) {
+		ctl &= ~PCI_EXP_DEVCTL_PAYLOAD;
+		ctl |= v;
+		err = pci_write_config_dword(dev, cap + PCI_EXP_DEVCTL, ctl);
+	}
+
+out:
+	return err;
+}
+
+static void set_readrq(struct pci_bus *bus, int rq)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		if (rq < pcie_get_readrq(dev))
+			pcie_set_readrq(dev, rq);
+		if (dev->subordinate)
+			set_readrq(dev->subordinate, rq);
+	}
+}
+
+static int get_max_payload(struct pci_bus *bus, int rq)
+{
+	struct pci_dev *dev;
+	int payload;
+	int max_payload = rq;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		payload = pcie_get_payload(dev);
+		if (payload < max_payload)
+			max_payload = payload;
+		if (dev->subordinate)
+			max_payload = get_max_payload(dev->subordinate,
+					max_payload);
+	}
+	return max_payload;
+}
+
+static void set_payload(struct pci_bus *bus, int rq)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		pcie_set_payload(dev, rq);
+		if (dev->subordinate)
+			set_payload(dev->subordinate, rq);
+	}
+}
+
+static void __init spear13xx_pcie_postinit(void)
+{
+	struct hw_pci *hw = &spear13xx_pci;
+	struct pci_sys_data *sys;
+	struct pci_bus *bus;
+	int cap, ctl, payload, readrq;
+	int max_payload = 4096;
+	struct pcie_port *pp;
+
+	/* allign Max_Payload_Size for all devices to the minimum
+	 * Max_Payload_Size of any of the device in tree.
+	 * Max_Read_Request_Size of any of the DS device should be less
+	 * than or equal to that of RC's Max_Read_Request_Size*/
+
+	list_for_each_entry(sys, &hw->buses, node) {
+		bus = sys->bus;
+		pp = bus_to_port(bus->number);
+		cap = pci_find_own_capability(pp, PCI_CAP_ID_EXP);
+		spear_dbi_read_reg(pp, cap + PCI_EXP_DEVCTL, 2, &ctl);
+		payload = 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
+		if (payload < max_payload)
+			max_payload = payload;
+		readrq = 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12);
+		max_payload = get_max_payload(bus, max_payload);
+		set_payload(bus, max_payload);
+		set_readrq(bus, readrq);
+	}
+}
+
+static int __init spear13xx_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+	struct pcie_port *pp;
+	/*u32 val = 0;*/
+
+	if (nr >= NUM_PCIE_PORTS)
+		return 0;
+
+	pp = &pcie_port[nr];
+	if (!(pp->config.is_host))
+		return 0;
+
+	pp->root_bus_nr = sys->busnr;
+
+	sys->resource[0] = &pp->res[0];
+	sys->resource[1] = &pp->res[1];
+	sys->resource[2] = NULL;
+
+	return 1;
+}
+
+static int pcie_valid_config(struct pcie_port *pp, struct pci_bus *bus, int dev)
+{
+	/*If there is no link, then there is no device*/
+	if (bus->number != pp->root_bus_nr) {
+		if (!spear13xx_pcie_link_up(pp->va_app_base))
+			return 0;
+	}
+	/*
+	 * Don't go out when trying to access nonexisting devices
+	 * on the local bus.
+	 * we have only one slot on each root port.
+	 */
+	if (bus->number == pp->root_bus_nr && dev > 0)
+		return 0;
+
+	/*do not read more than one device on the bus directly attached
+	 * to RC's (Virtual Bridge's) DS side*/
+	if (bus->primary == pp->root_bus_nr && dev > 0)
+		return 0;
+
+	return 1;
+}
+
+static int spear13xx_pcie_rd_conf(struct pcie_port *pp, struct pci_bus *bus,
+		u32 devfn, int where, int size, u32 *val)
+{
+	struct pcie_app_reg __iomem *app_reg = pp->va_app_base;
+	u32 address;
+	u32 armisc;
+
+	armisc = readl(&app_reg->slv_armisc);
+	armisc &= ~(AXI_OP_TYPE_MASK);
+	if (bus->parent->number == pp->root_bus_nr) {
+		address = (u32)pp->va_cfg0_base | (PCI_FUNC(devfn) << 16)
+			| (where & 0xFFFC);
+		writel((bus->number << 24) | (PCI_SLOT(devfn) << 19),
+			&app_reg->pom_cfg0_addr_start);
+		armisc |= AXI_OP_TYPE_CONFIG_RDRW_TYPE0;
+	} else {
+		address = (u32)pp->va_cfg1_base | (PCI_FUNC(devfn) << 16)
+			| (where & 0xFFFC);
+		writel((bus->number << 24) | (PCI_SLOT(devfn) << 19),
+			&app_reg->pom_cfg1_addr_start);
+		armisc |= AXI_OP_TYPE_CONFIG_RDRW_TYPE1;
+	}
+	writel(armisc, &app_reg->slv_armisc);
+	while (armisc != readl(&app_reg->slv_armisc))
+		;
+	*val = readl(address);
+	if (size == 1)
+		*val = (*val >> (8 * (where & 3))) & 0xff;
+	else if (size == 2)
+		*val = (*val >> (8 * (where & 3))) & 0xffff;
+
+	armisc &= ~(AXI_OP_TYPE_MASK);
+	writel(armisc, &app_reg->slv_armisc);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+			int size, u32 *val)
+{
+	struct pcie_port *pp = bus_to_port(bus->number);
+	unsigned long flags;
+	int ret;
+
+	if (pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
+		*val = 0xffffffff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	spin_lock_irqsave(&pp->conf_lock, flags);
+	if (bus->number != pp->root_bus_nr)
+		ret = spear13xx_pcie_rd_conf(pp, bus, devfn, where, size, val);
+	else {
+		spear_dbi_read_reg(pp, where, size, val);
+		ret = 0;
+	}
+	spin_unlock_irqrestore(&pp->conf_lock, flags);
+
+	return ret;
+}
+
+static int spear13xx_pcie_wr_conf(struct pcie_port *pp, struct pci_bus *bus,
+		u32 devfn, int where, int size, u32 val)
+{
+	int ret = PCIBIOS_SUCCESSFUL;
+	struct pcie_app_reg __iomem *app_reg = pp->va_app_base;
+	u32 address;
+	u32 awmisc;
+
+	awmisc = readl(&app_reg->slv_awmisc);
+	awmisc &= ~(AXI_OP_TYPE_MASK);
+
+	if (bus->parent->number == pp->root_bus_nr) {
+		address = (u32)pp->va_cfg0_base | (PCI_FUNC(devfn) << 16)
+			| (where & 0xFFFC);
+		writel((bus->number << 24) | (PCI_SLOT(devfn) << 19),
+			&app_reg->pom_cfg0_addr_start);
+		awmisc |= AXI_OP_TYPE_CONFIG_RDRW_TYPE0;
+	} else {
+		address = (u32)pp->va_cfg1_base | (PCI_FUNC(devfn) << 16)
+			| (where & 0xFFFC);
+		writel((bus->number << 24) | (PCI_SLOT(devfn) << 19),
+			&app_reg->pom_cfg1_addr_start);
+		awmisc |= AXI_OP_TYPE_CONFIG_RDRW_TYPE1;
+	}
+	writel(awmisc, &app_reg->slv_awmisc);
+	while (awmisc != readl(&app_reg->slv_awmisc))
+		;
+	if (size == 4)
+		writel(val, address);
+	else if (size == 2)
+		writew(val, address + (where & 2));
+	else if (size == 1)
+		writeb(val, address + (where & 3));
+	else
+		ret = PCIBIOS_BAD_REGISTER_NUMBER;
+
+	awmisc &= ~(AXI_OP_TYPE_MASK);
+	writel(awmisc, &app_reg->slv_awmisc);
+	return ret;
+}
+
+static int pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+			int where, int size, u32 val)
+{
+	struct pcie_port *pp = bus_to_port(bus->number);
+	unsigned long flags;
+	int ret;
+
+	if (pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	spin_lock_irqsave(&pp->conf_lock, flags);
+	if (bus->number != pp->root_bus_nr)
+		ret = spear13xx_pcie_wr_conf(pp, bus, devfn, where, size, val);
+	else {
+		spear_dbi_write_reg(pp, where, size, val);
+		ret = 0;
+	}
+	spin_unlock_irqrestore(&pp->conf_lock, flags);
+
+	return ret;
+}
+
+static struct pci_ops pcie_ops = {
+	.read = pcie_rd_conf,
+	.write = pcie_wr_conf,
+};
+
+static struct pci_bus __init *
+spear13xx_pcie_scan_bus(int nr, struct pci_sys_data *sys)
+{
+	struct pci_bus *bus;
+
+	if ((nr < NUM_PCIE_PORTS) && pcie_port[nr].config.is_host) {
+		bus = pci_scan_bus(sys->busnr, &pcie_ops, sys);
+	} else {
+		bus = NULL;
+		BUG();
+	}
+
+	return bus;
+}
+
+static int __init spear13xx_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+	struct pcie_port *pp = bus_to_port(dev->bus->number);
+	int irq = (SPEAR_INTX0_BASE + pp->port * SPEAR_NUM_INTX_IRQS + pin - 1);
+
+	return irq;
+}
+
+static struct hw_pci spear13xx_pci __initdata = {
+	.nr_controllers	= NUM_PCIE_PORTS,
+	.preinit	= spear13xx_pcie_preinit,
+	.postinit	= spear13xx_pcie_postinit,
+	.swizzle	= pci_std_swizzle,
+	.setup		= spear13xx_pcie_setup,
+	.scan		= spear13xx_pcie_scan_bus,
+	.map_irq	= spear13xx_pcie_map_irq,
+};
+
+void mask_intx_irq(unsigned int irq)
+{
+	int irq_offset = (irq - SPEAR_INTX0_BASE) % SPEAR_NUM_INTX_IRQS;
+	int port = (irq - SPEAR_INTX0_BASE) / SPEAR_NUM_INTX_IRQS;
+	struct pcie_port *pp = &pcie_port[port];
+	struct pcie_app_reg __iomem *app_reg = pp->va_app_base;
+
+	switch (irq_offset) {
+	case 0:
+		writel(readl(&app_reg->int_mask) & ~INTA_CTRL_INT,
+				&app_reg->int_mask);
+		break;
+	case 1:
+		writel(readl(&app_reg->int_mask) & ~INTB_CTRL_INT,
+				&app_reg->int_mask);
+		break;
+	case 2:
+		writel(readl(&app_reg->int_mask) & ~INTC_CTRL_INT,
+				&app_reg->int_mask);
+		break;
+	case 3:
+		writel(readl(&app_reg->int_mask) & ~INTD_CTRL_INT,
+				&app_reg->int_mask);
+		break;
+	}
+}
+
+void unmask_intx_irq(unsigned int irq)
+{
+	int irq_offset = (irq - SPEAR_INTX0_BASE) % SPEAR_NUM_INTX_IRQS;
+	int port = (irq - SPEAR_INTX0_BASE) / SPEAR_NUM_INTX_IRQS;
+	struct pcie_port *pp = &pcie_port[port];
+	struct pcie_app_reg __iomem *app_reg = pp->va_app_base;
+
+	switch (irq_offset) {
+	case 0:
+		writel(readl(&app_reg->int_mask) | INTA_CTRL_INT,
+				&app_reg->int_mask);
+		break;
+	case 1:
+		writel(readl(&app_reg->int_mask) | INTB_CTRL_INT,
+				&app_reg->int_mask);
+		break;
+	case 2:
+		writel(readl(&app_reg->int_mask) | INTC_CTRL_INT,
+				&app_reg->int_mask);
+		break;
+	case 3:
+		writel(readl(&app_reg->int_mask) | INTD_CTRL_INT,
+				&app_reg->int_mask);
+		break;
+	}
+}
+
+static struct irq_chip spear13xx_intx_chip = {
+	.name = "PCI-INTX",
+	.mask = mask_intx_irq,
+	.unmask = unmask_intx_irq,
+};
+
+static void spear_pcie_int_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct pcie_port *pp = &pcie_port[irq - IRQ_PCIE0];
+	struct pcie_app_reg __iomem *app_reg = pp->va_app_base;
+	unsigned int status;
+
+	status = readl(&app_reg->int_sts);
+
+	desc->chip->ack(irq);
+
+	if (status & MSI_CTRL_INT) {
+#ifdef CONFIG_PCI_MSI
+		handle_msi(pp);
+#endif
+		writel(MSI_CTRL_INT, &app_reg->int_clr);
+	} else if (status & INTA_CTRL_INT)
+		generic_handle_irq(SPEAR_INTX0_BASE
+				+ pp->port * SPEAR_NUM_INTX_IRQS);
+	else if (status & INTB_CTRL_INT)
+		generic_handle_irq(SPEAR_INTX0_BASE
+				+ pp->port * SPEAR_NUM_INTX_IRQS + 1);
+	else if (status & INTC_CTRL_INT)
+		generic_handle_irq(SPEAR_INTX0_BASE
+				+ pp->port * SPEAR_NUM_INTX_IRQS + 2);
+	else if (status & INTD_CTRL_INT)
+		generic_handle_irq(SPEAR_INTX0_BASE
+				+ pp->port * SPEAR_NUM_INTX_IRQS + 3);
+	else
+		writel(status, &app_reg->int_clr);
+
+	desc->chip->unmask(irq);
+}
+
+static void spear13xx_int_init(struct pcie_port *pp)
+{
+	int i, irq;
+	struct pcie_app_reg __iomem *app_reg;
+
+	set_irq_chained_handler(IRQ_PCIE0 + pp->port, spear_pcie_int_handler);
+
+#ifdef CONFIG_PCI_MSI
+	spear13xx_msi_init(pp);
+#endif
+	/* Enbale INTX interrupt*/
+	app_reg = pp->va_app_base;
+	writel(readl(&app_reg->int_mask) | INTA_CTRL_INT
+			| INTB_CTRL_INT	| INTC_CTRL_INT
+			| INTD_CTRL_INT, &app_reg->int_mask);
+
+	/* initilize INTX chip here only. MSI chip will be
+	 * initilized dynamically.*/
+	irq = (SPEAR_INTX0_BASE + pp->port * SPEAR_NUM_INTX_IRQS);
+	for (i = 0; i < SPEAR_NUM_INTX_IRQS; i++) {
+		set_irq_chip_and_handler(irq + i, &spear13xx_intx_chip,
+				handle_simple_irq);
+		set_irq_flags(irq + i, IRQF_VALID);
+	}
+}
+
+static void __init add_pcie_port(int port, void __iomem *base,
+		void __iomem *app_base)
+{
+	struct pcie_port *pp = &pcie_port[port];
+	struct pcie_app_reg __iomem *app_reg;
+
+	pp->port = port;
+	pp->root_bus_nr = -1;
+	pp->base = base;
+	pp->app_base = app_base;
+	pp->va_app_base = ioremap((ulong)app_base, 0x200);
+	if (!pp->va_app_base) {
+		pr_err("error with ioremap in function %s\n", __func__);
+		return;
+	}
+	pp->va_dbi_base = ioremap((ulong)base, 0x2000);
+	if (!pp->va_dbi_base) {
+		pr_err("error with ioremap in function %s\n", __func__);
+		return;
+	}
+	spin_lock_init(&pp->conf_lock);
+	memset(pp->res, 0, sizeof(pp->res));
+	pr_info("spear13xx PCIe port %d\n", port);
+	if (spear13xx_pcie_link_up(pp->va_app_base)) {
+		pr_info("link up in bios\n");
+	} else {
+		pr_info("link down in bios\n");
+		spear13xx_pcie_host_init(pp);
+		spear13xx_int_init(pp);
+		app_reg = pp->va_app_base;
+		pp->va_cfg0_base =
+			ioremap(readl(app_reg->in_cfg0_addr_start),
+					IN_CFG0_SIZE);
+		if (!pp->va_cfg0_base) {
+			pr_err("error with ioremap in function %s\n", __func__);
+			return;
+		}
+		pp->va_cfg1_base =
+			ioremap(readl(app_reg->in_cfg1_addr_start),
+					IN_CFG1_SIZE);
+		if (!pp->va_cfg1_base) {
+			pr_err("error with ioremap in function %s\n", __func__);
+			return;
+		}
+
+	}
+}
+
+static int __init spear13xx_pcie_init(void)
+{
+	int port;
+	struct clk *clk;
+	struct pcie_port_info *config;
+
+	for (port = 0; port < NUM_PCIE_PORTS; port++) {
+		/* do not enable clock if it is PCIE0. Ideally , all controller
+		 * should have been independent from others with respect to
+		 * clock. But PCIE1 and 2 depends on PCIE0.So PCIE0 clk
+		 * is provided during board init.*/
+		if (port == 1) {
+			/* Ideally CFG Clock should have been also enabled
+			 * here. But it is done currently during board
+			 * init routne*/
+			clk = clk_get_sys("pcie1", NULL);
+			if (IS_ERR(clk)) {
+				pr_err("%s:couldn't get clk for pcie1\n",
+						__func__);
+				continue;
+			}
+			if (clk_enable(clk)) {
+				pr_err("%s:couldn't enable clk for pcie1\n",
+						__func__);
+				continue;
+			}
+		} else if (port == 2) {
+			/* Ideally CFG Clock should have been also enabled
+			 * here. But it is done currently during board
+			 * init routne*/
+			clk = clk_get_sys("pcie2", NULL);
+			if (IS_ERR(clk)) {
+				pr_err("%s:couldn't get clk for pcie2\n",
+						__func__);
+				continue;
+			}
+			if (clk_enable(clk)) {
+				pr_err("%s:couldn't enable clk for pcie2\n",
+						__func__);
+				continue;
+			}
+		}
+
+		config = (*pcie_port_init)(port);
+		memcpy((void *)&pcie_port[port].config, (void *)config,
+				(sizeof(struct pcie_port_info)));
+
+		if (pcie_port[port].config.is_host)
+			add_pcie_port(port, pcie_base[port],
+					pcie_app_base[port]);
+	}
+
+	pci_common_init(&spear13xx_pci);
+	pr_info("pcie init successful\n");
+	return 0;
+}
+subsys_initcall(spear13xx_pcie_init);
diff --git a/arch/arm/mach-spear13xx/spear1300_evb.c b/arch/arm/mach-spear13xx/spear1300_evb.c
index cba0fee..82aa92d 100644
--- a/arch/arm/mach-spear13xx/spear1300_evb.c
+++ b/arch/arm/mach-spear13xx/spear1300_evb.c
@@ -16,6 +16,7 @@
 #include <asm/mach-types.h>
 #include <mach/generic.h>
 #include <mach/hardware.h>
+#include <mach/pcie.h>
 
 /* padmux devices to enable */
 static struct pmx_dev *pmx_devs[] = {
@@ -45,6 +46,37 @@ static struct amba_device *amba_devs[] __initdata = {
 static struct platform_device *plat_devs[] __initdata = {
 };
 
+#ifdef CONFIG_PCIEPORTBUS
+static struct pcie_port_info __initdata pcie_port_info[] = {
+	/*pcie0 port info*/
+	{
+		.is_host = 0,
+	}, {
+	/*pcie1 port info*/
+		.is_host = 1,
+	}, {
+	/*pcie2 port info*/
+		.is_host = 1,
+	}
+};
+
+/*
+ * This function is needed for PCIE host and device driver. Same controller can
+ * not be programmed as host as well as device. So host driver must call this
+ * function and if this function returns a configuration structure which tells
+ * that this port should be a host, then only host controller driver should add
+ * that particular port as RC. For a port to be added as device, one must also
+ * add device's information in plat_devs array defined in this file.
+ */
+static struct pcie_port_info * __init spear1300_pcie_port_init(int port)
+{
+	if (port < 3)
+		return &pcie_port_info[port];
+	else
+		return NULL;
+}
+#endif
+
 static void __init spear1300_evb_init(void)
 {
 	unsigned int i;
@@ -52,6 +84,12 @@ static void __init spear1300_evb_init(void)
 	/* call spear1300 machine init function */
 	spear1300_init(NULL, pmx_devs, ARRAY_SIZE(pmx_devs));
 
+#ifdef CONFIG_PCIEPORTBUS
+	/* Enable PCIE0 clk */
+	enable_pcie0_clk();
+	pcie_init(spear1300_pcie_port_init);
+#endif
+
 	/* Add Platform Devices */
 	platform_add_devices(plat_devs, ARRAY_SIZE(plat_devs));
 
diff --git a/arch/arm/mach-spear13xx/spear1310_evb.c b/arch/arm/mach-spear13xx/spear1310_evb.c
index 62af911..3d7d1c5 100644
--- a/arch/arm/mach-spear13xx/spear1310_evb.c
+++ b/arch/arm/mach-spear13xx/spear1310_evb.c
@@ -16,6 +16,7 @@
 #include <asm/mach-types.h>
 #include <mach/generic.h>
 #include <mach/hardware.h>
+#include <mach/pcie.h>
 
 /* padmux devices to enable */
 static struct pmx_dev *pmx_devs[] = {
@@ -64,6 +65,37 @@ static struct platform_device *plat_devs[] __initdata = {
 	&spear1310_can1_device,
 };
 
+#ifdef CONFIG_PCIEPORTBUS
+static struct pcie_port_info __initdata pcie_port_info[] = {
+	/*pcie0 port info*/
+	{
+		.is_host = 0,
+	}, {
+	/*pcie1 port info*/
+		.is_host = 1,
+	}, {
+	/*pcie2 port info*/
+		.is_host = 1,
+	}
+};
+
+/*
+ * This function is needed for PCIE host and device driver. Same controller can
+ * not be programmed as host as well as device. So host driver must call this
+ * function and if this function returns a configuration structure which tells
+ * that this port should be a host, then only host controller driver should add
+ * that particular port as RC. For a port to be added as device, one must also
+ * add device's information in plat_devs array defined in this file.
+ */
+static struct pcie_port_info * __init spear1310_pcie_port_init(int port)
+{
+	if (port < 3)
+		return &pcie_port_info[port];
+	else
+		return NULL;
+}
+#endif
+
 static void __init spear1310_evb_init(void)
 {
 	unsigned int i;
@@ -71,6 +103,12 @@ static void __init spear1310_evb_init(void)
 	/* call spear1310 machine init function */
 	spear1310_init(NULL, pmx_devs, ARRAY_SIZE(pmx_devs));
 
+#ifdef CONFIG_PCIEPORTBUS
+	/* Enable PCIE0 clk */
+	enable_pcie0_clk();
+	pcie_init(spear1310_pcie_port_init);
+#endif
+
 	/* Add Platform Devices */
 	platform_add_devices(plat_devs, ARRAY_SIZE(plat_devs));
 
diff --git a/arch/arm/mach-spear13xx/spear13xx.c b/arch/arm/mach-spear13xx/spear13xx.c
index 35582a6..6c2525a 100644
--- a/arch/arm/mach-spear13xx/spear13xx.c
+++ b/arch/arm/mach-spear13xx/spear13xx.c
@@ -76,6 +76,34 @@ struct amba_device spear13xx_uart_device = {
 	.irq = {IRQ_UART, NO_IRQ},
 };
 
+#ifdef CONFIG_PCIEPORTBUS
+/* PCIE0 clock always needs to be enabled if any of the three PCIE port
+ * have to be used. So call this function from the board initilization
+ * file. Ideally , all controller should have been independent from
+ * others with respect to clock.
+ */
+int enable_pcie0_clk(void)
+{
+	struct clk *clk;
+	/*Enable all CLK in CFG registers here only. Idealy only PCIE0
+	 * should have been enabled. But Controler does not work
+	 * properly if PCIE1 and PCIE2's CFG CLK is enabled in stages.
+	 */
+	writel(PCIE0_CFG_VAL | PCIE1_CFG_VAL | PCIE2_CFG_VAL, PCIE_CFG);
+	clk = clk_get_sys("pcie0", NULL);
+	if (IS_ERR(clk)) {
+		pr_err("%s:couldn't get clk for pcie0\n", __func__);
+		return -ENODEV;
+	}
+	if (clk_enable(clk)) {
+		pr_err("%s:couldn't enable clk for pcie0\n", __func__);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+#endif
+
 /* Do spear13xx familiy common initialization part here */
 void __init spear13xx_init(void)
 {
diff --git a/arch/arm/plat-spear/Kconfig b/arch/arm/plat-spear/Kconfig
index 29a25d2..ee5fd4a 100644
--- a/arch/arm/plat-spear/Kconfig
+++ b/arch/arm/plat-spear/Kconfig
@@ -12,6 +12,8 @@ config ARCH_SPEAR13XX
 	bool "SPEAr13XX"
 	select ARM_GIC
 	select CPU_V7
+	select ARCH_SUPPORTS_MSI
+	select MIGHT_HAVE_PCI
 	help
 	  Supports for ARM's SPEAR13XX family
 
-- 
1.7.2.2




More information about the linux-arm-kernel mailing list