[PATCH v3 06/10] ARM: tegra: Rewrite PCIe support as a driver
Thierry Reding
thierry.reding at avionic-design.de
Thu Jul 26 15:55:08 EDT 2012
This commit adds a platform device driver for the PCIe controller on
Tegra SOCs. Current users of the old code (TrimSlice and Harmony) are
converted and now initialize and register a corresponding platform
device.
Signed-off-by: Thierry Reding <thierry.reding at avionic-design.de>
---
Changes in v3:
- use devm_request_and_ioremap() and devm_clk_get()
- make root ports separate devices
- fix extended configuration space access
Changes in v2:
- use struct hw_pci's new private_data field
- fix DT initialization for TrimSlice
arch/arm/mach-tegra/board-harmony-pcie.c | 30 +-
arch/arm/mach-tegra/board-harmony.c | 1 +
arch/arm/mach-tegra/board-harmony.h | 1 +
arch/arm/mach-tegra/board-trimslice.c | 11 +-
arch/arm/mach-tegra/board.h | 2 +-
arch/arm/mach-tegra/devices.c | 135 ++++
arch/arm/mach-tegra/devices.h | 3 +
arch/arm/mach-tegra/include/mach/iomap.h | 3 -
arch/arm/mach-tegra/include/mach/pci-tegra.h | 38 ++
arch/arm/mach-tegra/pcie.c | 926 ++++++++++++++++-----------
10 files changed, 732 insertions(+), 418 deletions(-)
create mode 100644 arch/arm/mach-tegra/include/mach/pci-tegra.h
diff --git a/arch/arm/mach-tegra/board-harmony-pcie.c b/arch/arm/mach-tegra/board-harmony-pcie.c
index e8c3fda..712f3bd 100644
--- a/arch/arm/mach-tegra/board-harmony-pcie.c
+++ b/arch/arm/mach-tegra/board-harmony-pcie.c
@@ -22,12 +22,14 @@
#include <asm/mach-types.h>
+#include <mach/pci-tegra.h>
+
#include "board.h"
+#include "devices.h"
#include "board-harmony.h"
#ifdef CONFIG_TEGRA_PCI
-
-int __init harmony_pcie_init(void)
+static int harmony_pcie_board_init(struct platform_device *pdev)
{
struct regulator *regulator = NULL;
int err;
@@ -44,30 +46,24 @@ 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;
}
-static int __init harmony_pcie_initcall(void)
+int __init harmony_pcie_init(void)
{
- if (!machine_is_harmony())
- return 0;
+ tegra_pcie_pdata.init = harmony_pcie_board_init;
+ platform_device_register(&tegra_pcie_device);
- return harmony_pcie_init();
+ return 0;
+}
+#else
+int __init harmony_pcie_init(void)
+{
+ return 0;
}
-
-/* PCI should be initialized after I2C, mfd and regulators */
-subsys_initcall_sync(harmony_pcie_initcall);
-
#endif
diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c
index e5f3352..063c7d5 100644
--- a/arch/arm/mach-tegra/board-harmony.c
+++ b/arch/arm/mach-tegra/board-harmony.c
@@ -204,6 +204,7 @@ static void __init tegra_harmony_init(void)
pwm_add_table(harmony_pwm_lookup, ARRAY_SIZE(harmony_pwm_lookup));
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 776aa95..2667fe9 100644
--- a/arch/arm/mach-tegra/board-trimslice.c
+++ b/arch/arm/mach-tegra/board-trimslice.c
@@ -34,6 +34,7 @@
#include <asm/setup.h>
#include <mach/iomap.h>
+#include <mach/pci-tegra.h>
#include <mach/sdhci.h>
#include "board.h"
@@ -145,14 +146,11 @@ static __initdata struct tegra_clk_init_table trimslice_clk_init_table[] = {
{ NULL, NULL, 0, 0},
};
-static int __init tegra_trimslice_pci_init(void)
+static int __init trimslice_pci_init(void)
{
- if (!machine_is_trimslice())
- return 0;
-
- return tegra_pcie_init(true, true);
+ platform_device_register(&tegra_pcie_device);
+ return 0;
}
-subsys_initcall(tegra_trimslice_pci_init);
static void __init tegra_trimslice_init(void)
{
@@ -167,6 +165,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 f88e514..3a2a7e9 100644
--- a/arch/arm/mach-tegra/board.h
+++ b/arch/arm/mach-tegra/board.h
@@ -30,7 +30,6 @@ 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);
void tegra_init_late(void);
@@ -56,4 +55,5 @@ static inline int harmony_pcie_init(void) { return 0; }
void __init tegra_paz00_wifikill_init(void);
extern struct sys_timer tegra_timer;
+
#endif
diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c
index 4529561..203af2e 100644
--- a/arch/arm/mach-tegra/devices.c
+++ b/arch/arm/mach-tegra/devices.c
@@ -28,6 +28,7 @@
#include <mach/iomap.h>
#include <mach/dma.h>
#include <mach/usb_phy.h>
+#include <mach/pci-tegra.h>
#include "gpio-names.h"
#include "devices.h"
@@ -735,3 +736,137 @@ struct platform_device tegra_nand_device = {
.num_resources = ARRAY_SIZE(tegra_nand_resources),
.resource = tegra_nand_resources,
};
+
+static struct resource tegra_pcie_resources[] = {
+ /* PADS registers */
+ [0] = {
+ .start = 0x80003000,
+ .end = 0x800037ff,
+ .flags = IORESOURCE_MEM,
+ },
+ /* AFI registers */
+ [1] = {
+ .start = 0x80003800,
+ .end = 0x800039ff,
+ .flags = IORESOURCE_MEM,
+ },
+ /* PCI configuration space */
+ [2] = {
+ .start = 0x81000000,
+ .end = 0x81000000 + SZ_16M - 1,
+ .flags = IORESOURCE_MEM | IORESOURCE_PCI_CS,
+ },
+ /* PCI extended configuration space */
+ [3] = {
+ .start = 0x90000000,
+ .end = 0x90000000 + SZ_256M - 1,
+ .flags = IORESOURCE_MEM | IORESOURCE_PCI_CS,
+ },
+ [4] = {
+ .start = INT_PCIE_INTR,
+ .end = INT_PCIE_INTR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource tegra_pcie_rp0_resources[] = {
+ [0] = {
+ .start = 0x80000000,
+ .end = 0x80000000 + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource tegra_pcie_rp0_ranges[] = {
+ [0] = {
+ .start = 0x81000000,
+ .end = 0x81000000 + SZ_8M - 1,
+ .flags = IORESOURCE_MEM | IORESOURCE_PCI_CS,
+ },
+ [1] = {
+ .start = 0x90000000,
+ .end = 0x90000000 + SZ_128M - 1,
+ .flags = IORESOURCE_MEM | IORESOURCE_PCI_CS,
+ },
+ [2] = {
+ .start = 0x82000000,
+ .end = 0x82000000 + SZ_64K - 1,
+ .flags = IORESOURCE_IO,
+ },
+ [3] = {
+ .start = 0xa0000000,
+ .end = 0xa0000000 + SZ_128M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [4] = {
+ .start = 0xb0000000,
+ .end = 0xb0000000 + SZ_128M - 1,
+ .flags = IORESOURCE_MEM | IORESOURCE_PREFETCH,
+ },
+};
+
+static struct resource tegra_pcie_rp1_resources[] = {
+ [0] = {
+ .start = 0x80001000,
+ .end = 0x80001000 + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource tegra_pcie_rp1_ranges[] = {
+ [0] = {
+ .start = 0x81800000,
+ .end = 0x81800000 + SZ_8M - 1,
+ .flags = IORESOURCE_MEM | IORESOURCE_PCI_CS,
+ },
+ [1] = {
+ .start = 0x98000000,
+ .end = 0x98000000 + SZ_128M - 1,
+ .flags = IORESOURCE_MEM | IORESOURCE_PCI_CS,
+ },
+ [2] = {
+ .start = 0x82010000,
+ .end = 0x82010000 + SZ_64K - 1,
+ .flags = IORESOURCE_IO,
+ },
+ [3] = {
+ .start = 0xa8000000,
+ .end = 0xa8000000 + SZ_128M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [4] = {
+ .start = 0xb8000000,
+ .end = 0xb8000000 + SZ_128M - 1,
+ .flags = IORESOURCE_MEM | IORESOURCE_PREFETCH,
+ },
+};
+
+static struct tegra_pcie_rp tegra_pcie_ports[] = {
+ [0] = {
+ .resources = tegra_pcie_rp0_resources,
+ .num_resources = ARRAY_SIZE(tegra_pcie_rp0_resources),
+ .ranges = tegra_pcie_rp0_ranges,
+ .num_ranges = ARRAY_SIZE(tegra_pcie_rp0_ranges),
+ .num_lanes = 2,
+ },
+ [1] = {
+ .resources = tegra_pcie_rp1_resources,
+ .num_resources = ARRAY_SIZE(tegra_pcie_rp1_resources),
+ .ranges = tegra_pcie_rp1_ranges,
+ .num_ranges = ARRAY_SIZE(tegra_pcie_rp1_ranges),
+ .num_lanes = 2,
+ },
+};
+
+struct tegra_pcie_pdata tegra_pcie_pdata = {
+ .ports = tegra_pcie_ports,
+ .num_ports = ARRAY_SIZE(tegra_pcie_ports),
+};
+
+struct platform_device tegra_pcie_device = {
+ .name = "tegra-pcie",
+ .id = -1,
+ .resource = tegra_pcie_resources,
+ .num_resources = ARRAY_SIZE(tegra_pcie_resources),
+ .dev.platform_data = &tegra_pcie_pdata,
+};
diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h
index f054d10..eb28671 100644
--- a/arch/arm/mach-tegra/devices.h
+++ b/arch/arm/mach-tegra/devices.h
@@ -58,4 +58,7 @@ extern struct platform_device tegra_i2s_device2;
extern struct platform_device tegra_das_device;
extern struct platform_device tegra_pwm_device;
+extern struct tegra_pcie_pdata tegra_pcie_pdata;
+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 fee3a94..7e76da7 100644
--- a/arch/arm/mach-tegra/include/mach/iomap.h
+++ b/arch/arm/mach-tegra/include/mach/iomap.h
@@ -303,9 +303,6 @@
#define IO_APB_VIRT IOMEM(0xFE300000)
#define IO_APB_SIZE SZ_1M
-#define TEGRA_PCIE_BASE 0x80000000
-#define TEGRA_PCIE_IO_BASE (TEGRA_PCIE_BASE + SZ_4M)
-
#define IO_TO_VIRT_BETWEEN(p, st, sz) ((p) >= (st) && (p) < ((st) + (sz)))
#define IO_TO_VIRT_XLATE(p, pst, vst) (((p) - (pst) + (vst)))
diff --git a/arch/arm/mach-tegra/include/mach/pci-tegra.h b/arch/arm/mach-tegra/include/mach/pci-tegra.h
new file mode 100644
index 0000000..e6d9fc3
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/pci-tegra.h
@@ -0,0 +1,38 @@
+/*
+ * arch/arm/mach-tegra/include/mach/tegra-pcie.h
+ *
+ * Copyright (C) 2012 Avionic Design GmbH
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MACH_TEGRA_PCIE_H
+#define __MACH_TEGRA_PCIE_H
+
+#include <linux/platform_device.h>
+
+struct tegra_pcie_rp {
+ unsigned int index;
+ struct resource *resources;
+ unsigned int num_resources;
+ struct resource *ranges;
+ unsigned int num_ranges;
+ unsigned int num_lanes;
+};
+
+struct tegra_pcie_pdata {
+ int (*init)(struct platform_device *pdev);
+ int (*exit)(struct platform_device *pdev);
+
+ struct tegra_pcie_rp *ports;
+ unsigned int num_ports;
+};
+
+#endif
diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c
index efe71dd..3e5fb66 100644
--- a/arch/arm/mach-tegra/pcie.c
+++ b/arch/arm/mach-tegra/pcie.c
@@ -27,7 +27,9 @@
*/
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clk.h>
@@ -35,20 +37,17 @@
#include <linux/export.h>
#include <asm/sizes.h>
+#include <asm/mach/irq.h>
#include <asm/mach/pci.h>
#include <mach/iomap.h>
#include <mach/clk.h>
#include <mach/powergate.h>
+#include <mach/pci-tegra.h>
-#include "board.h"
#include "pmc.h"
/* register definitions */
-#define AFI_OFFSET 0x3800
-#define PADS_OFFSET 0x3000
-#define RP0_OFFSET 0x0000
-#define RP1_OFFSET 0x1000
#define AFI_AXI_BAR0_SZ 0x00
#define AFI_AXI_BAR1_SZ 0x04
@@ -161,141 +160,146 @@
* 0x90000000 - 0x9fffffff - non-prefetchable memory
* 0xa0000000 - 0xbfffffff - prefetchable memory
*/
-#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 MEM_BASE_0 (TEGRA_PCIE_BASE + SZ_256M)
-#define MEM_SIZE_0 SZ_128M
-#define MEM_BASE_1 (MEM_BASE_0 + MEM_SIZE_0)
-#define MEM_SIZE_1 SZ_128M
-#define PREFETCH_MEM_BASE_0 (MEM_BASE_1 + MEM_SIZE_1)
-#define PREFETCH_MEM_SIZE_0 SZ_128M
-#define PREFETCH_MEM_BASE_1 (PREFETCH_MEM_BASE_0 + PREFETCH_MEM_SIZE_0)
-#define PREFETCH_MEM_SIZE_1 SZ_128M
-
-#define PCIE_CONF_BUS(b) ((b) << 16)
-#define PCIE_CONF_DEV(d) ((d) << 11)
-#define PCIE_CONF_FUNC(f) ((f) << 8)
-#define PCIE_CONF_REG(r) \
- (((r) & ~0x3) | (((r) < 256) ? PCIE_CFG_OFF : PCIE_EXT_CFG_OFF))
-struct tegra_pcie_port {
- int index;
- u8 root_bus_nr;
- void __iomem *base;
+#define PCIE_CONF_BUS(b) ((b) << 16)
+#define PCIE_CONF_DEV(d) ((d) << 11)
+#define PCIE_CONF_FUNC(f) ((f) << 8)
+#define PCIE_CONF_REG(r) ((((r) & 0xf00) << 16) | ((r) & ~3))
- bool link_up;
+struct tegra_pcie {
+ struct device *dev;
- char mem_space_name[16];
- char prefetch_space_name[20];
- struct resource res[2];
-};
+ void __iomem *pads;
+ void __iomem *afi;
+ int irq;
+
+ struct resource *cfg;
+ struct resource *extcfg;
-struct tegra_pcie_info {
- struct tegra_pcie_port port[2];
- int num_ports;
+ void __iomem *cs;
+ void __iomem *extcs;
- void __iomem *regs;
- struct resource res_mmio;
+ struct resource io;
+ struct resource mem;
+ struct resource prefetch;
- struct clk *pex_clk;
- struct clk *afi_clk;
- struct clk *pcie_xclk;
- struct clk *pll_e;
+ struct clk *pex_clk;
+ struct clk *afi_clk;
+ struct clk *pcie_xclk;
+ struct clk *pll_e;
+
+ struct list_head ports;
+ unsigned int num_ports;
};
-static struct tegra_pcie_info tegra_pcie;
+struct tegra_pcie_port {
+ struct tegra_pcie *pcie;
+
+ void __iomem *base;
+ unsigned int index;
+
+ struct resource io;
+ struct resource mem;
+ struct resource prefetch;
+
+ struct list_head list;
+};
-static inline void afi_writel(u32 value, unsigned long offset)
+static inline struct tegra_pcie_port *sys_to_pcie(struct pci_sys_data *sys)
{
- writel(value, offset + AFI_OFFSET + tegra_pcie.regs);
+ return sys->private_data;
}
-static inline u32 afi_readl(unsigned long offset)
+static inline void afi_writel(struct tegra_pcie *pcie, u32 value,
+ unsigned long offset)
{
- return readl(offset + AFI_OFFSET + tegra_pcie.regs);
+ writel(value, pcie->afi + offset);
}
-static inline void pads_writel(u32 value, unsigned long offset)
+static inline u32 afi_readl(struct tegra_pcie *pcie, unsigned long offset)
{
- writel(value, offset + PADS_OFFSET + tegra_pcie.regs);
+ return readl(pcie->afi + offset);
}
-static inline u32 pads_readl(unsigned long offset)
+static inline void pads_writel(struct tegra_pcie *pcie, u32 value,
+ unsigned long offset)
{
- return readl(offset + PADS_OFFSET + tegra_pcie.regs);
+ writel(value, pcie->pads + offset);
}
-static struct tegra_pcie_port *bus_to_port(int bus)
+static inline u32 pads_readl(struct tegra_pcie *pcie, unsigned long offset)
{
- 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)
- break;
- }
-
- return i >= 0 ? tegra_pcie.port + i : NULL;
+ return readl(pcie->pads + offset);
}
static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
- int where, int size, u32 *val)
+ int where, int size, u32 *value)
{
- struct tegra_pcie_port *pp = bus_to_port(bus->number);
- void __iomem *addr;
+ struct tegra_pcie_port *port = sys_to_pcie(bus->sysdata);
+ struct tegra_pcie *pcie = port->pcie;
+ unsigned long offset = -1;
+ void __iomem *addr = NULL;
- if (pp) {
+ if (!bus->parent) {
if (devfn != 0) {
- *val = 0xffffffff;
+ *value = 0xffffffff;
return PCIBIOS_DEVICE_NOT_FOUND;
}
- addr = pp->base + (where & ~0x3);
+ addr = port->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));
+ if (where >= 0x100)
+ addr = pcie->extcs;
+ else
+ addr = pcie->cs;
+
+ offset = PCIE_CONF_BUS(bus->number) +
+ PCIE_CONF_DEV(PCI_SLOT(devfn)) +
+ PCIE_CONF_FUNC(PCI_FUNC(devfn)) +
+ PCIE_CONF_REG(where);
+ addr += offset;
}
- *val = readl(addr);
+ *value = readl(addr);
if (size == 1)
- *val = (*val >> (8 * (where & 3))) & 0xff;
+ *value = (*value >> (8 * (where & 3))) & 0xff;
else if (size == 2)
- *val = (*val >> (8 * (where & 3))) & 0xffff;
+ *value = (*value >> (8 * (where & 3))) & 0xffff;
return PCIBIOS_SUCCESSFUL;
}
static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn,
- int where, int size, u32 val)
+ int where, int size, u32 value)
{
- struct tegra_pcie_port *pp = bus_to_port(bus->number);
+ struct tegra_pcie_port *port = sys_to_pcie(bus->sysdata);
+ struct tegra_pcie *pcie = port->pcie;
+ unsigned long offset = -1;
void __iomem *addr;
-
u32 mask;
u32 tmp;
- if (pp) {
+ if (!bus->parent) {
if (devfn != 0)
return PCIBIOS_DEVICE_NOT_FOUND;
- addr = pp->base + (where & ~0x3);
+ addr = port->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));
+ if (where >= 0x100)
+ addr = pcie->extcs;
+ else
+ addr = pcie->cs;
+
+ offset = PCIE_CONF_BUS(bus->number) +
+ PCIE_CONF_DEV(PCI_SLOT(devfn)) +
+ PCIE_CONF_FUNC(PCI_FUNC(devfn)) +
+ PCIE_CONF_REG(where);
+ addr += offset;
}
if (size == 4) {
- writel(val, addr);
+ writel(value, addr);
return PCIBIOS_SUCCESSFUL;
}
@@ -307,7 +311,7 @@ static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn,
return PCIBIOS_BAD_REGISTER_NUMBER;
tmp = readl(addr) & mask;
- tmp |= val << ((where & 0x3) * 8);
+ tmp |= value << ((where & 0x3) * 8);
writel(tmp, addr);
return PCIBIOS_SUCCESSFUL;
@@ -358,85 +362,36 @@ 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_port *pp;
-
- if (nr >= tegra_pcie.num_ports)
- return 0;
+ struct tegra_pcie_port *port = sys_to_pcie(sys);
- pp = tegra_pcie.port + nr;
- pp->root_bus_nr = sys->busnr;
+ pci_add_resource_offset(&sys->resources, &port->io,
+ sys->io_offset);
+ pci_add_resource_offset(&sys->resources, &port->mem,
+ sys->mem_offset);
+ pci_add_resource_offset(&sys->resources, &port->prefetch,
+ sys->mem_offset);
- pci_ioremap_io(nr * SZ_64K, TEGRA_PCIE_IO_BASE);
-
- /*
- * IORESOURCE_MEM
- */
- snprintf(pp->mem_space_name, sizeof(pp->mem_space_name),
- "PCIe %d MEM", pp->index);
- pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0;
- pp->res[0].name = pp->mem_space_name;
- if (pp->index == 0) {
- pp->res[0].start = MEM_BASE_0;
- pp->res[0].end = pp->res[0].start + MEM_SIZE_0 - 1;
- } else {
- pp->res[0].start = MEM_BASE_1;
- pp->res[0].end = pp->res[0].start + MEM_SIZE_1 - 1;
- }
- pp->res[0].flags = IORESOURCE_MEM;
- if (request_resource(&iomem_resource, &pp->res[0]))
- panic("Request PCIe Memory resource failed\n");
- pci_add_resource_offset(&sys->resources, &pp->res[0], sys->mem_offset);
-
- /*
- * IORESOURCE_MEM | IORESOURCE_PREFETCH
- */
- snprintf(pp->prefetch_space_name, sizeof(pp->prefetch_space_name),
- "PCIe %d PREFETCH MEM", pp->index);
- pp->prefetch_space_name[sizeof(pp->prefetch_space_name) - 1] = 0;
- pp->res[1].name = pp->prefetch_space_name;
- if (pp->index == 0) {
- pp->res[1].start = PREFETCH_MEM_BASE_0;
- pp->res[1].end = pp->res[2].start + PREFETCH_MEM_SIZE_0 - 1;
- } else {
- pp->res[1].start = PREFETCH_MEM_BASE_1;
- pp->res[1].end = pp->res[1].start + PREFETCH_MEM_SIZE_1 - 1;
- }
- pp->res[1].flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
- if (request_resource(&iomem_resource, &pp->res[1]))
- panic("Request PCIe Prefetch Memory resource failed\n");
- pci_add_resource_offset(&sys->resources, &pp->res[1], sys->mem_offset);
+ pci_ioremap_io(nr * SZ_64K, port->io.start);
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_port *port = sys_to_pcie(pdev->bus->sysdata);
+
+ return port->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_port *pp;
+ struct tegra_pcie_port *port = sys_to_pcie(sys);
- if (nr >= tegra_pcie.num_ports)
- return NULL;
-
- pp = tegra_pcie.port + nr;
- pp->root_bus_nr = sys->busnr;
-
- return pci_scan_root_bus(NULL, sys->busnr, &tegra_pcie_ops, sys,
- &sys->resources);
+ return pci_scan_root_bus(port->pcie->dev, 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,
- .map_irq = tegra_pcie_map_irq,
-};
-
-
static irqreturn_t tegra_pcie_isr(int irq, void *arg)
{
const char *err_msg[] = {
@@ -448,14 +403,14 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
"Invalid write",
"Response decoding error",
"AXI response decoding error",
- "Transcation timeout",
+ "Transaction timeout",
};
-
+ struct tegra_pcie *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;
@@ -468,405 +423,594 @@ 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 *pcie)
{
u32 fpci_bar;
u32 size;
u32 axi_address;
/* Bar 0: config Bar */
- 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);
+ fpci_bar = 0xfdff0000;
+ size = resource_size(pcie->cfg);
+ axi_address = pcie->cfg->start;
+ 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);
+ fpci_bar = 0xfe100000;
+ size = resource_size(pcie->extcfg);
+ axi_address = pcie->extcfg->start;
+ 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 = SZ_128K;
- axi_address = TEGRA_PCIE_IO_BASE;
- afi_writel(axi_address, AFI_AXI_BAR2_START);
- afi_writel(size >> 12, AFI_AXI_BAR2_SZ);
- afi_writel(fpci_bar, AFI_FPCI_BAR2);
+ fpci_bar = 0xfdfc0000;
+ size = resource_size(&pcie->io);
+ axi_address = pcie->io.start;
+ 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);
+ fpci_bar = (((pcie->prefetch.start >> 12) & 0x0fffffff) << 4) | 0x1;
+ size = resource_size(&pcie->prefetch);
+ axi_address = pcie->prefetch.start;
+ 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);
+ fpci_bar = (((pcie->mem.start >> 12) & 0x0fffffff) << 4) | 0x1;
+ size = resource_size(&pcie->mem);
+ axi_address = pcie->mem.start;
+ 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);
-
- /* 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, 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);
+
+ /* MSI translations are setup only when needed */
+ 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 *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_PEX_CTRL_RST;
- afi_writel(val, reg);
-
- val = afi_readl(reg) | AFI_PEX_CTRL_RST;
- afi_writel(val, reg);
- }
+ unsigned int timeout;
+ unsigned long value;
- /* Enable dual controller and both ports */
- val = afi_readl(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);
+ /* enable dual controller and both ports */
+ value = afi_readl(pcie, AFI_PCIE_CONFIG);
+ value &= ~(AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE |
+ AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE |
+ AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK);
+ value |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL;
+ afi_writel(pcie, value, AFI_PCIE_CONFIG);
- val = afi_readl(AFI_FUSE) & ~AFI_FUSE_PCIE_T0_GEN2_DIS;
- afi_writel(val, AFI_FUSE);
+ value = afi_readl(pcie, AFI_FUSE);
+ value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS;
+ afi_writel(pcie, value, AFI_FUSE);
- /* Initialze internal PHY, enable up to 16 PCIE lanes */
- pads_writel(0x0, PADS_CTL_SEL);
+ /* initialze internal PHY, enable up to 16 PCIE lanes */
+ 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);
+ value = pads_readl(pcie, PADS_CTL);
+ value |= PADS_CTL_IDDQ_1L;
+ pads_writel(pcie, value, PADS_CTL);
/*
- * set up PHY PLL inputs select PLLE output as refclock,
- * set TX ref sel to div10 (not div5)
+ * 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_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);
+ value = pads_readl(pcie, PADS_PLL_CTL);
+ value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK);
+ value |= (PADS_PLL_CTL_REFCLK_INTERNAL_CML | PADS_PLL_CTL_TXCLKREF_DIV10);
+ pads_writel(pcie, value, 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);
+ value = pads_readl(pcie, PADS_PLL_CTL);
+ value |= PADS_PLL_CTL_RST_B4SM;
+ pads_writel(pcie, value, PADS_PLL_CTL);
/*
* Hack, set the clock voltage to the DEFAULT provided by hw folks.
- * This doesn't exist in the documentation
+ * This doesn't exist in the documentation.
*/
- pads_writel(0xfa5cfa5c, 0xc8);
+ pads_writel(pcie, 0xfa5cfa5c, 0xc8);
- /* Wait for the PLL to lock */
+ /* wait for the PLL to lock */
timeout = 300;
do {
- val = pads_readl(PADS_PLL_CTL);
+ value = pads_readl(pcie, PADS_PLL_CTL);
usleep_range(1000, 1000);
if (--timeout == 0) {
pr_err("Tegra PCIe error: timeout waiting for PLL\n");
return -EBUSY;
}
- } while (!(val & PADS_PLL_CTL_LOCKDET));
+ } while (!(value & PADS_PLL_CTL_LOCKDET));
/* turn off IDDQ override */
- val = pads_readl(PADS_CTL) & ~PADS_CTL_IDDQ_1L;
- pads_writel(val, PADS_CTL);
+ value = pads_readl(pcie, PADS_CTL);
+ value &= ~PADS_CTL_IDDQ_1L;
+ pads_writel(pcie, value, PADS_CTL);
/* enable TX/RX data */
- val = pads_readl(PADS_CTL);
- val |= (PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L);
- pads_writel(val, PADS_CTL);
+ value = pads_readl(pcie, PADS_CTL);
+ value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L;
+ pads_writel(pcie, value, PADS_CTL);
- /* Take the PCIe interface module out of reset */
- tegra_periph_reset_deassert(tegra_pcie.pcie_xclk);
+ /* take the PCIe interface module out of reset */
+ tegra_periph_reset_deassert(pcie->pcie_xclk);
- /* Finally enable PCIe */
- val = afi_readl(AFI_CONFIGURATION) | AFI_CONFIGURATION_EN_FPCI;
- afi_writel(val, AFI_CONFIGURATION);
+ /* finally enable PCIe */
+ value = afi_readl(pcie, AFI_CONFIGURATION);
+ value |= AFI_CONFIGURATION_EN_FPCI;
+ afi_writel(pcie, value, 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);
+ value = 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(pcie, value, 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);
+ /* don't enable MSI for now, only when needed */
+ afi_writel(pcie, AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK);
- /* Disable all execptions */
- afi_writel(0, AFI_FPCI_ERROR_MASKS);
+ /* disable all exceptions */
+ afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS);
return 0;
}
-static void tegra_pcie_power_off(void)
+static void tegra_pcie_power_off(struct tegra_pcie *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 *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_prepare_enable(tegra_pcie.afi_clk);
- clk_prepare_enable(tegra_pcie.pex_clk);
- return clk_prepare_enable(tegra_pcie.pll_e);
+ clk_prepare_enable(pcie->afi_clk);
+ clk_prepare_enable(pcie->pex_clk);
+ return clk_prepare_enable(pcie->pll_e);
}
-static int tegra_pcie_clocks_get(void)
+static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
{
- int err;
+ pcie->pex_clk = devm_clk_get(pcie->dev, "pex");
+ if (IS_ERR(pcie->pex_clk))
+ return PTR_ERR(pcie->pex_clk);
- tegra_pcie.pex_clk = clk_get(NULL, "pex");
- if (IS_ERR(tegra_pcie.pex_clk))
- return PTR_ERR(tegra_pcie.pex_clk);
+ pcie->afi_clk = devm_clk_get(pcie->dev, "afi");
+ if (IS_ERR(pcie->afi_clk))
+ return PTR_ERR(pcie->afi_clk);
- tegra_pcie.afi_clk = clk_get(NULL, "afi");
- if (IS_ERR(tegra_pcie.afi_clk)) {
- err = PTR_ERR(tegra_pcie.afi_clk);
- goto err_afi_clk;
- }
+ pcie->pcie_xclk = devm_clk_get(pcie->dev, "pcie_xclk");
+ if (IS_ERR(pcie->pcie_xclk))
+ return PTR_ERR(pcie->pcie_xclk);
- tegra_pcie.pcie_xclk = clk_get(NULL, "pcie_xclk");
- if (IS_ERR(tegra_pcie.pcie_xclk)) {
- err = PTR_ERR(tegra_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);
- goto err_pll_e;
- }
+ pcie->pll_e = devm_clk_get(pcie->dev, "pll_e");
+ if (IS_ERR(pcie->pll_e))
+ return PTR_ERR(pcie->pll_e);
return 0;
-
-err_pll_e:
- clk_put(tegra_pcie.pcie_xclk);
-err_pcie_xclk:
- clk_put(tegra_pcie.afi_clk);
-err_afi_clk:
- clk_put(tegra_pcie.pex_clk);
-
- return err;
-}
-
-static void tegra_pcie_clocks_put(void)
-{
- clk_put(tegra_pcie.pll_e);
- clk_put(tegra_pcie.pcie_xclk);
- clk_put(tegra_pcie.afi_clk);
- clk_put(tegra_pcie.pex_clk);
}
-static int __init tegra_pcie_get_resources(void)
+static int __devinit tegra_pcie_get_resources(struct tegra_pcie *pcie)
{
+ struct platform_device *pdev = to_platform_device(pcie->dev);
+ struct resource *pads, *afi;
int err;
- err = tegra_pcie_clocks_get();
+ 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);
- goto err_pwr_on;
+ dev_err(&pdev->dev, "failed to power up: %d\n", err);
+ return err;
}
- 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");
- err = -ENOMEM;
- goto err_map_reg;
+ /* request and remap controller registers */
+ pads = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!pads) {
+ err = -EADDRNOTAVAIL;
+ goto power_off;
}
- err = request_irq(INT_PCIE_INTR, tegra_pcie_isr,
- IRQF_SHARED, "PCIE", &tegra_pcie);
+ afi = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!afi) {
+ err = -EADDRNOTAVAIL;
+ goto power_off;
+ }
+
+ pcie->pads = devm_request_and_ioremap(&pdev->dev, pads);
+ if (!pcie->pads) {
+ err = -EADDRNOTAVAIL;
+ goto power_off;
+ }
+
+ pcie->afi = devm_request_and_ioremap(&pdev->dev, afi);
+ if (!pcie->afi) {
+ err = -EADDRNOTAVAIL;
+ goto power_off;
+ }
+
+ /* request and remap configuration space */
+ pcie->cfg = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!pcie->cfg) {
+ err = -EADDRNOTAVAIL;
+ goto power_off;
+ }
+
+ pcie->extcfg = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ if (!pcie->extcfg) {
+ err = -EADDRNOTAVAIL;
+ goto power_off;
+ }
+
+ pcie->cs = devm_request_and_ioremap(&pdev->dev, pcie->cfg);
+ if (!pcie->cs) {
+ err = -EADDRNOTAVAIL;
+ goto power_off;
+ }
+
+ pcie->extcs = devm_request_and_ioremap(&pdev->dev, pcie->extcfg);
+ if (!pcie->extcs) {
+ err = -EADDRNOTAVAIL;
+ goto power_off;
+ }
+
+ /* request interrupt */
+ err = platform_get_irq(pdev, 0);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ: %d\n", err);
+ goto power_off;
+ }
+
+ pcie->irq = err;
+
+ err = devm_request_irq(&pdev->dev, pcie->irq, tegra_pcie_isr,
+ IRQF_SHARED, "PCIE", pcie);
if (err) {
- pr_err("PCIE: Failed to register IRQ: %d\n", err);
- goto err_req_io;
+ dev_err(&pdev->dev, "failed to register IRQ: %d\n", err);
+ goto power_off;
}
- set_irq_flags(INT_PCIE_INTR, IRQF_VALID);
return 0;
-err_req_io:
- iounmap(tegra_pcie.regs);
-err_map_reg:
- tegra_pcie_power_off();
-err_pwr_on:
- tegra_pcie_clocks_put();
-
+power_off:
+ tegra_pcie_power_off(pcie);
return err;
}
+static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
+{
+ tegra_pcie_power_off(pcie);
+ return 0;
+}
+
+static inline void init_range(struct resource *range, unsigned long flags)
+{
+ range->start = ~0;
+ range->end = 0;
+ range->flags = flags;
+}
+
+static inline void merge_range(struct resource *range, struct resource *new)
+{
+ if (new->start < range->start)
+ range->start = new->start;
+
+ if (new->end > range->end)
+ range->end = new->end;
+}
+
+static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port)
+{
+ unsigned long ret = 0;
+
+ switch (port->index) {
+ case 0:
+ ret = AFI_PEX0_CTRL;
+ break;
+
+ case 1:
+ ret = AFI_PEX1_CTRL;
+ break;
+ }
+
+ return ret;
+}
+
/*
* 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,
- u32 reset_reg)
+static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
{
- u32 reg;
- int retries = 3;
- int timeout;
+ unsigned long value, ctrl = tegra_pcie_port_get_pex_ctrl(port);
+ unsigned int retries = 3;
+
+ /* enable reference clock */
+ value = afi_readl(port->pcie, ctrl);
+ value |= AFI_PEX_CTRL_REFCLK_EN;
+ afi_writel(port->pcie, value, ctrl);
do {
- timeout = TEGRA_PCIE_LINKUP_TIMEOUT;
- while (timeout) {
- reg = readl(pp->base + RP_VEND_XP);
+ unsigned int timeout = TEGRA_PCIE_LINKUP_TIMEOUT;
- if (reg & RP_VEND_XP_DL_UP)
+ do {
+ value = readl(port->base + RP_VEND_XP);
+
+ if (value & RP_VEND_XP_DL_UP)
break;
- mdelay(1);
- timeout--;
- }
+ usleep_range(1000, 1000);
+ } while (--timeout);
- if (!timeout) {
- pr_err("PCIE: port %d: link down, retrying\n", idx);
+ if (!timeout) {
+ dev_err(port->pcie->dev, "link %u down, retrying\n",
+ port->index);
goto retry;
}
timeout = TEGRA_PCIE_LINKUP_TIMEOUT;
- while (timeout) {
- reg = readl(pp->base + RP_LINK_CONTROL_STATUS);
- if (reg & 0x20000000)
+ do {
+ value = readl(port->base + RP_LINK_CONTROL_STATUS);
+
+ if (value & 0x20000000)
return true;
- mdelay(1);
- timeout--;
- }
+ usleep_range(1000, 1000);
+ } while (--timeout);
retry:
/* Pulse the PEX reset */
- reg = afi_readl(reset_reg) | AFI_PEX_CTRL_RST;
- afi_writel(reg, reset_reg);
- mdelay(1);
- reg = afi_readl(reset_reg) & ~AFI_PEX_CTRL_RST;
- afi_writel(reg, reset_reg);
+ value = afi_readl(port->pcie, ctrl);
+ value |= AFI_PEX_CTRL_RST;
+ afi_writel(port->pcie, value, ctrl);
- retries--;
- } while (retries);
+ usleep_range(1000, 1000);
+
+ value = afi_readl(port->pcie, ctrl);
+ value &= ~AFI_PEX_CTRL_RST;
+ afi_writel(port->pcie, value, ctrl);
+ } while (--retries);
return false;
}
-static void __init tegra_pcie_add_port(int index, u32 offset, u32 reset_reg)
+static int tegra_pcie_enable(struct tegra_pcie *pcie)
{
- struct tegra_pcie_port *pp;
+ struct tegra_pcie_port *port;
+ struct hw_pci hw;
- pp = tegra_pcie.port + tegra_pcie.num_ports;
+ list_for_each_entry(port, &pcie->ports, list) {
+ memset(&hw, 0, sizeof(hw));
- pp->index = -1;
- pp->base = tegra_pcie.regs + offset;
- pp->link_up = tegra_pcie_check_link(pp, index, reset_reg);
+ hw.nr_controllers = 1;
+ hw.private_data = (void **)&port;
+ hw.setup = tegra_pcie_setup;
+ hw.scan = tegra_pcie_scan_bus;
+ hw.map_irq = tegra_pcie_map_irq;
- if (!pp->link_up) {
- pp->base = NULL;
- printk(KERN_INFO "PCIE: port %d: link down, ignoring\n", index);
- return;
+ pci_common_init(&hw);
+ }
+
+ return 0;
+}
+
+static int tegra_pcie_add_port(struct tegra_pcie *pcie,
+ struct tegra_pcie_rp *rp)
+{
+ struct tegra_pcie_port *port;
+ unsigned int i;
+
+ if (!rp->num_resources)
+ return -ENODEV;
+
+ port = devm_kzalloc(pcie->dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&port->list);
+ port->index = rp->index;
+ port->pcie = pcie;
+
+ port->base = devm_request_and_ioremap(pcie->dev, &rp->resources[0]);
+ if (!port->base)
+ return -EADDRNOTAVAIL;
+
+ if (!tegra_pcie_port_check_link(port)) {
+ dev_info(pcie->dev, "link %u down, ignoring\n", port->index);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < rp->num_ranges; i++) {
+ struct resource *src = &rp->ranges[i], *dest;
+
+ switch (src->flags & IORESOURCE_TYPE_BITS) {
+ case IORESOURCE_IO:
+ if (resource_size(src) > SZ_64K) {
+ dev_warn(pcie->dev, "I/O region for port %u exceeds 64 KiB limit, truncating!\n",
+ port->index);
+ src->end = src->start + SZ_64K - 1;
+ }
+
+ merge_range(&pcie->io, src);
+ dest = &port->io;
+ break;
+
+ case IORESOURCE_MEM:
+ if (src->flags & IORESOURCE_PREFETCH) {
+ merge_range(&pcie->prefetch, src);
+ dest = &port->prefetch;
+ } else {
+ merge_range(&pcie->mem, src);
+ dest = &port->mem;
+ }
+ break;
+
+ default:
+ dev_dbg(pcie->dev, "unknown resource type: %#lx\n",
+ src->flags & IORESOURCE_TYPE_BITS);
+ continue;
+ }
+
+ memcpy(dest, src, sizeof(*src));
}
- tegra_pcie.num_ports++;
- pp->index = index;
- pp->root_bus_nr = -1;
- memset(pp->res, 0, sizeof(pp->res));
+ list_add_tail(&port->list, &pcie->ports);
+ pcie->num_ports++;
+
+ return 0;
}
-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 *pcie;
+ unsigned int i;
int err;
- if (!(init_port0 || init_port1))
+ pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ pcie->dev = &pdev->dev;
+
+ if (!pdata)
return -ENODEV;
pcibios_min_mem = 0;
- err = tegra_pcie_get_resources();
- if (err)
+ err = tegra_pcie_get_resources(pcie);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request resources: %d\n", err);
return err;
+ }
+
+ platform_set_drvdata(pdev, pcie);
+
+ if (pdata->init) {
+ err = pdata->init(pdev);
+ if (err < 0)
+ goto put_resources;
+ }
- err = tegra_pcie_enable_controller();
+ err = tegra_pcie_enable_controller(pcie);
if (err)
- return err;
+ goto put_resources;
+
+ /* probe root ports */
+ INIT_LIST_HEAD(&pcie->ports);
+ pcie->num_ports = 0;
+
+ for (i = 0; i < pdata->num_ports; i++) {
+ err = tegra_pcie_add_port(pcie, &pdata->ports[i]);
+ if (err < 0)
+ dev_dbg(&pdev->dev, "failed to add port %u: %d\n",
+ pdata->ports[i].index, err);
+ }
/* setup the AFI address translations */
- tegra_pcie_setup_translations();
+ tegra_pcie_setup_translations(pcie);
- if (init_port0)
- tegra_pcie_add_port(0, RP0_OFFSET, AFI_PEX0_CTRL);
+ err = tegra_pcie_enable(pcie);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to enable PCIe ports: %d\n", err);
+ goto put_resources;
+ }
- if (init_port1)
- tegra_pcie_add_port(1, RP1_OFFSET, AFI_PEX1_CTRL);
+ return 0;
- pci_common_init(&tegra_pcie_hw);
+put_resources:
+ tegra_pcie_put_resources(pcie);
+ return err;
+}
+
+static int __devexit tegra_pcie_remove(struct platform_device *pdev)
+{
+ struct tegra_pcie_pdata *pdata = pdev->dev.platform_data;
+ struct tegra_pcie *pcie = platform_get_drvdata(pdev);
+ int err;
+
+ err = tegra_pcie_put_resources(pcie);
+ 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.11.2
More information about the linux-arm-kernel
mailing list