[RFC v1 06/16] arm: mvebu: add functions to alloc/free PCIe decoding windows

Thomas Petazzoni thomas.petazzoni at free-electrons.com
Fri Dec 7 17:04:29 EST 2012


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 at 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(-)

diff --git a/arch/arm/mach-mvebu/addr-map.c b/arch/arm/mach-mvebu/addr-map.c
index ab9b3bd..c21aa7b 100644
--- 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_cfg addr_map_cfg = {
 	.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;
diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h
index aa27bc2..3cef81a 100644
--- 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
-- 
1.7.9.5




More information about the linux-arm-kernel mailing list