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