]> git.karo-electronics.de Git - karo-tx-linux.git/blob - arch/sh/kernel/setup.c
sh: Split out CPU topology initialization.
[karo-tx-linux.git] / arch / sh / kernel / setup.c
1 /*
2  * arch/sh/kernel/setup.c
3  *
4  * This file handles the architecture-dependent parts of initialization
5  *
6  *  Copyright (C) 1999  Niibe Yutaka
7  *  Copyright (C) 2002 - 2007 Paul Mundt
8  */
9 #include <linux/screen_info.h>
10 #include <linux/ioport.h>
11 #include <linux/init.h>
12 #include <linux/initrd.h>
13 #include <linux/bootmem.h>
14 #include <linux/console.h>
15 #include <linux/seq_file.h>
16 #include <linux/root_dev.h>
17 #include <linux/utsname.h>
18 #include <linux/nodemask.h>
19 #include <linux/cpu.h>
20 #include <linux/pfn.h>
21 #include <linux/fs.h>
22 #include <linux/mm.h>
23 #include <linux/kexec.h>
24 #include <asm/uaccess.h>
25 #include <asm/io.h>
26 #include <asm/page.h>
27 #include <asm/sections.h>
28 #include <asm/irq.h>
29 #include <asm/setup.h>
30 #include <asm/clock.h>
31 #include <asm/mmu_context.h>
32
33 extern void * __rd_start, * __rd_end;
34
35 /*
36  * Machine setup..
37  */
38
39 /*
40  * Initialize loops_per_jiffy as 10000000 (1000MIPS).
41  * This value will be used at the very early stage of serial setup.
42  * The bigger value means no problem.
43  */
44 struct sh_cpuinfo boot_cpu_data = { CPU_SH_NONE, 10000000, };
45 #ifdef CONFIG_VT
46 struct screen_info screen_info;
47 #endif
48
49 #if defined(CONFIG_SH_UNKNOWN)
50 struct sh_machine_vector sh_mv;
51 #endif
52
53 extern int root_mountflags;
54
55 #define MV_NAME_SIZE 32
56
57 static struct sh_machine_vector* __init get_mv_byname(const char* name);
58
59 /*
60  * This is set up by the setup-routine at boot-time
61  */
62 #define PARAM   ((unsigned char *)empty_zero_page)
63
64 #define MOUNT_ROOT_RDONLY (*(unsigned long *) (PARAM+0x000))
65 #define RAMDISK_FLAGS (*(unsigned long *) (PARAM+0x004))
66 #define ORIG_ROOT_DEV (*(unsigned long *) (PARAM+0x008))
67 #define LOADER_TYPE (*(unsigned long *) (PARAM+0x00c))
68 #define INITRD_START (*(unsigned long *) (PARAM+0x010))
69 #define INITRD_SIZE (*(unsigned long *) (PARAM+0x014))
70 /* ... */
71 #define COMMAND_LINE ((char *) (PARAM+0x100))
72
73 #define RAMDISK_IMAGE_START_MASK        0x07FF
74 #define RAMDISK_PROMPT_FLAG             0x8000
75 #define RAMDISK_LOAD_FLAG               0x4000
76
77 static char __initdata command_line[COMMAND_LINE_SIZE] = { 0, };
78
79 static struct resource code_resource = { .name = "Kernel code", };
80 static struct resource data_resource = { .name = "Kernel data", };
81
82 unsigned long memory_start, memory_end;
83
84 static inline void parse_cmdline (char ** cmdline_p, char mv_name[MV_NAME_SIZE],
85                                   struct sh_machine_vector** mvp,
86                                   unsigned long *mv_io_base)
87 {
88         char c = ' ', *to = command_line, *from = COMMAND_LINE;
89         int len = 0;
90
91         /* Save unparsed command line copy for /proc/cmdline */
92         memcpy(boot_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
93         boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
94
95         memory_start = (unsigned long)PAGE_OFFSET+__MEMORY_START;
96         memory_end = memory_start + __MEMORY_SIZE;
97
98         for (;;) {
99                 /*
100                  * "mem=XXX[kKmM]" defines a size of memory.
101                  */
102                 if (c == ' ' && !memcmp(from, "mem=", 4)) {
103                         if (to != command_line)
104                                 to--;
105                         {
106                                 unsigned long mem_size;
107
108                                 mem_size = memparse(from+4, &from);
109                                 memory_end = memory_start + mem_size;
110                         }
111                 }
112
113                 if (c == ' ' && !memcmp(from, "sh_mv=", 6)) {
114                         char* mv_end;
115                         char* mv_comma;
116                         int mv_len;
117                         if (to != command_line)
118                                 to--;
119                         from += 6;
120                         mv_end = strchr(from, ' ');
121                         if (mv_end == NULL)
122                                 mv_end = from + strlen(from);
123
124                         mv_comma = strchr(from, ',');
125                         if ((mv_comma != NULL) && (mv_comma < mv_end)) {
126                                 int ints[3];
127                                 get_options(mv_comma+1, ARRAY_SIZE(ints), ints);
128                                 *mv_io_base = ints[1];
129                                 mv_len = mv_comma - from;
130                         } else {
131                                 mv_len = mv_end - from;
132                         }
133                         if (mv_len > (MV_NAME_SIZE-1))
134                                 mv_len = MV_NAME_SIZE-1;
135                         memcpy(mv_name, from, mv_len);
136                         mv_name[mv_len] = '\0';
137                         from = mv_end;
138
139                         *mvp = get_mv_byname(mv_name);
140                 }
141
142                 c = *(from++);
143                 if (!c)
144                         break;
145                 if (COMMAND_LINE_SIZE <= ++len)
146                         break;
147                 *(to++) = c;
148         }
149         *to = '\0';
150         *cmdline_p = command_line;
151 }
152
153 static int __init sh_mv_setup(char **cmdline_p)
154 {
155 #ifdef CONFIG_SH_UNKNOWN
156         extern struct sh_machine_vector mv_unknown;
157 #endif
158         struct sh_machine_vector *mv = NULL;
159         char mv_name[MV_NAME_SIZE] = "";
160         unsigned long mv_io_base = 0;
161
162         parse_cmdline(cmdline_p, mv_name, &mv, &mv_io_base);
163
164 #ifdef CONFIG_SH_UNKNOWN
165         if (mv == NULL) {
166                 mv = &mv_unknown;
167                 if (*mv_name != '\0') {
168                         printk("Warning: Unsupported machine %s, using unknown\n",
169                                mv_name);
170                 }
171         }
172         sh_mv = *mv;
173 #endif
174
175         /*
176          * Manually walk the vec, fill in anything that the board hasn't yet
177          * by hand, wrapping to the generic implementation.
178          */
179 #define mv_set(elem) do { \
180         if (!sh_mv.mv_##elem) \
181                 sh_mv.mv_##elem = generic_##elem; \
182 } while (0)
183
184         mv_set(inb);    mv_set(inw);    mv_set(inl);
185         mv_set(outb);   mv_set(outw);   mv_set(outl);
186
187         mv_set(inb_p);  mv_set(inw_p);  mv_set(inl_p);
188         mv_set(outb_p); mv_set(outw_p); mv_set(outl_p);
189
190         mv_set(insb);   mv_set(insw);   mv_set(insl);
191         mv_set(outsb);  mv_set(outsw);  mv_set(outsl);
192
193         mv_set(readb);  mv_set(readw);  mv_set(readl);
194         mv_set(writeb); mv_set(writew); mv_set(writel);
195
196         mv_set(ioport_map);
197         mv_set(ioport_unmap);
198         mv_set(irq_demux);
199
200 #ifdef CONFIG_SH_UNKNOWN
201         __set_io_port_base(mv_io_base);
202 #endif
203
204         if (!sh_mv.mv_nr_irqs)
205                 sh_mv.mv_nr_irqs = NR_IRQS;
206
207         return 0;
208 }
209
210 /*
211  * Register fully available low RAM pages with the bootmem allocator.
212  */
213 static void __init register_bootmem_low_pages(void)
214 {
215         unsigned long curr_pfn, last_pfn, pages;
216
217         /*
218          * We are rounding up the start address of usable memory:
219          */
220         curr_pfn = PFN_UP(__MEMORY_START);
221
222         /*
223          * ... and at the end of the usable range downwards:
224          */
225         last_pfn = PFN_DOWN(__pa(memory_end));
226
227         if (last_pfn > max_low_pfn)
228                 last_pfn = max_low_pfn;
229
230         pages = last_pfn - curr_pfn;
231         free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(pages));
232 }
233
234 void __init setup_bootmem_allocator(unsigned long start_pfn)
235 {
236         unsigned long bootmap_size;
237
238         /*
239          * Find a proper area for the bootmem bitmap. After this
240          * bootstrap step all allocations (until the page allocator
241          * is intact) must be done via bootmem_alloc().
242          */
243         bootmap_size = init_bootmem_node(NODE_DATA(0), start_pfn,
244                                          min_low_pfn, max_low_pfn);
245
246         register_bootmem_low_pages();
247
248         node_set_online(0);
249
250         /*
251          * Reserve the kernel text and
252          * Reserve the bootmem bitmap. We do this in two steps (first step
253          * was init_bootmem()), because this catches the (definitely buggy)
254          * case of us accidentally initializing the bootmem allocator with
255          * an invalid RAM area.
256          */
257         reserve_bootmem(__MEMORY_START+PAGE_SIZE,
258                 (PFN_PHYS(start_pfn)+bootmap_size+PAGE_SIZE-1)-__MEMORY_START);
259
260         /*
261          * reserve physical page 0 - it's a special BIOS page on many boxes,
262          * enabling clean reboots, SMP operation, laptop functions.
263          */
264         reserve_bootmem(__MEMORY_START, PAGE_SIZE);
265
266 #ifdef CONFIG_BLK_DEV_INITRD
267         ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
268         if (&__rd_start != &__rd_end) {
269                 LOADER_TYPE = 1;
270                 INITRD_START = PHYSADDR((unsigned long)&__rd_start) -
271                                         __MEMORY_START;
272                 INITRD_SIZE = (unsigned long)&__rd_end -
273                               (unsigned long)&__rd_start;
274         }
275
276         if (LOADER_TYPE && INITRD_START) {
277                 if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) {
278                         reserve_bootmem(INITRD_START + __MEMORY_START,
279                                         INITRD_SIZE);
280                         initrd_start = INITRD_START + PAGE_OFFSET +
281                                         __MEMORY_START;
282                         initrd_end = initrd_start + INITRD_SIZE;
283                 } else {
284                         printk("initrd extends beyond end of memory "
285                             "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
286                                     INITRD_START + INITRD_SIZE,
287                                     max_low_pfn << PAGE_SHIFT);
288                         initrd_start = 0;
289                 }
290         }
291 #endif
292 #ifdef CONFIG_KEXEC
293         if (crashk_res.start != crashk_res.end)
294                 reserve_bootmem(crashk_res.start,
295                         crashk_res.end - crashk_res.start + 1);
296 #endif
297 }
298
299 #ifndef CONFIG_NEED_MULTIPLE_NODES
300 static void __init setup_memory(void)
301 {
302         unsigned long start_pfn;
303
304         /*
305          * Partially used pages are not usable - thus
306          * we are rounding upwards:
307          */
308         start_pfn = PFN_UP(__pa(_end));
309         setup_bootmem_allocator(start_pfn);
310 }
311 #else
312 extern void __init setup_memory(void);
313 #endif
314
315 void __init setup_arch(char **cmdline_p)
316 {
317         enable_mmu();
318
319 #ifdef CONFIG_CMDLINE_BOOL
320         strcpy(COMMAND_LINE, CONFIG_CMDLINE);
321 #endif
322
323         ROOT_DEV = old_decode_dev(ORIG_ROOT_DEV);
324
325 #ifdef CONFIG_BLK_DEV_RAM
326         rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
327         rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
328         rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);
329 #endif
330
331         if (!MOUNT_ROOT_RDONLY)
332                 root_mountflags &= ~MS_RDONLY;
333         init_mm.start_code = (unsigned long) _text;
334         init_mm.end_code = (unsigned long) _etext;
335         init_mm.end_data = (unsigned long) _edata;
336         init_mm.brk = (unsigned long) _end;
337
338         code_resource.start = virt_to_phys(_text);
339         code_resource.end = virt_to_phys(_etext)-1;
340         data_resource.start = virt_to_phys(_etext);
341         data_resource.end = virt_to_phys(_edata)-1;
342
343         parse_early_param();
344
345         sh_mv_setup(cmdline_p);
346
347         /*
348          * Find the highest page frame number we have available
349          */
350         max_pfn = PFN_DOWN(__pa(memory_end));
351
352         /*
353          * Determine low and high memory ranges:
354          */
355         max_low_pfn = max_pfn;
356         min_low_pfn = __MEMORY_START >> PAGE_SHIFT;
357
358         nodes_clear(node_online_map);
359         setup_memory();
360         paging_init();
361         sparse_init();
362
363 #ifdef CONFIG_DUMMY_CONSOLE
364         conswitchp = &dummy_con;
365 #endif
366
367         /* Perform the machine specific initialisation */
368         if (likely(sh_mv.mv_setup))
369                 sh_mv.mv_setup(cmdline_p);
370 }
371
372 struct sh_machine_vector* __init get_mv_byname(const char* name)
373 {
374         extern long __machvec_start, __machvec_end;
375         struct sh_machine_vector *all_vecs =
376                 (struct sh_machine_vector *)&__machvec_start;
377
378         int i, n = ((unsigned long)&__machvec_end
379                     - (unsigned long)&__machvec_start)/
380                 sizeof(struct sh_machine_vector);
381
382         for (i = 0; i < n; ++i) {
383                 struct sh_machine_vector *mv = &all_vecs[i];
384                 if (mv == NULL)
385                         continue;
386                 if (strcasecmp(name, get_system_type()) == 0) {
387                         return mv;
388                 }
389         }
390         return NULL;
391 }
392
393 static const char *cpu_name[] = {
394         [CPU_SH7206]    = "SH7206",     [CPU_SH7619]    = "SH7619",
395         [CPU_SH7604]    = "SH7604",     [CPU_SH7300]    = "SH7300",
396         [CPU_SH7705]    = "SH7705",     [CPU_SH7706]    = "SH7706",
397         [CPU_SH7707]    = "SH7707",     [CPU_SH7708]    = "SH7708",
398         [CPU_SH7709]    = "SH7709",     [CPU_SH7710]    = "SH7710",
399         [CPU_SH7712]    = "SH7712",
400         [CPU_SH7729]    = "SH7729",     [CPU_SH7750]    = "SH7750",
401         [CPU_SH7750S]   = "SH7750S",    [CPU_SH7750R]   = "SH7750R",
402         [CPU_SH7751]    = "SH7751",     [CPU_SH7751R]   = "SH7751R",
403         [CPU_SH7760]    = "SH7760",     [CPU_SH73180]   = "SH73180",
404         [CPU_ST40RA]    = "ST40RA",     [CPU_ST40GX1]   = "ST40GX1",
405         [CPU_SH4_202]   = "SH4-202",    [CPU_SH4_501]   = "SH4-501",
406         [CPU_SH7770]    = "SH7770",     [CPU_SH7780]    = "SH7780",
407         [CPU_SH7781]    = "SH7781",     [CPU_SH7343]    = "SH7343",
408         [CPU_SH7785]    = "SH7785",     [CPU_SH7722]    = "SH7722",
409         [CPU_SH_NONE]   = "Unknown"
410 };
411
412 const char *get_cpu_subtype(struct sh_cpuinfo *c)
413 {
414         return cpu_name[c->type];
415 }
416
417 #ifdef CONFIG_PROC_FS
418 /* Symbolic CPU flags, keep in sync with asm/cpu-features.h */
419 static const char *cpu_flags[] = {
420         "none", "fpu", "p2flush", "mmuassoc", "dsp", "perfctr",
421         "ptea", "llsc", "l2", "op32", NULL
422 };
423
424 static void show_cpuflags(struct seq_file *m, struct sh_cpuinfo *c)
425 {
426         unsigned long i;
427
428         seq_printf(m, "cpu flags\t:");
429
430         if (!c->flags) {
431                 seq_printf(m, " %s\n", cpu_flags[0]);
432                 return;
433         }
434
435         for (i = 0; cpu_flags[i]; i++)
436                 if ((c->flags & (1 << i)))
437                         seq_printf(m, " %s", cpu_flags[i+1]);
438
439         seq_printf(m, "\n");
440 }
441
442 static void show_cacheinfo(struct seq_file *m, const char *type,
443                            struct cache_info info)
444 {
445         unsigned int cache_size;
446
447         cache_size = info.ways * info.sets * info.linesz;
448
449         seq_printf(m, "%s size\t: %2dKiB (%d-way)\n",
450                    type, cache_size >> 10, info.ways);
451 }
452
453 /*
454  *      Get CPU information for use by the procfs.
455  */
456 static int show_cpuinfo(struct seq_file *m, void *v)
457 {
458         struct sh_cpuinfo *c = v;
459         unsigned int cpu = c - cpu_data;
460
461         if (!cpu_online(cpu))
462                 return 0;
463
464         if (cpu == 0)
465                 seq_printf(m, "machine\t\t: %s\n", get_system_type());
466
467         seq_printf(m, "processor\t: %d\n", cpu);
468         seq_printf(m, "cpu family\t: %s\n", init_utsname()->machine);
469         seq_printf(m, "cpu type\t: %s\n", get_cpu_subtype(c));
470
471         show_cpuflags(m, c);
472
473         seq_printf(m, "cache type\t: ");
474
475         /*
476          * Check for what type of cache we have, we support both the
477          * unified cache on the SH-2 and SH-3, as well as the harvard
478          * style cache on the SH-4.
479          */
480         if (c->icache.flags & SH_CACHE_COMBINED) {
481                 seq_printf(m, "unified\n");
482                 show_cacheinfo(m, "cache", c->icache);
483         } else {
484                 seq_printf(m, "split (harvard)\n");
485                 show_cacheinfo(m, "icache", c->icache);
486                 show_cacheinfo(m, "dcache", c->dcache);
487         }
488
489         /* Optional secondary cache */
490         if (c->flags & CPU_HAS_L2_CACHE)
491                 show_cacheinfo(m, "scache", c->scache);
492
493         seq_printf(m, "bogomips\t: %lu.%02lu\n",
494                      c->loops_per_jiffy/(500000/HZ),
495                      (c->loops_per_jiffy/(5000/HZ)) % 100);
496
497         return 0;
498 }
499
500 static void *c_start(struct seq_file *m, loff_t *pos)
501 {
502         return *pos < NR_CPUS ? cpu_data + *pos : NULL;
503 }
504 static void *c_next(struct seq_file *m, void *v, loff_t *pos)
505 {
506         ++*pos;
507         return c_start(m, pos);
508 }
509 static void c_stop(struct seq_file *m, void *v)
510 {
511 }
512 struct seq_operations cpuinfo_op = {
513         .start  = c_start,
514         .next   = c_next,
515         .stop   = c_stop,
516         .show   = show_cpuinfo,
517 };
518 #endif /* CONFIG_PROC_FS */