http://downloads.netgear.com/files/GPL/DM111PSP_v3.61d_GPL.tar.gz
[bcm963xx.git] / kernel / linux / arch / s390 / kernel / setup.c
1 /*
2  *  arch/s390/kernel/setup.c
3  *
4  *  S390 version
5  *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
6  *    Author(s): Hartmut Penner (hp@de.ibm.com),
7  *               Martin Schwidefsky (schwidefsky@de.ibm.com)
8  *
9  *  Derived from "arch/i386/kernel/setup.c"
10  *    Copyright (C) 1995, Linus Torvalds
11  */
12
13 /*
14  * This file handles the architecture-dependent parts of initialization
15  */
16
17 #include <linux/errno.h>
18 #include <linux/module.h>
19 #include <linux/sched.h>
20 #include <linux/kernel.h>
21 #include <linux/mm.h>
22 #include <linux/stddef.h>
23 #include <linux/unistd.h>
24 #include <linux/ptrace.h>
25 #include <linux/slab.h>
26 #include <linux/user.h>
27 #include <linux/a.out.h>
28 #include <linux/tty.h>
29 #include <linux/ioport.h>
30 #include <linux/delay.h>
31 #include <linux/config.h>
32 #include <linux/init.h>
33 #include <linux/initrd.h>
34 #include <linux/bootmem.h>
35 #include <linux/root_dev.h>
36 #include <linux/console.h>
37 #include <linux/seq_file.h>
38 #include <linux/kernel_stat.h>
39
40 #include <asm/uaccess.h>
41 #include <asm/system.h>
42 #include <asm/smp.h>
43 #include <asm/mmu_context.h>
44 #include <asm/cpcmd.h>
45 #include <asm/lowcore.h>
46 #include <asm/irq.h>
47
48 /*
49  * Machine setup..
50  */
51 unsigned int console_mode = 0;
52 unsigned int console_devno = -1;
53 unsigned int console_irq = -1;
54 unsigned long memory_size = 0;
55 unsigned long machine_flags = 0;
56 struct {
57         unsigned long addr, size, type;
58 } memory_chunk[MEMORY_CHUNKS] = { { 0 } };
59 #define CHUNK_READ_WRITE 0
60 #define CHUNK_READ_ONLY 1
61 volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */
62
63 /*
64  * Setup options
65  */
66 extern int _text,_etext, _edata, _end;
67
68 /*
69  * This is set up by the setup-routine at boot-time
70  * for S390 need to find out, what we have to setup
71  * using address 0x10400 ...
72  */
73
74 #include <asm/setup.h>
75
76 static char command_line[COMMAND_LINE_SIZE] = { 0, };
77
78 static struct resource code_resource = { "Kernel code", 0x100000, 0 };
79 static struct resource data_resource = { "Kernel data", 0, 0 };
80
81 /*
82  * cpu_init() initializes state that is per-CPU.
83  */
84 void __devinit cpu_init (void)
85 {
86         int addr = hard_smp_processor_id();
87
88         /*
89          * Store processor id in lowcore (used e.g. in timer_interrupt)
90          */
91         asm volatile ("stidp %0": "=m" (S390_lowcore.cpu_data.cpu_id));
92         S390_lowcore.cpu_data.cpu_addr = addr;
93
94         /*
95          * Force FPU initialization:
96          */
97         clear_thread_flag(TIF_USEDFPU);
98         current->used_math = 0;
99
100         /* Setup active_mm for idle_task  */
101         atomic_inc(&init_mm.mm_count);
102         current->active_mm = &init_mm;
103         if (current->mm)
104                 BUG();
105         enter_lazy_tlb(&init_mm, current);
106 }
107
108 /*
109  * VM halt and poweroff setup routines
110  */
111 char vmhalt_cmd[128] = "";
112 char vmpoff_cmd[128] = "";
113
114 static inline void strncpy_skip_quote(char *dst, char *src, int n)
115 {
116         int sx, dx;
117
118         dx = 0;
119         for (sx = 0; src[sx] != 0; sx++) {
120                 if (src[sx] == '"') continue;
121                 dst[dx++] = src[sx];
122                 if (dx >= n) break;
123         }
124 }
125
126 static int __init vmhalt_setup(char *str)
127 {
128         strncpy_skip_quote(vmhalt_cmd, str, 127);
129         vmhalt_cmd[127] = 0;
130         return 1;
131 }
132
133 __setup("vmhalt=", vmhalt_setup);
134
135 static int __init vmpoff_setup(char *str)
136 {
137         strncpy_skip_quote(vmpoff_cmd, str, 127);
138         vmpoff_cmd[127] = 0;
139         return 1;
140 }
141
142 __setup("vmpoff=", vmpoff_setup);
143
144 /*
145  * condev= and conmode= setup parameter.
146  */
147
148 static int __init condev_setup(char *str)
149 {
150         int vdev;
151
152         vdev = simple_strtoul(str, &str, 0);
153         if (vdev >= 0 && vdev < 65536) {
154                 console_devno = vdev;
155                 console_irq = -1;
156         }
157         return 1;
158 }
159
160 __setup("condev=", condev_setup);
161
162 static int __init conmode_setup(char *str)
163 {
164 #if defined(CONFIG_SCLP_CONSOLE)
165         if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0)
166                 SET_CONSOLE_SCLP;
167 #endif
168 #if defined(CONFIG_TN3215_CONSOLE)
169         if (strncmp(str, "3215", 5) == 0)
170                 SET_CONSOLE_3215;
171 #endif
172 #if defined(CONFIG_TN3270_CONSOLE)
173         if (strncmp(str, "3270", 5) == 0)
174                 SET_CONSOLE_3270;
175 #endif
176         return 1;
177 }
178
179 __setup("conmode=", conmode_setup);
180
181 static void __init conmode_default(void)
182 {
183         char query_buffer[1024];
184         char *ptr;
185
186         if (MACHINE_IS_VM) {
187                 cpcmd("QUERY CONSOLE", query_buffer, 1024);
188                 console_devno = simple_strtoul(query_buffer + 5, NULL, 16);
189                 ptr = strstr(query_buffer, "SUBCHANNEL =");
190                 console_irq = simple_strtoul(ptr + 13, NULL, 16);
191                 cpcmd("QUERY TERM", query_buffer, 1024);
192                 ptr = strstr(query_buffer, "CONMODE");
193                 /*
194                  * Set the conmode to 3215 so that the device recognition 
195                  * will set the cu_type of the console to 3215. If the
196                  * conmode is 3270 and we don't set it back then both
197                  * 3215 and the 3270 driver will try to access the console
198                  * device (3215 as console and 3270 as normal tty).
199                  */
200                 cpcmd("TERM CONMODE 3215", NULL, 0);
201                 if (ptr == NULL) {
202 #if defined(CONFIG_SCLP_CONSOLE)
203                         SET_CONSOLE_SCLP;
204 #endif
205                         return;
206                 }
207                 if (strncmp(ptr + 8, "3270", 4) == 0) {
208 #if defined(CONFIG_TN3270_CONSOLE)
209                         SET_CONSOLE_3270;
210 #elif defined(CONFIG_TN3215_CONSOLE)
211                         SET_CONSOLE_3215;
212 #elif defined(CONFIG_SCLP_CONSOLE)
213                         SET_CONSOLE_SCLP;
214 #endif
215                 } else if (strncmp(ptr + 8, "3215", 4) == 0) {
216 #if defined(CONFIG_TN3215_CONSOLE)
217                         SET_CONSOLE_3215;
218 #elif defined(CONFIG_TN3270_CONSOLE)
219                         SET_CONSOLE_3270;
220 #elif defined(CONFIG_SCLP_CONSOLE)
221                         SET_CONSOLE_SCLP;
222 #endif
223                 }
224         } else if (MACHINE_IS_P390) {
225 #if defined(CONFIG_TN3215_CONSOLE)
226                 SET_CONSOLE_3215;
227 #elif defined(CONFIG_TN3270_CONSOLE)
228                 SET_CONSOLE_3270;
229 #endif
230         } else {
231 #if defined(CONFIG_SCLP_CONSOLE)
232                 SET_CONSOLE_SCLP;
233 #endif
234         }
235 }
236
237 #ifdef CONFIG_SMP
238 extern void machine_restart_smp(char *);
239 extern void machine_halt_smp(void);
240 extern void machine_power_off_smp(void);
241
242 void (*_machine_restart)(char *command) = machine_restart_smp;
243 void (*_machine_halt)(void) = machine_halt_smp;
244 void (*_machine_power_off)(void) = machine_power_off_smp;
245 #else
246 /*
247  * Reboot, halt and power_off routines for non SMP.
248  */
249 extern void reipl(unsigned long devno);
250 static void do_machine_restart_nonsmp(char * __unused)
251 {
252         if (MACHINE_IS_VM)
253                 cpcmd ("IPL", NULL, 0);
254         else
255                 reipl (0x10000 | S390_lowcore.ipl_device);
256 }
257
258 static void do_machine_halt_nonsmp(void)
259 {
260         if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
261                 cpcmd(vmhalt_cmd, NULL, 0);
262         signal_processor(smp_processor_id(), sigp_stop_and_store_status);
263 }
264
265 static void do_machine_power_off_nonsmp(void)
266 {
267         if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
268                 cpcmd(vmpoff_cmd, NULL, 0);
269         signal_processor(smp_processor_id(), sigp_stop_and_store_status);
270 }
271
272 void (*_machine_restart)(char *command) = do_machine_restart_nonsmp;
273 void (*_machine_halt)(void) = do_machine_halt_nonsmp;
274 void (*_machine_power_off)(void) = do_machine_power_off_nonsmp;
275 #endif
276
277  /*
278  * Reboot, halt and power_off stubs. They just call _machine_restart,
279  * _machine_halt or _machine_power_off. 
280  */
281
282 void machine_restart(char *command)
283 {
284         console_unblank();
285         _machine_restart(command);
286 }
287
288 EXPORT_SYMBOL(machine_restart);
289
290 void machine_halt(void)
291 {
292         console_unblank();
293         _machine_halt();
294 }
295
296 EXPORT_SYMBOL(machine_halt);
297
298 void machine_power_off(void)
299 {
300         console_unblank();
301         _machine_power_off();
302 }
303
304 EXPORT_SYMBOL(machine_power_off);
305
306 /*
307  * Setup function called from init/main.c just after the banner
308  * was printed.
309  */
310 extern char _pstart, _pend, _stext;
311
312 void __init setup_arch(char **cmdline_p)
313 {
314         unsigned long bootmap_size;
315         unsigned long memory_start, memory_end;
316         char c = ' ', cn, *to = command_line, *from = COMMAND_LINE;
317         struct resource *res;
318         unsigned long start_pfn, end_pfn;
319         static unsigned int smptrap=0;
320         unsigned long delay = 0;
321         struct _lowcore *lc;
322         int i;
323
324         if (smptrap)
325                 return;
326         smptrap=1;
327
328         /*
329          * print what head.S has found out about the machine 
330          */
331 #ifndef CONFIG_ARCH_S390X
332         printk((MACHINE_IS_VM) ?
333                "We are running under VM (31 bit mode)\n" :
334                "We are running native (31 bit mode)\n");
335         printk((MACHINE_HAS_IEEE) ?
336                "This machine has an IEEE fpu\n" :
337                "This machine has no IEEE fpu\n");
338 #else /* CONFIG_ARCH_S390X */
339         printk((MACHINE_IS_VM) ?
340                "We are running under VM (64 bit mode)\n" :
341                "We are running native (64 bit mode)\n");
342 #endif /* CONFIG_ARCH_S390X */
343
344         ROOT_DEV = Root_RAM0;
345         memory_start = (unsigned long) &_end;    /* fixit if use $CODELO etc*/
346 #ifndef CONFIG_ARCH_S390X
347         memory_end = memory_size & ~0x400000UL;  /* align memory end to 4MB */
348         /*
349          * We need some free virtual space to be able to do vmalloc.
350          * On a machine with 2GB memory we make sure that we have at
351          * least 128 MB free space for vmalloc.
352          */
353         if (memory_end > 1920*1024*1024)
354                 memory_end = 1920*1024*1024;
355 #else /* CONFIG_ARCH_S390X */
356         memory_end = memory_size & ~0x200000UL;  /* detected in head.s */
357 #endif /* CONFIG_ARCH_S390X */
358         init_mm.start_code = PAGE_OFFSET;
359         init_mm.end_code = (unsigned long) &_etext;
360         init_mm.end_data = (unsigned long) &_edata;
361         init_mm.brk = (unsigned long) &_end;
362
363         code_resource.start = (unsigned long) &_text;
364         code_resource.end = (unsigned long) &_etext - 1;
365         data_resource.start = (unsigned long) &_etext;
366         data_resource.end = (unsigned long) &_edata - 1;
367
368         /* Save unparsed command line copy for /proc/cmdline */
369         memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
370         saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
371
372         for (;;) {
373                 /*
374                  * "mem=XXX[kKmM]" sets memsize 
375                  */
376                 if (c == ' ' && strncmp(from, "mem=", 4) == 0) {
377                         memory_end = simple_strtoul(from+4, &from, 0);
378                         if ( *from == 'K' || *from == 'k' ) {
379                                 memory_end = memory_end << 10;
380                                 from++;
381                         } else if ( *from == 'M' || *from == 'm' ) {
382                                 memory_end = memory_end << 20;
383                                 from++;
384                         }
385                 }
386                 /*
387                  * "ipldelay=XXX[sm]" sets ipl delay in seconds or minutes
388                  */
389                 if (c == ' ' && strncmp(from, "ipldelay=", 9) == 0) {
390                         delay = simple_strtoul(from+9, &from, 0);
391                         if (*from == 's' || *from == 'S') {
392                                 delay = delay*1000000;
393                                 from++;
394                         } else if (*from == 'm' || *from == 'M') {
395                                 delay = delay*60*1000000;
396                                 from++;
397                         }
398                         /* now wait for the requested amount of time */
399                         udelay(delay);
400                 }
401                 cn = *(from++);
402                 if (!cn)
403                         break;
404                 if (cn == '\n')
405                         cn = ' ';  /* replace newlines with space */
406                 if (cn == 0x0d)
407                         cn = ' ';  /* replace 0x0d with space */
408                 if (cn == ' ' && c == ' ')
409                         continue;  /* remove additional spaces */
410                 c = cn;
411                 if (to - command_line >= COMMAND_LINE_SIZE)
412                         break;
413                 *(to++) = c;
414         }
415         if (c == ' ' && to > command_line) to--;
416         *to = '\0';
417         *cmdline_p = command_line;
418
419         /*
420          * partially used pages are not usable - thus
421          * we are rounding upwards:
422          */
423         start_pfn = (__pa(&_end) + PAGE_SIZE - 1) >> PAGE_SHIFT;
424         end_pfn = max_pfn = memory_end >> PAGE_SHIFT;
425
426         /*
427          * Initialize the boot-time allocator (with low memory only):
428          */
429         bootmap_size = init_bootmem(start_pfn, end_pfn);
430
431         /*
432          * Register RAM areas with the bootmem allocator.
433          */
434         for (i = 0; i < 16 && memory_chunk[i].size > 0; i++) {
435                 unsigned long start_chunk, end_chunk;
436
437                 if (memory_chunk[i].type != CHUNK_READ_WRITE)
438                         continue;
439                 start_chunk = (memory_chunk[i].addr + PAGE_SIZE - 1);
440                 start_chunk >>= PAGE_SHIFT;
441                 end_chunk = (memory_chunk[i].addr + memory_chunk[i].size);
442                 end_chunk >>= PAGE_SHIFT;
443                 if (start_chunk < start_pfn)
444                         start_chunk = start_pfn;
445                 if (end_chunk > end_pfn)
446                         end_chunk = end_pfn;
447                 if (start_chunk < end_chunk)
448                         free_bootmem(start_chunk << PAGE_SHIFT,
449                                      (end_chunk - start_chunk) << PAGE_SHIFT);
450         }
451
452         /*
453          * Reserve the bootmem bitmap itself as well. We do this in two
454          * steps (first step was init_bootmem()) because this catches
455          * the (very unlikely) case of us accidentally initializing the
456          * bootmem allocator with an invalid RAM area.
457          */
458         reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size);
459
460 #ifdef CONFIG_BLK_DEV_INITRD
461         if (INITRD_START) {
462                 if (INITRD_START + INITRD_SIZE <= memory_end) {
463                         reserve_bootmem(INITRD_START, INITRD_SIZE);
464                         initrd_start = INITRD_START;
465                         initrd_end = initrd_start + INITRD_SIZE;
466                 } else {
467                         printk("initrd extends beyond end of memory "
468                                "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
469                                initrd_start + INITRD_SIZE, memory_end);
470                         initrd_start = initrd_end = 0;
471                 }
472         }
473 #endif
474
475         /*
476          * Setup lowcore for boot cpu
477          */
478 #ifndef CONFIG_ARCH_S390X
479         lc = (struct _lowcore *) __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0);
480         memset(lc, 0, PAGE_SIZE);
481 #else /* CONFIG_ARCH_S390X */
482         lc = (struct _lowcore *) __alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0);
483         memset(lc, 0, 2*PAGE_SIZE);
484 #endif /* CONFIG_ARCH_S390X */
485         lc->restart_psw.mask = PSW_BASE_BITS;
486         lc->restart_psw.addr =
487                 PSW_ADDR_AMODE | (unsigned long) restart_int_handler;
488         lc->external_new_psw.mask = PSW_KERNEL_BITS;
489         lc->external_new_psw.addr =
490                 PSW_ADDR_AMODE | (unsigned long) ext_int_handler;
491         lc->svc_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_IO | PSW_MASK_EXT;
492         lc->svc_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) system_call;
493         lc->program_new_psw.mask = PSW_KERNEL_BITS;
494         lc->program_new_psw.addr =
495                 PSW_ADDR_AMODE | (unsigned long)pgm_check_handler;
496         lc->mcck_new_psw.mask = PSW_KERNEL_BITS;
497         lc->mcck_new_psw.addr =
498                 PSW_ADDR_AMODE | (unsigned long) mcck_int_handler;
499         lc->io_new_psw.mask = PSW_KERNEL_BITS;
500         lc->io_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) io_int_handler;
501         lc->ipl_device = S390_lowcore.ipl_device;
502         lc->jiffy_timer = -1LL;
503         lc->kernel_stack = ((unsigned long) &init_thread_union) + THREAD_SIZE;
504         lc->async_stack = (unsigned long)
505                 __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0) + ASYNC_SIZE;
506         lc->current_task = (unsigned long) init_thread_union.thread_info.task;
507         lc->thread_info = (unsigned long) &init_thread_union;
508 #ifdef CONFIG_ARCH_S390X
509         if (MACHINE_HAS_DIAG44)
510                 lc->diag44_opcode = 0x83000044;
511         else
512                 lc->diag44_opcode = 0x07000700;
513 #endif /* CONFIG_ARCH_S390X */
514         set_prefix((u32)(unsigned long) lc);
515         cpu_init();
516         __cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr;
517
518         /*
519          * Create kernel page tables and switch to virtual addressing.
520          */
521         paging_init();
522
523         res = alloc_bootmem_low(sizeof(struct resource));
524         res->start = 0;
525         res->end = memory_end;
526         res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
527         request_resource(&iomem_resource, res);
528         request_resource(res, &code_resource);
529         request_resource(res, &data_resource);
530
531         /* Setup default console */
532         conmode_default();
533 }
534
535 void print_cpu_info(struct cpuinfo_S390 *cpuinfo)
536 {
537    printk("cpu %d "
538 #ifdef CONFIG_SMP
539            "phys_idx=%d "
540 #endif
541            "vers=%02X ident=%06X machine=%04X unused=%04X\n",
542            cpuinfo->cpu_nr,
543 #ifdef CONFIG_SMP
544            cpuinfo->cpu_addr,
545 #endif
546            cpuinfo->cpu_id.version,
547            cpuinfo->cpu_id.ident,
548            cpuinfo->cpu_id.machine,
549            cpuinfo->cpu_id.unused);
550 }
551
552 /*
553  * show_cpuinfo - Get information on one CPU for use by procfs.
554  */
555
556 static int show_cpuinfo(struct seq_file *m, void *v)
557 {
558         struct cpuinfo_S390 *cpuinfo;
559         unsigned long n = (unsigned long) v - 1;
560
561         if (!n) {
562                 seq_printf(m, "vendor_id       : IBM/S390\n"
563                                "# processors    : %i\n"
564                                "bogomips per cpu: %lu.%02lu\n",
565                                num_online_cpus(), loops_per_jiffy/(500000/HZ),
566                                (loops_per_jiffy/(5000/HZ))%100);
567         }
568         if (cpu_online(n)) {
569 #ifdef CONFIG_SMP
570                 if (smp_processor_id() == n)
571                         cpuinfo = &S390_lowcore.cpu_data;
572                 else
573                         cpuinfo = &lowcore_ptr[n]->cpu_data;
574 #else
575                 cpuinfo = &S390_lowcore.cpu_data;
576 #endif
577                 seq_printf(m, "processor %li: "
578                                "version = %02X,  "
579                                "identification = %06X,  "
580                                "machine = %04X\n",
581                                n, cpuinfo->cpu_id.version,
582                                cpuinfo->cpu_id.ident,
583                                cpuinfo->cpu_id.machine);
584         }
585         return 0;
586 }
587
588 static void *c_start(struct seq_file *m, loff_t *pos)
589 {
590         return *pos < NR_CPUS ? (void *)((unsigned long) *pos + 1) : NULL;
591 }
592 static void *c_next(struct seq_file *m, void *v, loff_t *pos)
593 {
594         ++*pos;
595         return c_start(m, pos);
596 }
597 static void c_stop(struct seq_file *m, void *v)
598 {
599 }
600 struct seq_operations cpuinfo_op = {
601         .start  = c_start,
602         .next   = c_next,
603         .stop   = c_stop,
604         .show   = show_cpuinfo,
605 };
606
607 /*
608  * show_interrupts is needed by /proc/interrupts.
609  */
610
611 static const char *intrclass_names[] = {
612         "EXT",
613         "I/O",
614 };
615
616 int show_interrupts(struct seq_file *p, void *v)
617 {
618         int i = *(loff_t *) v, j;
619         
620         if (i == 0) {
621                 seq_puts(p, "           ");
622                 for (j=0; j<NR_CPUS; j++)
623                         if (cpu_online(j))
624                                 seq_printf(p, "CPU%d       ",j);
625                 seq_putc(p, '\n');
626         }
627         
628         if (i < NR_IRQS) {
629                 seq_printf(p, "%s: ", intrclass_names[i]);
630 #ifndef CONFIG_SMP
631                 seq_printf(p, "%10u ", kstat_irqs(i));
632 #else
633                 for (j = 0; j < NR_CPUS; j++)
634                         if (cpu_online(j))
635                                 seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
636 #endif
637                 seq_putc(p, '\n');
638                 
639         }
640         
641         return 0;
642 }
643
644 /*
645  * For compatibilty only. S/390 specific setup of interrupts et al. is done
646  * much later in init_channel_subsystem().
647  */
648 void __init
649 init_IRQ(void)
650 {
651         /* nothing... */
652 }