Signed-off-by: Thierry Reding <thierry.red...@avionic-design.de> --- arch/arm/mach-tegra/board-harmony-pcie.c | 38 ++- arch/arm/mach-tegra/board-harmony.c | 1 + arch/arm/mach-tegra/board-harmony.h | 1 + arch/arm/mach-tegra/board-trimslice.c | 16 +- arch/arm/mach-tegra/board.h | 11 +- arch/arm/mach-tegra/devices.c | 25 ++ arch/arm/mach-tegra/devices.h | 1 + arch/arm/mach-tegra/include/mach/iomap.h | 6 + arch/arm/mach-tegra/pcie.c | 509 ++++++++++++++++++------------ 9 files changed, 386 insertions(+), 222 deletions(-)
diff --git a/arch/arm/mach-tegra/board-harmony-pcie.c b/arch/arm/mach-tegra/board-harmony-pcie.c index 33c4fed..e624a9c 100644 --- a/arch/arm/mach-tegra/board-harmony-pcie.c +++ b/arch/arm/mach-tegra/board-harmony-pcie.c @@ -23,18 +23,15 @@ #include <asm/mach-types.h> #include "board.h" +#include "devices.h" #include "board-harmony.h" #ifdef CONFIG_TEGRA_PCI - -static int __init harmony_pcie_init(void) +static int tegra_pcie_init(struct platform_device *pdev) { struct regulator *regulator = NULL; int err; - if (!machine_is_harmony()) - return 0; - err = gpio_request(TEGRA_GPIO_EN_VDD_1V05_GPIO, "EN_VDD_1V05"); if (err) return err; @@ -47,22 +44,35 @@ static int __init harmony_pcie_init(void) regulator_enable(regulator); - err = tegra_pcie_init(true, true); - if (err) - goto err_pcie; - return 0; -err_pcie: - regulator_disable(regulator); - regulator_put(regulator); err_reg: gpio_free(TEGRA_GPIO_EN_VDD_1V05_GPIO); return err; } -/* PCI should be initialized after I2C, mfd and regulators */ -subsys_initcall_sync(harmony_pcie_init); +static struct tegra_pcie_pdata tegra_pcie_pdata = { + .init = tegra_pcie_init, + .enable_ports = { + [0] = true, + [1] = true, + }, +}; + +int __init harmony_pcie_init(void) +{ + if (!machine_is_harmony()) + return 0; + + tegra_pcie_device.dev.platform_data = &tegra_pcie_pdata; + platform_device_register(&tegra_pcie_device); + return 0; +} +#else +int __init harmony_pcie_init(void) +{ + return 0; +} #endif diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c index c00aadb..ca20680 100644 --- a/arch/arm/mach-tegra/board-harmony.c +++ b/arch/arm/mach-tegra/board-harmony.c @@ -180,6 +180,7 @@ static void __init tegra_harmony_init(void) platform_add_devices(harmony_devices, ARRAY_SIZE(harmony_devices)); harmony_i2c_init(); harmony_regulator_init(); + harmony_pcie_init(); } MACHINE_START(HARMONY, "harmony") diff --git a/arch/arm/mach-tegra/board-harmony.h b/arch/arm/mach-tegra/board-harmony.h index 139d96c..afa68e2 100644 --- a/arch/arm/mach-tegra/board-harmony.h +++ b/arch/arm/mach-tegra/board-harmony.h @@ -37,5 +37,6 @@ void harmony_pinmux_init(void); int harmony_regulator_init(void); +int harmony_pcie_init(void); #endif diff --git a/arch/arm/mach-tegra/board-trimslice.c b/arch/arm/mach-tegra/board-trimslice.c index cd52820..9bc414a 100644 --- a/arch/arm/mach-tegra/board-trimslice.c +++ b/arch/arm/mach-tegra/board-trimslice.c @@ -147,14 +147,23 @@ static __initdata struct tegra_clk_init_table trimslice_clk_init_table[] = { { NULL, NULL, 0, 0}, }; -static int __init tegra_trimslice_pci_init(void) +static struct tegra_pcie_pdata tegra_pcie_pdata = { + .enable_ports = { + [0] = true, + [1] = true, + }, +}; + +static int __init trimslice_pci_init(void) { if (!machine_is_trimslice()) return 0; - return tegra_pcie_init(true, true); + tegra_pcie_device.dev.platform_data = &tegra_pcie_pdata; + platform_device_register(&tegra_pcie_device); + + return 0; } -subsys_initcall(tegra_trimslice_pci_init); static void __init tegra_trimslice_init(void) { @@ -169,6 +178,7 @@ static void __init tegra_trimslice_init(void) trimslice_i2c_init(); trimslice_usb_init(); + trimslice_pci_init(); } MACHINE_START(TRIMSLICE, "trimslice") diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index 75d1543..40f7e6b 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -21,6 +21,7 @@ #ifndef __MACH_TEGRA_BOARD_H #define __MACH_TEGRA_BOARD_H +#include <linux/platform_device.h> #include <linux/types.h> void tegra_assert_system_reset(char mode, const char *cmd); @@ -30,7 +31,15 @@ void __init tegra30_init_early(void); void __init tegra_map_common_io(void); void __init tegra_init_irq(void); void __init tegra_dt_init_irq(void); -int __init tegra_pcie_init(bool init_port0, bool init_port1); extern struct sys_timer tegra_timer; + +#define TEGRA_PCIE_MAX_PORTS 2 + +struct tegra_pcie_pdata { + int (*init)(struct platform_device *pdev); + int (*exit)(struct platform_device *pdev); + bool enable_ports[TEGRA_PCIE_MAX_PORTS]; +}; + #endif diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c index 7a2a02d..09e24e1 100644 --- a/arch/arm/mach-tegra/devices.c +++ b/arch/arm/mach-tegra/devices.c @@ -704,3 +704,28 @@ struct platform_device tegra_pcm_device = { .name = "tegra-pcm-audio", .id = -1, }; + +static struct resource tegra_pcie_resources[] = { + [0] = { + .start = TEGRA_PCIE_BASE, + .end = TEGRA_PCIE_BASE + TEGRA_PCIE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = TEGRA_PCIE_MMIO_BASE, + .end = TEGRA_PCIE_MMIO_BASE + TEGRA_PCIE_MMIO_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = INT_PCIE_INTR, + .end = INT_PCIE_INTR, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device tegra_pcie_device = { + .name = "tegra-pcie", + .id = -1, + .resource = tegra_pcie_resources, + .num_resources = ARRAY_SIZE(tegra_pcie_resources), +}; diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h index 873ecb2..95f0a97 100644 --- a/arch/arm/mach-tegra/devices.h +++ b/arch/arm/mach-tegra/devices.h @@ -48,5 +48,6 @@ extern struct platform_device tegra_i2s_device1; extern struct platform_device tegra_i2s_device2; extern struct platform_device tegra_das_device; extern struct platform_device tegra_pcm_device; +extern struct platform_device tegra_pcie_device; #endif diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h index cff672a..53b0dbe 100644 --- a/arch/arm/mach-tegra/include/mach/iomap.h +++ b/arch/arm/mach-tegra/include/mach/iomap.h @@ -242,6 +242,12 @@ #define TEGRA_CSITE_BASE 0x70040000 #define TEGRA_CSITE_SIZE SZ_256K +#define TEGRA_PCIE_BASE 0x80000000 +#define TEGRA_PCIE_SIZE SZ_4M + +#define TEGRA_PCIE_MMIO_BASE 0x80400000 +#define TEGRA_PCIE_MMIO_SIZE SZ_64K + #define TEGRA_USB_BASE 0xC5000000 #define TEGRA_USB_SIZE SZ_16K diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c index 7704af7a..8b20bc5 100644 --- a/arch/arm/mach-tegra/pcie.c +++ b/arch/arm/mach-tegra/pcie.c @@ -28,6 +28,7 @@ #include <linux/kernel.h> #include <linux/pci.h> +#include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/clk.h> @@ -161,17 +162,12 @@ * 0x90000000 - 0x9fffffff - non-prefetchable memory * 0xa0000000 - 0xbfffffff - prefetchable memory */ -#define TEGRA_PCIE_BASE 0x80000000 - #define PCIE_REGS_SZ SZ_16K #define PCIE_CFG_OFF PCIE_REGS_SZ #define PCIE_CFG_SZ SZ_1M #define PCIE_EXT_CFG_OFF (PCIE_CFG_SZ + PCIE_CFG_OFF) #define PCIE_EXT_CFG_SZ SZ_1M -#define PCIE_IOMAP_SZ (PCIE_REGS_SZ + PCIE_CFG_SZ + PCIE_EXT_CFG_SZ) -#define MMIO_BASE (TEGRA_PCIE_BASE + SZ_4M) -#define MMIO_SIZE SZ_64K #define MEM_BASE_0 (TEGRA_PCIE_BASE + SZ_256M) #define MEM_SIZE_0 SZ_128M #define MEM_BASE_1 (MEM_BASE_0 + MEM_SIZE_0) @@ -201,11 +197,15 @@ struct tegra_pcie_port { }; struct tegra_pcie_info { - struct tegra_pcie_port port[2]; + struct device *dev; + struct hw_pci hw; + + struct tegra_pcie_port port[TEGRA_PCIE_MAX_PORTS]; int num_ports; void __iomem *regs; - struct resource res_mmio; + void __iomem *mmio; + int irq; struct clk *pex_clk; struct clk *afi_clk; @@ -213,55 +213,53 @@ struct tegra_pcie_info { struct clk *pll_e; }; -static struct tegra_pcie_info tegra_pcie = { - .res_mmio = { - .name = "PCI IO", - .start = MMIO_BASE, - .end = MMIO_BASE + MMIO_SIZE - 1, - .flags = IORESOURCE_MEM, - }, -}; +static inline struct tegra_pcie_info *sys_to_pcie(struct pci_sys_data *sys) +{ + return container_of(sys->hw, struct tegra_pcie_info, hw); +} void __iomem *tegra_pcie_io_base; EXPORT_SYMBOL(tegra_pcie_io_base); -static inline void afi_writel(u32 value, unsigned long offset) +static inline void afi_writel(struct tegra_pcie_info *pcie, u32 value, unsigned long offset) { - writel(value, offset + AFI_OFFSET + tegra_pcie.regs); + writel(value, offset + AFI_OFFSET + pcie->regs); } -static inline u32 afi_readl(unsigned long offset) +static inline u32 afi_readl(struct tegra_pcie_info *pcie, unsigned long offset) { - return readl(offset + AFI_OFFSET + tegra_pcie.regs); + return readl(offset + AFI_OFFSET + pcie->regs); } -static inline void pads_writel(u32 value, unsigned long offset) +static inline void pads_writel(struct tegra_pcie_info *pcie, u32 value, unsigned long offset) { - writel(value, offset + PADS_OFFSET + tegra_pcie.regs); + writel(value, offset + PADS_OFFSET + pcie->regs); } -static inline u32 pads_readl(unsigned long offset) +static inline u32 pads_readl(struct tegra_pcie_info *pcie, unsigned long offset) { - return readl(offset + PADS_OFFSET + tegra_pcie.regs); + return readl(offset + PADS_OFFSET + pcie->regs); } -static struct tegra_pcie_port *bus_to_port(int bus) +static struct tegra_pcie_port *bus_to_port(struct pci_bus *bus) { + struct tegra_pcie_info *pcie = sys_to_pcie(bus->sysdata); int i; - for (i = tegra_pcie.num_ports - 1; i >= 0; i--) { - int rbus = tegra_pcie.port[i].root_bus_nr; - if (rbus != -1 && rbus == bus) + for (i = pcie->num_ports - 1; i >= 0; i--) { + int rbus = pcie->port[i].root_bus_nr; + if (rbus != -1 && rbus == bus->number) break; } - return i >= 0 ? tegra_pcie.port + i : NULL; + return i >= 0 ? pcie->port + i : NULL; } static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { - struct tegra_pcie_port *pp = bus_to_port(bus->number); + struct tegra_pcie_info *pcie = sys_to_pcie(bus->sysdata); + struct tegra_pcie_port *pp = bus_to_port(bus); void __iomem *addr; if (pp) { @@ -272,10 +270,10 @@ static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, addr = pp->base + (where & ~0x3); } else { - addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) + - PCIE_CONF_DEV(PCI_SLOT(devfn)) + - PCIE_CONF_FUNC(PCI_FUNC(devfn)) + - PCIE_CONF_REG(where)); + addr = pcie->regs + (PCIE_CONF_BUS(bus->number) + + PCIE_CONF_DEV(PCI_SLOT(devfn)) + + PCIE_CONF_FUNC(PCI_FUNC(devfn)) + + PCIE_CONF_REG(where)); } *val = readl(addr); @@ -291,7 +289,8 @@ static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) { - struct tegra_pcie_port *pp = bus_to_port(bus->number); + struct tegra_pcie_info *pcie = sys_to_pcie(bus->sysdata); + struct tegra_pcie_port *pp = bus_to_port(bus); void __iomem *addr; u32 mask; @@ -303,10 +302,10 @@ static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, addr = pp->base + (where & ~0x3); } else { - addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) + - PCIE_CONF_DEV(PCI_SLOT(devfn)) + - PCIE_CONF_FUNC(PCI_FUNC(devfn)) + - PCIE_CONF_REG(where)); + addr = pcie->regs + (PCIE_CONF_BUS(bus->number) + + PCIE_CONF_DEV(PCI_SLOT(devfn)) + + PCIE_CONF_FUNC(PCI_FUNC(devfn)) + + PCIE_CONF_REG(where)); } if (size == 4) { @@ -373,12 +372,13 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable); static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) { + struct tegra_pcie_info *pcie = sys_to_pcie(sys); struct tegra_pcie_port *pp; - if (nr >= tegra_pcie.num_ports) + if (nr >= pcie->num_ports) return 0; - pp = tegra_pcie.port + nr; + pp = pcie->port + nr; pp->root_bus_nr = sys->busnr; /* @@ -441,35 +441,29 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) return 1; } -static int tegra_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { - return INT_PCIE_INTR; + struct tegra_pcie_info *pcie = sys_to_pcie(pdev->bus->sysdata); + + return pcie->irq; } -static struct pci_bus __init *tegra_pcie_scan_bus(int nr, - struct pci_sys_data *sys) +static struct pci_bus __devinit *tegra_pcie_scan_bus(int nr, + struct pci_sys_data *sys) { + struct tegra_pcie_info *pcie = sys_to_pcie(sys); struct tegra_pcie_port *pp; - if (nr >= tegra_pcie.num_ports) + if (nr >= pcie->num_ports) return NULL; - pp = tegra_pcie.port + nr; + pp = pcie->port + nr; pp->root_bus_nr = sys->busnr; return pci_scan_root_bus(NULL, sys->busnr, &tegra_pcie_ops, sys, &sys->resources); } -static struct hw_pci tegra_pcie_hw __initdata = { - .nr_controllers = 2, - .setup = tegra_pcie_setup, - .scan = tegra_pcie_scan_bus, - .swizzle = pci_std_swizzle, - .map_irq = tegra_pcie_map_irq, -}; - - static irqreturn_t tegra_pcie_isr(int irq, void *arg) { const char *err_msg[] = { @@ -483,12 +477,12 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) "AXI response decoding error", "Transcation timeout", }; - + struct tegra_pcie_info *pcie = arg; u32 code, signature; - code = afi_readl(AFI_INTR_CODE) & AFI_INTR_CODE_MASK; - signature = afi_readl(AFI_INTR_SIGNATURE); - afi_writel(0, AFI_INTR_CODE); + code = afi_readl(pcie, AFI_INTR_CODE) & AFI_INTR_CODE_MASK; + signature = afi_readl(pcie, AFI_INTR_SIGNATURE); + afi_writel(pcie, 0, AFI_INTR_CODE); if (code == AFI_INTR_LEGACY) return IRQ_NONE; @@ -501,14 +495,16 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) * happen a lot during enumeration */ if (code == AFI_INTR_MASTER_ABORT) - pr_debug("PCIE: %s, signature: %08x\n", err_msg[code], signature); + dev_dbg(pcie->dev, "%s, signature: %08x\n", err_msg[code], + signature); else - pr_err("PCIE: %s, signature: %08x\n", err_msg[code], signature); + dev_err(pcie->dev, "%s, signature: %08x\n", err_msg[code], + signature); return IRQ_HANDLED; } -static void tegra_pcie_setup_translations(void) +static void tegra_pcie_setup_translations(struct tegra_pcie_info *pcie) { u32 fpci_bar; u32 size; @@ -518,120 +514,120 @@ static void tegra_pcie_setup_translations(void) fpci_bar = ((u32)0xfdff << 16); size = PCIE_CFG_SZ; axi_address = TEGRA_PCIE_BASE + PCIE_CFG_OFF; - afi_writel(axi_address, AFI_AXI_BAR0_START); - afi_writel(size >> 12, AFI_AXI_BAR0_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR0); + afi_writel(pcie, axi_address, AFI_AXI_BAR0_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR0); /* Bar 1: extended config Bar */ fpci_bar = ((u32)0xfe1 << 20); size = PCIE_EXT_CFG_SZ; axi_address = TEGRA_PCIE_BASE + PCIE_EXT_CFG_OFF; - afi_writel(axi_address, AFI_AXI_BAR1_START); - afi_writel(size >> 12, AFI_AXI_BAR1_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR1); + afi_writel(pcie, axi_address, AFI_AXI_BAR1_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1); /* Bar 2: downstream IO bar */ fpci_bar = ((__u32)0xfdfc << 16); - size = MMIO_SIZE; - axi_address = MMIO_BASE; - afi_writel(axi_address, AFI_AXI_BAR2_START); - afi_writel(size >> 12, AFI_AXI_BAR2_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR2); + size = TEGRA_PCIE_MMIO_SIZE; + axi_address = TEGRA_PCIE_MMIO_BASE; + afi_writel(pcie, axi_address, AFI_AXI_BAR2_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR2_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR2); /* Bar 3: prefetchable memory BAR */ fpci_bar = (((PREFETCH_MEM_BASE_0 >> 12) & 0x0fffffff) << 4) | 0x1; size = PREFETCH_MEM_SIZE_0 + PREFETCH_MEM_SIZE_1; axi_address = PREFETCH_MEM_BASE_0; - afi_writel(axi_address, AFI_AXI_BAR3_START); - afi_writel(size >> 12, AFI_AXI_BAR3_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR3); + afi_writel(pcie, axi_address, AFI_AXI_BAR3_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR3_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR3); /* Bar 4: non prefetchable memory BAR */ fpci_bar = (((MEM_BASE_0 >> 12) & 0x0FFFFFFF) << 4) | 0x1; size = MEM_SIZE_0 + MEM_SIZE_1; axi_address = MEM_BASE_0; - afi_writel(axi_address, AFI_AXI_BAR4_START); - afi_writel(size >> 12, AFI_AXI_BAR4_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR4); + afi_writel(pcie, axi_address, AFI_AXI_BAR4_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR4_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR4); /* Bar 5: NULL out the remaining BAR as it is not used */ fpci_bar = 0; size = 0; axi_address = 0; - afi_writel(axi_address, AFI_AXI_BAR5_START); - afi_writel(size >> 12, AFI_AXI_BAR5_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR5); + afi_writel(pcie, axi_address, AFI_AXI_BAR5_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR5_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR5); /* map all upstream transactions as uncached */ - afi_writel(PHYS_OFFSET, AFI_CACHE_BAR0_ST); - afi_writel(0, AFI_CACHE_BAR0_SZ); - afi_writel(0, AFI_CACHE_BAR1_ST); - afi_writel(0, AFI_CACHE_BAR1_SZ); + afi_writel(pcie, PHYS_OFFSET, AFI_CACHE_BAR0_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ); + afi_writel(pcie, 0, AFI_CACHE_BAR1_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ); /* No MSI */ - afi_writel(0, AFI_MSI_FPCI_BAR_ST); - afi_writel(0, AFI_MSI_BAR_SZ); - afi_writel(0, AFI_MSI_AXI_BAR_ST); - afi_writel(0, AFI_MSI_BAR_SZ); + afi_writel(pcie, 0, AFI_MSI_FPCI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); + afi_writel(pcie, 0, AFI_MSI_AXI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); } -static int tegra_pcie_enable_controller(void) +static int tegra_pcie_enable_controller(struct tegra_pcie_info *pcie) { u32 val, reg; int i, timeout; /* Enable slot clock and pulse the reset signals */ for (i = 0, reg = AFI_PEX0_CTRL; i < 2; i++, reg += 0x8) { - val = afi_readl(reg) | AFI_PEX_CTRL_REFCLK_EN; - afi_writel(val, reg); + val = afi_readl(pcie, reg) | AFI_PEX_CTRL_REFCLK_EN; + afi_writel(pcie, val, reg); val &= ~AFI_PEX_CTRL_RST; - afi_writel(val, reg); + afi_writel(pcie, val, reg); - val = afi_readl(reg) | AFI_PEX_CTRL_RST; - afi_writel(val, reg); + val = afi_readl(pcie, reg) | AFI_PEX_CTRL_RST; + afi_writel(pcie, val, reg); } /* Enable dual controller and both ports */ - val = afi_readl(AFI_PCIE_CONFIG); + val = afi_readl(pcie, AFI_PCIE_CONFIG); val &= ~(AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE | AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE | AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK); val |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; - afi_writel(val, AFI_PCIE_CONFIG); + afi_writel(pcie, val, AFI_PCIE_CONFIG); - val = afi_readl(AFI_FUSE) & ~AFI_FUSE_PCIE_T0_GEN2_DIS; - afi_writel(val, AFI_FUSE); + val = afi_readl(pcie, AFI_FUSE) & ~AFI_FUSE_PCIE_T0_GEN2_DIS; + afi_writel(pcie, val, AFI_FUSE); /* Initialze internal PHY, enable up to 16 PCIE lanes */ - pads_writel(0x0, PADS_CTL_SEL); + pads_writel(pcie, 0x0, PADS_CTL_SEL); /* override IDDQ to 1 on all 4 lanes */ - val = pads_readl(PADS_CTL) | PADS_CTL_IDDQ_1L; - pads_writel(val, PADS_CTL); + val = pads_readl(pcie, PADS_CTL) | PADS_CTL_IDDQ_1L; + pads_writel(pcie, val, PADS_CTL); /* * set up PHY PLL inputs select PLLE output as refclock, * set TX ref sel to div10 (not div5) */ - val = pads_readl(PADS_PLL_CTL); + val = pads_readl(pcie, PADS_PLL_CTL); val &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK); val |= (PADS_PLL_CTL_REFCLK_INTERNAL_CML | PADS_PLL_CTL_TXCLKREF_DIV10); - pads_writel(val, PADS_PLL_CTL); + pads_writel(pcie, val, PADS_PLL_CTL); /* take PLL out of reset */ - val = pads_readl(PADS_PLL_CTL) | PADS_PLL_CTL_RST_B4SM; - pads_writel(val, PADS_PLL_CTL); + val = pads_readl(pcie, PADS_PLL_CTL) | PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, val, PADS_PLL_CTL); /* * Hack, set the clock voltage to the DEFAULT provided by hw folks. * This doesn't exist in the documentation */ - pads_writel(0xfa5cfa5c, 0xc8); + pads_writel(pcie, 0xfa5cfa5c, 0xc8); /* Wait for the PLL to lock */ timeout = 2000; do { - val = pads_readl(PADS_PLL_CTL); + val = pads_readl(pcie, PADS_PLL_CTL); mdelay(1); if (--timeout == 0) { pr_err("Tegra PCIe error: timeout waiting for PLL\n"); @@ -640,188 +636,237 @@ static int tegra_pcie_enable_controller(void) } while (!(val & PADS_PLL_CTL_LOCKDET)); /* turn off IDDQ override */ - val = pads_readl(PADS_CTL) & ~PADS_CTL_IDDQ_1L; - pads_writel(val, PADS_CTL); + val = pads_readl(pcie, PADS_CTL) & ~PADS_CTL_IDDQ_1L; + pads_writel(pcie, val, PADS_CTL); /* enable TX/RX data */ - val = pads_readl(PADS_CTL); + val = pads_readl(pcie, PADS_CTL); val |= (PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L); - pads_writel(val, PADS_CTL); + pads_writel(pcie, val, PADS_CTL); /* Take the PCIe interface module out of reset */ - tegra_periph_reset_deassert(tegra_pcie.pcie_xclk); + tegra_periph_reset_deassert(pcie->pcie_xclk); /* Finally enable PCIe */ - val = afi_readl(AFI_CONFIGURATION) | AFI_CONFIGURATION_EN_FPCI; - afi_writel(val, AFI_CONFIGURATION); + val = afi_readl(pcie, AFI_CONFIGURATION) | AFI_CONFIGURATION_EN_FPCI; + afi_writel(pcie, val, AFI_CONFIGURATION); val = (AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR | AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR | AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR); - afi_writel(val, AFI_AFI_INTR_ENABLE); - afi_writel(0xffffffff, AFI_SM_INTR_ENABLE); + afi_writel(pcie, val, AFI_AFI_INTR_ENABLE); + afi_writel(pcie, 0xffffffff, AFI_SM_INTR_ENABLE); /* FIXME: No MSI for now, only INT */ - afi_writel(AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK); + afi_writel(pcie, AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK); /* Disable all execptions */ - afi_writel(0, AFI_FPCI_ERROR_MASKS); + afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS); - return; + return 0; } -static void tegra_pcie_power_off(void) +static void tegra_pcie_power_off(struct tegra_pcie_info *pcie) { - tegra_periph_reset_assert(tegra_pcie.pcie_xclk); - tegra_periph_reset_assert(tegra_pcie.afi_clk); - tegra_periph_reset_assert(tegra_pcie.pex_clk); + tegra_periph_reset_assert(pcie->pcie_xclk); + tegra_periph_reset_assert(pcie->afi_clk); + tegra_periph_reset_assert(pcie->pex_clk); tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); tegra_pmc_pcie_xclk_clamp(true); } -static int tegra_pcie_power_regate(void) +static int tegra_pcie_power_regate(struct tegra_pcie_info *pcie) { int err; - tegra_pcie_power_off(); + tegra_pcie_power_off(pcie); tegra_pmc_pcie_xclk_clamp(true); - tegra_periph_reset_assert(tegra_pcie.pcie_xclk); - tegra_periph_reset_assert(tegra_pcie.afi_clk); + tegra_periph_reset_assert(pcie->pcie_xclk); + tegra_periph_reset_assert(pcie->afi_clk); err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, - tegra_pcie.pex_clk); + pcie->pex_clk); if (err) { - pr_err("PCIE: powerup sequence failed: %d\n", err); + dev_err(pcie->dev, "powerup sequence failed: %d\n", err); return err; } - tegra_periph_reset_deassert(tegra_pcie.afi_clk); + tegra_periph_reset_deassert(pcie->afi_clk); tegra_pmc_pcie_xclk_clamp(false); - clk_enable(tegra_pcie.afi_clk); - clk_enable(tegra_pcie.pex_clk); - return clk_enable(tegra_pcie.pll_e); + clk_enable(pcie->afi_clk); + clk_enable(pcie->pex_clk); + return clk_enable(pcie->pll_e); } -static int tegra_pcie_clocks_get(void) +static int tegra_pcie_clocks_get(struct tegra_pcie_info *pcie) { int err; - tegra_pcie.pex_clk = clk_get(NULL, "pex"); - if (IS_ERR(tegra_pcie.pex_clk)) - return PTR_ERR(tegra_pcie.pex_clk); + pcie->pex_clk = clk_get(NULL, "pex"); + if (IS_ERR(pcie->pex_clk)) + return PTR_ERR(pcie->pex_clk); - tegra_pcie.afi_clk = clk_get(NULL, "afi"); - if (IS_ERR(tegra_pcie.afi_clk)) { - err = PTR_ERR(tegra_pcie.afi_clk); + pcie->afi_clk = clk_get(NULL, "afi"); + if (IS_ERR(pcie->afi_clk)) { + err = PTR_ERR(pcie->afi_clk); goto err_afi_clk; } - tegra_pcie.pcie_xclk = clk_get(NULL, "pcie_xclk"); - if (IS_ERR(tegra_pcie.pcie_xclk)) { - err = PTR_ERR(tegra_pcie.pcie_xclk); + pcie->pcie_xclk = clk_get(NULL, "pcie_xclk"); + if (IS_ERR(pcie->pcie_xclk)) { + err = PTR_ERR(pcie->pcie_xclk); goto err_pcie_xclk; } - tegra_pcie.pll_e = clk_get_sys(NULL, "pll_e"); - if (IS_ERR(tegra_pcie.pll_e)) { - err = PTR_ERR(tegra_pcie.pll_e); + pcie->pll_e = clk_get_sys(NULL, "pll_e"); + if (IS_ERR(pcie->pll_e)) { + err = PTR_ERR(pcie->pll_e); goto err_pll_e; } return 0; err_pll_e: - clk_put(tegra_pcie.pcie_xclk); + clk_put(pcie->pcie_xclk); err_pcie_xclk: - clk_put(tegra_pcie.afi_clk); + clk_put(pcie->afi_clk); err_afi_clk: - clk_put(tegra_pcie.pex_clk); + clk_put(pcie->pex_clk); return err; } -static void tegra_pcie_clocks_put(void) +static void tegra_pcie_clocks_put(struct tegra_pcie_info *pcie) { - clk_put(tegra_pcie.pll_e); - clk_put(tegra_pcie.pcie_xclk); - clk_put(tegra_pcie.afi_clk); - clk_put(tegra_pcie.pex_clk); + clk_put(pcie->pll_e); + clk_put(pcie->pcie_xclk); + clk_put(pcie->afi_clk); + clk_put(pcie->pex_clk); } -static int __init tegra_pcie_get_resources(void) +static int __devinit tegra_pcie_get_resources(struct platform_device *pdev) { - struct resource *res_mmio = &tegra_pcie.res_mmio; + struct tegra_pcie_info *pcie = platform_get_drvdata(pdev); + struct resource *regs; + struct resource *mmio; int err; - err = tegra_pcie_clocks_get(); + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mmio = platform_get_resource(pdev, IORESOURCE_MEM, 1); + + if (!regs || !mmio) { + dev_err(&pdev->dev, "failed to get I/O resources\n"); + return -ENXIO; + } + + err = tegra_pcie_clocks_get(pcie); if (err) { - pr_err("PCIE: failed to get clocks: %d\n", err); + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); return err; } - err = tegra_pcie_power_regate(); + err = tegra_pcie_power_regate(pcie); if (err) { - pr_err("PCIE: failed to power up: %d\n", err); + dev_err(&pdev->dev, "failed to power up: %d\n", err); goto err_pwr_on; } - tegra_pcie.regs = ioremap_nocache(TEGRA_PCIE_BASE, PCIE_IOMAP_SZ); - if (tegra_pcie.regs == NULL) { - pr_err("PCIE: Failed to map PCI/AFI registers\n"); + regs = request_mem_region(regs->start, resource_size(regs), "PCI/AFI"); + if (regs == NULL) { + dev_err(&pdev->dev, "failed to request PCI/AFI region: %d\n", err); + goto err_req_reg; + } + + pcie->regs = ioremap_nocache(regs->start, resource_size(regs)); + if (pcie->regs == NULL) { + dev_err(&pdev->dev, "failed to map PCI/AFI registers\n"); err = -ENOMEM; goto err_map_reg; } - err = request_resource(&iomem_resource, res_mmio); - if (err) { - pr_err("PCIE: Failed to request resources: %d\n", err); + mmio = request_mem_region(mmio->start, resource_size(mmio), "PCI I/O"); + if (mmio == NULL) { + dev_err(&pdev->dev, "failed to request PCI I/O region: %d\n", err); goto err_req_io; } - tegra_pcie_io_base = ioremap_nocache(res_mmio->start, - resource_size(res_mmio)); - if (tegra_pcie_io_base == NULL) { - pr_err("PCIE: Failed to map IO\n"); + pcie->mmio = ioremap_nocache(mmio->start, resource_size(mmio)); + if (pcie->mmio == NULL) { + dev_err(&pdev->dev, "failed to map PCI I/O region\n"); err = -ENOMEM; goto err_map_io; } - err = request_irq(INT_PCIE_INTR, tegra_pcie_isr, - IRQF_SHARED, "PCIE", &tegra_pcie); + tegra_pcie_io_base = pcie->mmio; + + err = platform_get_irq(pdev, 0); + if (err < 0) { + dev_err(&pdev->dev, "failed to get IRQ: %d\n", err); + goto err_irq; + } + + pcie->irq = err; + + err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", + pcie); if (err) { - pr_err("PCIE: Failed to register IRQ: %d\n", err); + dev_err(&pdev->dev, "failed to register IRQ: %d\n", err); goto err_irq; } - set_irq_flags(INT_PCIE_INTR, IRQF_VALID); return 0; err_irq: - iounmap(tegra_pcie_io_base); + iounmap(pcie->mmio); err_map_io: - release_resource(&tegra_pcie.res_mmio); + release_resource(mmio); err_req_io: - iounmap(tegra_pcie.regs); + iounmap(pcie->regs); err_map_reg: - tegra_pcie_power_off(); + release_resource(regs); +err_req_reg: + tegra_pcie_power_off(pcie); err_pwr_on: - tegra_pcie_clocks_put(); + tegra_pcie_clocks_put(pcie); return err; } +static int tegra_pcie_put_resources(struct platform_device *pdev) +{ + struct tegra_pcie_info *pcie = platform_get_drvdata(pdev); + struct resource *regs; + struct resource *mmio; + + free_irq(pcie->irq, pcie); + + iounmap(pcie->mmio); + mmio = platform_get_resource(pdev, IORESOURCE_MEM, 1); + release_resource(mmio); + + iounmap(pcie->regs); + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_resource(regs); + + tegra_pcie_power_off(pcie); + tegra_pcie_clocks_put(pcie); + + return 0; +} + /* * FIXME: If there are no PCIe cards attached, then calling this function * can result in the increase of the bootup time as there are big timeout * loops. */ #define TEGRA_PCIE_LINKUP_TIMEOUT 200 /* up to 1.2 seconds */ -static bool tegra_pcie_check_link(struct tegra_pcie_port *pp, int idx, +static bool tegra_pcie_check_link(struct tegra_pcie_info *pcie, + struct tegra_pcie_port *pp, int idx, u32 reset_reg) { u32 reg; @@ -841,7 +886,7 @@ static bool tegra_pcie_check_link(struct tegra_pcie_port *pp, int idx, } if (!timeout) { - pr_err("PCIE: port %d: link down, retrying\n", idx); + dev_err(pcie->dev, "port %d: link down, retrying\n", idx); goto retry; } @@ -858,11 +903,11 @@ static bool tegra_pcie_check_link(struct tegra_pcie_port *pp, int idx, retry: /* Pulse the PEX reset */ - reg = afi_readl(reset_reg) | AFI_PEX_CTRL_RST; - afi_writel(reg, reset_reg); + reg = afi_readl(pcie, reset_reg) | AFI_PEX_CTRL_RST; + afi_writel(pcie, reg, reset_reg); mdelay(1); - reg = afi_readl(reset_reg) & ~AFI_PEX_CTRL_RST; - afi_writel(reg, reset_reg); + reg = afi_readl(pcie, reset_reg) & ~AFI_PEX_CTRL_RST; + afi_writel(pcie, reg, reset_reg); retries--; } while (retries); @@ -870,55 +915,111 @@ retry: return false; } -static void __init tegra_pcie_add_port(int index, u32 offset, u32 reset_reg) +static void __devinit tegra_pcie_add_port(struct tegra_pcie_info *pcie, + int index, u32 offset, + u32 reset_reg) { struct tegra_pcie_port *pp; - pp = tegra_pcie.port + tegra_pcie.num_ports; + pp = pcie->port + pcie->num_ports; pp->index = -1; - pp->base = tegra_pcie.regs + offset; - pp->link_up = tegra_pcie_check_link(pp, index, reset_reg); + pp->base = pcie->regs + offset; + pp->link_up = tegra_pcie_check_link(pcie, pp, index, reset_reg); if (!pp->link_up) { pp->base = NULL; - printk(KERN_INFO "PCIE: port %d: link down, ignoring\n", index); + dev_info(pcie->dev, "port %d: link down, ignoring\n", index); return; } - tegra_pcie.num_ports++; + pcie->num_ports++; pp->index = index; pp->root_bus_nr = -1; memset(pp->res, 0, sizeof(pp->res)); } -int __init tegra_pcie_init(bool init_port0, bool init_port1) +static int __devinit tegra_pcie_probe(struct platform_device *pdev) { + struct tegra_pcie_pdata *pdata = pdev->dev.platform_data; + struct tegra_pcie_info *pcie; int err; - if (!(init_port0 || init_port1)) + if (!pdata->enable_ports[0] && !pdata->enable_ports[1]) return -ENODEV; + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + platform_set_drvdata(pdev, pcie); + pcie->dev = &pdev->dev; + pcibios_min_mem = 0; - err = tegra_pcie_get_resources(); - if (err) + err = tegra_pcie_get_resources(pdev); + if (err < 0) { + dev_err(&pdev->dev, "failed to request resources: %d\n", err); return err; + } - err = tegra_pcie_enable_controller(); + if (pdata->init) { + err = pdata->init(pdev); + if (err < 0) { + tegra_pcie_put_resources(pdev); + return err; + } + } + + err = tegra_pcie_enable_controller(pcie); if (err) return err; /* setup the AFI address translations */ - tegra_pcie_setup_translations(); + tegra_pcie_setup_translations(pcie); + + if (pdata->enable_ports[0]) + tegra_pcie_add_port(pcie, 0, RP0_OFFSET, AFI_PEX0_CTRL); - if (init_port0) - tegra_pcie_add_port(0, RP0_OFFSET, AFI_PEX0_CTRL); + if (pdata->enable_ports[1]) + tegra_pcie_add_port(pcie, 1, RP1_OFFSET, AFI_PEX1_CTRL); - if (init_port1) - tegra_pcie_add_port(1, RP1_OFFSET, AFI_PEX1_CTRL); + pcie->hw.nr_controllers = 2; + pcie->hw.setup = tegra_pcie_setup; + pcie->hw.scan = tegra_pcie_scan_bus; + pcie->hw.swizzle = pci_std_swizzle; + pcie->hw.map_irq = tegra_pcie_map_irq; - pci_common_init(&tegra_pcie_hw); + pci_common_init(&pcie->hw); return 0; } + +static int __devexit tegra_pcie_remove(struct platform_device *pdev) +{ + struct tegra_pcie_pdata *pdata = pdev->dev.platform_data; + int err; + + err = tegra_pcie_put_resources(pdev); + if (err < 0) + return err; + + if (pdata->exit) { + err = pdata->exit(pdev); + if (err < 0) + return err; + } + + return 0; +} + +static struct platform_driver tegra_pcie_driver = { + .driver = { + .name = "tegra-pcie", + .owner = THIS_MODULE, + }, + .probe = tegra_pcie_probe, + .remove = __devexit_p(tegra_pcie_remove), +}; + +module_platform_driver(tegra_pcie_driver); -- 1.7.9.3 _______________________________________________ devicetree-discuss mailing list devicetree-discuss@lists.ozlabs.org https://lists.ozlabs.org/listinfo/devicetree-discuss