mvebu: add preliminary support for PCI express
[openwrt/svn-archive/archive.git] / target / linux / mvebu / patches-3.8 / 033-arm_mvebu_add_functions_to_alloc_free_pcie.patch
1 This commit adds two functions armada_370_xp_alloc_pcie_window() and
2 armada_370_xp_free_pcie_window() that respectively allocate and free
3 an address decoding window pointing to either a memory or I/O region
4 of a PCIe device.
5
6 Those functions will be used by the PCIe driver to create and remove
7 those regions depending on the PCIe devices that are detected.
8
9 Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
10 ---
11 arch/arm/mach-mvebu/addr-map.c | 156 ++++++++++++++++++++++++++++++++++++++--
12 arch/arm/mach-mvebu/common.h | 4 ++
13 2 files changed, 156 insertions(+), 4 deletions(-)
14
15 --- a/arch/arm/mach-mvebu/addr-map.c
16 +++ b/arch/arm/mach-mvebu/addr-map.c
17 @@ -24,14 +24,10 @@
18 #define ARMADA_XP_TARGET_DEV_BUS 1
19 #define ARMADA_XP_ATTR_DEV_BOOTROM 0x1D
20 #define ARMADA_XP_TARGET_ETH1 3
21 -#define ARMADA_XP_TARGET_PCIE_0_2 4
22 #define ARMADA_XP_TARGET_ETH0 7
23 -#define ARMADA_XP_TARGET_PCIE_1_3 8
24
25 #define ARMADA_370_TARGET_DEV_BUS 1
26 #define ARMADA_370_ATTR_DEV_BOOTROM 0x1D
27 -#define ARMADA_370_TARGET_PCIE_0 4
28 -#define ARMADA_370_TARGET_PCIE_1 8
29
30 #define ARMADA_WINDOW_8_PLUS_OFFSET 0x90
31 #define ARMADA_SDRAM_ADDR_DECODING_OFFSET 0x180
32 @@ -89,6 +85,158 @@ static struct __initdata orion_addr_map_
33 .win_cfg_base = armada_cfg_base,
34 };
35
36 +#ifdef CONFIG_PCI
37 +/*
38 + * PCIe windows allocation code.
39 + */
40 +#define ARMADA_370_XP_PCIE_MEM_START 0xC0000000
41 +#define ARMADA_370_XP_PCIE_MEM_END (ARMADA_370_XP_PCIE_MEM_START + SZ_256M)
42 +#define ARMADA_370_XP_PCIE_IO_START 0xF2000000
43 +#define ARMADA_370_XP_PCIE_IO_END (ARMADA_370_XP_PCIE_IO_START + SZ_1M)
44 +
45 +static unsigned long armada_370_xp_pcie_memaddr = ARMADA_370_XP_PCIE_MEM_START;
46 +static unsigned long armada_370_xp_pcie_ioaddr = ARMADA_370_XP_PCIE_IO_START;
47 +
48 +/*
49 + * This structure and the following arrays allow to map a PCIe (port,
50 + * lane) tuple to the corresponding (target, attribute) tuple needed
51 + * to configure an address decoding window for the given PCIe (port,
52 + * lane).
53 + */
54 +struct pcie_mapping {
55 + int port;
56 + int lane;
57 + u8 target;
58 + u8 attr;
59 +};
60 +
61 +struct pcie_mapping armada_xp_pcie_mappings[] = {
62 + { .port = 0, .lane = 0, .target = 4, .attr = 0xE0 },
63 + { .port = 0, .lane = 1, .target = 4, .attr = 0xD0 },
64 + { .port = 0, .lane = 2, .target = 4, .attr = 0xB0 },
65 + { .port = 0, .lane = 3, .target = 4, .attr = 0x70 },
66 + { .port = 1, .lane = 0, .target = 8, .attr = 0xE0 },
67 + { .port = 1, .lane = 1, .target = 8, .attr = 0xD0 },
68 + { .port = 1, .lane = 2, .target = 8, .attr = 0xB0 },
69 + { .port = 1, .lane = 3, .target = 8, .attr = 0x70 },
70 + { .port = 2, .lane = 0, .target = 4, .attr = 0xF0 },
71 + { .port = 3, .lane = 0, .target = 8, .attr = 0xF0 },
72 + { .port = -1 },
73 +};
74 +
75 +struct pcie_mapping armada_370_pcie_mappings[] = {
76 + { .port = 0, .lane = 0, .target = 4, .attr = 0xE0 },
77 + { .port = 1, .lane = 0, .target = 8, .attr = 0xE0 },
78 + { .port = -1 },
79 +};
80 +
81 +/*
82 + * This function finds an available physical address range between
83 + * ARMADA_370_XP_PCIE_MEM_START and ARMADA_370_XP_PCIE_MEM_END (for
84 + * PCIe memory regions) or between ARMADA_370_XP_PCIE_IO_START and
85 + * ARMADA_370_XP_PCIE_IO_END (for PCIe I/O regions) and creates an
86 + * address decoding window from this allocated address pointing to the
87 + * right PCIe device.
88 + *
89 + * An error code is returned, the allocated base address is returned
90 + * through the 'outbase' argument.
91 + */
92 +int __init armada_370_xp_alloc_pcie_window(int pcie_port, int pcie_lane,
93 + int type, u32 size,
94 + unsigned long *outbase)
95 +{
96 + struct pcie_mapping *mapping, *mappings;
97 + u8 target, attr;
98 + u32 base;
99 + int ret;
100 +
101 + if (of_machine_is_compatible("marvell,armadaxp"))
102 + mappings = armada_xp_pcie_mappings;
103 + else if (of_machine_is_compatible("marvell,armada370"))
104 + mappings = armada_370_pcie_mappings;
105 + else
106 + return -ENODEV;
107 +
108 + for (mapping = mappings; mapping->port != -1; mapping++)
109 + if (mapping->port == pcie_port && mapping->lane == pcie_lane)
110 + break;
111 +
112 + if (mapping->port == -1)
113 + return -ENODEV;
114 +
115 + target = mapping->target;
116 + attr = mapping->attr;
117 +
118 + if (type == IORESOURCE_MEM) {
119 + /*
120 + * Bit 3 of the attributes indicates that it is a
121 + * memory region, as opposed to an I/O region
122 + */
123 + attr |= (1 << 3);
124 +
125 + if (armada_370_xp_pcie_memaddr + size >
126 + ARMADA_370_XP_PCIE_MEM_END)
127 + return -ENOMEM;
128 +
129 + base = armada_370_xp_pcie_memaddr;
130 + armada_370_xp_pcie_memaddr += size;
131 +
132 + ret = orion_alloc_cpu_win(&addr_map_cfg, base, size, target,
133 + attr, -1);
134 + if (ret) {
135 + armada_370_xp_pcie_memaddr -= size;
136 + return ret;
137 + }
138 + } else if (type == IORESOURCE_IO) {
139 + if (armada_370_xp_pcie_ioaddr + size >
140 + ARMADA_370_XP_PCIE_IO_END)
141 + return -ENOMEM;
142 +
143 + base = armada_370_xp_pcie_ioaddr;
144 + armada_370_xp_pcie_ioaddr += size;
145 +
146 + ret = orion_alloc_cpu_win(&addr_map_cfg, base, size, target,
147 + attr, -1);
148 + if (ret) {
149 + armada_370_xp_pcie_ioaddr -= size;
150 + return ret;
151 + }
152 + } else
153 + return -ENODEV;
154 +
155 + *outbase = base;
156 + return 0;
157 +}
158 +
159 +/*
160 + * Frees an address decoding window previously allocated by
161 + * armada_370_xp_alloc_pcie_window(). Note that only the last window
162 + * allocated for a given type (MEM or IO) can be freed, due to the
163 + * simplicity of the allocator. This is however sufficient to handle
164 + * the error cases when initializing one PCIe device.
165 + */
166 +int __init armada_370_xp_free_pcie_window(int type, unsigned long base,
167 + u32 size)
168 +{
169 + if (type == IORESOURCE_MEM) {
170 + /* We can only free the last allocated window */
171 + if (base + size != armada_370_xp_pcie_memaddr)
172 + return -EINVAL;
173 + orion_free_cpu_win(&addr_map_cfg, base);
174 + armada_370_xp_pcie_memaddr -= size;
175 + } else if (type == IORESOURCE_IO) {
176 + /* We can only free the last allocated window */
177 + if (base + size != armada_370_xp_pcie_ioaddr)
178 + return -EINVAL;
179 + orion_free_cpu_win(&addr_map_cfg, base);
180 + armada_370_xp_pcie_ioaddr -= size;
181 + } else
182 + return -EINVAL;
183 +
184 + return 0;
185 +}
186 +#endif
187 +
188 static int __init armada_setup_cpu_mbus(void)
189 {
190 struct device_node *np;
191 --- a/arch/arm/mach-mvebu/common.h
192 +++ b/arch/arm/mach-mvebu/common.h
193 @@ -25,4 +25,8 @@ int armada_370_xp_coherency_init(void);
194 int armada_370_xp_pmsu_init(void);
195 void armada_xp_secondary_startup(void);
196 extern struct smp_operations armada_xp_smp_ops;
197 +
198 +int armada_370_xp_alloc_pcie_window(int pcie_port, int pcie_lane,
199 + int type, u32 size, unsigned long *outbase);
200 +int armada_370_xp_free_pcie_window(int type, unsigned long base, u32 size);
201 #endif