summaryrefslogtreecommitdiffstats
path: root/target/linux/mvebu/patches-3.8/033-arm_mvebu_add_functions_to_alloc_free_pcie.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/mvebu/patches-3.8/033-arm_mvebu_add_functions_to_alloc_free_pcie.patch')
-rw-r--r--target/linux/mvebu/patches-3.8/033-arm_mvebu_add_functions_to_alloc_free_pcie.patch201
1 files changed, 201 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-3.8/033-arm_mvebu_add_functions_to_alloc_free_pcie.patch b/target/linux/mvebu/patches-3.8/033-arm_mvebu_add_functions_to_alloc_free_pcie.patch
new file mode 100644
index 000000000..fd3c1af92
--- /dev/null
+++ b/target/linux/mvebu/patches-3.8/033-arm_mvebu_add_functions_to_alloc_free_pcie.patch
@@ -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