| 1 | |
| 2 | |
| 3 | * OpenRISC setup.c
|
| 4 | *
|
| 5 | * Linux architectural port borrowing liberally from similar works of
|
| 6 | * others. All original copyrights apply as per the original source
|
| 7 | * declaration.
|
| 8 | *
|
| 9 | * Modifications for the OpenRISC architecture:
|
| 10 | * Copyright (C) 2003 Matjaz Breskvar <[email protected]>
|
| 11 | * Copyright (C) 2010-2011 Jonas Bonn <[email protected]>
|
| 12 | *
|
| 13 | * This file handles the architecture-dependent parts of initialization
|
| 14 |
|
| 15 |
|
| 16 | #include <linux/errno.h>
|
| 17 | #include <linux/sched.h>
|
| 18 | #include <linux/kernel.h>
|
| 19 | #include <linux/mm.h>
|
| 20 | #include <linux/stddef.h>
|
| 21 | #include <linux/unistd.h>
|
| 22 | #include <linux/ptrace.h>
|
| 23 | #include <linux/slab.h>
|
| 24 | #include <linux/tty.h>
|
| 25 | #include <linux/ioport.h>
|
| 26 | #include <linux/delay.h>
|
| 27 | #include <linux/console.h>
|
| 28 | #include <linux/init.h>
|
| 29 | #include <linux/memblock.h>
|
| 30 | #include <linux/seq_file.h>
|
| 31 | #include <linux/serial.h>
|
| 32 | #include <linux/initrd.h>
|
| 33 | #include <linux/of_fdt.h>
|
| 34 | #include <linux/of.h>
|
| 35 | #include <linux/device.h>
|
| 36 |
|
| 37 | #include <asm/sections.h>
|
| 38 | #include <asm/types.h>
|
| 39 | #include <asm/setup.h>
|
| 40 | #include <asm/io.h>
|
| 41 | #include <asm/cpuinfo.h>
|
| 42 | #include <asm/delay.h>
|
| 43 |
|
| 44 | #include "vmlinux.h"
|
| 45 |
|
| 46 | static void __init setup_memory(void)
|
| 47 | {
|
| 48 | unsigned long ram_start_pfn;
|
| 49 | unsigned long ram_end_pfn;
|
| 50 | phys_addr_t memory_start, memory_end;
|
| 51 |
|
| 52 | memory_end = memory_start = 0;
|
| 53 |
|
| 54 |
|
| 55 | memory_start = memblock_start_of_DRAM();
|
| 56 | memory_end = memblock_end_of_DRAM();
|
| 57 |
|
| 58 | if (!memory_end) {
|
| 59 | panic("No memory!");
|
| 60 | }
|
| 61 |
|
| 62 | ram_start_pfn = PFN_UP(memory_start);
|
| 63 | ram_end_pfn = PFN_DOWN(memblock_end_of_DRAM());
|
| 64 |
|
| 65 |
|
| 66 | min_low_pfn = ram_start_pfn;
|
| 67 | max_low_pfn = ram_end_pfn;
|
| 68 | max_pfn = ram_end_pfn;
|
| 69 |
|
| 70 | |
| 71 | * initialize the boot-time allocator (with low memory only).
|
| 72 | *
|
| 73 | * This makes the memory from the end of the kernel to the end of
|
| 74 | * RAM usable.
|
| 75 |
|
| 76 | memblock_reserve(__pa(_stext), _end - _stext);
|
| 77 |
|
| 78 | #ifdef CONFIG_BLK_DEV_INITRD
|
| 79 |
|
| 80 | if (initrd_start && (initrd_end > initrd_start)) {
|
| 81 | unsigned long aligned_start = ALIGN_DOWN(initrd_start, PAGE_SIZE);
|
| 82 | unsigned long aligned_end = ALIGN(initrd_end, PAGE_SIZE);
|
| 83 |
|
| 84 | memblock_reserve(__pa(aligned_start), aligned_end - aligned_start);
|
| 85 | }
|
| 86 | #endif
|
| 87 |
|
| 88 | early_init_fdt_reserve_self();
|
| 89 | early_init_fdt_scan_reserved_mem();
|
| 90 |
|
| 91 | memblock_dump_all();
|
| 92 | }
|
| 93 |
|
| 94 | struct cpuinfo_or1k cpuinfo_or1k[NR_CPUS];
|
| 95 |
|
| 96 | static void print_cpuinfo(void)
|
| 97 | {
|
| 98 | unsigned long upr = mfspr(SPR_UPR);
|
| 99 | unsigned long vr = mfspr(SPR_VR);
|
| 100 | unsigned int version;
|
| 101 | unsigned int revision;
|
| 102 | struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[smp_processor_id()];
|
| 103 |
|
| 104 | version = (vr & SPR_VR_VER) >> 24;
|
| 105 | revision = (vr & SPR_VR_REV);
|
| 106 |
|
| 107 | printk(KERN_INFO "CPU: OpenRISC-%x (revision %d) @%d MHz\n",
|
| 108 | version, revision, cpuinfo->clock_frequency / 1000000);
|
| 109 |
|
| 110 | if (!(upr & SPR_UPR_UP)) {
|
| 111 | printk(KERN_INFO
|
| 112 | "-- no UPR register... unable to detect configuration\n");
|
| 113 | return;
|
| 114 | }
|
| 115 |
|
| 116 | if (upr & SPR_UPR_DMP)
|
| 117 | printk(KERN_INFO "-- dmmu: %4d entries, %lu way(s)\n",
|
| 118 | 1 << ((mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTS) >> 2),
|
| 119 | 1 + (mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTW));
|
| 120 | if (upr & SPR_UPR_IMP)
|
| 121 | printk(KERN_INFO "-- immu: %4d entries, %lu way(s)\n",
|
| 122 | 1 << ((mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTS) >> 2),
|
| 123 | 1 + (mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTW));
|
| 124 |
|
| 125 | printk(KERN_INFO "-- additional features:\n");
|
| 126 | if (upr & SPR_UPR_DUP)
|
| 127 | printk(KERN_INFO "-- debug unit\n");
|
| 128 | if (upr & SPR_UPR_PCUP)
|
| 129 | printk(KERN_INFO "-- performance counters\n");
|
| 130 | if (upr & SPR_UPR_PMP)
|
| 131 | printk(KERN_INFO "-- power management\n");
|
| 132 | if (upr & SPR_UPR_PICP)
|
| 133 | printk(KERN_INFO "-- PIC\n");
|
| 134 | if (upr & SPR_UPR_TTP)
|
| 135 | printk(KERN_INFO "-- timer\n");
|
| 136 | if (upr & SPR_UPR_CUP)
|
| 137 | printk(KERN_INFO "-- custom unit(s)\n");
|
| 138 | }
|
| 139 |
|
| 140 | void __init setup_cpuinfo(void)
|
| 141 | {
|
| 142 | struct device_node *cpu;
|
| 143 | int cpu_id = smp_processor_id();
|
| 144 | struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[cpu_id];
|
| 145 |
|
| 146 | cpu = of_get_cpu_node(cpu_id, NULL);
|
| 147 | if (!cpu)
|
| 148 | panic("Couldn't find CPU%d in device tree...\n", cpu_id);
|
| 149 |
|
| 150 | if (of_property_read_u32(cpu, "clock-frequency",
|
| 151 | &cpuinfo->clock_frequency)) {
|
| 152 | printk(KERN_WARNING
|
| 153 | "Device tree missing CPU 'clock-frequency' parameter."
|
| 154 | "Assuming frequency 25MHZ"
|
| 155 | "This is probably not what you want.");
|
| 156 | }
|
| 157 |
|
| 158 | cpuinfo->coreid = mfspr(SPR_COREID);
|
| 159 |
|
| 160 | of_node_put(cpu);
|
| 161 |
|
| 162 | print_cpuinfo();
|
| 163 | }
|
| 164 |
|
| 165 | |
| 166 | * or1k_early_setup
|
| 167 | * @fdt: pointer to the start of the device tree in memory or NULL
|
| 168 | *
|
| 169 | * Handles the pointer to the device tree that this kernel is to use
|
| 170 | * for establishing the available platform devices.
|
| 171 | *
|
| 172 | * Falls back on built-in device tree in case null pointer is passed.
|
| 173 |
|
| 174 |
|
| 175 | void __init or1k_early_setup(void *fdt)
|
| 176 | {
|
| 177 | if (fdt)
|
| 178 | pr_info("FDT at %p\n", fdt);
|
| 179 | else {
|
| 180 | fdt = __dtb_start;
|
| 181 | pr_info("Compiled-in FDT at %p\n", fdt);
|
| 182 | }
|
| 183 | early_init_devtree(fdt);
|
| 184 | }
|
| 185 |
|
| 186 | static inline unsigned long extract_value_bits(unsigned long reg,
|
| 187 | short bit_nr, short width)
|
| 188 | {
|
| 189 | return (reg >> bit_nr) & (0 << width);
|
| 190 | }
|
| 191 |
|
| 192 | static inline unsigned long extract_value(unsigned long reg, unsigned long mask)
|
| 193 | {
|
| 194 | while (!(mask & 0x1)) {
|
| 195 | reg = reg >> 1;
|
| 196 | mask = mask >> 1;
|
| 197 | }
|
| 198 | return mask & reg;
|
| 199 | }
|
| 200 |
|
| 201 | |
| 202 | * calibrate_delay
|
| 203 | *
|
| 204 | * Lightweight calibrate_delay implementation that calculates loops_per_jiffy
|
| 205 | * from the clock frequency passed in via the device tree
|
| 206 | *
|
| 207 |
|
| 208 |
|
| 209 | void calibrate_delay(void)
|
| 210 | {
|
| 211 | const int *val;
|
| 212 | struct device_node *cpu = of_get_cpu_node(smp_processor_id(), NULL);
|
| 213 |
|
| 214 | val = of_get_property(cpu, "clock-frequency", NULL);
|
| 215 | if (!val)
|
| 216 | panic("no cpu 'clock-frequency' parameter in device tree");
|
| 217 | loops_per_jiffy = *val / HZ;
|
| 218 | pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n",
|
| 219 | loops_per_jiffy / (500000 / HZ),
|
| 220 | (loops_per_jiffy / (5000 / HZ)) % 100, loops_per_jiffy);
|
| 221 |
|
| 222 | of_node_put(cpu);
|
| 223 | }
|
| 224 |
|
| 225 | void __init setup_arch(char **cmdline_p)
|
| 226 | {
|
| 227 |
|
| 228 | setup_memory();
|
| 229 |
|
| 230 | unflatten_and_copy_device_tree();
|
| 231 |
|
| 232 | setup_cpuinfo();
|
| 233 |
|
| 234 | #ifdef CONFIG_SMP
|
| 235 | smp_init_cpus();
|
| 236 | #endif
|
| 237 |
|
| 238 |
|
| 239 | setup_initial_init_mm(_stext, _etext, _edata, _end);
|
| 240 |
|
| 241 | #ifdef CONFIG_BLK_DEV_INITRD
|
| 242 | if (initrd_start == initrd_end) {
|
| 243 | printk(KERN_INFO "Initial ramdisk not found\n");
|
| 244 | initrd_start = 0;
|
| 245 | initrd_end = 0;
|
| 246 | } else {
|
| 247 | printk(KERN_INFO "Initial ramdisk at: 0x%p (%lu bytes)\n",
|
| 248 | (void *)(initrd_start), initrd_end - initrd_start);
|
| 249 | initrd_below_start_ok = 1;
|
| 250 | }
|
| 251 | #endif
|
| 252 |
|
| 253 | jump_label_init();
|
| 254 |
|
| 255 |
|
| 256 | paging_init();
|
| 257 |
|
| 258 | *cmdline_p = boot_command_line;
|
| 259 |
|
| 260 | printk(KERN_INFO "OpenRISC Linux -- http://openrisc.io\n");
|
| 261 | }
|
| 262 |
|
| 263 | static int show_cpuinfo(struct seq_file *m, void *v)
|
| 264 | {
|
| 265 | unsigned int vr, cpucfgr;
|
| 266 | unsigned int avr;
|
| 267 | unsigned int version;
|
| 268 | #ifdef CONFIG_SMP
|
| 269 | struct cpuinfo_or1k *cpuinfo = v;
|
| 270 | seq_printf(m, "processor\t\t: %d\n", cpuinfo->coreid);
|
| 271 | #endif
|
| 272 |
|
| 273 | vr = mfspr(SPR_VR);
|
| 274 | cpucfgr = mfspr(SPR_CPUCFGR);
|
| 275 |
|
| 276 | if (vr & SPR_VR_UVRP) {
|
| 277 | vr = mfspr(SPR_VR2);
|
| 278 | version = vr & SPR_VR2_VER;
|
| 279 | avr = mfspr(SPR_AVR);
|
| 280 | seq_printf(m, "cpu architecture\t: "
|
| 281 | "OpenRISC 1000 (%d.%d-rev%d)\n",
|
| 282 | (avr >> 24) & 0xff,
|
| 283 | (avr >> 16) & 0xff,
|
| 284 | (avr >> 8) & 0xff);
|
| 285 | seq_printf(m, "cpu implementation id\t: 0x%x\n",
|
| 286 | (vr & SPR_VR2_CPUID) >> 24);
|
| 287 | seq_printf(m, "cpu version\t\t: 0x%x\n", version);
|
| 288 | } else {
|
| 289 | version = (vr & SPR_VR_VER) >> 24;
|
| 290 | seq_printf(m, "cpu\t\t\t: OpenRISC-%x\n", version);
|
| 291 | seq_printf(m, "revision\t\t: %d\n", vr & SPR_VR_REV);
|
| 292 | }
|
| 293 | seq_printf(m, "frequency\t\t: %ld\n", loops_per_jiffy * HZ);
|
| 294 | seq_printf(m, "immu\t\t\t: %d entries, %lu ways\n",
|
| 295 | 1 << ((mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTS) >> 2),
|
| 296 | 1 + (mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTW));
|
| 297 | seq_printf(m, "dmmu\t\t\t: %d entries, %lu ways\n",
|
| 298 | 1 << ((mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTS) >> 2),
|
| 299 | 1 + (mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTW));
|
| 300 | seq_printf(m, "bogomips\t\t: %lu.%02lu\n",
|
| 301 | (loops_per_jiffy * HZ) / 500000,
|
| 302 | ((loops_per_jiffy * HZ) / 5000) % 100);
|
| 303 |
|
| 304 | seq_puts(m, "features\t\t: ");
|
| 305 | seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OB32S ? "orbis32" : "");
|
| 306 | seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OB64S ? "orbis64" : "");
|
| 307 | seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OF32S ? "orfpx32" : "");
|
| 308 | seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OF64S ? "orfpx64" : "");
|
| 309 | seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OV64S ? "orvdx64" : "");
|
| 310 | seq_puts(m, "\n");
|
| 311 |
|
| 312 | seq_puts(m, "\n");
|
| 313 |
|
| 314 | return 0;
|
| 315 | }
|
| 316 |
|
| 317 | static void *c_start(struct seq_file *m, loff_t *pos)
|
| 318 | {
|
| 319 | *pos = cpumask_next(*pos - 1, cpu_online_mask);
|
| 320 | if ((*pos) < nr_cpu_ids)
|
| 321 | return &cpuinfo_or1k[*pos];
|
| 322 | return NULL;
|
| 323 | }
|
| 324 |
|
| 325 | static void *c_next(struct seq_file *m, void *v, loff_t *pos)
|
| 326 | {
|
| 327 | (*pos)++;
|
| 328 | return c_start(m, pos);
|
| 329 | }
|
| 330 |
|
| 331 | static void c_stop(struct seq_file *m, void *v)
|
| 332 | {
|
| 333 | }
|
| 334 |
|
| 335 | const struct seq_operations cpuinfo_op = {
|
| 336 | .start = c_start,
|
| 337 | .next = c_next,
|
| 338 | .stop = c_stop,
|
| 339 | .show = show_cpuinfo,
|
| 340 | };
|