You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
201 lines
6.4 KiB
201 lines
6.4 KiB
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
|
|
|