diff options
Diffstat (limited to 'arch/powerpc/kernel/prom.c')
-rw-r--r-- | arch/powerpc/kernel/prom.c | 468 |
1 files changed, 52 insertions, 416 deletions
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 3bf968e74095..977ee3adaf2d 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/initrd.h> | 29 | #include <linux/initrd.h> |
30 | #include <linux/bitops.h> | 30 | #include <linux/bitops.h> |
31 | #include <linux/module.h> | 31 | #include <linux/module.h> |
32 | #include <linux/kexec.h> | ||
32 | 33 | ||
33 | #include <asm/prom.h> | 34 | #include <asm/prom.h> |
34 | #include <asm/rtas.h> | 35 | #include <asm/rtas.h> |
@@ -37,6 +38,7 @@ | |||
37 | #include <asm/processor.h> | 38 | #include <asm/processor.h> |
38 | #include <asm/irq.h> | 39 | #include <asm/irq.h> |
39 | #include <asm/io.h> | 40 | #include <asm/io.h> |
41 | #include <asm/kdump.h> | ||
40 | #include <asm/smp.h> | 42 | #include <asm/smp.h> |
41 | #include <asm/system.h> | 43 | #include <asm/system.h> |
42 | #include <asm/mmu.h> | 44 | #include <asm/mmu.h> |
@@ -55,21 +57,6 @@ | |||
55 | #define DBG(fmt...) | 57 | #define DBG(fmt...) |
56 | #endif | 58 | #endif |
57 | 59 | ||
58 | struct pci_reg_property { | ||
59 | struct pci_address addr; | ||
60 | u32 size_hi; | ||
61 | u32 size_lo; | ||
62 | }; | ||
63 | |||
64 | struct isa_reg_property { | ||
65 | u32 space; | ||
66 | u32 address; | ||
67 | u32 size; | ||
68 | }; | ||
69 | |||
70 | |||
71 | typedef int interpret_func(struct device_node *, unsigned long *, | ||
72 | int, int, int); | ||
73 | 60 | ||
74 | static int __initdata dt_root_addr_cells; | 61 | static int __initdata dt_root_addr_cells; |
75 | static int __initdata dt_root_size_cells; | 62 | static int __initdata dt_root_size_cells; |
@@ -311,6 +298,16 @@ static int __devinit finish_node_interrupts(struct device_node *np, | |||
311 | int i, j, n, sense; | 298 | int i, j, n, sense; |
312 | unsigned int *irq, virq; | 299 | unsigned int *irq, virq; |
313 | struct device_node *ic; | 300 | struct device_node *ic; |
301 | int trace = 0; | ||
302 | |||
303 | //#define TRACE(fmt...) do { if (trace) { printk(fmt); mdelay(1000); } } while(0) | ||
304 | #define TRACE(fmt...) | ||
305 | |||
306 | if (!strcmp(np->name, "smu-doorbell")) | ||
307 | trace = 1; | ||
308 | |||
309 | TRACE("Finishing SMU doorbell ! num_interrupt_controllers = %d\n", | ||
310 | num_interrupt_controllers); | ||
314 | 311 | ||
315 | if (num_interrupt_controllers == 0) { | 312 | if (num_interrupt_controllers == 0) { |
316 | /* | 313 | /* |
@@ -345,11 +342,12 @@ static int __devinit finish_node_interrupts(struct device_node *np, | |||
345 | } | 342 | } |
346 | 343 | ||
347 | ints = (unsigned int *) get_property(np, "interrupts", &intlen); | 344 | ints = (unsigned int *) get_property(np, "interrupts", &intlen); |
345 | TRACE("ints=%p, intlen=%d\n", ints, intlen); | ||
348 | if (ints == NULL) | 346 | if (ints == NULL) |
349 | return 0; | 347 | return 0; |
350 | intrcells = prom_n_intr_cells(np); | 348 | intrcells = prom_n_intr_cells(np); |
351 | intlen /= intrcells * sizeof(unsigned int); | 349 | intlen /= intrcells * sizeof(unsigned int); |
352 | 350 | TRACE("intrcells=%d, new intlen=%d\n", intrcells, intlen); | |
353 | np->intrs = prom_alloc(intlen * sizeof(*(np->intrs)), mem_start); | 351 | np->intrs = prom_alloc(intlen * sizeof(*(np->intrs)), mem_start); |
354 | if (!np->intrs) | 352 | if (!np->intrs) |
355 | return -ENOMEM; | 353 | return -ENOMEM; |
@@ -360,6 +358,7 @@ static int __devinit finish_node_interrupts(struct device_node *np, | |||
360 | intrcount = 0; | 358 | intrcount = 0; |
361 | for (i = 0; i < intlen; ++i, ints += intrcells) { | 359 | for (i = 0; i < intlen; ++i, ints += intrcells) { |
362 | n = map_interrupt(&irq, &ic, np, ints, intrcells); | 360 | n = map_interrupt(&irq, &ic, np, ints, intrcells); |
361 | TRACE("map, irq=%d, ic=%p, n=%d\n", irq, ic, n); | ||
363 | if (n <= 0) | 362 | if (n <= 0) |
364 | continue; | 363 | continue; |
365 | 364 | ||
@@ -370,6 +369,7 @@ static int __devinit finish_node_interrupts(struct device_node *np, | |||
370 | np->intrs[intrcount].sense = map_isa_senses[sense]; | 369 | np->intrs[intrcount].sense = map_isa_senses[sense]; |
371 | } else { | 370 | } else { |
372 | virq = virt_irq_create_mapping(irq[0]); | 371 | virq = virt_irq_create_mapping(irq[0]); |
372 | TRACE("virq=%d\n", virq); | ||
373 | #ifdef CONFIG_PPC64 | 373 | #ifdef CONFIG_PPC64 |
374 | if (virq == NO_IRQ) { | 374 | if (virq == NO_IRQ) { |
375 | printk(KERN_CRIT "Could not allocate interrupt" | 375 | printk(KERN_CRIT "Could not allocate interrupt" |
@@ -379,6 +379,12 @@ static int __devinit finish_node_interrupts(struct device_node *np, | |||
379 | #endif | 379 | #endif |
380 | np->intrs[intrcount].line = irq_offset_up(virq); | 380 | np->intrs[intrcount].line = irq_offset_up(virq); |
381 | sense = (n > 1)? (irq[1] & 3): 1; | 381 | sense = (n > 1)? (irq[1] & 3): 1; |
382 | |||
383 | /* Apple uses bits in there in a different way, let's | ||
384 | * only keep the real sense bit on macs | ||
385 | */ | ||
386 | if (_machine == PLATFORM_POWERMAC) | ||
387 | sense &= 0x1; | ||
382 | np->intrs[intrcount].sense = map_mpic_senses[sense]; | 388 | np->intrs[intrcount].sense = map_mpic_senses[sense]; |
383 | } | 389 | } |
384 | 390 | ||
@@ -388,12 +394,13 @@ static int __devinit finish_node_interrupts(struct device_node *np, | |||
388 | char *name = get_property(ic->parent, "name", NULL); | 394 | char *name = get_property(ic->parent, "name", NULL); |
389 | if (name && !strcmp(name, "u3")) | 395 | if (name && !strcmp(name, "u3")) |
390 | np->intrs[intrcount].line += 128; | 396 | np->intrs[intrcount].line += 128; |
391 | else if (!(name && !strcmp(name, "mac-io"))) | 397 | else if (!(name && (!strcmp(name, "mac-io") || |
398 | !strcmp(name, "u4")))) | ||
392 | /* ignore other cascaded controllers, such as | 399 | /* ignore other cascaded controllers, such as |
393 | the k2-sata-root */ | 400 | the k2-sata-root */ |
394 | break; | 401 | break; |
395 | } | 402 | } |
396 | #endif | 403 | #endif /* CONFIG_PPC64 */ |
397 | if (n > 2) { | 404 | if (n > 2) { |
398 | printk("hmmm, got %d intr cells for %s:", n, | 405 | printk("hmmm, got %d intr cells for %s:", n, |
399 | np->full_name); | 406 | np->full_name); |
@@ -408,234 +415,19 @@ static int __devinit finish_node_interrupts(struct device_node *np, | |||
408 | return 0; | 415 | return 0; |
409 | } | 416 | } |
410 | 417 | ||
411 | static int __devinit interpret_pci_props(struct device_node *np, | ||
412 | unsigned long *mem_start, | ||
413 | int naddrc, int nsizec, | ||
414 | int measure_only) | ||
415 | { | ||
416 | struct address_range *adr; | ||
417 | struct pci_reg_property *pci_addrs; | ||
418 | int i, l, n_addrs; | ||
419 | |||
420 | pci_addrs = (struct pci_reg_property *) | ||
421 | get_property(np, "assigned-addresses", &l); | ||
422 | if (!pci_addrs) | ||
423 | return 0; | ||
424 | |||
425 | n_addrs = l / sizeof(*pci_addrs); | ||
426 | |||
427 | adr = prom_alloc(n_addrs * sizeof(*adr), mem_start); | ||
428 | if (!adr) | ||
429 | return -ENOMEM; | ||
430 | |||
431 | if (measure_only) | ||
432 | return 0; | ||
433 | |||
434 | np->addrs = adr; | ||
435 | np->n_addrs = n_addrs; | ||
436 | |||
437 | for (i = 0; i < n_addrs; i++) { | ||
438 | adr[i].space = pci_addrs[i].addr.a_hi; | ||
439 | adr[i].address = pci_addrs[i].addr.a_lo | | ||
440 | ((u64)pci_addrs[i].addr.a_mid << 32); | ||
441 | adr[i].size = pci_addrs[i].size_lo; | ||
442 | } | ||
443 | |||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | static int __init interpret_dbdma_props(struct device_node *np, | ||
448 | unsigned long *mem_start, | ||
449 | int naddrc, int nsizec, | ||
450 | int measure_only) | ||
451 | { | ||
452 | struct reg_property32 *rp; | ||
453 | struct address_range *adr; | ||
454 | unsigned long base_address; | ||
455 | int i, l; | ||
456 | struct device_node *db; | ||
457 | |||
458 | base_address = 0; | ||
459 | if (!measure_only) { | ||
460 | for (db = np->parent; db != NULL; db = db->parent) { | ||
461 | if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { | ||
462 | base_address = db->addrs[0].address; | ||
463 | break; | ||
464 | } | ||
465 | } | ||
466 | } | ||
467 | |||
468 | rp = (struct reg_property32 *) get_property(np, "reg", &l); | ||
469 | if (rp != 0 && l >= sizeof(struct reg_property32)) { | ||
470 | i = 0; | ||
471 | adr = (struct address_range *) (*mem_start); | ||
472 | while ((l -= sizeof(struct reg_property32)) >= 0) { | ||
473 | if (!measure_only) { | ||
474 | adr[i].space = 2; | ||
475 | adr[i].address = rp[i].address + base_address; | ||
476 | adr[i].size = rp[i].size; | ||
477 | } | ||
478 | ++i; | ||
479 | } | ||
480 | np->addrs = adr; | ||
481 | np->n_addrs = i; | ||
482 | (*mem_start) += i * sizeof(struct address_range); | ||
483 | } | ||
484 | |||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static int __init interpret_macio_props(struct device_node *np, | ||
489 | unsigned long *mem_start, | ||
490 | int naddrc, int nsizec, | ||
491 | int measure_only) | ||
492 | { | ||
493 | struct reg_property32 *rp; | ||
494 | struct address_range *adr; | ||
495 | unsigned long base_address; | ||
496 | int i, l; | ||
497 | struct device_node *db; | ||
498 | |||
499 | base_address = 0; | ||
500 | if (!measure_only) { | ||
501 | for (db = np->parent; db != NULL; db = db->parent) { | ||
502 | if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { | ||
503 | base_address = db->addrs[0].address; | ||
504 | break; | ||
505 | } | ||
506 | } | ||
507 | } | ||
508 | |||
509 | rp = (struct reg_property32 *) get_property(np, "reg", &l); | ||
510 | if (rp != 0 && l >= sizeof(struct reg_property32)) { | ||
511 | i = 0; | ||
512 | adr = (struct address_range *) (*mem_start); | ||
513 | while ((l -= sizeof(struct reg_property32)) >= 0) { | ||
514 | if (!measure_only) { | ||
515 | adr[i].space = 2; | ||
516 | adr[i].address = rp[i].address + base_address; | ||
517 | adr[i].size = rp[i].size; | ||
518 | } | ||
519 | ++i; | ||
520 | } | ||
521 | np->addrs = adr; | ||
522 | np->n_addrs = i; | ||
523 | (*mem_start) += i * sizeof(struct address_range); | ||
524 | } | ||
525 | |||
526 | return 0; | ||
527 | } | ||
528 | |||
529 | static int __init interpret_isa_props(struct device_node *np, | ||
530 | unsigned long *mem_start, | ||
531 | int naddrc, int nsizec, | ||
532 | int measure_only) | ||
533 | { | ||
534 | struct isa_reg_property *rp; | ||
535 | struct address_range *adr; | ||
536 | int i, l; | ||
537 | |||
538 | rp = (struct isa_reg_property *) get_property(np, "reg", &l); | ||
539 | if (rp != 0 && l >= sizeof(struct isa_reg_property)) { | ||
540 | i = 0; | ||
541 | adr = (struct address_range *) (*mem_start); | ||
542 | while ((l -= sizeof(struct isa_reg_property)) >= 0) { | ||
543 | if (!measure_only) { | ||
544 | adr[i].space = rp[i].space; | ||
545 | adr[i].address = rp[i].address; | ||
546 | adr[i].size = rp[i].size; | ||
547 | } | ||
548 | ++i; | ||
549 | } | ||
550 | np->addrs = adr; | ||
551 | np->n_addrs = i; | ||
552 | (*mem_start) += i * sizeof(struct address_range); | ||
553 | } | ||
554 | |||
555 | return 0; | ||
556 | } | ||
557 | |||
558 | static int __init interpret_root_props(struct device_node *np, | ||
559 | unsigned long *mem_start, | ||
560 | int naddrc, int nsizec, | ||
561 | int measure_only) | ||
562 | { | ||
563 | struct address_range *adr; | ||
564 | int i, l; | ||
565 | unsigned int *rp; | ||
566 | int rpsize = (naddrc + nsizec) * sizeof(unsigned int); | ||
567 | |||
568 | rp = (unsigned int *) get_property(np, "reg", &l); | ||
569 | if (rp != 0 && l >= rpsize) { | ||
570 | i = 0; | ||
571 | adr = (struct address_range *) (*mem_start); | ||
572 | while ((l -= rpsize) >= 0) { | ||
573 | if (!measure_only) { | ||
574 | adr[i].space = 0; | ||
575 | adr[i].address = rp[naddrc - 1]; | ||
576 | adr[i].size = rp[naddrc + nsizec - 1]; | ||
577 | } | ||
578 | ++i; | ||
579 | rp += naddrc + nsizec; | ||
580 | } | ||
581 | np->addrs = adr; | ||
582 | np->n_addrs = i; | ||
583 | (*mem_start) += i * sizeof(struct address_range); | ||
584 | } | ||
585 | |||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static int __devinit finish_node(struct device_node *np, | 418 | static int __devinit finish_node(struct device_node *np, |
590 | unsigned long *mem_start, | 419 | unsigned long *mem_start, |
591 | interpret_func *ifunc, | ||
592 | int naddrc, int nsizec, | ||
593 | int measure_only) | 420 | int measure_only) |
594 | { | 421 | { |
595 | struct device_node *child; | 422 | struct device_node *child; |
596 | int *ip, rc = 0; | 423 | int rc = 0; |
597 | |||
598 | /* get the device addresses and interrupts */ | ||
599 | if (ifunc != NULL) | ||
600 | rc = ifunc(np, mem_start, naddrc, nsizec, measure_only); | ||
601 | if (rc) | ||
602 | goto out; | ||
603 | 424 | ||
604 | rc = finish_node_interrupts(np, mem_start, measure_only); | 425 | rc = finish_node_interrupts(np, mem_start, measure_only); |
605 | if (rc) | 426 | if (rc) |
606 | goto out; | 427 | goto out; |
607 | 428 | ||
608 | /* Look for #address-cells and #size-cells properties. */ | ||
609 | ip = (int *) get_property(np, "#address-cells", NULL); | ||
610 | if (ip != NULL) | ||
611 | naddrc = *ip; | ||
612 | ip = (int *) get_property(np, "#size-cells", NULL); | ||
613 | if (ip != NULL) | ||
614 | nsizec = *ip; | ||
615 | |||
616 | if (!strcmp(np->name, "device-tree") || np->parent == NULL) | ||
617 | ifunc = interpret_root_props; | ||
618 | else if (np->type == 0) | ||
619 | ifunc = NULL; | ||
620 | else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) | ||
621 | ifunc = interpret_pci_props; | ||
622 | else if (!strcmp(np->type, "dbdma")) | ||
623 | ifunc = interpret_dbdma_props; | ||
624 | else if (!strcmp(np->type, "mac-io") || ifunc == interpret_macio_props) | ||
625 | ifunc = interpret_macio_props; | ||
626 | else if (!strcmp(np->type, "isa")) | ||
627 | ifunc = interpret_isa_props; | ||
628 | else if (!strcmp(np->name, "uni-n") || !strcmp(np->name, "u3")) | ||
629 | ifunc = interpret_root_props; | ||
630 | else if (!((ifunc == interpret_dbdma_props | ||
631 | || ifunc == interpret_macio_props) | ||
632 | && (!strcmp(np->type, "escc") | ||
633 | || !strcmp(np->type, "media-bay")))) | ||
634 | ifunc = NULL; | ||
635 | |||
636 | for (child = np->child; child != NULL; child = child->sibling) { | 429 | for (child = np->child; child != NULL; child = child->sibling) { |
637 | rc = finish_node(child, mem_start, ifunc, | 430 | rc = finish_node(child, mem_start, measure_only); |
638 | naddrc, nsizec, measure_only); | ||
639 | if (rc) | 431 | if (rc) |
640 | goto out; | 432 | goto out; |
641 | } | 433 | } |
@@ -697,10 +489,10 @@ void __init finish_device_tree(void) | |||
697 | * reason and then remove those additional 16 bytes | 489 | * reason and then remove those additional 16 bytes |
698 | */ | 490 | */ |
699 | size = 16; | 491 | size = 16; |
700 | finish_node(allnodes, &size, NULL, 0, 0, 1); | 492 | finish_node(allnodes, &size, 1); |
701 | size -= 16; | 493 | size -= 16; |
702 | end = start = (unsigned long) __va(lmb_alloc(size, 128)); | 494 | end = start = (unsigned long) __va(lmb_alloc(size, 128)); |
703 | finish_node(allnodes, &end, NULL, 0, 0, 0); | 495 | finish_node(allnodes, &end, 0); |
704 | BUG_ON(end != start + size); | 496 | BUG_ON(end != start + size); |
705 | 497 | ||
706 | DBG(" <- finish_device_tree\n"); | 498 | DBG(" <- finish_device_tree\n"); |
@@ -1197,6 +989,16 @@ static int __init early_init_dt_scan_chosen(unsigned long node, | |||
1197 | } | 989 | } |
1198 | #endif /* CONFIG_PPC_RTAS */ | 990 | #endif /* CONFIG_PPC_RTAS */ |
1199 | 991 | ||
992 | #ifdef CONFIG_KEXEC | ||
993 | lprop = (u64*)of_get_flat_dt_prop(node, "linux,crashkernel-base", NULL); | ||
994 | if (lprop) | ||
995 | crashk_res.start = *lprop; | ||
996 | |||
997 | lprop = (u64*)of_get_flat_dt_prop(node, "linux,crashkernel-size", NULL); | ||
998 | if (lprop) | ||
999 | crashk_res.end = crashk_res.start + *lprop - 1; | ||
1000 | #endif | ||
1001 | |||
1200 | /* break now */ | 1002 | /* break now */ |
1201 | return 1; | 1003 | return 1; |
1202 | } | 1004 | } |
@@ -1263,7 +1065,9 @@ static int __init early_init_dt_scan_memory(unsigned long node, | |||
1263 | } else if (strcmp(type, "memory") != 0) | 1065 | } else if (strcmp(type, "memory") != 0) |
1264 | return 0; | 1066 | return 0; |
1265 | 1067 | ||
1266 | reg = (cell_t *)of_get_flat_dt_prop(node, "reg", &l); | 1068 | reg = (cell_t *)of_get_flat_dt_prop(node, "linux,usable-memory", &l); |
1069 | if (reg == NULL) | ||
1070 | reg = (cell_t *)of_get_flat_dt_prop(node, "reg", &l); | ||
1267 | if (reg == NULL) | 1071 | if (reg == NULL) |
1268 | return 0; | 1072 | return 0; |
1269 | 1073 | ||
@@ -1335,11 +1139,14 @@ void __init early_init_devtree(void *params) | |||
1335 | of_scan_flat_dt(early_init_dt_scan_memory, NULL); | 1139 | of_scan_flat_dt(early_init_dt_scan_memory, NULL); |
1336 | lmb_enforce_memory_limit(memory_limit); | 1140 | lmb_enforce_memory_limit(memory_limit); |
1337 | lmb_analyze(); | 1141 | lmb_analyze(); |
1338 | lmb_reserve(0, __pa(klimit)); | ||
1339 | 1142 | ||
1340 | DBG("Phys. mem: %lx\n", lmb_phys_mem_size()); | 1143 | DBG("Phys. mem: %lx\n", lmb_phys_mem_size()); |
1341 | 1144 | ||
1342 | /* Reserve LMB regions used by kernel, initrd, dt, etc... */ | 1145 | /* Reserve LMB regions used by kernel, initrd, dt, etc... */ |
1146 | lmb_reserve(PHYSICAL_START, __pa(klimit) - PHYSICAL_START); | ||
1147 | #ifdef CONFIG_CRASH_DUMP | ||
1148 | lmb_reserve(0, KDUMP_RESERVE_LIMIT); | ||
1149 | #endif | ||
1343 | early_reserve_mem(); | 1150 | early_reserve_mem(); |
1344 | 1151 | ||
1345 | DBG("Scanning CPUs ...\n"); | 1152 | DBG("Scanning CPUs ...\n"); |
@@ -1802,7 +1609,6 @@ static void of_node_release(struct kref *kref) | |||
1802 | prop = next; | 1609 | prop = next; |
1803 | } | 1610 | } |
1804 | kfree(node->intrs); | 1611 | kfree(node->intrs); |
1805 | kfree(node->addrs); | ||
1806 | kfree(node->full_name); | 1612 | kfree(node->full_name); |
1807 | kfree(node->data); | 1613 | kfree(node->data); |
1808 | kfree(node); | 1614 | kfree(node); |
@@ -1884,9 +1690,7 @@ void of_detach_node(const struct device_node *np) | |||
1884 | * This should probably be split up into smaller chunks. | 1690 | * This should probably be split up into smaller chunks. |
1885 | */ | 1691 | */ |
1886 | 1692 | ||
1887 | static int of_finish_dynamic_node(struct device_node *node, | 1693 | static int of_finish_dynamic_node(struct device_node *node) |
1888 | unsigned long *unused1, int unused2, | ||
1889 | int unused3, int unused4) | ||
1890 | { | 1694 | { |
1891 | struct device_node *parent = of_get_parent(node); | 1695 | struct device_node *parent = of_get_parent(node); |
1892 | int err = 0; | 1696 | int err = 0; |
@@ -1907,7 +1711,8 @@ static int of_finish_dynamic_node(struct device_node *node, | |||
1907 | return -ENODEV; | 1711 | return -ENODEV; |
1908 | 1712 | ||
1909 | /* fix up new node's linux_phandle field */ | 1713 | /* fix up new node's linux_phandle field */ |
1910 | if ((ibm_phandle = (unsigned int *)get_property(node, "ibm,phandle", NULL))) | 1714 | if ((ibm_phandle = (unsigned int *)get_property(node, |
1715 | "ibm,phandle", NULL))) | ||
1911 | node->linux_phandle = *ibm_phandle; | 1716 | node->linux_phandle = *ibm_phandle; |
1912 | 1717 | ||
1913 | out: | 1718 | out: |
@@ -1922,7 +1727,9 @@ static int prom_reconfig_notifier(struct notifier_block *nb, | |||
1922 | 1727 | ||
1923 | switch (action) { | 1728 | switch (action) { |
1924 | case PSERIES_RECONFIG_ADD: | 1729 | case PSERIES_RECONFIG_ADD: |
1925 | err = finish_node(node, NULL, of_finish_dynamic_node, 0, 0, 0); | 1730 | err = of_finish_dynamic_node(node); |
1731 | if (!err) | ||
1732 | finish_node(node, NULL, 0); | ||
1926 | if (err < 0) { | 1733 | if (err < 0) { |
1927 | printk(KERN_ERR "finish_node returned %d\n", err); | 1734 | printk(KERN_ERR "finish_node returned %d\n", err); |
1928 | err = NOTIFY_BAD; | 1735 | err = NOTIFY_BAD; |
@@ -1996,175 +1803,4 @@ int prom_add_property(struct device_node* np, struct property* prop) | |||
1996 | return 0; | 1803 | return 0; |
1997 | } | 1804 | } |
1998 | 1805 | ||
1999 | /* I quickly hacked that one, check against spec ! */ | ||
2000 | static inline unsigned long | ||
2001 | bus_space_to_resource_flags(unsigned int bus_space) | ||
2002 | { | ||
2003 | u8 space = (bus_space >> 24) & 0xf; | ||
2004 | if (space == 0) | ||
2005 | space = 0x02; | ||
2006 | if (space == 0x02) | ||
2007 | return IORESOURCE_MEM; | ||
2008 | else if (space == 0x01) | ||
2009 | return IORESOURCE_IO; | ||
2010 | else { | ||
2011 | printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n", | ||
2012 | bus_space); | ||
2013 | return 0; | ||
2014 | } | ||
2015 | } | ||
2016 | |||
2017 | #ifdef CONFIG_PCI | ||
2018 | static struct resource *find_parent_pci_resource(struct pci_dev* pdev, | ||
2019 | struct address_range *range) | ||
2020 | { | ||
2021 | unsigned long mask; | ||
2022 | int i; | ||
2023 | |||
2024 | /* Check this one */ | ||
2025 | mask = bus_space_to_resource_flags(range->space); | ||
2026 | for (i=0; i<DEVICE_COUNT_RESOURCE; i++) { | ||
2027 | if ((pdev->resource[i].flags & mask) == mask && | ||
2028 | pdev->resource[i].start <= range->address && | ||
2029 | pdev->resource[i].end > range->address) { | ||
2030 | if ((range->address + range->size - 1) > pdev->resource[i].end) { | ||
2031 | /* Add better message */ | ||
2032 | printk(KERN_WARNING "PCI/OF resource overlap !\n"); | ||
2033 | return NULL; | ||
2034 | } | ||
2035 | break; | ||
2036 | } | ||
2037 | } | ||
2038 | if (i == DEVICE_COUNT_RESOURCE) | ||
2039 | return NULL; | ||
2040 | return &pdev->resource[i]; | ||
2041 | } | ||
2042 | |||
2043 | /* | ||
2044 | * Request an OF device resource. Currently handles child of PCI devices, | ||
2045 | * or other nodes attached to the root node. Ultimately, put some | ||
2046 | * link to resources in the OF node. | ||
2047 | */ | ||
2048 | struct resource *request_OF_resource(struct device_node* node, int index, | ||
2049 | const char* name_postfix) | ||
2050 | { | ||
2051 | struct pci_dev* pcidev; | ||
2052 | u8 pci_bus, pci_devfn; | ||
2053 | unsigned long iomask; | ||
2054 | struct device_node* nd; | ||
2055 | struct resource* parent; | ||
2056 | struct resource *res = NULL; | ||
2057 | int nlen, plen; | ||
2058 | |||
2059 | if (index >= node->n_addrs) | ||
2060 | goto fail; | ||
2061 | |||
2062 | /* Sanity check on bus space */ | ||
2063 | iomask = bus_space_to_resource_flags(node->addrs[index].space); | ||
2064 | if (iomask & IORESOURCE_MEM) | ||
2065 | parent = &iomem_resource; | ||
2066 | else if (iomask & IORESOURCE_IO) | ||
2067 | parent = &ioport_resource; | ||
2068 | else | ||
2069 | goto fail; | ||
2070 | |||
2071 | /* Find a PCI parent if any */ | ||
2072 | nd = node; | ||
2073 | pcidev = NULL; | ||
2074 | while (nd) { | ||
2075 | if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) | ||
2076 | pcidev = pci_find_slot(pci_bus, pci_devfn); | ||
2077 | if (pcidev) break; | ||
2078 | nd = nd->parent; | ||
2079 | } | ||
2080 | if (pcidev) | ||
2081 | parent = find_parent_pci_resource(pcidev, &node->addrs[index]); | ||
2082 | if (!parent) { | ||
2083 | printk(KERN_WARNING "request_OF_resource(%s), parent not found\n", | ||
2084 | node->name); | ||
2085 | goto fail; | ||
2086 | } | ||
2087 | 1806 | ||
2088 | res = __request_region(parent, node->addrs[index].address, | ||
2089 | node->addrs[index].size, NULL); | ||
2090 | if (!res) | ||
2091 | goto fail; | ||
2092 | nlen = strlen(node->name); | ||
2093 | plen = name_postfix ? strlen(name_postfix) : 0; | ||
2094 | res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL); | ||
2095 | if (res->name) { | ||
2096 | strcpy((char *)res->name, node->name); | ||
2097 | if (plen) | ||
2098 | strcpy((char *)res->name+nlen, name_postfix); | ||
2099 | } | ||
2100 | return res; | ||
2101 | fail: | ||
2102 | return NULL; | ||
2103 | } | ||
2104 | EXPORT_SYMBOL(request_OF_resource); | ||
2105 | |||
2106 | int release_OF_resource(struct device_node *node, int index) | ||
2107 | { | ||
2108 | struct pci_dev* pcidev; | ||
2109 | u8 pci_bus, pci_devfn; | ||
2110 | unsigned long iomask, start, end; | ||
2111 | struct device_node* nd; | ||
2112 | struct resource* parent; | ||
2113 | struct resource *res = NULL; | ||
2114 | |||
2115 | if (index >= node->n_addrs) | ||
2116 | return -EINVAL; | ||
2117 | |||
2118 | /* Sanity check on bus space */ | ||
2119 | iomask = bus_space_to_resource_flags(node->addrs[index].space); | ||
2120 | if (iomask & IORESOURCE_MEM) | ||
2121 | parent = &iomem_resource; | ||
2122 | else if (iomask & IORESOURCE_IO) | ||
2123 | parent = &ioport_resource; | ||
2124 | else | ||
2125 | return -EINVAL; | ||
2126 | |||
2127 | /* Find a PCI parent if any */ | ||
2128 | nd = node; | ||
2129 | pcidev = NULL; | ||
2130 | while(nd) { | ||
2131 | if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) | ||
2132 | pcidev = pci_find_slot(pci_bus, pci_devfn); | ||
2133 | if (pcidev) break; | ||
2134 | nd = nd->parent; | ||
2135 | } | ||
2136 | if (pcidev) | ||
2137 | parent = find_parent_pci_resource(pcidev, &node->addrs[index]); | ||
2138 | if (!parent) { | ||
2139 | printk(KERN_WARNING "release_OF_resource(%s), parent not found\n", | ||
2140 | node->name); | ||
2141 | return -ENODEV; | ||
2142 | } | ||
2143 | |||
2144 | /* Find us in the parent and its childs */ | ||
2145 | res = parent->child; | ||
2146 | start = node->addrs[index].address; | ||
2147 | end = start + node->addrs[index].size - 1; | ||
2148 | while (res) { | ||
2149 | if (res->start == start && res->end == end && | ||
2150 | (res->flags & IORESOURCE_BUSY)) | ||
2151 | break; | ||
2152 | if (res->start <= start && res->end >= end) | ||
2153 | res = res->child; | ||
2154 | else | ||
2155 | res = res->sibling; | ||
2156 | } | ||
2157 | if (!res) | ||
2158 | return -ENODEV; | ||
2159 | |||
2160 | if (res->name) { | ||
2161 | kfree(res->name); | ||
2162 | res->name = NULL; | ||
2163 | } | ||
2164 | release_resource(res); | ||
2165 | kfree(res); | ||
2166 | |||
2167 | return 0; | ||
2168 | } | ||
2169 | EXPORT_SYMBOL(release_OF_resource); | ||
2170 | #endif /* CONFIG_PCI */ | ||