[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