]>
Commit | Line | Data |
---|---|---|
e81a8c7d PB |
1 | /* |
2 | * Copyright (C) 2015 Imagination Technologies | |
48c834be | 3 | * Author: Paul Burton <paul.burton@mips.com> |
e81a8c7d PB |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the | |
7 | * Free Software Foundation; either version 2 of the License, or (at your | |
8 | * option) any later version. | |
9 | */ | |
10 | ||
11 | #include <linux/bug.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/libfdt.h> | |
14 | #include <linux/of_fdt.h> | |
15 | #include <linux/sizes.h> | |
0051fc2e | 16 | #include <asm/addrspace.h> |
e81a8c7d PB |
17 | #include <asm/bootinfo.h> |
18 | #include <asm/fw/fw.h> | |
0051fc2e | 19 | #include <asm/mips-boards/generic.h> |
38ec82fe | 20 | #include <asm/mips-boards/malta.h> |
e83f7e02 | 21 | #include <asm/mips-cps.h> |
e81a8c7d PB |
22 | #include <asm/page.h> |
23 | ||
0051fc2e PB |
24 | #define ROCIT_REG_BASE 0x1f403000 |
25 | #define ROCIT_CONFIG_GEN1 (ROCIT_REG_BASE + 0x04) | |
26 | #define ROCIT_CONFIG_GEN1_MEMMAP_SHIFT 8 | |
27 | #define ROCIT_CONFIG_GEN1_MEMMAP_MASK (0xf << 8) | |
28 | ||
e81a8c7d PB |
29 | static unsigned char fdt_buf[16 << 10] __initdata; |
30 | ||
31 | /* determined physical memory size, not overridden by command line args */ | |
32 | extern unsigned long physical_memsize; | |
33 | ||
0051fc2e PB |
34 | enum mem_map { |
35 | MEM_MAP_V1 = 0, | |
36 | MEM_MAP_V2, | |
37 | }; | |
38 | ||
39 | #define MAX_MEM_ARRAY_ENTRIES 2 | |
40 | ||
41 | static __init int malta_scon(void) | |
42 | { | |
43 | int scon = MIPS_REVISION_SCONID; | |
44 | ||
45 | if (scon != MIPS_REVISION_SCON_OTHER) | |
46 | return scon; | |
47 | ||
48 | switch (MIPS_REVISION_CORID) { | |
49 | case MIPS_REVISION_CORID_QED_RM5261: | |
50 | case MIPS_REVISION_CORID_CORE_LV: | |
51 | case MIPS_REVISION_CORID_CORE_FPGA: | |
52 | case MIPS_REVISION_CORID_CORE_FPGAR2: | |
53 | return MIPS_REVISION_SCON_GT64120; | |
54 | ||
55 | case MIPS_REVISION_CORID_CORE_EMUL_BON: | |
56 | case MIPS_REVISION_CORID_BONITO64: | |
57 | case MIPS_REVISION_CORID_CORE_20K: | |
58 | return MIPS_REVISION_SCON_BONITO; | |
59 | ||
60 | case MIPS_REVISION_CORID_CORE_MSC: | |
61 | case MIPS_REVISION_CORID_CORE_FPGA2: | |
62 | case MIPS_REVISION_CORID_CORE_24K: | |
63 | return MIPS_REVISION_SCON_SOCIT; | |
64 | ||
65 | case MIPS_REVISION_CORID_CORE_FPGA3: | |
66 | case MIPS_REVISION_CORID_CORE_FPGA4: | |
67 | case MIPS_REVISION_CORID_CORE_FPGA5: | |
68 | case MIPS_REVISION_CORID_CORE_EMUL_MSC: | |
69 | default: | |
70 | return MIPS_REVISION_SCON_ROCIT; | |
71 | } | |
72 | } | |
e81a8c7d | 73 | |
0051fc2e PB |
74 | static unsigned __init gen_fdt_mem_array(__be32 *mem_array, unsigned long size, |
75 | enum mem_map map) | |
e81a8c7d PB |
76 | { |
77 | unsigned long size_preio; | |
78 | unsigned entries; | |
79 | ||
80 | entries = 1; | |
81 | mem_array[0] = cpu_to_be32(PHYS_OFFSET); | |
97f2645f | 82 | if (IS_ENABLED(CONFIG_EVA)) { |
e81a8c7d PB |
83 | /* |
84 | * The current Malta EVA configuration is "special" in that it | |
85 | * always makes use of addresses in the upper half of the 32 bit | |
86 | * physical address map, which gives it a contiguous region of | |
87 | * DDR but limits it to 2GB. | |
88 | */ | |
89 | mem_array[1] = cpu_to_be32(size); | |
0051fc2e PB |
90 | goto done; |
91 | } | |
92 | ||
93 | size_preio = min_t(unsigned long, size, SZ_256M); | |
94 | mem_array[1] = cpu_to_be32(size_preio); | |
95 | size -= size_preio; | |
96 | if (!size) | |
97 | goto done; | |
98 | ||
99 | if (map == MEM_MAP_V2) { | |
100 | /* | |
101 | * We have a flat 32 bit physical memory map with DDR filling | |
102 | * all 4GB of the memory map, apart from the I/O region which | |
103 | * obscures 256MB from 0x10000000-0x1fffffff. | |
104 | * | |
105 | * Therefore we discard the 256MB behind the I/O region. | |
106 | */ | |
107 | if (size <= SZ_256M) | |
108 | goto done; | |
109 | size -= SZ_256M; | |
110 | ||
111 | /* Make use of the memory following the I/O region */ | |
112 | entries++; | |
113 | mem_array[2] = cpu_to_be32(PHYS_OFFSET + SZ_512M); | |
114 | mem_array[3] = cpu_to_be32(size); | |
e81a8c7d | 115 | } else { |
0051fc2e PB |
116 | /* |
117 | * We have a 32 bit physical memory map with a 2GB DDR region | |
118 | * aliased in the upper & lower halves of it. The I/O region | |
119 | * obscures 256MB from 0x10000000-0x1fffffff in the low alias | |
120 | * but the DDR it obscures is accessible via the high alias. | |
121 | * | |
122 | * Simply access everything beyond the lowest 256MB of DDR using | |
123 | * the high alias. | |
124 | */ | |
125 | entries++; | |
126 | mem_array[2] = cpu_to_be32(PHYS_OFFSET + SZ_2G + SZ_256M); | |
127 | mem_array[3] = cpu_to_be32(size); | |
e81a8c7d PB |
128 | } |
129 | ||
0051fc2e | 130 | done: |
e81a8c7d PB |
131 | BUG_ON(entries > MAX_MEM_ARRAY_ENTRIES); |
132 | return entries; | |
133 | } | |
134 | ||
135 | static void __init append_memory(void *fdt, int root_off) | |
136 | { | |
137 | __be32 mem_array[2 * MAX_MEM_ARRAY_ENTRIES]; | |
138 | unsigned long memsize; | |
139 | unsigned mem_entries; | |
140 | int i, err, mem_off; | |
0051fc2e PB |
141 | enum mem_map mem_map; |
142 | u32 config; | |
e81a8c7d PB |
143 | char *var, param_name[10], *var_names[] = { |
144 | "ememsize", "memsize", | |
145 | }; | |
146 | ||
147 | /* if a memory node already exists, leave it alone */ | |
148 | mem_off = fdt_path_offset(fdt, "/memory"); | |
149 | if (mem_off >= 0) | |
150 | return; | |
151 | ||
152 | /* find memory size from the bootloader environment */ | |
153 | for (i = 0; i < ARRAY_SIZE(var_names); i++) { | |
154 | var = fw_getenv(var_names[i]); | |
155 | if (!var) | |
156 | continue; | |
157 | ||
158 | err = kstrtoul(var, 0, &physical_memsize); | |
159 | if (!err) | |
160 | break; | |
161 | ||
162 | pr_warn("Failed to read the '%s' env variable '%s'\n", | |
163 | var_names[i], var); | |
164 | } | |
165 | ||
166 | if (!physical_memsize) { | |
167 | pr_warn("The bootloader didn't provide memsize: defaulting to 32MB\n"); | |
168 | physical_memsize = 32 << 20; | |
169 | } | |
170 | ||
97f2645f | 171 | if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { |
e81a8c7d PB |
172 | /* |
173 | * SOC-it swaps, or perhaps doesn't swap, when DMA'ing | |
174 | * the last word of physical memory. | |
175 | */ | |
176 | physical_memsize -= PAGE_SIZE; | |
177 | } | |
178 | ||
179 | /* default to using all available RAM */ | |
180 | memsize = physical_memsize; | |
181 | ||
182 | /* allow the user to override the usable memory */ | |
183 | for (i = 0; i < ARRAY_SIZE(var_names); i++) { | |
184 | snprintf(param_name, sizeof(param_name), "%s=", var_names[i]); | |
185 | var = strstr(arcs_cmdline, param_name); | |
186 | if (!var) | |
187 | continue; | |
188 | ||
189 | memsize = memparse(var + strlen(param_name), NULL); | |
190 | } | |
191 | ||
192 | /* if the user says there's more RAM than we thought, believe them */ | |
193 | physical_memsize = max_t(unsigned long, physical_memsize, memsize); | |
194 | ||
0051fc2e PB |
195 | /* detect the memory map in use */ |
196 | if (malta_scon() == MIPS_REVISION_SCON_ROCIT) { | |
197 | /* ROCit has a register indicating the memory map in use */ | |
198 | config = readl((void __iomem *)CKSEG1ADDR(ROCIT_CONFIG_GEN1)); | |
199 | mem_map = config & ROCIT_CONFIG_GEN1_MEMMAP_MASK; | |
200 | mem_map >>= ROCIT_CONFIG_GEN1_MEMMAP_SHIFT; | |
201 | } else { | |
202 | /* if not using ROCit, presume the v1 memory map */ | |
203 | mem_map = MEM_MAP_V1; | |
204 | } | |
205 | if (mem_map > MEM_MAP_V2) | |
206 | panic("Unsupported physical memory map v%u detected", | |
207 | (unsigned int)mem_map); | |
208 | ||
e81a8c7d PB |
209 | /* append memory to the DT */ |
210 | mem_off = fdt_add_subnode(fdt, root_off, "memory"); | |
211 | if (mem_off < 0) | |
212 | panic("Unable to add memory node to DT: %d", mem_off); | |
213 | ||
214 | err = fdt_setprop_string(fdt, mem_off, "device_type", "memory"); | |
215 | if (err) | |
216 | panic("Unable to set memory node device_type: %d", err); | |
217 | ||
0051fc2e | 218 | mem_entries = gen_fdt_mem_array(mem_array, physical_memsize, mem_map); |
e81a8c7d PB |
219 | err = fdt_setprop(fdt, mem_off, "reg", mem_array, |
220 | mem_entries * 2 * sizeof(mem_array[0])); | |
221 | if (err) | |
222 | panic("Unable to set memory regs property: %d", err); | |
223 | ||
0051fc2e | 224 | mem_entries = gen_fdt_mem_array(mem_array, memsize, mem_map); |
e81a8c7d PB |
225 | err = fdt_setprop(fdt, mem_off, "linux,usable-memory", mem_array, |
226 | mem_entries * 2 * sizeof(mem_array[0])); | |
227 | if (err) | |
228 | panic("Unable to set linux,usable-memory property: %d", err); | |
229 | } | |
230 | ||
38ec82fe PB |
231 | static void __init remove_gic(void *fdt) |
232 | { | |
233 | int err, gic_off, i8259_off, cpu_off; | |
234 | void __iomem *biu_base; | |
235 | uint32_t cpu_phandle, sc_cfg; | |
236 | ||
237 | /* if we have a CM which reports a GIC is present, leave the DT alone */ | |
238 | err = mips_cm_probe(); | |
93c5bba5 | 239 | if (!err && (read_gcr_gic_status() & CM_GCR_GIC_STATUS_EX)) |
38ec82fe PB |
240 | return; |
241 | ||
242 | if (malta_scon() == MIPS_REVISION_SCON_ROCIT) { | |
243 | /* | |
244 | * On systems using the RocIT system controller a GIC may be | |
245 | * present without a CM. Detect whether that is the case. | |
246 | */ | |
247 | biu_base = ioremap_nocache(MSC01_BIU_REG_BASE, | |
248 | MSC01_BIU_ADDRSPACE_SZ); | |
249 | sc_cfg = __raw_readl(biu_base + MSC01_SC_CFG_OFS); | |
250 | if (sc_cfg & MSC01_SC_CFG_GICPRES_MSK) { | |
251 | /* enable the GIC at the system controller level */ | |
252 | sc_cfg |= BIT(MSC01_SC_CFG_GICENA_SHF); | |
253 | __raw_writel(sc_cfg, biu_base + MSC01_SC_CFG_OFS); | |
254 | return; | |
255 | } | |
256 | } | |
257 | ||
258 | gic_off = fdt_node_offset_by_compatible(fdt, -1, "mti,gic"); | |
259 | if (gic_off < 0) { | |
260 | pr_warn("malta-dtshim: unable to find DT GIC node: %d\n", | |
261 | gic_off); | |
262 | return; | |
263 | } | |
264 | ||
265 | err = fdt_nop_node(fdt, gic_off); | |
266 | if (err) | |
267 | pr_warn("malta-dtshim: unable to nop GIC node\n"); | |
268 | ||
269 | i8259_off = fdt_node_offset_by_compatible(fdt, -1, "intel,i8259"); | |
270 | if (i8259_off < 0) { | |
271 | pr_warn("malta-dtshim: unable to find DT i8259 node: %d\n", | |
272 | i8259_off); | |
273 | return; | |
274 | } | |
275 | ||
276 | cpu_off = fdt_node_offset_by_compatible(fdt, -1, | |
277 | "mti,cpu-interrupt-controller"); | |
278 | if (cpu_off < 0) { | |
279 | pr_warn("malta-dtshim: unable to find CPU intc node: %d\n", | |
280 | cpu_off); | |
281 | return; | |
282 | } | |
283 | ||
284 | cpu_phandle = fdt_get_phandle(fdt, cpu_off); | |
285 | if (!cpu_phandle) { | |
286 | pr_warn("malta-dtshim: unable to get CPU intc phandle\n"); | |
287 | return; | |
288 | } | |
289 | ||
290 | err = fdt_setprop_u32(fdt, i8259_off, "interrupt-parent", cpu_phandle); | |
291 | if (err) { | |
292 | pr_warn("malta-dtshim: unable to set i8259 interrupt-parent: %d\n", | |
293 | err); | |
294 | return; | |
295 | } | |
296 | ||
297 | err = fdt_setprop_u32(fdt, i8259_off, "interrupts", 2); | |
298 | if (err) { | |
299 | pr_warn("malta-dtshim: unable to set i8259 interrupts: %d\n", | |
300 | err); | |
301 | return; | |
302 | } | |
303 | } | |
304 | ||
e81a8c7d PB |
305 | void __init *malta_dt_shim(void *fdt) |
306 | { | |
307 | int root_off, len, err; | |
308 | const char *compat; | |
309 | ||
310 | if (fdt_check_header(fdt)) | |
311 | panic("Corrupt DT"); | |
312 | ||
313 | err = fdt_open_into(fdt, fdt_buf, sizeof(fdt_buf)); | |
314 | if (err) | |
315 | panic("Unable to open FDT: %d", err); | |
316 | ||
317 | root_off = fdt_path_offset(fdt_buf, "/"); | |
318 | if (root_off < 0) | |
319 | panic("No / node in DT"); | |
320 | ||
321 | compat = fdt_getprop(fdt_buf, root_off, "compatible", &len); | |
322 | if (!compat) | |
323 | panic("No root compatible property in DT: %d", len); | |
324 | ||
325 | /* if this isn't Malta, leave the DT alone */ | |
326 | if (strncmp(compat, "mti,malta", len)) | |
327 | return fdt; | |
328 | ||
329 | append_memory(fdt_buf, root_off); | |
38ec82fe | 330 | remove_gic(fdt_buf); |
e81a8c7d PB |
331 | |
332 | err = fdt_pack(fdt_buf); | |
333 | if (err) | |
334 | panic("Unable to pack FDT: %d\n", err); | |
335 | ||
336 | return fdt_buf; | |
337 | } |