linux/arch/mips/mti-malta/malta-dtshim.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Imagination Technologies
   3 * Author: Paul Burton <paul.burton@mips.com>
   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>
  16#include <asm/addrspace.h>
  17#include <asm/bootinfo.h>
  18#include <asm/fw/fw.h>
  19#include <asm/mips-boards/generic.h>
  20#include <asm/mips-boards/malta.h>
  21#include <asm/mips-cps.h>
  22#include <asm/page.h>
  23
  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
  29static unsigned char fdt_buf[16 << 10] __initdata;
  30
  31/* determined physical memory size, not overridden by command line args  */
  32extern unsigned long physical_memsize;
  33
  34enum mem_map {
  35        MEM_MAP_V1 = 0,
  36        MEM_MAP_V2,
  37};
  38
  39#define MAX_MEM_ARRAY_ENTRIES 2
  40
  41static __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}
  73
  74static unsigned __init gen_fdt_mem_array(__be32 *mem_array, unsigned long size,
  75                                         enum mem_map map)
  76{
  77        unsigned long size_preio;
  78        unsigned entries;
  79
  80        entries = 1;
  81        mem_array[0] = cpu_to_be32(PHYS_OFFSET);
  82        if (IS_ENABLED(CONFIG_EVA)) {
  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);
  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);
 115        } else {
 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);
 128        }
 129
 130done:
 131        BUG_ON(entries > MAX_MEM_ARRAY_ENTRIES);
 132        return entries;
 133}
 134
 135static 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;
 141        enum mem_map mem_map;
 142        u32 config;
 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
 171        if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) {
 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
 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
 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
 218        mem_entries = gen_fdt_mem_array(mem_array, physical_memsize, mem_map);
 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
 224        mem_entries = gen_fdt_mem_array(mem_array, memsize, mem_map);
 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
 231static 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();
 239        if (!err && (read_gcr_gic_status() & CM_GCR_GIC_STATUS_EX))
 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
 305void __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);
 330        remove_gic(fdt_buf);
 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}
 338