mvebu: add preliminary support for PCI express
Signed-off-by: Florian Fainelli <florian@openwrt.org> SVN-Revision: 35211
This commit is contained in:
parent
29c21b973b
commit
7e1e7063c2
16 changed files with 1434 additions and 0 deletions
|
@ -172,6 +172,7 @@ CONFIG_MACH_ARMADA_XP=y
|
|||
CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_MDIO_BOARDINFO=y
|
||||
CONFIG_MIGHT_HAVE_CACHE_L2X0=y
|
||||
CONFIG_MIGHT_HAVE_PCI=y
|
||||
CONFIG_MODULES_USE_ELF_REL=y
|
||||
# CONFIG_MPCORE_WATCHDOG is not set
|
||||
CONFIG_MSDOS_FS=y
|
||||
|
@ -210,6 +211,7 @@ CONFIG_OUTER_CACHE=y
|
|||
CONFIG_OUTER_CACHE_SYNC=y
|
||||
CONFIG_PAGEFLAGS_EXTENDED=y
|
||||
CONFIG_PAGE_OFFSET=0xC0000000
|
||||
# CONFIG_PCI is not set
|
||||
# CONFIG_PCI_SYSCALL is not set
|
||||
CONFIG_PERCPU_RWSEM=y
|
||||
CONFIG_PERF_USE_VMALLOC=y
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
The pcim_*() functions are used by the libata-sff subsystem, and this
|
||||
subsystem is used for many SATA drivers on ARM platforms that do not
|
||||
necessarily have I/O ports.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
Cc: Paul Gortmaker <paul.gortmaker@windriver.com>
|
||||
Cc: Jesse Barnes <jbarnes@virtuousgeek.org>
|
||||
Cc: Yinghai Lu <yinghai@kernel.org>
|
||||
Cc: linux-kernel@vger.kernel.org
|
||||
---
|
||||
lib/devres.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/lib/devres.c
|
||||
+++ b/lib/devres.c
|
||||
@@ -195,6 +195,7 @@ void devm_ioport_unmap(struct device *de
|
||||
devm_ioport_map_match, (void *)addr));
|
||||
}
|
||||
EXPORT_SYMBOL(devm_ioport_unmap);
|
||||
+#endif /* CONFIG_HAS_IOPORT */
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
/*
|
||||
@@ -400,4 +401,3 @@ void pcim_iounmap_regions(struct pci_dev
|
||||
}
|
||||
EXPORT_SYMBOL(pcim_iounmap_regions);
|
||||
#endif /* CONFIG_PCI */
|
||||
-#endif /* CONFIG_HAS_IOPORT */
|
|
@ -0,0 +1,24 @@
|
|||
The Armada 370 has two gatable clocks for each PCIe interface, and we
|
||||
want both of them to be enabled. We therefore make one of the two
|
||||
clocks a child of the other, as we did for the sataX and sataXlnk
|
||||
clocks on Armada XP.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
Cc: Mike Turquette <mturquette@linaro.org>
|
||||
---
|
||||
drivers/clk/mvebu/clk-gating-ctrl.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/clk/mvebu/clk-gating-ctrl.c
|
||||
+++ b/drivers/clk/mvebu/clk-gating-ctrl.c
|
||||
@@ -119,8 +119,8 @@ static const struct mvebu_soc_descr __in
|
||||
{ "pex1_en", NULL, 2 },
|
||||
{ "ge1", NULL, 3 },
|
||||
{ "ge0", NULL, 4 },
|
||||
- { "pex0", NULL, 5 },
|
||||
- { "pex1", NULL, 9 },
|
||||
+ { "pex0", "pex0_en", 5 },
|
||||
+ { "pex1", "pex1_en", 9 },
|
||||
{ "sata0", NULL, 15 },
|
||||
{ "sdio", NULL, 17 },
|
||||
{ "tdm", NULL, 25 },
|
|
@ -0,0 +1,28 @@
|
|||
Instead of hardcoding "1" as being the bit value to enable an address
|
||||
decoding window, introduce and use a WIN_CTRL_ENABLE definition.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/plat-orion/addr-map.c | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/arm/plat-orion/addr-map.c
|
||||
+++ b/arch/arm/plat-orion/addr-map.c
|
||||
@@ -38,6 +38,7 @@ EXPORT_SYMBOL_GPL(mv_mbus_dram_info);
|
||||
* CPU Address Decode Windows registers
|
||||
*/
|
||||
#define WIN_CTRL_OFF 0x0000
|
||||
+#define WIN_CTRL_ENABLE BIT(0)
|
||||
#define WIN_BASE_OFF 0x0004
|
||||
#define WIN_REMAP_LO_OFF 0x0008
|
||||
#define WIN_REMAP_HI_OFF 0x000c
|
||||
@@ -79,7 +80,8 @@ void __init orion_setup_cpu_win(const st
|
||||
}
|
||||
|
||||
base_high = base & 0xffff0000;
|
||||
- ctrl = ((size - 1) & 0xffff0000) | (attr << 8) | (target << 4) | 1;
|
||||
+ ctrl = ((size - 1) & 0xffff0000) | (attr << 8) | (target << 4) |
|
||||
+ WIN_CTRL_ENABLE;
|
||||
|
||||
writel(base_high, addr + WIN_BASE_OFF);
|
||||
writel(ctrl, addr + WIN_CTRL_OFF);
|
|
@ -0,0 +1,80 @@
|
|||
In the address decoding code, the orion_disable_wins() function is
|
||||
used at boot time to disable all address decoding windows, before
|
||||
configuring only the ones that are needed. This allows to make sure
|
||||
that no configuration is left from the bootloader.
|
||||
|
||||
As a preparation for the introduction of address decoding window
|
||||
allocation/deallocation function, we refactor this function into an
|
||||
orion_disable_cpu_win() which disables a single window.
|
||||
|
||||
The orion_config_wins() function is changed to call
|
||||
orion_disable_cpu_win() in a loop, to preserve an identical behavior.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/plat-orion/addr-map.c | 35 +++++++++++++++++------------------
|
||||
1 file changed, 17 insertions(+), 18 deletions(-)
|
||||
|
||||
--- a/arch/arm/plat-orion/addr-map.c
|
||||
+++ b/arch/arm/plat-orion/addr-map.c
|
||||
@@ -95,6 +95,19 @@ void __init orion_setup_cpu_win(const st
|
||||
}
|
||||
}
|
||||
|
||||
+static void __init orion_disable_cpu_win(const struct orion_addr_map_cfg *cfg,
|
||||
+ const int win)
|
||||
+{
|
||||
+ void __iomem *addr = cfg->win_cfg_base(cfg, win);
|
||||
+
|
||||
+ writel(0, addr + WIN_BASE_OFF);
|
||||
+ writel(0, addr + WIN_CTRL_OFF);
|
||||
+ if (cfg->cpu_win_can_remap(cfg, win)) {
|
||||
+ writel(0, addr + WIN_REMAP_LO_OFF);
|
||||
+ writel(0, addr + WIN_REMAP_HI_OFF);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Configure a number of windows.
|
||||
*/
|
||||
@@ -108,36 +121,22 @@ static void __init orion_setup_cpu_wins(
|
||||
}
|
||||
}
|
||||
|
||||
-static void __init orion_disable_wins(const struct orion_addr_map_cfg * cfg)
|
||||
-{
|
||||
- void __iomem *addr;
|
||||
- int i;
|
||||
-
|
||||
- for (i = 0; i < cfg->num_wins; i++) {
|
||||
- addr = cfg->win_cfg_base(cfg, i);
|
||||
-
|
||||
- writel(0, addr + WIN_BASE_OFF);
|
||||
- writel(0, addr + WIN_CTRL_OFF);
|
||||
- if (cfg->cpu_win_can_remap(cfg, i)) {
|
||||
- writel(0, addr + WIN_REMAP_LO_OFF);
|
||||
- writel(0, addr + WIN_REMAP_HI_OFF);
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
/*
|
||||
* Disable, clear and configure windows.
|
||||
*/
|
||||
void __init orion_config_wins(struct orion_addr_map_cfg * cfg,
|
||||
const struct orion_addr_map_info *info)
|
||||
{
|
||||
+ int win;
|
||||
+
|
||||
if (!cfg->cpu_win_can_remap)
|
||||
cfg->cpu_win_can_remap = orion_cpu_win_can_remap;
|
||||
|
||||
if (!cfg->win_cfg_base)
|
||||
cfg->win_cfg_base = orion_win_cfg_base;
|
||||
|
||||
- orion_disable_wins(cfg);
|
||||
+ for (win = 0; win < cfg->num_wins; win++)
|
||||
+ orion_disable_cpu_win(cfg, win);
|
||||
|
||||
if (info)
|
||||
orion_setup_cpu_wins(cfg, info);
|
|
@ -0,0 +1,96 @@
|
|||
In the address decoding code, we implement two new functions:
|
||||
orion_alloc_cpu_win() and orion_free_cpu_win(). The first function
|
||||
finds an unused address decoding window, and configures it according
|
||||
to the given arguments (in terms of base address, size, target,
|
||||
attributes). The second function frees an address decoding window,
|
||||
given a physical base address.
|
||||
|
||||
Those two new functions will be used by the PCIe code, which needs to
|
||||
dynamically register address decoding windows depending on the PCIe
|
||||
devices that are detected.
|
||||
|
||||
The orion_free_cpu_win() function is only here to handle error cases
|
||||
in the PCIe devices initialization, in the normal case, address
|
||||
decoding windows are never freed.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/plat-orion/addr-map.c | 50 +++++++++++++++++++++++++++
|
||||
arch/arm/plat-orion/include/plat/addr-map.h | 7 ++++
|
||||
2 files changed, 57 insertions(+)
|
||||
|
||||
--- a/arch/arm/plat-orion/addr-map.c
|
||||
+++ b/arch/arm/plat-orion/addr-map.c
|
||||
@@ -109,6 +109,56 @@ static void __init orion_disable_cpu_win
|
||||
}
|
||||
|
||||
/*
|
||||
+ * Find an unused address decoding window, and enable it according to
|
||||
+ * the arguments passed (base, size, target, attributes, remap).
|
||||
+ */
|
||||
+int __init orion_alloc_cpu_win(const struct orion_addr_map_cfg *cfg,
|
||||
+ const u32 base, const u32 size,
|
||||
+ const u8 target, const u8 attr, const int remap)
|
||||
+{
|
||||
+ int win;
|
||||
+
|
||||
+ for (win = 0; win < cfg->num_wins; win++) {
|
||||
+ void __iomem *addr = cfg->win_cfg_base(cfg, win);
|
||||
+ u32 ctrl = readl(addr + WIN_CTRL_OFF);
|
||||
+ if (!(ctrl & WIN_CTRL_ENABLE))
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* No more windows available */
|
||||
+ if (win == cfg->num_wins)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ orion_setup_cpu_win(cfg, win, base, size, target, attr, remap);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Free an address decoding window, given its base address.
|
||||
+ */
|
||||
+int __init orion_free_cpu_win(const struct orion_addr_map_cfg *cfg,
|
||||
+ const u32 base)
|
||||
+{
|
||||
+ int win;
|
||||
+
|
||||
+ for (win = 0; win < cfg->num_wins; win++) {
|
||||
+ void __iomem *addr = cfg->win_cfg_base(cfg, win);
|
||||
+ u32 winbase = readl(addr + WIN_BASE_OFF);
|
||||
+ u32 ctrl = readl(addr + WIN_CTRL_OFF);
|
||||
+
|
||||
+ if (!(ctrl & WIN_CTRL_ENABLE))
|
||||
+ continue;
|
||||
+
|
||||
+ if (winbase == (base & 0xffff0000)) {
|
||||
+ orion_disable_cpu_win(cfg, win);
|
||||
+ return 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
* Configure a number of windows.
|
||||
*/
|
||||
static void __init orion_setup_cpu_wins(const struct orion_addr_map_cfg * cfg,
|
||||
--- a/arch/arm/plat-orion/include/plat/addr-map.h
|
||||
+++ b/arch/arm/plat-orion/include/plat/addr-map.h
|
||||
@@ -49,6 +49,13 @@ void __init orion_setup_cpu_win(const st
|
||||
const u32 size, const u8 target,
|
||||
const u8 attr, const int remap);
|
||||
|
||||
+int __init orion_alloc_cpu_win(const struct orion_addr_map_cfg *cfg,
|
||||
+ const u32 base, const u32 size,
|
||||
+ const u8 target, const u8 attr, const int remap);
|
||||
+
|
||||
+int __init orion_free_cpu_win(const struct orion_addr_map_cfg *cfg,
|
||||
+ const u32 base);
|
||||
+
|
||||
void __init orion_setup_cpu_mbus_target(const struct orion_addr_map_cfg *cfg,
|
||||
const void __iomem *ddr_window_cpu_base);
|
||||
#endif
|
|
@ -0,0 +1,201 @@
|
|||
This commit adds two functions armada_370_xp_alloc_pcie_window() and
|
||||
armada_370_xp_free_pcie_window() that respectively allocate and free
|
||||
an address decoding window pointing to either a memory or I/O region
|
||||
of a PCIe device.
|
||||
|
||||
Those functions will be used by the PCIe driver to create and remove
|
||||
those regions depending on the PCIe devices that are detected.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/mach-mvebu/addr-map.c | 156 ++++++++++++++++++++++++++++++++++++++--
|
||||
arch/arm/mach-mvebu/common.h | 4 ++
|
||||
2 files changed, 156 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/arch/arm/mach-mvebu/addr-map.c
|
||||
+++ b/arch/arm/mach-mvebu/addr-map.c
|
||||
@@ -24,14 +24,10 @@
|
||||
#define ARMADA_XP_TARGET_DEV_BUS 1
|
||||
#define ARMADA_XP_ATTR_DEV_BOOTROM 0x1D
|
||||
#define ARMADA_XP_TARGET_ETH1 3
|
||||
-#define ARMADA_XP_TARGET_PCIE_0_2 4
|
||||
#define ARMADA_XP_TARGET_ETH0 7
|
||||
-#define ARMADA_XP_TARGET_PCIE_1_3 8
|
||||
|
||||
#define ARMADA_370_TARGET_DEV_BUS 1
|
||||
#define ARMADA_370_ATTR_DEV_BOOTROM 0x1D
|
||||
-#define ARMADA_370_TARGET_PCIE_0 4
|
||||
-#define ARMADA_370_TARGET_PCIE_1 8
|
||||
|
||||
#define ARMADA_WINDOW_8_PLUS_OFFSET 0x90
|
||||
#define ARMADA_SDRAM_ADDR_DECODING_OFFSET 0x180
|
||||
@@ -89,6 +85,158 @@ static struct __initdata orion_addr_map_
|
||||
.win_cfg_base = armada_cfg_base,
|
||||
};
|
||||
|
||||
+#ifdef CONFIG_PCI
|
||||
+/*
|
||||
+ * PCIe windows allocation code.
|
||||
+ */
|
||||
+#define ARMADA_370_XP_PCIE_MEM_START 0xC0000000
|
||||
+#define ARMADA_370_XP_PCIE_MEM_END (ARMADA_370_XP_PCIE_MEM_START + SZ_256M)
|
||||
+#define ARMADA_370_XP_PCIE_IO_START 0xF2000000
|
||||
+#define ARMADA_370_XP_PCIE_IO_END (ARMADA_370_XP_PCIE_IO_START + SZ_1M)
|
||||
+
|
||||
+static unsigned long armada_370_xp_pcie_memaddr = ARMADA_370_XP_PCIE_MEM_START;
|
||||
+static unsigned long armada_370_xp_pcie_ioaddr = ARMADA_370_XP_PCIE_IO_START;
|
||||
+
|
||||
+/*
|
||||
+ * This structure and the following arrays allow to map a PCIe (port,
|
||||
+ * lane) tuple to the corresponding (target, attribute) tuple needed
|
||||
+ * to configure an address decoding window for the given PCIe (port,
|
||||
+ * lane).
|
||||
+ */
|
||||
+struct pcie_mapping {
|
||||
+ int port;
|
||||
+ int lane;
|
||||
+ u8 target;
|
||||
+ u8 attr;
|
||||
+};
|
||||
+
|
||||
+struct pcie_mapping armada_xp_pcie_mappings[] = {
|
||||
+ { .port = 0, .lane = 0, .target = 4, .attr = 0xE0 },
|
||||
+ { .port = 0, .lane = 1, .target = 4, .attr = 0xD0 },
|
||||
+ { .port = 0, .lane = 2, .target = 4, .attr = 0xB0 },
|
||||
+ { .port = 0, .lane = 3, .target = 4, .attr = 0x70 },
|
||||
+ { .port = 1, .lane = 0, .target = 8, .attr = 0xE0 },
|
||||
+ { .port = 1, .lane = 1, .target = 8, .attr = 0xD0 },
|
||||
+ { .port = 1, .lane = 2, .target = 8, .attr = 0xB0 },
|
||||
+ { .port = 1, .lane = 3, .target = 8, .attr = 0x70 },
|
||||
+ { .port = 2, .lane = 0, .target = 4, .attr = 0xF0 },
|
||||
+ { .port = 3, .lane = 0, .target = 8, .attr = 0xF0 },
|
||||
+ { .port = -1 },
|
||||
+};
|
||||
+
|
||||
+struct pcie_mapping armada_370_pcie_mappings[] = {
|
||||
+ { .port = 0, .lane = 0, .target = 4, .attr = 0xE0 },
|
||||
+ { .port = 1, .lane = 0, .target = 8, .attr = 0xE0 },
|
||||
+ { .port = -1 },
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * This function finds an available physical address range between
|
||||
+ * ARMADA_370_XP_PCIE_MEM_START and ARMADA_370_XP_PCIE_MEM_END (for
|
||||
+ * PCIe memory regions) or between ARMADA_370_XP_PCIE_IO_START and
|
||||
+ * ARMADA_370_XP_PCIE_IO_END (for PCIe I/O regions) and creates an
|
||||
+ * address decoding window from this allocated address pointing to the
|
||||
+ * right PCIe device.
|
||||
+ *
|
||||
+ * An error code is returned, the allocated base address is returned
|
||||
+ * through the 'outbase' argument.
|
||||
+ */
|
||||
+int __init armada_370_xp_alloc_pcie_window(int pcie_port, int pcie_lane,
|
||||
+ int type, u32 size,
|
||||
+ unsigned long *outbase)
|
||||
+{
|
||||
+ struct pcie_mapping *mapping, *mappings;
|
||||
+ u8 target, attr;
|
||||
+ u32 base;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (of_machine_is_compatible("marvell,armadaxp"))
|
||||
+ mappings = armada_xp_pcie_mappings;
|
||||
+ else if (of_machine_is_compatible("marvell,armada370"))
|
||||
+ mappings = armada_370_pcie_mappings;
|
||||
+ else
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ for (mapping = mappings; mapping->port != -1; mapping++)
|
||||
+ if (mapping->port == pcie_port && mapping->lane == pcie_lane)
|
||||
+ break;
|
||||
+
|
||||
+ if (mapping->port == -1)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ target = mapping->target;
|
||||
+ attr = mapping->attr;
|
||||
+
|
||||
+ if (type == IORESOURCE_MEM) {
|
||||
+ /*
|
||||
+ * Bit 3 of the attributes indicates that it is a
|
||||
+ * memory region, as opposed to an I/O region
|
||||
+ */
|
||||
+ attr |= (1 << 3);
|
||||
+
|
||||
+ if (armada_370_xp_pcie_memaddr + size >
|
||||
+ ARMADA_370_XP_PCIE_MEM_END)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ base = armada_370_xp_pcie_memaddr;
|
||||
+ armada_370_xp_pcie_memaddr += size;
|
||||
+
|
||||
+ ret = orion_alloc_cpu_win(&addr_map_cfg, base, size, target,
|
||||
+ attr, -1);
|
||||
+ if (ret) {
|
||||
+ armada_370_xp_pcie_memaddr -= size;
|
||||
+ return ret;
|
||||
+ }
|
||||
+ } else if (type == IORESOURCE_IO) {
|
||||
+ if (armada_370_xp_pcie_ioaddr + size >
|
||||
+ ARMADA_370_XP_PCIE_IO_END)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ base = armada_370_xp_pcie_ioaddr;
|
||||
+ armada_370_xp_pcie_ioaddr += size;
|
||||
+
|
||||
+ ret = orion_alloc_cpu_win(&addr_map_cfg, base, size, target,
|
||||
+ attr, -1);
|
||||
+ if (ret) {
|
||||
+ armada_370_xp_pcie_ioaddr -= size;
|
||||
+ return ret;
|
||||
+ }
|
||||
+ } else
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ *outbase = base;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Frees an address decoding window previously allocated by
|
||||
+ * armada_370_xp_alloc_pcie_window(). Note that only the last window
|
||||
+ * allocated for a given type (MEM or IO) can be freed, due to the
|
||||
+ * simplicity of the allocator. This is however sufficient to handle
|
||||
+ * the error cases when initializing one PCIe device.
|
||||
+ */
|
||||
+int __init armada_370_xp_free_pcie_window(int type, unsigned long base,
|
||||
+ u32 size)
|
||||
+{
|
||||
+ if (type == IORESOURCE_MEM) {
|
||||
+ /* We can only free the last allocated window */
|
||||
+ if (base + size != armada_370_xp_pcie_memaddr)
|
||||
+ return -EINVAL;
|
||||
+ orion_free_cpu_win(&addr_map_cfg, base);
|
||||
+ armada_370_xp_pcie_memaddr -= size;
|
||||
+ } else if (type == IORESOURCE_IO) {
|
||||
+ /* We can only free the last allocated window */
|
||||
+ if (base + size != armada_370_xp_pcie_ioaddr)
|
||||
+ return -EINVAL;
|
||||
+ orion_free_cpu_win(&addr_map_cfg, base);
|
||||
+ armada_370_xp_pcie_ioaddr -= size;
|
||||
+ } else
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
static int __init armada_setup_cpu_mbus(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
--- a/arch/arm/mach-mvebu/common.h
|
||||
+++ b/arch/arm/mach-mvebu/common.h
|
||||
@@ -25,4 +25,8 @@ int armada_370_xp_coherency_init(void);
|
||||
int armada_370_xp_pmsu_init(void);
|
||||
void armada_xp_secondary_startup(void);
|
||||
extern struct smp_operations armada_xp_smp_ops;
|
||||
+
|
||||
+int armada_370_xp_alloc_pcie_window(int pcie_port, int pcie_lane,
|
||||
+ int type, u32 size, unsigned long *outbase);
|
||||
+int armada_370_xp_free_pcie_window(int type, unsigned long base, u32 size);
|
||||
#endif
|
|
@ -0,0 +1,25 @@
|
|||
mvebu is a new-style Orion platform, so it only selects PLAT_ORION,
|
||||
but not PLAT_ORION_LEGACY. It will however need the common PCIe code
|
||||
from plat-orion, so make this code available for PLAT_ORION platforms
|
||||
as a whole, and not only PLAT_ORION_LEGACY platforms.
|
||||
|
||||
We also take this opportunity to build the PCIe code only when
|
||||
CONFIG_PCI is enabled.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/plat-orion/Makefile | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/arm/plat-orion/Makefile
|
||||
+++ b/arch/arm/plat-orion/Makefile
|
||||
@@ -4,7 +4,8 @@
|
||||
ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include
|
||||
|
||||
obj-y += addr-map.o
|
||||
+obj-$(CONFIG_PCI) += pcie.o
|
||||
|
||||
orion-gpio-$(CONFIG_GENERIC_GPIO) += gpio.o
|
||||
-obj-$(CONFIG_PLAT_ORION_LEGACY) += irq.o pcie.o time.o common.o mpp.o
|
||||
+obj-$(CONFIG_PLAT_ORION_LEGACY) += irq.o time.o common.o mpp.o
|
||||
obj-$(CONFIG_PLAT_ORION_LEGACY) += $(orion-gpio-y)
|
|
@ -0,0 +1,474 @@
|
|||
This driver implements the hw_pci operations needed by the core ARM
|
||||
PCI code to setup PCI devices and get their corresponding IRQs, and
|
||||
the pci_ops operations that are used by the PCI core to read/write the
|
||||
configuration space of PCI devices.
|
||||
|
||||
In addition, this driver enumerates the different PCIe slots, and for
|
||||
those having a device plugged in, it allocates the necessary address
|
||||
decoding windows, using the new armada_370_xp_alloc_pcie_window()
|
||||
function from mach-mvebu/addr-map.c.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
.../devicetree/bindings/pci/armada-370-xp-pcie.txt | 136 +++++++++
|
||||
arch/arm/mach-mvebu/Makefile | 1 +
|
||||
arch/arm/mach-mvebu/pcie.c | 306 ++++++++++++++++++++
|
||||
3 files changed, 443 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/pci/armada-370-xp-pcie.txt
|
||||
create mode 100644 arch/arm/mach-mvebu/pcie.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/pci/armada-370-xp-pcie.txt
|
||||
@@ -0,0 +1,136 @@
|
||||
+* Marvell Armada 370/XP PCIe interfaces
|
||||
+
|
||||
+Mandatory properties:
|
||||
+- compatible: must be "marvell,armada-370-xp-pcie"
|
||||
+- status: either "disabled" or "okay"
|
||||
+- #address-cells, set to <1>
|
||||
+- #size-cells, set to <1>
|
||||
+- ranges: describes the association between the physical addresses of
|
||||
+ the PCIe registers for each PCIe interface with "virtual" addresses
|
||||
+ as seen by the sub-nodes. One entry per PCIe interface. Each entry
|
||||
+ must have 3 values: the "virtual" address seen by the sub-nodes, the
|
||||
+ real physical address of the PCIe registers, and the size.
|
||||
+
|
||||
+In addition, the Device Tree node must have sub-nodes describing each
|
||||
+PCIe interface, having the following mandatory properties:
|
||||
+- reg: the address and size of the PCIe registers (translated
|
||||
+ addresses according to the ranges property of the parent)
|
||||
+- interrupts: the interrupt number of this PCIe interface
|
||||
+- clocks: the clock associated to this PCIe interface
|
||||
+- marvell,pcie-port: the physical PCIe port number
|
||||
+- status: either "disabled" or "okay"
|
||||
+
|
||||
+and the following optional properties:
|
||||
+- marvell,pcie-lane: the physical PCIe lane number, for ports having
|
||||
+ multiple lanes. If this property is not found, we assume that the
|
||||
+ value is 0.
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+pcie-controller {
|
||||
+ compatible = "marvell,armada-370-xp-pcie";
|
||||
+ status = "disabled";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+ ranges = <0 0xd0040000 0x2000 /* port0x1_port0 */
|
||||
+ 0x2000 0xd0042000 0x2000 /* port2x1_port0 */
|
||||
+ 0x4000 0xd0044000 0x2000 /* port0x1_port1 */
|
||||
+ 0x8000 0xd0048000 0x2000 /* port0x1_port2 */
|
||||
+ 0xC000 0xd004C000 0x2000 /* port0x1_port3 */
|
||||
+ 0x10000 0xd0080000 0x2000 /* port1x1_port0 */
|
||||
+ 0x12000 0xd0082000 0x2000 /* port3x1_port0 */
|
||||
+ 0x14000 0xd0084000 0x2000 /* port1x1_port1 */
|
||||
+ 0x18000 0xd0088000 0x2000 /* port1x1_port2 */
|
||||
+ 0x1C000 0xd008C000 0x2000 /* port1x1_port3 */>;
|
||||
+
|
||||
+ pcie0.0@0xd0040000 {
|
||||
+ reg = <0x0 0x2000>;
|
||||
+ interrupts = <58>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.1@0xd0044000 {
|
||||
+ reg = <0x4000 0x2000>;
|
||||
+ interrupts = <59>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <1>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.2@0xd0048000 {
|
||||
+ reg = <0x8000 0x2000>;
|
||||
+ interrupts = <60>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <2>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.3@0xd004C000 {
|
||||
+ reg = <0xC000 0x2000>;
|
||||
+ interrupts = <61>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie1.0@0xd0040000 {
|
||||
+ reg = <0x10000 0x2000>;
|
||||
+ interrupts = <62>;
|
||||
+ clocks = <&gateclk 6>;
|
||||
+ marvell,pcie-port = <1>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie1.1@0xd0044000 {
|
||||
+ reg = <0x14000 0x2000>;
|
||||
+ interrupts = <63>;
|
||||
+ clocks = <&gateclk 6>;
|
||||
+ marvell,pcie-port = <1>;
|
||||
+ marvell,pcie-lane = <1>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie1.2@0xd0048000 {
|
||||
+ reg = <0x18000 0x2000>;
|
||||
+ interrupts = <64>;
|
||||
+ clocks = <&gateclk 6>;
|
||||
+ marvell,pcie-port = <1>;
|
||||
+ marvell,pcie-lane = <2>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie1.3@0xd004C000 {
|
||||
+ reg = <0x1C000 0x2000>;
|
||||
+ interrupts = <65>;
|
||||
+ clocks = <&gateclk 6>;
|
||||
+ marvell,pcie-port = <1>;
|
||||
+ marvell,pcie-lane = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie2@0xd0042000 {
|
||||
+ reg = <0x2000 0x2000>;
|
||||
+ interrupts = <99>;
|
||||
+ clocks = <&gateclk 7>;
|
||||
+ marvell,pcie-port = <2>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie3@0xd0082000 {
|
||||
+ reg = <0x12000 0x2000>;
|
||||
+ interrupts = <103>;
|
||||
+ clocks = <&gateclk 8>;
|
||||
+ marvell,pcie-port = <3>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+};
|
||||
+
|
||||
--- a/arch/arm/mach-mvebu/Makefile
|
||||
+++ b/arch/arm/mach-mvebu/Makefile
|
||||
@@ -5,3 +5,4 @@ obj-y += system-controller.o
|
||||
obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o coherency.o coherency_ll.o pmsu.o
|
||||
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
|
||||
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
+obj-$(CONFIG_PCI) += pcie.o
|
||||
--- /dev/null
|
||||
+++ b/arch/arm/mach-mvebu/pcie.c
|
||||
@@ -0,0 +1,306 @@
|
||||
+/*
|
||||
+ * PCIe driver for Marvell Armada 370 and Armada XP SoCs
|
||||
+ *
|
||||
+ * This file is licensed under the terms of the GNU General Public
|
||||
+ * License version 2. This program is licensed "as is" without any
|
||||
+ * warranty of any kind, whether express or implied.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/pci.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/of_address.h>
|
||||
+#include <linux/of_pci.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+#include <linux/of_platform.h>
|
||||
+#include <plat/pcie.h>
|
||||
+#include "common.h"
|
||||
+
|
||||
+struct pcie_port {
|
||||
+ u8 root_bus_nr;
|
||||
+ void __iomem *base;
|
||||
+ spinlock_t conf_lock;
|
||||
+ int irq;
|
||||
+ struct resource res;
|
||||
+ int haslink;
|
||||
+ u32 port;
|
||||
+ u32 lane;
|
||||
+ struct clk *clk;
|
||||
+};
|
||||
+
|
||||
+static struct pcie_port *pcie_ports;
|
||||
+
|
||||
+static int pcie_valid_config(struct pcie_port *pp, int bus, int dev)
|
||||
+{
|
||||
+ /*
|
||||
+ * Don't go out when trying to access --
|
||||
+ * 1. nonexisting device on local bus
|
||||
+ * 2. where there's no device connected (no link)
|
||||
+ */
|
||||
+ if (bus == pp->root_bus_nr && dev == 0)
|
||||
+ return 1;
|
||||
+
|
||||
+ if (!orion_pcie_link_up(pp->base))
|
||||
+ return 0;
|
||||
+
|
||||
+ if (bus == pp->root_bus_nr && dev != 1)
|
||||
+ return 0;
|
||||
+
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * PCIe config cycles are done by programming the PCIE_CONF_ADDR register
|
||||
+ * and then reading the PCIE_CONF_DATA register. Need to make sure these
|
||||
+ * transactions are atomic.
|
||||
+ */
|
||||
+static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
|
||||
+ int size, u32 *val)
|
||||
+{
|
||||
+ struct pci_sys_data *sys = bus->sysdata;
|
||||
+ struct pcie_port *pp = sys->private_data;
|
||||
+ unsigned long flags;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) {
|
||||
+ *val = 0xffffffff;
|
||||
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
+ }
|
||||
+
|
||||
+ spin_lock_irqsave(&pp->conf_lock, flags);
|
||||
+ ret = orion_pcie_rd_conf(pp->base, bus, devfn, where, size, val);
|
||||
+ spin_unlock_irqrestore(&pp->conf_lock, flags);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int pcie_wr_conf(struct pci_bus *bus, u32 devfn,
|
||||
+ int where, int size, u32 val)
|
||||
+{
|
||||
+ struct pci_sys_data *sys = bus->sysdata;
|
||||
+ struct pcie_port *pp = sys->private_data;
|
||||
+ unsigned long flags;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0)
|
||||
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
+
|
||||
+ spin_lock_irqsave(&pp->conf_lock, flags);
|
||||
+ ret = orion_pcie_wr_conf(pp->base, bus, devfn, where, size, val);
|
||||
+ spin_unlock_irqrestore(&pp->conf_lock, flags);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static struct pci_ops pcie_ops = {
|
||||
+ .read = pcie_rd_conf,
|
||||
+ .write = pcie_wr_conf,
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Returns 0 when the device could not be initialized, 1 when
|
||||
+ * initialization is successful
|
||||
+ */
|
||||
+static int __init armada_370_xp_pcie_setup(int nr, struct pci_sys_data *sys)
|
||||
+{
|
||||
+ struct pcie_port *port = &pcie_ports[nr];
|
||||
+ unsigned long membase, iobase;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!port->haslink)
|
||||
+ return 0;
|
||||
+
|
||||
+ sys->private_data = port;
|
||||
+ port->root_bus_nr = sys->busnr;
|
||||
+ spin_lock_init(&port->conf_lock);
|
||||
+
|
||||
+ ret = armada_370_xp_alloc_pcie_window(port->port, port->lane,
|
||||
+ IORESOURCE_MEM, SZ_64M, &membase);
|
||||
+ if (ret) {
|
||||
+ pr_err("PCIe%d.%d: Cannot get memory window, device disabled\n",
|
||||
+ port->port, port->lane);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ ret = armada_370_xp_alloc_pcie_window(port->port, port->lane,
|
||||
+ IORESOURCE_IO, SZ_64K, &iobase);
|
||||
+ if (ret) {
|
||||
+ pr_err("PCIe%d.%d: Cannot get I/O window, device disabled\n",
|
||||
+ port->port, port->lane);
|
||||
+ armada_370_xp_free_pcie_window(IORESOURCE_MEM, membase, SZ_64M);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ port->res.name = kasprintf(GFP_KERNEL, "PCIe %d.%d MEM",
|
||||
+ port->port, port->lane);
|
||||
+ if (!port->res.name) {
|
||||
+ armada_370_xp_free_pcie_window(IORESOURCE_IO, iobase, SZ_64K);
|
||||
+ armada_370_xp_free_pcie_window(IORESOURCE_MEM, membase, SZ_64M);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ port->res.start = membase;
|
||||
+ port->res.end = membase + SZ_32M - 1;
|
||||
+ port->res.flags = IORESOURCE_MEM;
|
||||
+
|
||||
+ pci_ioremap_io(SZ_64K * sys->busnr, iobase);
|
||||
+
|
||||
+ if (request_resource(&iomem_resource, &port->res)) {
|
||||
+ pr_err("PCIe%d.%d: Cannot request memory resource\n",
|
||||
+ port->port, port->lane);
|
||||
+ kfree(port->res.name);
|
||||
+ armada_370_xp_free_pcie_window(IORESOURCE_IO, iobase, SZ_64K);
|
||||
+ armada_370_xp_free_pcie_window(IORESOURCE_MEM, membase, SZ_64M);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ pci_add_resource_offset(&sys->resources, &port->res, sys->mem_offset);
|
||||
+
|
||||
+ orion_pcie_set_local_bus_nr(port->base, sys->busnr);
|
||||
+ orion_pcie_setup(port->base);
|
||||
+
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+static void __devinit rc_pci_fixup(struct pci_dev *dev)
|
||||
+{
|
||||
+ /*
|
||||
+ * Prevent enumeration of root complex.
|
||||
+ */
|
||||
+ if (dev->bus->parent == NULL && dev->devfn == 0) {
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
|
||||
+ dev->resource[i].start = 0;
|
||||
+ dev->resource[i].end = 0;
|
||||
+ dev->resource[i].flags = 0;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup);
|
||||
+
|
||||
+static int __init armada_370_xp_pcie_map_irq(const struct pci_dev *dev, u8 slot,
|
||||
+ u8 pin)
|
||||
+{
|
||||
+ struct pci_sys_data *sys = dev->sysdata;
|
||||
+ struct pcie_port *port = sys->private_data;
|
||||
+
|
||||
+ return port->irq;
|
||||
+}
|
||||
+
|
||||
+static struct hw_pci armada_370_xp_pci __initdata = {
|
||||
+ .setup = armada_370_xp_pcie_setup,
|
||||
+ .map_irq = armada_370_xp_pcie_map_irq,
|
||||
+ .ops = &pcie_ops,
|
||||
+};
|
||||
+
|
||||
+static int __init armada_370_xp_pcie_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device_node *child;
|
||||
+ int nports, i;
|
||||
+
|
||||
+ nports = 0;
|
||||
+ for_each_child_of_node(pdev->dev.of_node, child) {
|
||||
+ if (!of_device_is_available(child))
|
||||
+ continue;
|
||||
+ nports++;
|
||||
+ }
|
||||
+
|
||||
+ pcie_ports = devm_kzalloc(&pdev->dev, nports * sizeof(*pcie_ports),
|
||||
+ GFP_KERNEL);
|
||||
+ if (!pcie_ports)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ i = 0;
|
||||
+ for_each_child_of_node(pdev->dev.of_node, child) {
|
||||
+ struct pcie_port *port = &pcie_ports[i];
|
||||
+
|
||||
+ if (!of_device_is_available(child))
|
||||
+ continue;
|
||||
+
|
||||
+ if (of_property_read_u32(child, "marvell,pcie-port",
|
||||
+ &port->port))
|
||||
+ continue;
|
||||
+
|
||||
+ if (of_property_read_u32(child, "marvell,pcie-lane",
|
||||
+ &port->lane))
|
||||
+ port->lane = 0;
|
||||
+
|
||||
+ port->base = of_iomap(child, 0);
|
||||
+ if (!port->base) {
|
||||
+ dev_err(&pdev->dev, "PCIe%d.%d: cannot map registers\n",
|
||||
+ port->port, port->lane);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (orion_pcie_link_up(port->base)) {
|
||||
+ port->haslink = 1;
|
||||
+ dev_info(&pdev->dev, "PCIe%d.%d: link up\n",
|
||||
+ port->port, port->lane);
|
||||
+ } else {
|
||||
+ port->haslink = 0;
|
||||
+ dev_info(&pdev->dev, "PCIe%d.%d: link down\n",
|
||||
+ port->port, port->lane);
|
||||
+ iounmap(port->base);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ port->irq = irq_of_parse_and_map(child, 0);
|
||||
+ if (port->irq == 0) {
|
||||
+ dev_err(&pdev->dev, "PCIe%d.%d: cannot parse and map IRQ\n",
|
||||
+ port->port, port->lane);
|
||||
+ iounmap(port->base);
|
||||
+ port->haslink = 0;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ port->clk = of_clk_get_by_name(child, NULL);
|
||||
+ if (!port->clk) {
|
||||
+ dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n",
|
||||
+ port->port, port->lane);
|
||||
+ irq_dispose_mapping(port->irq);
|
||||
+ iounmap(port->base);
|
||||
+ port->haslink = 0;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ clk_prepare_enable(port->clk);
|
||||
+
|
||||
+ i++;
|
||||
+ }
|
||||
+
|
||||
+ armada_370_xp_pci.nr_controllers = nports;
|
||||
+ pci_common_init(&armada_370_xp_pci);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id armada_370_xp_pcie_of_match_table[] = {
|
||||
+ { .compatible = "marvell,armada-370-xp-pcie", },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, armada_370_xp_pcie_of_match_table);
|
||||
+
|
||||
+static struct platform_driver armada_370_xp_pcie_driver = {
|
||||
+ .driver = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .name = "armada-370-xp-pcie",
|
||||
+ .of_match_table =
|
||||
+ of_match_ptr(armada_370_xp_pcie_of_match_table),
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int armada_370_xp_pcie_init(void)
|
||||
+{
|
||||
+ return platform_driver_probe(&armada_370_xp_pcie_driver,
|
||||
+ armada_370_xp_pcie_probe);
|
||||
+}
|
||||
+
|
||||
+subsys_initcall(armada_370_xp_pcie_init);
|
||||
+
|
||||
+MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
|
||||
+MODULE_DESCRIPTION("Marvell Armada 370/XP PCIe driver");
|
||||
+MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,20 @@
|
|||
Now that the PCIe driver for mvebu has been integrated and all its
|
||||
relevant dependencies, we can mark the ARCH_MVEBU platform has
|
||||
MIGHT_HAVE_PCI, which allows to select the PCI bus support if needed.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/mach-mvebu/Kconfig | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/arch/arm/mach-mvebu/Kconfig
|
||||
+++ b/arch/arm/mach-mvebu/Kconfig
|
||||
@@ -13,6 +13,8 @@ config ARCH_MVEBU
|
||||
select MVEBU_CLK_CORE
|
||||
select MVEBU_CLK_CPU
|
||||
select MVEBU_CLK_GATING
|
||||
+ select MIGHT_HAVE_PCI
|
||||
+ select PCI_QUIRKS if PCI
|
||||
|
||||
if ARCH_MVEBU
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
The Armada 370 SoC has two 1x PCIe 2.0 interfaces, so we add the
|
||||
necessary Device Tree informations to make these interfaces availabel.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/boot/dts/armada-370.dtsi | 25 +++++++++++++++++++++++++
|
||||
1 file changed, 25 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/armada-370.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-370.dtsi
|
||||
@@ -153,5 +153,29 @@
|
||||
clocks = <&coreclk 0>;
|
||||
};
|
||||
|
||||
+ pcie-controller {
|
||||
+ compatible = "marvell,armada-370-xp-pcie";
|
||||
+ status = "disabled";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+ ranges = <0 0xd0040000 0x2000
|
||||
+ 0x2000 0xd0080000 0x2000>;
|
||||
+
|
||||
+ pcie0@0xd0040000 {
|
||||
+ reg = <0x0 0x2000>;
|
||||
+ interrupts = <58>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ status = "disabled";
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ };
|
||||
+
|
||||
+ pcie1@0xd0080000 {
|
||||
+ reg = <0x2000 0x2000>;
|
||||
+ interrupts = <62>;
|
||||
+ clocks = <&gateclk 9>;
|
||||
+ status = "disabled";
|
||||
+ marvell,pcie-port = <1>;
|
||||
+ };
|
||||
+ };
|
||||
};
|
||||
};
|
|
@ -0,0 +1,284 @@
|
|||
The Armada XP SoCs have multiple PCIe interfaces. The MV78230 has 2
|
||||
PCIe units (one 4x or quad 1x, the other 1x only), the MV78260 has 3
|
||||
PCIe units (two 4x or quad 1x and one 4x/1x), the MV78460 has 4 PCIe
|
||||
units (two 4x or quad 1x and two 4x/1x). We therefore add the
|
||||
necessary Device Tree informations to make those PCIe interfaces
|
||||
usable.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/boot/dts/armada-xp-mv78230.dtsi | 62 +++++++++++++++++
|
||||
arch/arm/boot/dts/armada-xp-mv78260.dtsi | 72 +++++++++++++++++++
|
||||
arch/arm/boot/dts/armada-xp-mv78460.dtsi | 112 ++++++++++++++++++++++++++++++
|
||||
3 files changed, 246 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
|
||||
@@ -76,5 +76,67 @@
|
||||
#interrupts-cells = <2>;
|
||||
interrupts = <87>, <88>, <89>;
|
||||
};
|
||||
+
|
||||
+ /*
|
||||
+ * MV78230 has 2 PCIe units Gen2.0: One unit can be
|
||||
+ * configured as x4 or quad x1 lanes. One unit is
|
||||
+ * x4/x1.
|
||||
+ */
|
||||
+ pcie-controller {
|
||||
+ compatible = "marvell,armada-370-xp-pcie";
|
||||
+ status = "disabled";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+ ranges = <0 0xd0040000 0x2000 /* port0x1_port0 */
|
||||
+ 0x2000 0xd0042000 0x2000 /* port2x1_port0 */
|
||||
+ 0x4000 0xd0044000 0x2000 /* port0x1_port1 */
|
||||
+ 0x8000 0xd0048000 0x2000 /* port0x1_port2 */
|
||||
+ 0xC000 0xd004C000 0x2000 /* port0x1_port3 */>;
|
||||
+
|
||||
+ pcie0.0@0xd0040000 {
|
||||
+ reg = <0x0 0x2000>;
|
||||
+ interrupts = <58>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.1@0xd0044000 {
|
||||
+ reg = <0x4000 0x2000>;
|
||||
+ interrupts = <59>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <1>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.2@0xd0048000 {
|
||||
+ reg = <0x8000 0x2000>;
|
||||
+ interrupts = <60>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <2>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.3@0xd004C000 {
|
||||
+ reg = <0xC000 0x2000>;
|
||||
+ interrupts = <61>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie2@0xd0042000 {
|
||||
+ reg = <0x2000 0x2000>;
|
||||
+ interrupts = <99>;
|
||||
+ clocks = <&gateclk 7>;
|
||||
+ marvell,pcie-port = <2>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+ };
|
||||
};
|
||||
};
|
||||
--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
|
||||
@@ -96,5 +96,77 @@
|
||||
clocks = <&gateclk 1>;
|
||||
status = "disabled";
|
||||
};
|
||||
+
|
||||
+ /*
|
||||
+ * MV78260 has 3 PCIe units Gen2.0: Two units can be
|
||||
+ * configured as x4 or quad x1 lanes. One unit is
|
||||
+ * x4/x1.
|
||||
+ */
|
||||
+ pcie-controller {
|
||||
+ compatible = "marvell,armada-370-xp-pcie";
|
||||
+ status = "okay";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+ ranges = <0 0xd0040000 0x2000 /* port0x1_port0 */
|
||||
+ 0x2000 0xd0042000 0x2000 /* port2x1_port0 */
|
||||
+ 0x4000 0xd0044000 0x2000 /* port0x1_port1 */
|
||||
+ 0x8000 0xd0048000 0x2000 /* port0x1_port2 */
|
||||
+ 0xC000 0xd004C000 0x2000 /* port0x1_port3 */
|
||||
+ 0x12000 0xd0082000 0x2000 /* port3x1_port0 */>;
|
||||
+
|
||||
+ pcie0.0@0xd0040000 {
|
||||
+ reg = <0x0 0x2000>;
|
||||
+ interrupts = <58>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.1@0xd0044000 {
|
||||
+ reg = <0x4000 0x2000>;
|
||||
+ interrupts = <59>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <1>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.2@0xd0048000 {
|
||||
+ reg = <0x8000 0x2000>;
|
||||
+ interrupts = <60>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <2>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.3@0xd004C000 {
|
||||
+ reg = <0xC000 0x2000>;
|
||||
+ interrupts = <61>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie2@0xd0042000 {
|
||||
+ reg = <0x2000 0x2000>;
|
||||
+ interrupts = <99>;
|
||||
+ clocks = <&gateclk 7>;
|
||||
+ marvell,pcie-port = <2>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie3@0xd0082000 {
|
||||
+ reg = <0x12000 0x2000>;
|
||||
+ interrupts = <103>;
|
||||
+ clocks = <&gateclk 8>;
|
||||
+ marvell,pcie-port = <3>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+ };
|
||||
};
|
||||
};
|
||||
--- a/arch/arm/boot/dts/armada-xp-mv78460.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-xp-mv78460.dtsi
|
||||
@@ -111,5 +111,117 @@
|
||||
clocks = <&gateclk 1>;
|
||||
status = "disabled";
|
||||
};
|
||||
+
|
||||
+ /*
|
||||
+ * MV78460 has 4 PCIe units Gen2.0: Two units can be
|
||||
+ * configured as x4 or quad x1 lanes. Two units are
|
||||
+ * x4/x1.
|
||||
+ */
|
||||
+ pcie-controller {
|
||||
+ compatible = "marvell,armada-370-xp-pcie";
|
||||
+ status = "disabled";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+ ranges = <0 0xd0040000 0x2000 /* port0x1_port0 */
|
||||
+ 0x2000 0xd0042000 0x2000 /* port2x1_port0 */
|
||||
+ 0x4000 0xd0044000 0x2000 /* port0x1_port1 */
|
||||
+ 0x8000 0xd0048000 0x2000 /* port0x1_port2 */
|
||||
+ 0xC000 0xd004C000 0x2000 /* port0x1_port3 */
|
||||
+ 0x10000 0xd0080000 0x2000 /* port1x1_port0 */
|
||||
+ 0x12000 0xd0082000 0x2000 /* port3x1_port0 */
|
||||
+ 0x14000 0xd0084000 0x2000 /* port1x1_port1 */
|
||||
+ 0x18000 0xd0088000 0x2000 /* port1x1_port2 */
|
||||
+ 0x1C000 0xd008C000 0x2000 /* port1x1_port3 */>;
|
||||
+
|
||||
+ pcie0.0@0xd0040000 {
|
||||
+ reg = <0x0 0x2000>;
|
||||
+ interrupts = <58>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.1@0xd0044000 {
|
||||
+ reg = <0x4000 0x2000>;
|
||||
+ interrupts = <59>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <1>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.2@0xd0048000 {
|
||||
+ reg = <0x8000 0x2000>;
|
||||
+ interrupts = <60>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <2>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie0.3@0xd004C000 {
|
||||
+ reg = <0xC000 0x2000>;
|
||||
+ interrupts = <61>;
|
||||
+ clocks = <&gateclk 5>;
|
||||
+ marvell,pcie-port = <0>;
|
||||
+ marvell,pcie-lane = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie1.0@0xd0040000 {
|
||||
+ reg = <0x10000 0x2000>;
|
||||
+ interrupts = <62>;
|
||||
+ clocks = <&gateclk 6>;
|
||||
+ marvell,pcie-port = <1>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie1.1@0xd0044000 {
|
||||
+ reg = <0x14000 0x2000>;
|
||||
+ interrupts = <63>;
|
||||
+ clocks = <&gateclk 6>;
|
||||
+ marvell,pcie-port = <1>;
|
||||
+ marvell,pcie-lane = <1>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie1.2@0xd0048000 {
|
||||
+ reg = <0x18000 0x2000>;
|
||||
+ interrupts = <64>;
|
||||
+ clocks = <&gateclk 6>;
|
||||
+ marvell,pcie-port = <1>;
|
||||
+ marvell,pcie-lane = <2>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie1.3@0xd004C000 {
|
||||
+ reg = <0x1C000 0x2000>;
|
||||
+ interrupts = <65>;
|
||||
+ clocks = <&gateclk 6>;
|
||||
+ marvell,pcie-port = <1>;
|
||||
+ marvell,pcie-lane = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie2@0xd0042000 {
|
||||
+ reg = <0x2000 0x2000>;
|
||||
+ interrupts = <99>;
|
||||
+ clocks = <&gateclk 7>;
|
||||
+ marvell,pcie-port = <2>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pcie3@0xd0082000 {
|
||||
+ reg = <0x12000 0x2000>;
|
||||
+ interrupts = <103>;
|
||||
+ clocks = <&gateclk 8>;
|
||||
+ marvell,pcie-port = <3>;
|
||||
+ marvell,pcie-lane = <0>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+ };
|
||||
};
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
The PlatHome OpenBlocks AX3-4 has an internal mini-PCIe slot that can
|
||||
be used to plug mini-PCIe devices. We therefore enable the PCIe
|
||||
interface that corresponds to this slot.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts | 7 +++++++
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
|
||||
+++ b/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
|
||||
@@ -130,5 +130,12 @@
|
||||
usb@d0052000 {
|
||||
status = "okay";
|
||||
};
|
||||
+ pcie-controller {
|
||||
+ status = "okay";
|
||||
+ /* Internal mini-PCIe connector */
|
||||
+ pcie0.0@0xd0040000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+ };
|
||||
};
|
||||
};
|
|
@ -0,0 +1,44 @@
|
|||
The Marvell evaluation board (DB) for the Armada XP SoC has 6
|
||||
physicals full-size PCIe slots, so we enable the corresponding PCIe
|
||||
interfaces in the Device Tree.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/boot/dts/armada-xp-db.dts | 27 +++++++++++++++++++++++++++
|
||||
1 file changed, 27 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/armada-xp-db.dts
|
||||
+++ b/arch/arm/boot/dts/armada-xp-db.dts
|
||||
@@ -109,5 +109,32 @@
|
||||
usb@d0052000 {
|
||||
status = "okay";
|
||||
};
|
||||
+
|
||||
+ pcie-controller {
|
||||
+ status = "okay";
|
||||
+
|
||||
+ /*
|
||||
+ * All 6 slots are physically present as
|
||||
+ * standard PCIe slots on the board.
|
||||
+ */
|
||||
+ pcie0.0@0xd0040000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+ pcie0.1@0xd0044000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+ pcie0.2@0xd0048000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+ pcie0.3@0xd004C000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+ pcie2@0xd0042000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+ pcie3@0xd0082000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+ };
|
||||
};
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
The Globalscale Mirabox platform uses one PCIe interface for an
|
||||
available mini-PCIe slot, and the other PCIe interface for an internal
|
||||
USB 3.0 controller. We add the necessary Device Tree informations to
|
||||
enable those two interfaces.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/boot/dts/armada-370-mirabox.dts | 14 ++++++++++++++
|
||||
1 file changed, 14 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/armada-370-mirabox.dts
|
||||
+++ b/arch/arm/boot/dts/armada-370-mirabox.dts
|
||||
@@ -70,5 +70,19 @@
|
||||
usb@d0051000 {
|
||||
status = "okay";
|
||||
};
|
||||
+
|
||||
+ pcie-controller {
|
||||
+ status = "okay";
|
||||
+
|
||||
+ /* Internal mini-PCIe connector */
|
||||
+ pcie0@0xd0040000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+
|
||||
+ /* Connected on the PCB to a USB 3.0 XHCI controller */
|
||||
+ pcie1@0xd0080000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+ };
|
||||
};
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
The Marvell evaluation board (DB) for the Armada 370 SoC has 2
|
||||
physical full-size PCIe slots, so we enable the corresponding PCIe
|
||||
interfaces in the Device Tree.
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
---
|
||||
arch/arm/boot/dts/armada-370-db.dts | 15 +++++++++++++++
|
||||
1 file changed, 15 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/armada-370-db.dts
|
||||
+++ b/arch/arm/boot/dts/armada-370-db.dts
|
||||
@@ -82,5 +82,20 @@
|
||||
usb@d0051000 {
|
||||
status = "okay";
|
||||
};
|
||||
+
|
||||
+ pcie-controller {
|
||||
+ status = "okay";
|
||||
+ /*
|
||||
+ * The two PCIe units are accessible through
|
||||
+ * both standard PCIe slots and mini-PCIe
|
||||
+ * slots on the board.
|
||||
+ */
|
||||
+ pcie0@0xd0040000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+ pcie1@0xd0080000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+ };
|
||||
};
|
||||
};
|
Loading…
Reference in a new issue