162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 Imagination Technologies 462306a36Sopenharmony_ci * Author: Paul Burton <paul.burton@mips.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/bug.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/libfdt.h> 1062306a36Sopenharmony_ci#include <linux/of_fdt.h> 1162306a36Sopenharmony_ci#include <linux/sizes.h> 1262306a36Sopenharmony_ci#include <asm/addrspace.h> 1362306a36Sopenharmony_ci#include <asm/bootinfo.h> 1462306a36Sopenharmony_ci#include <asm/fw/fw.h> 1562306a36Sopenharmony_ci#include <asm/mips-boards/generic.h> 1662306a36Sopenharmony_ci#include <asm/mips-boards/malta.h> 1762306a36Sopenharmony_ci#include <asm/mips-cps.h> 1862306a36Sopenharmony_ci#include <asm/page.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define ROCIT_REG_BASE 0x1f403000 2162306a36Sopenharmony_ci#define ROCIT_CONFIG_GEN1 (ROCIT_REG_BASE + 0x04) 2262306a36Sopenharmony_ci#define ROCIT_CONFIG_GEN1_MEMMAP_SHIFT 8 2362306a36Sopenharmony_ci#define ROCIT_CONFIG_GEN1_MEMMAP_MASK (0xf << 8) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic unsigned char fdt_buf[16 << 10] __initdata __aligned(8); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* determined physical memory size, not overridden by command line args */ 2862306a36Sopenharmony_ciextern unsigned long physical_memsize; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cienum mem_map { 3162306a36Sopenharmony_ci MEM_MAP_V1 = 0, 3262306a36Sopenharmony_ci MEM_MAP_V2, 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define MAX_MEM_ARRAY_ENTRIES 2 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic __init int malta_scon(void) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci int scon = MIPS_REVISION_SCONID; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (scon != MIPS_REVISION_SCON_OTHER) 4262306a36Sopenharmony_ci return scon; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci switch (MIPS_REVISION_CORID) { 4562306a36Sopenharmony_ci case MIPS_REVISION_CORID_QED_RM5261: 4662306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_LV: 4762306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_FPGA: 4862306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_FPGAR2: 4962306a36Sopenharmony_ci return MIPS_REVISION_SCON_GT64120; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_EMUL_BON: 5262306a36Sopenharmony_ci case MIPS_REVISION_CORID_BONITO64: 5362306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_20K: 5462306a36Sopenharmony_ci return MIPS_REVISION_SCON_BONITO; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_MSC: 5762306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_FPGA2: 5862306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_24K: 5962306a36Sopenharmony_ci return MIPS_REVISION_SCON_SOCIT; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_FPGA3: 6262306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_FPGA4: 6362306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_FPGA5: 6462306a36Sopenharmony_ci case MIPS_REVISION_CORID_CORE_EMUL_MSC: 6562306a36Sopenharmony_ci default: 6662306a36Sopenharmony_ci return MIPS_REVISION_SCON_ROCIT; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic unsigned __init gen_fdt_mem_array(__be32 *mem_array, unsigned long size, 7162306a36Sopenharmony_ci enum mem_map map) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci unsigned long size_preio; 7462306a36Sopenharmony_ci unsigned entries; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci entries = 1; 7762306a36Sopenharmony_ci mem_array[0] = cpu_to_be32(PHYS_OFFSET); 7862306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_EVA)) { 7962306a36Sopenharmony_ci /* 8062306a36Sopenharmony_ci * The current Malta EVA configuration is "special" in that it 8162306a36Sopenharmony_ci * always makes use of addresses in the upper half of the 32 bit 8262306a36Sopenharmony_ci * physical address map, which gives it a contiguous region of 8362306a36Sopenharmony_ci * DDR but limits it to 2GB. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci mem_array[1] = cpu_to_be32(size); 8662306a36Sopenharmony_ci goto done; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci size_preio = min_t(unsigned long, size, SZ_256M); 9062306a36Sopenharmony_ci mem_array[1] = cpu_to_be32(size_preio); 9162306a36Sopenharmony_ci size -= size_preio; 9262306a36Sopenharmony_ci if (!size) 9362306a36Sopenharmony_ci goto done; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (map == MEM_MAP_V2) { 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * We have a flat 32 bit physical memory map with DDR filling 9862306a36Sopenharmony_ci * all 4GB of the memory map, apart from the I/O region which 9962306a36Sopenharmony_ci * obscures 256MB from 0x10000000-0x1fffffff. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Therefore we discard the 256MB behind the I/O region. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci if (size <= SZ_256M) 10462306a36Sopenharmony_ci goto done; 10562306a36Sopenharmony_ci size -= SZ_256M; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Make use of the memory following the I/O region */ 10862306a36Sopenharmony_ci entries++; 10962306a36Sopenharmony_ci mem_array[2] = cpu_to_be32(PHYS_OFFSET + SZ_512M); 11062306a36Sopenharmony_ci mem_array[3] = cpu_to_be32(size); 11162306a36Sopenharmony_ci } else { 11262306a36Sopenharmony_ci /* 11362306a36Sopenharmony_ci * We have a 32 bit physical memory map with a 2GB DDR region 11462306a36Sopenharmony_ci * aliased in the upper & lower halves of it. The I/O region 11562306a36Sopenharmony_ci * obscures 256MB from 0x10000000-0x1fffffff in the low alias 11662306a36Sopenharmony_ci * but the DDR it obscures is accessible via the high alias. 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * Simply access everything beyond the lowest 256MB of DDR using 11962306a36Sopenharmony_ci * the high alias. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci entries++; 12262306a36Sopenharmony_ci mem_array[2] = cpu_to_be32(PHYS_OFFSET + SZ_2G + SZ_256M); 12362306a36Sopenharmony_ci mem_array[3] = cpu_to_be32(size); 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cidone: 12762306a36Sopenharmony_ci BUG_ON(entries > MAX_MEM_ARRAY_ENTRIES); 12862306a36Sopenharmony_ci return entries; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void __init append_memory(void *fdt, int root_off) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci __be32 mem_array[2 * MAX_MEM_ARRAY_ENTRIES]; 13462306a36Sopenharmony_ci unsigned long memsize; 13562306a36Sopenharmony_ci unsigned mem_entries; 13662306a36Sopenharmony_ci int i, err, mem_off; 13762306a36Sopenharmony_ci enum mem_map mem_map; 13862306a36Sopenharmony_ci u32 config; 13962306a36Sopenharmony_ci char *var, param_name[10], *var_names[] = { 14062306a36Sopenharmony_ci "ememsize", "memsize", 14162306a36Sopenharmony_ci }; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* if a memory node already exists, leave it alone */ 14462306a36Sopenharmony_ci mem_off = fdt_path_offset(fdt, "/memory"); 14562306a36Sopenharmony_ci if (mem_off >= 0) 14662306a36Sopenharmony_ci return; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* find memory size from the bootloader environment */ 14962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(var_names); i++) { 15062306a36Sopenharmony_ci var = fw_getenv(var_names[i]); 15162306a36Sopenharmony_ci if (!var) 15262306a36Sopenharmony_ci continue; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci err = kstrtoul(var, 0, &physical_memsize); 15562306a36Sopenharmony_ci if (!err) 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci pr_warn("Failed to read the '%s' env variable '%s'\n", 15962306a36Sopenharmony_ci var_names[i], var); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (!physical_memsize) { 16362306a36Sopenharmony_ci pr_warn("The bootloader didn't provide memsize: defaulting to 32MB\n"); 16462306a36Sopenharmony_ci physical_memsize = 32 << 20; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * SOC-it swaps, or perhaps doesn't swap, when DMA'ing 17062306a36Sopenharmony_ci * the last word of physical memory. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci physical_memsize -= PAGE_SIZE; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* default to using all available RAM */ 17662306a36Sopenharmony_ci memsize = physical_memsize; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* allow the user to override the usable memory */ 17962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(var_names); i++) { 18062306a36Sopenharmony_ci snprintf(param_name, sizeof(param_name), "%s=", var_names[i]); 18162306a36Sopenharmony_ci var = strstr(arcs_cmdline, param_name); 18262306a36Sopenharmony_ci if (!var) 18362306a36Sopenharmony_ci continue; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci memsize = memparse(var + strlen(param_name), NULL); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* if the user says there's more RAM than we thought, believe them */ 18962306a36Sopenharmony_ci physical_memsize = max_t(unsigned long, physical_memsize, memsize); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* detect the memory map in use */ 19262306a36Sopenharmony_ci if (malta_scon() == MIPS_REVISION_SCON_ROCIT) { 19362306a36Sopenharmony_ci /* ROCit has a register indicating the memory map in use */ 19462306a36Sopenharmony_ci config = readl((void __iomem *)CKSEG1ADDR(ROCIT_CONFIG_GEN1)); 19562306a36Sopenharmony_ci mem_map = config & ROCIT_CONFIG_GEN1_MEMMAP_MASK; 19662306a36Sopenharmony_ci mem_map >>= ROCIT_CONFIG_GEN1_MEMMAP_SHIFT; 19762306a36Sopenharmony_ci } else { 19862306a36Sopenharmony_ci /* if not using ROCit, presume the v1 memory map */ 19962306a36Sopenharmony_ci mem_map = MEM_MAP_V1; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci if (mem_map > MEM_MAP_V2) 20262306a36Sopenharmony_ci panic("Unsupported physical memory map v%u detected", 20362306a36Sopenharmony_ci (unsigned int)mem_map); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* append memory to the DT */ 20662306a36Sopenharmony_ci mem_off = fdt_add_subnode(fdt, root_off, "memory"); 20762306a36Sopenharmony_ci if (mem_off < 0) 20862306a36Sopenharmony_ci panic("Unable to add memory node to DT: %d", mem_off); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci err = fdt_setprop_string(fdt, mem_off, "device_type", "memory"); 21162306a36Sopenharmony_ci if (err) 21262306a36Sopenharmony_ci panic("Unable to set memory node device_type: %d", err); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci mem_entries = gen_fdt_mem_array(mem_array, physical_memsize, mem_map); 21562306a36Sopenharmony_ci err = fdt_setprop(fdt, mem_off, "reg", mem_array, 21662306a36Sopenharmony_ci mem_entries * 2 * sizeof(mem_array[0])); 21762306a36Sopenharmony_ci if (err) 21862306a36Sopenharmony_ci panic("Unable to set memory regs property: %d", err); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci mem_entries = gen_fdt_mem_array(mem_array, memsize, mem_map); 22162306a36Sopenharmony_ci err = fdt_setprop(fdt, mem_off, "linux,usable-memory", mem_array, 22262306a36Sopenharmony_ci mem_entries * 2 * sizeof(mem_array[0])); 22362306a36Sopenharmony_ci if (err) 22462306a36Sopenharmony_ci panic("Unable to set linux,usable-memory property: %d", err); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void __init remove_gic(void *fdt) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci int err, gic_off, i8259_off, cpu_off; 23062306a36Sopenharmony_ci void __iomem *biu_base; 23162306a36Sopenharmony_ci uint32_t cpu_phandle, sc_cfg; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* if we have a CM which reports a GIC is present, leave the DT alone */ 23462306a36Sopenharmony_ci err = mips_cm_probe(); 23562306a36Sopenharmony_ci if (!err && (read_gcr_gic_status() & CM_GCR_GIC_STATUS_EX)) 23662306a36Sopenharmony_ci return; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (malta_scon() == MIPS_REVISION_SCON_ROCIT) { 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * On systems using the RocIT system controller a GIC may be 24162306a36Sopenharmony_ci * present without a CM. Detect whether that is the case. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci biu_base = ioremap(MSC01_BIU_REG_BASE, 24462306a36Sopenharmony_ci MSC01_BIU_ADDRSPACE_SZ); 24562306a36Sopenharmony_ci sc_cfg = __raw_readl(biu_base + MSC01_SC_CFG_OFS); 24662306a36Sopenharmony_ci if (sc_cfg & MSC01_SC_CFG_GICPRES_MSK) { 24762306a36Sopenharmony_ci /* enable the GIC at the system controller level */ 24862306a36Sopenharmony_ci sc_cfg |= BIT(MSC01_SC_CFG_GICENA_SHF); 24962306a36Sopenharmony_ci __raw_writel(sc_cfg, biu_base + MSC01_SC_CFG_OFS); 25062306a36Sopenharmony_ci return; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci gic_off = fdt_node_offset_by_compatible(fdt, -1, "mti,gic"); 25562306a36Sopenharmony_ci if (gic_off < 0) { 25662306a36Sopenharmony_ci pr_warn("malta-dtshim: unable to find DT GIC node: %d\n", 25762306a36Sopenharmony_ci gic_off); 25862306a36Sopenharmony_ci return; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci err = fdt_nop_node(fdt, gic_off); 26262306a36Sopenharmony_ci if (err) 26362306a36Sopenharmony_ci pr_warn("malta-dtshim: unable to nop GIC node\n"); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci i8259_off = fdt_node_offset_by_compatible(fdt, -1, "intel,i8259"); 26662306a36Sopenharmony_ci if (i8259_off < 0) { 26762306a36Sopenharmony_ci pr_warn("malta-dtshim: unable to find DT i8259 node: %d\n", 26862306a36Sopenharmony_ci i8259_off); 26962306a36Sopenharmony_ci return; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci cpu_off = fdt_node_offset_by_compatible(fdt, -1, 27362306a36Sopenharmony_ci "mti,cpu-interrupt-controller"); 27462306a36Sopenharmony_ci if (cpu_off < 0) { 27562306a36Sopenharmony_ci pr_warn("malta-dtshim: unable to find CPU intc node: %d\n", 27662306a36Sopenharmony_ci cpu_off); 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci cpu_phandle = fdt_get_phandle(fdt, cpu_off); 28162306a36Sopenharmony_ci if (!cpu_phandle) { 28262306a36Sopenharmony_ci pr_warn("malta-dtshim: unable to get CPU intc phandle\n"); 28362306a36Sopenharmony_ci return; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci err = fdt_setprop_u32(fdt, i8259_off, "interrupt-parent", cpu_phandle); 28762306a36Sopenharmony_ci if (err) { 28862306a36Sopenharmony_ci pr_warn("malta-dtshim: unable to set i8259 interrupt-parent: %d\n", 28962306a36Sopenharmony_ci err); 29062306a36Sopenharmony_ci return; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci err = fdt_setprop_u32(fdt, i8259_off, "interrupts", 2); 29462306a36Sopenharmony_ci if (err) { 29562306a36Sopenharmony_ci pr_warn("malta-dtshim: unable to set i8259 interrupts: %d\n", 29662306a36Sopenharmony_ci err); 29762306a36Sopenharmony_ci return; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_civoid __init *malta_dt_shim(void *fdt) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci int root_off, len, err; 30462306a36Sopenharmony_ci const char *compat; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (fdt_check_header(fdt)) 30762306a36Sopenharmony_ci panic("Corrupt DT"); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci err = fdt_open_into(fdt, fdt_buf, sizeof(fdt_buf)); 31062306a36Sopenharmony_ci if (err) 31162306a36Sopenharmony_ci panic("Unable to open FDT: %d", err); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci root_off = fdt_path_offset(fdt_buf, "/"); 31462306a36Sopenharmony_ci if (root_off < 0) 31562306a36Sopenharmony_ci panic("No / node in DT"); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci compat = fdt_getprop(fdt_buf, root_off, "compatible", &len); 31862306a36Sopenharmony_ci if (!compat) 31962306a36Sopenharmony_ci panic("No root compatible property in DT: %d", len); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* if this isn't Malta, leave the DT alone */ 32262306a36Sopenharmony_ci if (strncmp(compat, "mti,malta", len)) 32362306a36Sopenharmony_ci return fdt; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci append_memory(fdt_buf, root_off); 32662306a36Sopenharmony_ci remove_gic(fdt_buf); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci err = fdt_pack(fdt_buf); 32962306a36Sopenharmony_ci if (err) 33062306a36Sopenharmony_ci panic("Unable to pack FDT: %d\n", err); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return fdt_buf; 33362306a36Sopenharmony_ci} 334