]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
9d02a428 JB |
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 <phoenix@bsemi.com> | |
11 | * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se> | |
12 | * | |
9d02a428 JB |
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> | |
57c8a661 | 29 | #include <linux/memblock.h> |
9d02a428 JB |
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> | |
9d02a428 | 35 | #include <linux/device.h> |
9d02a428 | 36 | |
be5940c9 | 37 | #include <asm/sections.h> |
9d02a428 JB |
38 | #include <asm/pgtable.h> |
39 | #include <asm/types.h> | |
40 | #include <asm/setup.h> | |
41 | #include <asm/io.h> | |
42 | #include <asm/cpuinfo.h> | |
43 | #include <asm/delay.h> | |
44 | ||
45 | #include "vmlinux.h" | |
46 | ||
266c7fad | 47 | static void __init setup_memory(void) |
9d02a428 | 48 | { |
9d02a428 | 49 | unsigned long ram_start_pfn; |
9d02a428 JB |
50 | unsigned long ram_end_pfn; |
51 | phys_addr_t memory_start, memory_end; | |
52 | struct memblock_region *region; | |
53 | ||
54 | memory_end = memory_start = 0; | |
55 | ||
266c7fad | 56 | /* Find main memory where is the kernel, we assume its the only one */ |
9d02a428 JB |
57 | for_each_memblock(memory, region) { |
58 | memory_start = region->base; | |
59 | memory_end = region->base + region->size; | |
60 | printk(KERN_INFO "%s: Memory: 0x%x-0x%x\n", __func__, | |
61 | memory_start, memory_end); | |
62 | } | |
63 | ||
64 | if (!memory_end) { | |
65 | panic("No memory!"); | |
66 | } | |
67 | ||
68 | ram_start_pfn = PFN_UP(memory_start); | |
9d02a428 JB |
69 | ram_end_pfn = PFN_DOWN(memblock_end_of_DRAM()); |
70 | ||
266c7fad SH |
71 | /* setup bootmem globals (we use no_bootmem, but mm still depends on this) */ |
72 | min_low_pfn = ram_start_pfn; | |
73 | max_low_pfn = ram_end_pfn; | |
9d02a428 JB |
74 | max_pfn = ram_end_pfn; |
75 | ||
76 | /* | |
77 | * initialize the boot-time allocator (with low memory only). | |
78 | * | |
79 | * This makes the memory from the end of the kernel to the end of | |
80 | * RAM usable. | |
9d02a428 | 81 | */ |
266c7fad SH |
82 | memblock_reserve(__pa(_stext), _end - _stext); |
83 | ||
84 | early_init_fdt_reserve_self(); | |
85 | early_init_fdt_scan_reserved_mem(); | |
9d02a428 | 86 | |
266c7fad | 87 | memblock_dump_all(); |
9d02a428 JB |
88 | } |
89 | ||
8e6d08e0 | 90 | struct cpuinfo_or1k cpuinfo_or1k[NR_CPUS]; |
9d02a428 JB |
91 | |
92 | static void print_cpuinfo(void) | |
93 | { | |
94 | unsigned long upr = mfspr(SPR_UPR); | |
95 | unsigned long vr = mfspr(SPR_VR); | |
96 | unsigned int version; | |
97 | unsigned int revision; | |
8e6d08e0 | 98 | struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[smp_processor_id()]; |
9d02a428 JB |
99 | |
100 | version = (vr & SPR_VR_VER) >> 24; | |
101 | revision = (vr & SPR_VR_REV); | |
102 | ||
103 | printk(KERN_INFO "CPU: OpenRISC-%x (revision %d) @%d MHz\n", | |
8e6d08e0 | 104 | version, revision, cpuinfo->clock_frequency / 1000000); |
9d02a428 JB |
105 | |
106 | if (!(upr & SPR_UPR_UP)) { | |
107 | printk(KERN_INFO | |
108 | "-- no UPR register... unable to detect configuration\n"); | |
109 | return; | |
110 | } | |
111 | ||
112 | if (upr & SPR_UPR_DCP) | |
113 | printk(KERN_INFO | |
114 | "-- dcache: %4d bytes total, %2d bytes/line, %d way(s)\n", | |
8e6d08e0 SK |
115 | cpuinfo->dcache_size, cpuinfo->dcache_block_size, |
116 | cpuinfo->dcache_ways); | |
9d02a428 JB |
117 | else |
118 | printk(KERN_INFO "-- dcache disabled\n"); | |
119 | if (upr & SPR_UPR_ICP) | |
120 | printk(KERN_INFO | |
121 | "-- icache: %4d bytes total, %2d bytes/line, %d way(s)\n", | |
8e6d08e0 SK |
122 | cpuinfo->icache_size, cpuinfo->icache_block_size, |
123 | cpuinfo->icache_ways); | |
9d02a428 JB |
124 | else |
125 | printk(KERN_INFO "-- icache disabled\n"); | |
126 | ||
127 | if (upr & SPR_UPR_DMP) | |
128 | printk(KERN_INFO "-- dmmu: %4d entries, %lu way(s)\n", | |
129 | 1 << ((mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTS) >> 2), | |
130 | 1 + (mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTW)); | |
131 | if (upr & SPR_UPR_IMP) | |
132 | printk(KERN_INFO "-- immu: %4d entries, %lu way(s)\n", | |
133 | 1 << ((mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTS) >> 2), | |
134 | 1 + (mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTW)); | |
135 | ||
136 | printk(KERN_INFO "-- additional features:\n"); | |
137 | if (upr & SPR_UPR_DUP) | |
138 | printk(KERN_INFO "-- debug unit\n"); | |
139 | if (upr & SPR_UPR_PCUP) | |
140 | printk(KERN_INFO "-- performance counters\n"); | |
141 | if (upr & SPR_UPR_PMP) | |
142 | printk(KERN_INFO "-- power management\n"); | |
143 | if (upr & SPR_UPR_PICP) | |
144 | printk(KERN_INFO "-- PIC\n"); | |
145 | if (upr & SPR_UPR_TTP) | |
146 | printk(KERN_INFO "-- timer\n"); | |
147 | if (upr & SPR_UPR_CUP) | |
148 | printk(KERN_INFO "-- custom unit(s)\n"); | |
149 | } | |
150 | ||
8e6d08e0 SK |
151 | static struct device_node *setup_find_cpu_node(int cpu) |
152 | { | |
153 | u32 hwid; | |
154 | struct device_node *cpun; | |
8e6d08e0 | 155 | |
5e5abae8 | 156 | for_each_of_cpu_node(cpun) { |
8e6d08e0 SK |
157 | if (of_property_read_u32(cpun, "reg", &hwid)) |
158 | continue; | |
159 | if (hwid == cpu) | |
160 | return cpun; | |
161 | } | |
162 | ||
163 | return NULL; | |
164 | } | |
165 | ||
9d02a428 JB |
166 | void __init setup_cpuinfo(void) |
167 | { | |
168 | struct device_node *cpu; | |
169 | unsigned long iccfgr, dccfgr; | |
3e06a163 | 170 | unsigned long cache_set_size; |
8e6d08e0 SK |
171 | int cpu_id = smp_processor_id(); |
172 | struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[cpu_id]; | |
9d02a428 | 173 | |
8e6d08e0 | 174 | cpu = setup_find_cpu_node(cpu_id); |
9d02a428 | 175 | if (!cpu) |
8e6d08e0 | 176 | panic("Couldn't find CPU%d in device tree...\n", cpu_id); |
9d02a428 JB |
177 | |
178 | iccfgr = mfspr(SPR_ICCFGR); | |
8e6d08e0 | 179 | cpuinfo->icache_ways = 1 << (iccfgr & SPR_ICCFGR_NCW); |
9d02a428 | 180 | cache_set_size = 1 << ((iccfgr & SPR_ICCFGR_NCS) >> 3); |
8e6d08e0 SK |
181 | cpuinfo->icache_block_size = 16 << ((iccfgr & SPR_ICCFGR_CBS) >> 7); |
182 | cpuinfo->icache_size = | |
183 | cache_set_size * cpuinfo->icache_ways * cpuinfo->icache_block_size; | |
9d02a428 JB |
184 | |
185 | dccfgr = mfspr(SPR_DCCFGR); | |
8e6d08e0 | 186 | cpuinfo->dcache_ways = 1 << (dccfgr & SPR_DCCFGR_NCW); |
9d02a428 | 187 | cache_set_size = 1 << ((dccfgr & SPR_DCCFGR_NCS) >> 3); |
8e6d08e0 SK |
188 | cpuinfo->dcache_block_size = 16 << ((dccfgr & SPR_DCCFGR_CBS) >> 7); |
189 | cpuinfo->dcache_size = | |
190 | cache_set_size * cpuinfo->dcache_ways * cpuinfo->dcache_block_size; | |
9d02a428 JB |
191 | |
192 | if (of_property_read_u32(cpu, "clock-frequency", | |
8e6d08e0 | 193 | &cpuinfo->clock_frequency)) { |
9d02a428 JB |
194 | printk(KERN_WARNING |
195 | "Device tree missing CPU 'clock-frequency' parameter." | |
196 | "Assuming frequency 25MHZ" | |
197 | "This is probably not what you want."); | |
198 | } | |
199 | ||
8e6d08e0 SK |
200 | cpuinfo->coreid = mfspr(SPR_COREID); |
201 | ||
9d02a428 JB |
202 | of_node_put(cpu); |
203 | ||
204 | print_cpuinfo(); | |
205 | } | |
206 | ||
207 | /** | |
208 | * or32_early_setup | |
209 | * | |
210 | * Handles the pointer to the device tree that this kernel is to use | |
211 | * for establishing the available platform devices. | |
212 | * | |
dec83018 | 213 | * Falls back on built-in device tree in case null pointer is passed. |
9d02a428 JB |
214 | */ |
215 | ||
621c2cd8 | 216 | void __init or32_early_setup(void *fdt) |
9d02a428 | 217 | { |
621c2cd8 GU |
218 | if (fdt) |
219 | pr_info("FDT at %p\n", fdt); | |
220 | else { | |
221 | fdt = __dtb_start; | |
222 | pr_info("Compiled-in FDT at %p\n", fdt); | |
dec83018 | 223 | } |
621c2cd8 | 224 | early_init_devtree(fdt); |
9d02a428 JB |
225 | } |
226 | ||
9d02a428 JB |
227 | static inline unsigned long extract_value_bits(unsigned long reg, |
228 | short bit_nr, short width) | |
229 | { | |
230 | return (reg >> bit_nr) & (0 << width); | |
231 | } | |
232 | ||
233 | static inline unsigned long extract_value(unsigned long reg, unsigned long mask) | |
234 | { | |
235 | while (!(mask & 0x1)) { | |
236 | reg = reg >> 1; | |
237 | mask = mask >> 1; | |
238 | } | |
239 | return mask & reg; | |
240 | } | |
241 | ||
242 | void __init detect_unit_config(unsigned long upr, unsigned long mask, | |
243 | char *text, void (*func) (void)) | |
244 | { | |
245 | if (text != NULL) | |
246 | printk("%s", text); | |
247 | ||
248 | if (upr & mask) { | |
249 | if (func != NULL) | |
250 | func(); | |
251 | else | |
252 | printk("present\n"); | |
253 | } else | |
254 | printk("not present\n"); | |
255 | } | |
256 | ||
257 | /* | |
258 | * calibrate_delay | |
259 | * | |
260 | * Lightweight calibrate_delay implementation that calculates loops_per_jiffy | |
261 | * from the clock frequency passed in via the device tree | |
262 | * | |
263 | */ | |
264 | ||
8e8550ef | 265 | void calibrate_delay(void) |
9d02a428 JB |
266 | { |
267 | const int *val; | |
8e6d08e0 SK |
268 | struct device_node *cpu = setup_find_cpu_node(smp_processor_id()); |
269 | ||
9d02a428 JB |
270 | val = of_get_property(cpu, "clock-frequency", NULL); |
271 | if (!val) | |
272 | panic("no cpu 'clock-frequency' parameter in device tree"); | |
273 | loops_per_jiffy = *val / HZ; | |
274 | pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n", | |
275 | loops_per_jiffy / (500000 / HZ), | |
276 | (loops_per_jiffy / (5000 / HZ)) % 100, loops_per_jiffy); | |
277 | } | |
278 | ||
279 | void __init setup_arch(char **cmdline_p) | |
280 | { | |
3486892f | 281 | unflatten_and_copy_device_tree(); |
9d02a428 JB |
282 | |
283 | setup_cpuinfo(); | |
284 | ||
8e6d08e0 SK |
285 | #ifdef CONFIG_SMP |
286 | smp_init_cpus(); | |
287 | #endif | |
288 | ||
9d02a428 | 289 | /* process 1's initial memory region is the kernel code/data */ |
be5940c9 GU |
290 | init_mm.start_code = (unsigned long)_stext; |
291 | init_mm.end_code = (unsigned long)_etext; | |
292 | init_mm.end_data = (unsigned long)_edata; | |
293 | init_mm.brk = (unsigned long)_end; | |
9d02a428 JB |
294 | |
295 | #ifdef CONFIG_BLK_DEV_INITRD | |
296 | initrd_start = (unsigned long)&__initrd_start; | |
297 | initrd_end = (unsigned long)&__initrd_end; | |
298 | if (initrd_start == initrd_end) { | |
299 | initrd_start = 0; | |
300 | initrd_end = 0; | |
301 | } | |
302 | initrd_below_start_ok = 1; | |
303 | #endif | |
304 | ||
266c7fad SH |
305 | /* setup memblock allocator */ |
306 | setup_memory(); | |
9d02a428 JB |
307 | |
308 | /* paging_init() sets up the MMU and marks all pages as reserved */ | |
309 | paging_init(); | |
310 | ||
311 | #if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE) | |
312 | if (!conswitchp) | |
313 | conswitchp = &dummy_con; | |
314 | #endif | |
315 | ||
bbf28b50 | 316 | *cmdline_p = boot_command_line; |
9d02a428 | 317 | |
d01e1f35 | 318 | printk(KERN_INFO "OpenRISC Linux -- http://openrisc.io\n"); |
9d02a428 JB |
319 | } |
320 | ||
321 | static int show_cpuinfo(struct seq_file *m, void *v) | |
322 | { | |
8e6d08e0 SK |
323 | unsigned int vr, cpucfgr; |
324 | unsigned int avr; | |
325 | unsigned int version; | |
326 | struct cpuinfo_or1k *cpuinfo = v; | |
9d02a428 JB |
327 | |
328 | vr = mfspr(SPR_VR); | |
8e6d08e0 SK |
329 | cpucfgr = mfspr(SPR_CPUCFGR); |
330 | ||
331 | #ifdef CONFIG_SMP | |
332 | seq_printf(m, "processor\t\t: %d\n", cpuinfo->coreid); | |
333 | #endif | |
334 | if (vr & SPR_VR_UVRP) { | |
335 | vr = mfspr(SPR_VR2); | |
336 | version = vr & SPR_VR2_VER; | |
337 | avr = mfspr(SPR_AVR); | |
338 | seq_printf(m, "cpu architecture\t: " | |
339 | "OpenRISC 1000 (%d.%d-rev%d)\n", | |
340 | (avr >> 24) & 0xff, | |
341 | (avr >> 16) & 0xff, | |
342 | (avr >> 8) & 0xff); | |
343 | seq_printf(m, "cpu implementation id\t: 0x%x\n", | |
344 | (vr & SPR_VR2_CPUID) >> 24); | |
345 | seq_printf(m, "cpu version\t\t: 0x%x\n", version); | |
346 | } else { | |
347 | version = (vr & SPR_VR_VER) >> 24; | |
348 | seq_printf(m, "cpu\t\t\t: OpenRISC-%x\n", version); | |
349 | seq_printf(m, "revision\t\t: %d\n", vr & SPR_VR_REV); | |
350 | } | |
351 | seq_printf(m, "frequency\t\t: %ld\n", loops_per_jiffy * HZ); | |
352 | seq_printf(m, "dcache size\t\t: %d bytes\n", cpuinfo->dcache_size); | |
353 | seq_printf(m, "dcache block size\t: %d bytes\n", | |
354 | cpuinfo->dcache_block_size); | |
355 | seq_printf(m, "dcache ways\t\t: %d\n", cpuinfo->dcache_ways); | |
356 | seq_printf(m, "icache size\t\t: %d bytes\n", cpuinfo->icache_size); | |
357 | seq_printf(m, "icache block size\t: %d bytes\n", | |
358 | cpuinfo->icache_block_size); | |
359 | seq_printf(m, "icache ways\t\t: %d\n", cpuinfo->icache_ways); | |
360 | seq_printf(m, "immu\t\t\t: %d entries, %lu ways\n", | |
361 | 1 << ((mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTS) >> 2), | |
362 | 1 + (mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTW)); | |
363 | seq_printf(m, "dmmu\t\t\t: %d entries, %lu ways\n", | |
364 | 1 << ((mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTS) >> 2), | |
365 | 1 + (mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTW)); | |
366 | seq_printf(m, "bogomips\t\t: %lu.%02lu\n", | |
367 | (loops_per_jiffy * HZ) / 500000, | |
368 | ((loops_per_jiffy * HZ) / 5000) % 100); | |
369 | ||
370 | seq_puts(m, "features\t\t: "); | |
371 | seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OB32S ? "orbis32" : ""); | |
372 | seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OB64S ? "orbis64" : ""); | |
373 | seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OF32S ? "orfpx32" : ""); | |
374 | seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OF64S ? "orfpx64" : ""); | |
375 | seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OV64S ? "orvdx64" : ""); | |
376 | seq_puts(m, "\n"); | |
377 | ||
378 | seq_puts(m, "\n"); | |
379 | ||
58a1aa7c | 380 | return 0; |
9d02a428 JB |
381 | } |
382 | ||
8e6d08e0 | 383 | static void *c_start(struct seq_file *m, loff_t *pos) |
9d02a428 | 384 | { |
8e6d08e0 SK |
385 | *pos = cpumask_next(*pos - 1, cpu_online_mask); |
386 | if ((*pos) < nr_cpu_ids) | |
387 | return &cpuinfo_or1k[*pos]; | |
388 | return NULL; | |
9d02a428 JB |
389 | } |
390 | ||
8e6d08e0 | 391 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) |
9d02a428 | 392 | { |
8e6d08e0 SK |
393 | (*pos)++; |
394 | return c_start(m, pos); | |
9d02a428 JB |
395 | } |
396 | ||
397 | static void c_stop(struct seq_file *m, void *v) | |
398 | { | |
399 | } | |
400 | ||
401 | const struct seq_operations cpuinfo_op = { | |
402 | .start = c_start, | |
403 | .next = c_next, | |
404 | .stop = c_stop, | |
405 | .show = show_cpuinfo, | |
406 | }; |