blob: 45217617c60d5508623c191d4efa608c747a921a [file] [log] [blame]
Chris Zankel5a0015d2005-06-23 22:01:16 -07001/*
Uwe Zeisbergerf30c2262006-10-03 23:01:26 +02002 * arch/xtensa/kernel/setup.c
Chris Zankel5a0015d2005-06-23 22:01:16 -07003 *
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file "COPYING" in the main directory of this archive
6 * for more details.
7 *
8 * Copyright (C) 1995 Linus Torvalds
9 * Copyright (C) 2001 - 2005 Tensilica Inc.
10 *
11 * Chris Zankel <chris@zankel.net>
12 * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
13 * Kevin Chea
14 * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
15 */
16
Chris Zankel5a0015d2005-06-23 22:01:16 -070017#include <linux/errno.h>
18#include <linux/init.h>
Andrea Righi27ac7922008-07-23 21:28:13 -070019#include <linux/mm.h>
Chris Zankel5a0015d2005-06-23 22:01:16 -070020#include <linux/proc_fs.h>
Jon Smirl894673e2006-07-10 04:44:13 -070021#include <linux/screen_info.h>
Chris Zankel5a0015d2005-06-23 22:01:16 -070022#include <linux/bootmem.h>
23#include <linux/kernel.h>
24
25#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
26# include <linux/console.h>
27#endif
28
29#ifdef CONFIG_RTC
30# include <linux/timex.h>
31#endif
32
33#ifdef CONFIG_PROC_FS
34# include <linux/seq_file.h>
35#endif
36
Chris Zankel5a0015d2005-06-23 22:01:16 -070037#include <asm/bootparam.h>
38#include <asm/pgtable.h>
39#include <asm/processor.h>
40#include <asm/timex.h>
41#include <asm/platform.h>
42#include <asm/page.h>
43#include <asm/setup.h>
Chris Zankelde4f6e52007-05-31 17:47:01 -070044#include <asm/param.h>
Max Filippov00273122012-11-28 11:33:02 +040045#include <asm/traps.h>
Chris Zankel5a0015d2005-06-23 22:01:16 -070046
Alexey Dobriyan5a891ed2009-03-10 12:55:49 -070047#include <platform/hardware.h>
48
Chris Zankel5a0015d2005-06-23 22:01:16 -070049#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
50struct screen_info screen_info = { 0, 24, 0, 0, 0, 80, 0, 0, 0, 24, 1, 16};
51#endif
52
53#ifdef CONFIG_BLK_DEV_FD
54extern struct fd_ops no_fd_ops;
55struct fd_ops *fd_ops;
56#endif
57
Chris Zankel5a0015d2005-06-23 22:01:16 -070058extern struct rtc_ops no_rtc_ops;
59struct rtc_ops *rtc_ops;
60
Chris Zankel5a0015d2005-06-23 22:01:16 -070061#ifdef CONFIG_BLK_DEV_INITRD
62extern void *initrd_start;
63extern void *initrd_end;
Chris Zankel5a0015d2005-06-23 22:01:16 -070064int initrd_is_mapped = 0;
65extern int initrd_below_start_ok;
66#endif
67
68unsigned char aux_device_present;
69extern unsigned long loops_per_jiffy;
70
71/* Command line specified as configuration option. */
72
Alon Bar-Levd3e9cce2007-02-12 00:54:25 -080073static char __initdata command_line[COMMAND_LINE_SIZE];
Chris Zankel5a0015d2005-06-23 22:01:16 -070074
75#ifdef CONFIG_CMDLINE_BOOL
76static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
77#endif
78
79sysmem_info_t __initdata sysmem;
80
Johannes Weinere5083a62009-03-04 16:21:31 +010081#ifdef CONFIG_MMU
Chris Zankel5a0015d2005-06-23 22:01:16 -070082extern void init_mmu(void);
Johannes Weinere5083a62009-03-04 16:21:31 +010083#else
84static inline void init_mmu(void) { }
85#endif
86
87extern void zones_init(void);
Chris Zankel5a0015d2005-06-23 22:01:16 -070088
89/*
90 * Boot parameter parsing.
91 *
92 * The Xtensa port uses a list of variable-sized tags to pass data to
93 * the kernel. The first tag must be a BP_TAG_FIRST tag for the list
94 * to be recognised. The list is terminated with a zero-sized
95 * BP_TAG_LAST tag.
96 */
97
98typedef struct tagtable {
99 u32 tag;
100 int (*parse)(const bp_tag_t*);
101} tagtable_t;
102
103#define __tagtable(tag, fn) static tagtable_t __tagtable_##fn \
Max Filippovf4349b62012-10-15 03:55:37 +0400104 __attribute__((used, section(".taglist"))) = { tag, fn }
Chris Zankel5a0015d2005-06-23 22:01:16 -0700105
106/* parse current tag */
107
108static int __init parse_tag_mem(const bp_tag_t *tag)
109{
110 meminfo_t *mi = (meminfo_t*)(tag->data);
111
112 if (mi->type != MEMORY_TYPE_CONVENTIONAL)
113 return -1;
114
115 if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) {
116 printk(KERN_WARNING
117 "Ignoring memory bank 0x%08lx size %ldKB\n",
118 (unsigned long)mi->start,
119 (unsigned long)mi->end - (unsigned long)mi->start);
120 return -EINVAL;
121 }
122 sysmem.bank[sysmem.nr_banks].type = mi->type;
123 sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(mi->start);
Max Filippovf4349b62012-10-15 03:55:37 +0400124 sysmem.bank[sysmem.nr_banks].end = mi->end & PAGE_MASK;
Chris Zankel5a0015d2005-06-23 22:01:16 -0700125 sysmem.nr_banks++;
126
127 return 0;
128}
129
130__tagtable(BP_TAG_MEMORY, parse_tag_mem);
131
132#ifdef CONFIG_BLK_DEV_INITRD
133
134static int __init parse_tag_initrd(const bp_tag_t* tag)
135{
136 meminfo_t* mi;
137 mi = (meminfo_t*)(tag->data);
138 initrd_start = (void*)(mi->start);
139 initrd_end = (void*)(mi->end);
140
141 return 0;
142}
143
144__tagtable(BP_TAG_INITRD, parse_tag_initrd);
145
146#endif /* CONFIG_BLK_DEV_INITRD */
147
148static int __init parse_tag_cmdline(const bp_tag_t* tag)
149{
150 strncpy(command_line, (char*)(tag->data), COMMAND_LINE_SIZE);
151 command_line[COMMAND_LINE_SIZE - 1] = '\0';
152 return 0;
153}
154
155__tagtable(BP_TAG_COMMAND_LINE, parse_tag_cmdline);
156
157static int __init parse_bootparam(const bp_tag_t* tag)
158{
159 extern tagtable_t __tagtable_begin, __tagtable_end;
160 tagtable_t *t;
161
162 /* Boot parameters must start with a BP_TAG_FIRST tag. */
163
164 if (tag->id != BP_TAG_FIRST) {
165 printk(KERN_WARNING "Invalid boot parameters!\n");
166 return 0;
167 }
168
169 tag = (bp_tag_t*)((unsigned long)tag + sizeof(bp_tag_t) + tag->size);
170
171 /* Parse all tags. */
172
173 while (tag != NULL && tag->id != BP_TAG_LAST) {
174 for (t = &__tagtable_begin; t < &__tagtable_end; t++) {
175 if (tag->id == t->tag) {
176 t->parse(tag);
177 break;
178 }
179 }
180 if (t == &__tagtable_end)
181 printk(KERN_WARNING "Ignoring tag "
182 "0x%08x\n", tag->id);
183 tag = (bp_tag_t*)((unsigned long)(tag + 1) + tag->size);
184 }
185
186 return 0;
187}
188
189/*
190 * Initialize architecture. (Early stage)
191 */
192
193void __init init_arch(bp_tag_t *bp_start)
194{
Chris Zankel5a0015d2005-06-23 22:01:16 -0700195 sysmem.nr_banks = 0;
196
197#ifdef CONFIG_CMDLINE_BOOL
198 strcpy(command_line, default_command_line);
199#endif
200
201 /* Parse boot parameters */
202
203 if (bp_start)
204 parse_bootparam(bp_start);
205
206 if (sysmem.nr_banks == 0) {
207 sysmem.nr_banks = 1;
208 sysmem.bank[0].start = PLATFORM_DEFAULT_MEM_START;
209 sysmem.bank[0].end = PLATFORM_DEFAULT_MEM_START
210 + PLATFORM_DEFAULT_MEM_SIZE;
211 }
212
213 /* Early hook for platforms */
214
215 platform_init(bp_start);
216
217 /* Initialize MMU. */
218
219 init_mmu();
220}
221
222/*
223 * Initialize system. Setup memory and reserve regions.
224 */
225
226extern char _end;
227extern char _stext;
228extern char _WindowVectors_text_start;
229extern char _WindowVectors_text_end;
230extern char _DebugInterruptVector_literal_start;
231extern char _DebugInterruptVector_text_end;
232extern char _KernelExceptionVector_literal_start;
233extern char _KernelExceptionVector_text_end;
234extern char _UserExceptionVector_literal_start;
235extern char _UserExceptionVector_text_end;
236extern char _DoubleExceptionVector_literal_start;
237extern char _DoubleExceptionVector_text_end;
238
Max Filippov00273122012-11-28 11:33:02 +0400239
240#ifdef CONFIG_S32C1I_SELFTEST
241#if XCHAL_HAVE_S32C1I
242
243static int __initdata rcw_word, rcw_probe_pc, rcw_exc;
244
245/*
246 * Basic atomic compare-and-swap, that records PC of S32C1I for probing.
247 *
248 * If *v == cmp, set *v = set. Return previous *v.
249 */
250static inline int probed_compare_swap(int *v, int cmp, int set)
251{
252 int tmp;
253
254 __asm__ __volatile__(
255 " movi %1, 1f\n"
256 " s32i %1, %4, 0\n"
257 " wsr %2, scompare1\n"
258 "1: s32c1i %0, %3, 0\n"
259 : "=a" (set), "=&a" (tmp)
260 : "a" (cmp), "a" (v), "a" (&rcw_probe_pc), "0" (set)
261 : "memory"
262 );
263 return set;
264}
265
266/* Handle probed exception */
267
268void __init do_probed_exception(struct pt_regs *regs, unsigned long exccause)
269{
270 if (regs->pc == rcw_probe_pc) { /* exception on s32c1i ? */
271 regs->pc += 3; /* skip the s32c1i instruction */
272 rcw_exc = exccause;
273 } else {
274 do_unhandled(regs, exccause);
275 }
276}
277
278/* Simple test of S32C1I (soc bringup assist) */
279
280void __init check_s32c1i(void)
281{
282 int n, cause1, cause2;
283 void *handbus, *handdata, *handaddr; /* temporarily saved handlers */
284
285 rcw_probe_pc = 0;
286 handbus = trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR,
287 do_probed_exception);
288 handdata = trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR,
289 do_probed_exception);
290 handaddr = trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR,
291 do_probed_exception);
292
293 /* First try an S32C1I that does not store: */
294 rcw_exc = 0;
295 rcw_word = 1;
296 n = probed_compare_swap(&rcw_word, 0, 2);
297 cause1 = rcw_exc;
298
299 /* took exception? */
300 if (cause1 != 0) {
301 /* unclean exception? */
302 if (n != 2 || rcw_word != 1)
303 panic("S32C1I exception error");
304 } else if (rcw_word != 1 || n != 1) {
305 panic("S32C1I compare error");
306 }
307
308 /* Then an S32C1I that stores: */
309 rcw_exc = 0;
310 rcw_word = 0x1234567;
311 n = probed_compare_swap(&rcw_word, 0x1234567, 0xabcde);
312 cause2 = rcw_exc;
313
314 if (cause2 != 0) {
315 /* unclean exception? */
316 if (n != 0xabcde || rcw_word != 0x1234567)
317 panic("S32C1I exception error (b)");
318 } else if (rcw_word != 0xabcde || n != 0x1234567) {
319 panic("S32C1I store error");
320 }
321
322 /* Verify consistency of exceptions: */
323 if (cause1 || cause2) {
324 pr_warn("S32C1I took exception %d, %d\n", cause1, cause2);
325 /* If emulation of S32C1I upon bus error gets implemented,
326 we can get rid of this panic for single core (not SMP) */
327 panic("S32C1I exceptions not currently supported");
328 }
329 if (cause1 != cause2)
330 panic("inconsistent S32C1I exceptions");
331
332 trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, handbus);
333 trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, handdata);
334 trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, handaddr);
335}
336
337#else /* XCHAL_HAVE_S32C1I */
338
339/* This condition should not occur with a commercially deployed processor.
340 Display reminder for early engr test or demo chips / FPGA bitstreams */
341void __init check_s32c1i(void)
342{
343 pr_warn("Processor configuration lacks atomic compare-and-swap support!\n");
344}
345
346#endif /* XCHAL_HAVE_S32C1I */
347#else /* CONFIG_S32C1I_SELFTEST */
348
349void __init check_s32c1i(void)
350{
351}
352
353#endif /* CONFIG_S32C1I_SELFTEST */
354
355
Chris Zankel5a0015d2005-06-23 22:01:16 -0700356void __init setup_arch(char **cmdline_p)
357{
358 extern int mem_reserve(unsigned long, unsigned long, int);
359 extern void bootmem_init(void);
360
Alon Bar-Levd3e9cce2007-02-12 00:54:25 -0800361 memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
362 boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
Chris Zankel5a0015d2005-06-23 22:01:16 -0700363 *cmdline_p = command_line;
364
Max Filippov00273122012-11-28 11:33:02 +0400365 check_s32c1i();
366
Chris Zankel5a0015d2005-06-23 22:01:16 -0700367 /* Reserve some memory regions */
368
369#ifdef CONFIG_BLK_DEV_INITRD
370 if (initrd_start < initrd_end) {
371 initrd_is_mapped = mem_reserve(__pa(initrd_start),
372 __pa(initrd_end), 0);
373 initrd_below_start_ok = 1;
374 } else {
375 initrd_start = 0;
376 }
377#endif
378
379 mem_reserve(__pa(&_stext),__pa(&_end), 1);
380
381 mem_reserve(__pa(&_WindowVectors_text_start),
382 __pa(&_WindowVectors_text_end), 0);
383
384 mem_reserve(__pa(&_DebugInterruptVector_literal_start),
385 __pa(&_DebugInterruptVector_text_end), 0);
386
387 mem_reserve(__pa(&_KernelExceptionVector_literal_start),
388 __pa(&_KernelExceptionVector_text_end), 0);
389
390 mem_reserve(__pa(&_UserExceptionVector_literal_start),
391 __pa(&_UserExceptionVector_text_end), 0);
392
393 mem_reserve(__pa(&_DoubleExceptionVector_literal_start),
394 __pa(&_DoubleExceptionVector_text_end), 0);
395
396 bootmem_init();
397
398 platform_setup(cmdline_p);
399
400
401 paging_init();
Johannes Weinere5083a62009-03-04 16:21:31 +0100402 zones_init();
Chris Zankel5a0015d2005-06-23 22:01:16 -0700403
404#ifdef CONFIG_VT
405# if defined(CONFIG_VGA_CONSOLE)
406 conswitchp = &vga_con;
407# elif defined(CONFIG_DUMMY_CONSOLE)
408 conswitchp = &dummy_con;
409# endif
410#endif
411
Chris Zankel288a60c2005-09-22 21:44:23 -0700412#ifdef CONFIG_PCI
Chris Zankel5a0015d2005-06-23 22:01:16 -0700413 platform_pcibios_init();
414#endif
415}
416
417void machine_restart(char * cmd)
418{
419 platform_restart();
420}
421
422void machine_halt(void)
423{
424 platform_halt();
425 while (1);
426}
427
428void machine_power_off(void)
429{
430 platform_power_off();
431 while (1);
432}
433#ifdef CONFIG_PROC_FS
434
435/*
436 * Display some core information through /proc/cpuinfo.
437 */
438
439static int
440c_show(struct seq_file *f, void *slot)
441{
442 /* high-level stuff */
443 seq_printf(f,"processor\t: 0\n"
444 "vendor_id\t: Tensilica\n"
Chris Zankel173d6682006-12-10 02:18:48 -0800445 "model\t\t: Xtensa " XCHAL_HW_VERSION_NAME "\n"
Chris Zankel5a0015d2005-06-23 22:01:16 -0700446 "core ID\t\t: " XCHAL_CORE_ID "\n"
447 "build ID\t: 0x%x\n"
448 "byte order\t: %s\n"
449 "cpu MHz\t\t: %lu.%02lu\n"
450 "bogomips\t: %lu.%02lu\n",
451 XCHAL_BUILD_UNIQUE_ID,
452 XCHAL_HAVE_BE ? "big" : "little",
453 CCOUNT_PER_JIFFY/(1000000/HZ),
454 (CCOUNT_PER_JIFFY/(10000/HZ)) % 100,
455 loops_per_jiffy/(500000/HZ),
456 (loops_per_jiffy/(5000/HZ)) % 100);
457
458 seq_printf(f,"flags\t\t: "
459#if XCHAL_HAVE_NMI
460 "nmi "
461#endif
462#if XCHAL_HAVE_DEBUG
463 "debug "
464# if XCHAL_HAVE_OCD
465 "ocd "
466# endif
467#endif
468#if XCHAL_HAVE_DENSITY
469 "density "
470#endif
471#if XCHAL_HAVE_BOOLEANS
472 "boolean "
473#endif
474#if XCHAL_HAVE_LOOPS
475 "loop "
476#endif
477#if XCHAL_HAVE_NSA
478 "nsa "
479#endif
480#if XCHAL_HAVE_MINMAX
481 "minmax "
482#endif
483#if XCHAL_HAVE_SEXT
484 "sext "
485#endif
486#if XCHAL_HAVE_CLAMPS
487 "clamps "
488#endif
489#if XCHAL_HAVE_MAC16
490 "mac16 "
491#endif
492#if XCHAL_HAVE_MUL16
493 "mul16 "
494#endif
495#if XCHAL_HAVE_MUL32
496 "mul32 "
497#endif
498#if XCHAL_HAVE_MUL32_HIGH
499 "mul32h "
500#endif
501#if XCHAL_HAVE_FP
502 "fpu "
503#endif
Max Filippov2f6ea6a2012-11-11 04:44:22 +0400504#if XCHAL_HAVE_S32C1I
505 "s32c1i "
506#endif
Chris Zankel5a0015d2005-06-23 22:01:16 -0700507 "\n");
508
509 /* Registers. */
510 seq_printf(f,"physical aregs\t: %d\n"
511 "misc regs\t: %d\n"
512 "ibreak\t\t: %d\n"
513 "dbreak\t\t: %d\n",
514 XCHAL_NUM_AREGS,
515 XCHAL_NUM_MISC_REGS,
516 XCHAL_NUM_IBREAK,
517 XCHAL_NUM_DBREAK);
518
519
520 /* Interrupt. */
521 seq_printf(f,"num ints\t: %d\n"
522 "ext ints\t: %d\n"
523 "int levels\t: %d\n"
524 "timers\t\t: %d\n"
525 "debug level\t: %d\n",
526 XCHAL_NUM_INTERRUPTS,
527 XCHAL_NUM_EXTINTERRUPTS,
528 XCHAL_NUM_INTLEVELS,
529 XCHAL_NUM_TIMERS,
530 XCHAL_DEBUGLEVEL);
531
Chris Zankel5a0015d2005-06-23 22:01:16 -0700532 /* Cache */
533 seq_printf(f,"icache line size: %d\n"
534 "icache ways\t: %d\n"
535 "icache size\t: %d\n"
536 "icache flags\t: "
537#if XCHAL_ICACHE_LINE_LOCKABLE
Max Filippov415217e2012-11-11 01:29:10 +0400538 "lock "
Chris Zankel5a0015d2005-06-23 22:01:16 -0700539#endif
540 "\n"
541 "dcache line size: %d\n"
542 "dcache ways\t: %d\n"
543 "dcache size\t: %d\n"
544 "dcache flags\t: "
545#if XCHAL_DCACHE_IS_WRITEBACK
Max Filippov415217e2012-11-11 01:29:10 +0400546 "writeback "
Chris Zankel5a0015d2005-06-23 22:01:16 -0700547#endif
548#if XCHAL_DCACHE_LINE_LOCKABLE
Max Filippov415217e2012-11-11 01:29:10 +0400549 "lock "
Chris Zankel5a0015d2005-06-23 22:01:16 -0700550#endif
551 "\n",
552 XCHAL_ICACHE_LINESIZE,
553 XCHAL_ICACHE_WAYS,
554 XCHAL_ICACHE_SIZE,
555 XCHAL_DCACHE_LINESIZE,
556 XCHAL_DCACHE_WAYS,
557 XCHAL_DCACHE_SIZE);
558
Chris Zankel5a0015d2005-06-23 22:01:16 -0700559 return 0;
560}
561
562/*
563 * We show only CPU #0 info.
564 */
565static void *
566c_start(struct seq_file *f, loff_t *pos)
567{
568 return (void *) ((*pos == 0) ? (void *)1 : NULL);
569}
570
571static void *
572c_next(struct seq_file *f, void *v, loff_t *pos)
573{
574 return NULL;
575}
576
577static void
578c_stop(struct seq_file *f, void *v)
579{
580}
581
Jan Engelhardt03a44822008-02-08 04:21:19 -0800582const struct seq_operations cpuinfo_op =
Chris Zankel5a0015d2005-06-23 22:01:16 -0700583{
584 start: c_start,
585 next: c_next,
586 stop: c_stop,
587 show: c_show
588};
589
590#endif /* CONFIG_PROC_FS */
591