[PATCH 09/11] ARM: tegra: Rewrite PCIe support as a driver
Thierry Reding
thierry.reding at avionic-design.de
Fri Mar 9 01:51:29 EST 2012
Signed-off-by: Thierry Reding <thierry.reding at 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
More information about the devicetree-discuss
mailing list