Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * arch/ppc/platforms/setup.c | |
3 | * | |
4 | * PowerPC version | |
5 | * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) | |
6 | * | |
7 | * Adapted for Power Macintosh by Paul Mackerras | |
8 | * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) | |
9 | * | |
10 | * Derived from "arch/alpha/kernel/setup.c" | |
11 | * Copyright (C) 1995 Linus Torvalds | |
12 | * | |
13 | * Maintained by Benjamin Herrenschmidt (benh@kernel.crashing.org) | |
14 | * | |
15 | * This program is free software; you can redistribute it and/or | |
16 | * modify it under the terms of the GNU General Public License | |
17 | * as published by the Free Software Foundation; either version | |
18 | * 2 of the License, or (at your option) any later version. | |
19 | * | |
20 | */ | |
21 | ||
22 | /* | |
23 | * bootup setup stuff.. | |
24 | */ | |
25 | ||
26 | #undef DEBUG | |
27 | ||
28 | #include <linux/config.h> | |
29 | #include <linux/init.h> | |
30 | #include <linux/errno.h> | |
31 | #include <linux/sched.h> | |
32 | #include <linux/kernel.h> | |
33 | #include <linux/mm.h> | |
34 | #include <linux/stddef.h> | |
35 | #include <linux/unistd.h> | |
36 | #include <linux/ptrace.h> | |
37 | #include <linux/slab.h> | |
38 | #include <linux/user.h> | |
39 | #include <linux/a.out.h> | |
40 | #include <linux/tty.h> | |
41 | #include <linux/string.h> | |
42 | #include <linux/delay.h> | |
43 | #include <linux/ioport.h> | |
44 | #include <linux/major.h> | |
45 | #include <linux/initrd.h> | |
46 | #include <linux/vt_kern.h> | |
47 | #include <linux/console.h> | |
48 | #include <linux/ide.h> | |
49 | #include <linux/pci.h> | |
50 | #include <linux/adb.h> | |
51 | #include <linux/cuda.h> | |
52 | #include <linux/pmu.h> | |
53 | #include <linux/irq.h> | |
54 | #include <linux/seq_file.h> | |
55 | #include <linux/root_dev.h> | |
56 | #include <linux/bitops.h> | |
57 | ||
58 | #include <asm/processor.h> | |
59 | #include <asm/sections.h> | |
60 | #include <asm/prom.h> | |
61 | #include <asm/system.h> | |
62 | #include <asm/io.h> | |
63 | #include <asm/pci-bridge.h> | |
64 | #include <asm/iommu.h> | |
65 | #include <asm/machdep.h> | |
66 | #include <asm/dma.h> | |
67 | #include <asm/btext.h> | |
68 | #include <asm/cputable.h> | |
69 | #include <asm/pmac_feature.h> | |
70 | #include <asm/time.h> | |
71 | #include <asm/of_device.h> | |
72 | #include <asm/lmb.h> | |
73 | #include <asm/smu.h> | |
180a3362 | 74 | #include <asm/pmc.h> |
bbeb3f4c | 75 | #include <asm/mpic.h> |
40ef8cbc | 76 | #include <asm/udbg.h> |
1da177e4 LT |
77 | |
78 | #include "pmac.h" | |
1da177e4 LT |
79 | |
80 | #ifdef DEBUG | |
81 | #define DBG(fmt...) udbg_printf(fmt) | |
82 | #else | |
83 | #define DBG(fmt...) | |
84 | #endif | |
85 | ||
86 | static int current_root_goodness = -1; | |
87 | #define DEFAULT_ROOT_DEVICE Root_SDA1 /* sda1 - slightly silly choice */ | |
88 | ||
89 | extern int powersave_nap; | |
90 | int sccdbg; | |
91 | ||
92 | sys_ctrler_t sys_ctrler; | |
93 | EXPORT_SYMBOL(sys_ctrler); | |
94 | ||
95 | #ifdef CONFIG_PMAC_SMU | |
96 | unsigned long smu_cmdbuf_abs; | |
97 | EXPORT_SYMBOL(smu_cmdbuf_abs); | |
98 | #endif | |
99 | ||
100 | extern void udbg_init_scc(struct device_node *np); | |
101 | ||
aacaf9bd | 102 | static void pmac_show_cpuinfo(struct seq_file *m) |
1da177e4 LT |
103 | { |
104 | struct device_node *np; | |
105 | char *pp; | |
106 | int plen; | |
107 | char* mbname; | |
108 | int mbmodel = pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, | |
109 | PMAC_MB_INFO_MODEL, 0); | |
110 | unsigned int mbflags = pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, | |
111 | PMAC_MB_INFO_FLAGS, 0); | |
112 | ||
113 | if (pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, PMAC_MB_INFO_NAME, | |
114 | (long)&mbname) != 0) | |
115 | mbname = "Unknown"; | |
116 | ||
117 | /* find motherboard type */ | |
118 | seq_printf(m, "machine\t\t: "); | |
119 | np = find_devices("device-tree"); | |
120 | if (np != NULL) { | |
121 | pp = (char *) get_property(np, "model", NULL); | |
122 | if (pp != NULL) | |
123 | seq_printf(m, "%s\n", pp); | |
124 | else | |
125 | seq_printf(m, "PowerMac\n"); | |
126 | pp = (char *) get_property(np, "compatible", &plen); | |
127 | if (pp != NULL) { | |
128 | seq_printf(m, "motherboard\t:"); | |
129 | while (plen > 0) { | |
130 | int l = strlen(pp) + 1; | |
131 | seq_printf(m, " %s", pp); | |
132 | plen -= l; | |
133 | pp += l; | |
134 | } | |
135 | seq_printf(m, "\n"); | |
136 | } | |
137 | } else | |
138 | seq_printf(m, "PowerMac\n"); | |
139 | ||
140 | /* print parsed model */ | |
141 | seq_printf(m, "detected as\t: %d (%s)\n", mbmodel, mbname); | |
142 | seq_printf(m, "pmac flags\t: %08x\n", mbflags); | |
143 | ||
144 | /* Indicate newworld */ | |
145 | seq_printf(m, "pmac-generation\t: NewWorld\n"); | |
146 | } | |
147 | ||
148 | ||
6fdfb382 | 149 | static void __init pmac_setup_arch(void) |
1da177e4 LT |
150 | { |
151 | /* init to some ~sane value until calibrate_delay() runs */ | |
152 | loops_per_jiffy = 50000000; | |
153 | ||
154 | /* Probe motherboard chipset */ | |
155 | pmac_feature_init(); | |
156 | #if 0 | |
157 | /* Lock-enable the SCC channel used for debug */ | |
158 | if (sccdbg) { | |
159 | np = of_find_node_by_name(NULL, "escc"); | |
160 | if (np) | |
161 | pmac_call_feature(PMAC_FTR_SCC_ENABLE, np, | |
162 | PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1); | |
163 | } | |
164 | #endif | |
165 | /* We can NAP */ | |
166 | powersave_nap = 1; | |
167 | ||
168 | #ifdef CONFIG_ADB_PMU | |
169 | /* Initialize the PMU if any */ | |
170 | find_via_pmu(); | |
171 | #endif | |
172 | #ifdef CONFIG_PMAC_SMU | |
173 | /* Initialize the SMU if any */ | |
174 | smu_init(); | |
175 | #endif | |
176 | ||
177 | /* Init NVRAM access */ | |
178 | pmac_nvram_init(); | |
179 | ||
180 | /* Setup SMP callback */ | |
181 | #ifdef CONFIG_SMP | |
182 | pmac_setup_smp(); | |
183 | #endif | |
184 | ||
185 | /* Lookup PCI hosts */ | |
186 | pmac_pci_init(); | |
187 | ||
188 | #ifdef CONFIG_DUMMY_CONSOLE | |
189 | conswitchp = &dummy_con; | |
190 | #endif | |
62d60e9f ME |
191 | |
192 | printk(KERN_INFO "Using native/NAP idle loop\n"); | |
1da177e4 LT |
193 | } |
194 | ||
195 | #ifdef CONFIG_SCSI | |
196 | void note_scsi_host(struct device_node *node, void *host) | |
197 | { | |
198 | /* Obsolete */ | |
199 | } | |
200 | #endif | |
201 | ||
202 | ||
203 | static int initializing = 1; | |
204 | ||
205 | static int pmac_late_init(void) | |
206 | { | |
207 | initializing = 0; | |
208 | return 0; | |
209 | } | |
210 | ||
211 | late_initcall(pmac_late_init); | |
212 | ||
213 | /* can't be __init - can be called whenever a disk is first accessed */ | |
aacaf9bd | 214 | void note_bootable_part(dev_t dev, int part, int goodness) |
1da177e4 LT |
215 | { |
216 | extern dev_t boot_dev; | |
217 | char *p; | |
218 | ||
219 | if (!initializing) | |
220 | return; | |
221 | if ((goodness <= current_root_goodness) && | |
222 | ROOT_DEV != DEFAULT_ROOT_DEVICE) | |
223 | return; | |
224 | p = strstr(saved_command_line, "root="); | |
225 | if (p != NULL && (p == saved_command_line || p[-1] == ' ')) | |
226 | return; | |
227 | ||
228 | if (!boot_dev || dev == boot_dev) { | |
229 | ROOT_DEV = dev + part; | |
230 | boot_dev = 0; | |
231 | current_root_goodness = goodness; | |
232 | } | |
233 | } | |
234 | ||
aacaf9bd | 235 | static void pmac_restart(char *cmd) |
1da177e4 LT |
236 | { |
237 | switch(sys_ctrler) { | |
238 | #ifdef CONFIG_ADB_PMU | |
239 | case SYS_CTRLER_PMU: | |
240 | pmu_restart(); | |
241 | break; | |
242 | #endif | |
243 | ||
244 | #ifdef CONFIG_PMAC_SMU | |
245 | case SYS_CTRLER_SMU: | |
246 | smu_restart(); | |
247 | break; | |
248 | #endif | |
249 | default: | |
250 | ; | |
251 | } | |
252 | } | |
253 | ||
aacaf9bd | 254 | static void pmac_power_off(void) |
1da177e4 LT |
255 | { |
256 | switch(sys_ctrler) { | |
257 | #ifdef CONFIG_ADB_PMU | |
258 | case SYS_CTRLER_PMU: | |
259 | pmu_shutdown(); | |
260 | break; | |
261 | #endif | |
262 | #ifdef CONFIG_PMAC_SMU | |
263 | case SYS_CTRLER_SMU: | |
264 | smu_shutdown(); | |
265 | break; | |
266 | #endif | |
267 | default: | |
268 | ; | |
269 | } | |
270 | } | |
271 | ||
aacaf9bd | 272 | static void pmac_halt(void) |
1da177e4 LT |
273 | { |
274 | pmac_power_off(); | |
275 | } | |
276 | ||
277 | #ifdef CONFIG_BOOTX_TEXT | |
1da177e4 LT |
278 | static void btext_putc(unsigned char c) |
279 | { | |
280 | btext_drawchar(c); | |
281 | } | |
282 | ||
283 | static void __init init_boot_display(void) | |
284 | { | |
285 | char *name; | |
286 | struct device_node *np = NULL; | |
287 | int rc = -ENODEV; | |
288 | ||
289 | printk("trying to initialize btext ...\n"); | |
290 | ||
291 | name = (char *)get_property(of_chosen, "linux,stdout-path", NULL); | |
292 | if (name != NULL) { | |
293 | np = of_find_node_by_path(name); | |
294 | if (np != NULL) { | |
295 | if (strcmp(np->type, "display") != 0) { | |
296 | printk("boot stdout isn't a display !\n"); | |
297 | of_node_put(np); | |
298 | np = NULL; | |
299 | } | |
300 | } | |
301 | } | |
302 | if (np) | |
303 | rc = btext_initialize(np); | |
304 | if (rc == 0) | |
305 | return; | |
306 | ||
307 | for (np = NULL; (np = of_find_node_by_type(np, "display"));) { | |
308 | if (get_property(np, "linux,opened", NULL)) { | |
309 | printk("trying %s ...\n", np->full_name); | |
310 | rc = btext_initialize(np); | |
311 | printk("result: %d\n", rc); | |
312 | } | |
313 | if (rc == 0) | |
314 | return; | |
315 | } | |
316 | } | |
317 | #endif /* CONFIG_BOOTX_TEXT */ | |
318 | ||
319 | /* | |
320 | * Early initialization. | |
321 | */ | |
6fdfb382 | 322 | static void __init pmac_init_early(void) |
1da177e4 LT |
323 | { |
324 | DBG(" -> pmac_init_early\n"); | |
325 | ||
326 | /* Initialize hash table, from now on, we can take hash faults | |
327 | * and call ioremap | |
328 | */ | |
329 | hpte_init_native(); | |
330 | ||
331 | /* Init SCC */ | |
332 | if (strstr(cmd_line, "sccdbg")) { | |
333 | sccdbg = 1; | |
334 | udbg_init_scc(NULL); | |
335 | } | |
1da177e4 | 336 | #ifdef CONFIG_BOOTX_TEXT |
c8f1c8be | 337 | else { |
1da177e4 LT |
338 | init_boot_display(); |
339 | ||
c8f1c8be | 340 | udbg_putc = btext_putc; |
1da177e4 | 341 | } |
c8f1c8be | 342 | #endif /* CONFIG_BOOTX_TEXT */ |
1da177e4 LT |
343 | |
344 | /* Setup interrupt mapping options */ | |
345 | ppc64_interrupt_controller = IC_OPEN_PIC; | |
346 | ||
347 | iommu_init_early_u3(); | |
348 | ||
349 | DBG(" <- pmac_init_early\n"); | |
350 | } | |
351 | ||
352 | static int pmac_u3_cascade(struct pt_regs *regs, void *data) | |
353 | { | |
354 | return mpic_get_one_irq((struct mpic *)data, regs); | |
355 | } | |
356 | ||
357 | static __init void pmac_init_IRQ(void) | |
358 | { | |
359 | struct device_node *irqctrler = NULL; | |
360 | struct device_node *irqctrler2 = NULL; | |
361 | struct device_node *np = NULL; | |
362 | struct mpic *mpic1, *mpic2; | |
363 | ||
364 | /* We first try to detect Apple's new Core99 chipset, since mac-io | |
365 | * is quite different on those machines and contains an IBM MPIC2. | |
366 | */ | |
367 | while ((np = of_find_node_by_type(np, "open-pic")) != NULL) { | |
368 | struct device_node *parent = of_get_parent(np); | |
369 | if (parent && !strcmp(parent->name, "u3")) | |
370 | irqctrler2 = of_node_get(np); | |
371 | else | |
372 | irqctrler = of_node_get(np); | |
373 | of_node_put(parent); | |
374 | } | |
375 | if (irqctrler != NULL && irqctrler->n_addrs > 0) { | |
376 | unsigned char senses[128]; | |
377 | ||
378 | printk(KERN_INFO "PowerMac using OpenPIC irq controller at 0x%08x\n", | |
379 | (unsigned int)irqctrler->addrs[0].address); | |
380 | ||
381 | prom_get_irq_senses(senses, 0, 128); | |
382 | mpic1 = mpic_alloc(irqctrler->addrs[0].address, | |
383 | MPIC_PRIMARY | MPIC_WANTS_RESET, | |
384 | 0, 0, 128, 256, senses, 128, " K2-MPIC "); | |
385 | BUG_ON(mpic1 == NULL); | |
386 | mpic_init(mpic1); | |
387 | ||
388 | if (irqctrler2 != NULL && irqctrler2->n_intrs > 0 && | |
389 | irqctrler2->n_addrs > 0) { | |
390 | printk(KERN_INFO "Slave OpenPIC at 0x%08x hooked on IRQ %d\n", | |
391 | (u32)irqctrler2->addrs[0].address, | |
392 | irqctrler2->intrs[0].line); | |
393 | ||
394 | pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler2, 0, 0); | |
395 | prom_get_irq_senses(senses, 128, 128 + 128); | |
396 | ||
397 | /* We don't need to set MPIC_BROKEN_U3 here since we don't have | |
398 | * hypertransport interrupts routed to it | |
399 | */ | |
400 | mpic2 = mpic_alloc(irqctrler2->addrs[0].address, | |
401 | MPIC_BIG_ENDIAN | MPIC_WANTS_RESET, | |
402 | 0, 128, 128, 0, senses, 128, " U3-MPIC "); | |
403 | BUG_ON(mpic2 == NULL); | |
404 | mpic_init(mpic2); | |
405 | mpic_setup_cascade(irqctrler2->intrs[0].line, | |
406 | pmac_u3_cascade, mpic2); | |
407 | } | |
408 | } | |
409 | of_node_put(irqctrler); | |
410 | of_node_put(irqctrler2); | |
411 | } | |
412 | ||
413 | static void __init pmac_progress(char *s, unsigned short hex) | |
414 | { | |
415 | if (sccdbg) { | |
416 | udbg_puts(s); | |
417 | udbg_puts("\n"); | |
418 | } | |
419 | #ifdef CONFIG_BOOTX_TEXT | |
420 | else if (boot_text_mapped) { | |
421 | btext_drawstring(s); | |
422 | btext_drawstring("\n"); | |
423 | } | |
424 | #endif /* CONFIG_BOOTX_TEXT */ | |
425 | } | |
426 | ||
427 | /* | |
428 | * pmac has no legacy IO, anything calling this function has to | |
429 | * fail or bad things will happen | |
430 | */ | |
431 | static int pmac_check_legacy_ioport(unsigned int baseport) | |
432 | { | |
433 | return -ENODEV; | |
434 | } | |
435 | ||
436 | static int __init pmac_declare_of_platform_devices(void) | |
437 | { | |
0365ba7f | 438 | struct device_node *np, *npp; |
1da177e4 | 439 | |
0365ba7f BH |
440 | npp = of_find_node_by_name(NULL, "u3"); |
441 | if (npp) { | |
442 | for (np = NULL; (np = of_get_next_child(npp, np)) != NULL;) { | |
1da177e4 | 443 | if (strncmp(np->name, "i2c", 3) == 0) { |
0365ba7f BH |
444 | of_platform_device_create(np, "u3-i2c", NULL); |
445 | of_node_put(np); | |
1da177e4 LT |
446 | break; |
447 | } | |
0365ba7f BH |
448 | } |
449 | of_node_put(npp); | |
450 | } | |
451 | npp = of_find_node_by_type(NULL, "smu"); | |
452 | if (npp) { | |
453 | of_platform_device_create(npp, "smu", NULL); | |
454 | of_node_put(npp); | |
1da177e4 LT |
455 | } |
456 | ||
457 | return 0; | |
458 | } | |
459 | ||
460 | device_initcall(pmac_declare_of_platform_devices); | |
461 | ||
462 | /* | |
463 | * Called very early, MMU is off, device-tree isn't unflattened | |
464 | */ | |
465 | static int __init pmac_probe(int platform) | |
466 | { | |
467 | if (platform != PLATFORM_POWERMAC) | |
468 | return 0; | |
469 | /* | |
470 | * On U3, the DART (iommu) must be allocated now since it | |
471 | * has an impact on htab_initialize (due to the large page it | |
472 | * occupies having to be broken up so the DART itself is not | |
473 | * part of the cacheable linar mapping | |
474 | */ | |
475 | alloc_u3_dart_table(); | |
476 | ||
477 | #ifdef CONFIG_PMAC_SMU | |
478 | /* | |
479 | * SMU based G5s need some memory below 2Gb, at least the current | |
480 | * driver needs that. We have to allocate it now. We allocate 4k | |
481 | * (1 small page) for now. | |
482 | */ | |
483 | smu_cmdbuf_abs = lmb_alloc_base(4096, 4096, 0x80000000UL); | |
484 | #endif /* CONFIG_PMAC_SMU */ | |
485 | ||
486 | return 1; | |
487 | } | |
488 | ||
4267292b PM |
489 | static int pmac_probe_mode(struct pci_bus *bus) |
490 | { | |
491 | struct device_node *node = bus->sysdata; | |
492 | ||
493 | /* We need to use normal PCI probing for the AGP bus, | |
494 | since the device for the AGP bridge isn't in the tree. */ | |
495 | if (bus->self == NULL && device_is_compatible(node, "u3-agp")) | |
496 | return PCI_PROBE_NORMAL; | |
497 | ||
498 | return PCI_PROBE_DEVTREE; | |
499 | } | |
500 | ||
1da177e4 LT |
501 | struct machdep_calls __initdata pmac_md = { |
502 | #ifdef CONFIG_HOTPLUG_CPU | |
503 | .cpu_die = generic_mach_cpu_die, | |
504 | #endif | |
505 | .probe = pmac_probe, | |
506 | .setup_arch = pmac_setup_arch, | |
507 | .init_early = pmac_init_early, | |
508 | .get_cpuinfo = pmac_show_cpuinfo, | |
509 | .init_IRQ = pmac_init_IRQ, | |
510 | .get_irq = mpic_get_irq, | |
511 | .pcibios_fixup = pmac_pcibios_fixup, | |
4267292b | 512 | .pci_probe_mode = pmac_probe_mode, |
1da177e4 LT |
513 | .restart = pmac_restart, |
514 | .power_off = pmac_power_off, | |
515 | .halt = pmac_halt, | |
516 | .get_boot_time = pmac_get_boot_time, | |
517 | .set_rtc_time = pmac_set_rtc_time, | |
518 | .get_rtc_time = pmac_get_rtc_time, | |
519 | .calibrate_decr = pmac_calibrate_decr, | |
520 | .feature_call = pmac_do_feature_call, | |
521 | .progress = pmac_progress, | |
62d60e9f ME |
522 | .check_legacy_ioport = pmac_check_legacy_ioport, |
523 | .idle_loop = native_idle, | |
180a3362 | 524 | .enable_pmcs = power4_enable_pmcs, |
1da177e4 | 525 | }; |