diff options
Diffstat (limited to 'arch/powerpc/platforms/powermac/smp.c')
-rw-r--r-- | arch/powerpc/platforms/powermac/smp.c | 530 |
1 files changed, 340 insertions, 190 deletions
diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index ccaa0534d60f..e1f9443cc872 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c | |||
@@ -44,20 +44,33 @@ | |||
44 | #include <asm/io.h> | 44 | #include <asm/io.h> |
45 | #include <asm/prom.h> | 45 | #include <asm/prom.h> |
46 | #include <asm/smp.h> | 46 | #include <asm/smp.h> |
47 | #include <asm/residual.h> | ||
48 | #include <asm/machdep.h> | 47 | #include <asm/machdep.h> |
49 | #include <asm/pmac_feature.h> | 48 | #include <asm/pmac_feature.h> |
50 | #include <asm/time.h> | 49 | #include <asm/time.h> |
51 | #include <asm/mpic.h> | 50 | #include <asm/mpic.h> |
52 | #include <asm/cacheflush.h> | 51 | #include <asm/cacheflush.h> |
53 | #include <asm/keylargo.h> | 52 | #include <asm/keylargo.h> |
53 | #include <asm/pmac_low_i2c.h> | ||
54 | |||
55 | #undef DEBUG | ||
56 | |||
57 | #ifdef DEBUG | ||
58 | #define DBG(fmt...) udbg_printf(fmt) | ||
59 | #else | ||
60 | #define DBG(fmt...) | ||
61 | #endif | ||
62 | |||
63 | extern void __secondary_start_pmac_0(void); | ||
64 | |||
65 | #ifdef CONFIG_PPC32 | ||
66 | |||
67 | /* Sync flag for HW tb sync */ | ||
68 | static volatile int sec_tb_reset = 0; | ||
54 | 69 | ||
55 | /* | 70 | /* |
56 | * Powersurge (old powermac SMP) support. | 71 | * Powersurge (old powermac SMP) support. |
57 | */ | 72 | */ |
58 | 73 | ||
59 | extern void __secondary_start_pmac_0(void); | ||
60 | |||
61 | /* Addresses for powersurge registers */ | 74 | /* Addresses for powersurge registers */ |
62 | #define HAMMERHEAD_BASE 0xf8000000 | 75 | #define HAMMERHEAD_BASE 0xf8000000 |
63 | #define HHEAD_CONFIG 0x90 | 76 | #define HHEAD_CONFIG 0x90 |
@@ -106,47 +119,6 @@ static volatile u32 __iomem *psurge_start; | |||
106 | /* what sort of powersurge board we have */ | 119 | /* what sort of powersurge board we have */ |
107 | static int psurge_type = PSURGE_NONE; | 120 | static int psurge_type = PSURGE_NONE; |
108 | 121 | ||
109 | /* L2 and L3 cache settings to pass from CPU0 to CPU1 */ | ||
110 | volatile static long int core99_l2_cache; | ||
111 | volatile static long int core99_l3_cache; | ||
112 | |||
113 | /* Timebase freeze GPIO */ | ||
114 | static unsigned int core99_tb_gpio; | ||
115 | |||
116 | /* Sync flag for HW tb sync */ | ||
117 | static volatile int sec_tb_reset = 0; | ||
118 | static unsigned int pri_tb_hi, pri_tb_lo; | ||
119 | static unsigned int pri_tb_stamp; | ||
120 | |||
121 | static void __devinit core99_init_caches(int cpu) | ||
122 | { | ||
123 | if (!cpu_has_feature(CPU_FTR_L2CR)) | ||
124 | return; | ||
125 | |||
126 | if (cpu == 0) { | ||
127 | core99_l2_cache = _get_L2CR(); | ||
128 | printk("CPU0: L2CR is %lx\n", core99_l2_cache); | ||
129 | } else { | ||
130 | printk("CPU%d: L2CR was %lx\n", cpu, _get_L2CR()); | ||
131 | _set_L2CR(0); | ||
132 | _set_L2CR(core99_l2_cache); | ||
133 | printk("CPU%d: L2CR set to %lx\n", cpu, core99_l2_cache); | ||
134 | } | ||
135 | |||
136 | if (!cpu_has_feature(CPU_FTR_L3CR)) | ||
137 | return; | ||
138 | |||
139 | if (cpu == 0){ | ||
140 | core99_l3_cache = _get_L3CR(); | ||
141 | printk("CPU0: L3CR is %lx\n", core99_l3_cache); | ||
142 | } else { | ||
143 | printk("CPU%d: L3CR was %lx\n", cpu, _get_L3CR()); | ||
144 | _set_L3CR(0); | ||
145 | _set_L3CR(core99_l3_cache); | ||
146 | printk("CPU%d: L3CR set to %lx\n", cpu, core99_l3_cache); | ||
147 | } | ||
148 | } | ||
149 | |||
150 | /* | 122 | /* |
151 | * Set and clear IPIs for powersurge. | 123 | * Set and clear IPIs for powersurge. |
152 | */ | 124 | */ |
@@ -436,151 +408,188 @@ void __init smp_psurge_give_timebase(void) | |||
436 | /* Dummy implementation */ | 408 | /* Dummy implementation */ |
437 | } | 409 | } |
438 | 410 | ||
439 | static int __init smp_core99_probe(void) | 411 | /* PowerSurge-style Macs */ |
440 | { | 412 | struct smp_ops_t psurge_smp_ops = { |
441 | #ifdef CONFIG_6xx | 413 | .message_pass = smp_psurge_message_pass, |
442 | extern int powersave_nap; | 414 | .probe = smp_psurge_probe, |
443 | #endif | 415 | .kick_cpu = smp_psurge_kick_cpu, |
444 | struct device_node *cpus, *firstcpu; | 416 | .setup_cpu = smp_psurge_setup_cpu, |
445 | int i, ncpus = 0, boot_cpu = -1; | 417 | .give_timebase = smp_psurge_give_timebase, |
446 | u32 *tbprop = NULL; | 418 | .take_timebase = smp_psurge_take_timebase, |
447 | 419 | }; | |
448 | if (ppc_md.progress) ppc_md.progress("smp_core99_probe", 0x345); | 420 | #endif /* CONFIG_PPC32 - actually powersurge support */ |
449 | cpus = firstcpu = find_type_devices("cpu"); | ||
450 | while(cpus != NULL) { | ||
451 | u32 *regprop = (u32 *)get_property(cpus, "reg", NULL); | ||
452 | char *stateprop = (char *)get_property(cpus, "state", NULL); | ||
453 | if (regprop != NULL && stateprop != NULL && | ||
454 | !strncmp(stateprop, "running", 7)) | ||
455 | boot_cpu = *regprop; | ||
456 | ++ncpus; | ||
457 | cpus = cpus->next; | ||
458 | } | ||
459 | if (boot_cpu == -1) | ||
460 | printk(KERN_WARNING "Couldn't detect boot CPU !\n"); | ||
461 | if (boot_cpu != 0) | ||
462 | printk(KERN_WARNING "Boot CPU is %d, unsupported setup !\n", boot_cpu); | ||
463 | 421 | ||
464 | if (machine_is_compatible("MacRISC4")) { | 422 | #ifdef CONFIG_PPC64 |
465 | extern struct smp_ops_t core99_smp_ops; | 423 | /* |
424 | * G5s enable/disable the timebase via an i2c-connected clock chip. | ||
425 | */ | ||
426 | static struct device_node *pmac_tb_clock_chip_host; | ||
427 | static u8 pmac_tb_pulsar_addr; | ||
428 | static void (*pmac_tb_freeze)(int freeze); | ||
429 | static DEFINE_SPINLOCK(timebase_lock); | ||
430 | static unsigned long timebase; | ||
466 | 431 | ||
467 | core99_smp_ops.take_timebase = smp_generic_take_timebase; | 432 | static void smp_core99_cypress_tb_freeze(int freeze) |
468 | core99_smp_ops.give_timebase = smp_generic_give_timebase; | 433 | { |
469 | } else { | 434 | u8 data; |
470 | if (firstcpu != NULL) | 435 | int rc; |
471 | tbprop = (u32 *)get_property(firstcpu, "timebase-enable", NULL); | ||
472 | if (tbprop) | ||
473 | core99_tb_gpio = *tbprop; | ||
474 | else | ||
475 | core99_tb_gpio = KL_GPIO_TB_ENABLE; | ||
476 | } | ||
477 | 436 | ||
478 | if (ncpus > 1) { | 437 | /* Strangely, the device-tree says address is 0xd2, but darwin |
479 | mpic_request_ipis(); | 438 | * accesses 0xd0 ... |
480 | for (i = 1; i < ncpus; ++i) | 439 | */ |
481 | smp_hw_index[i] = i; | 440 | pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_combined); |
482 | #ifdef CONFIG_6xx | 441 | rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, |
483 | powersave_nap = 0; | 442 | 0xd0 | pmac_low_i2c_read, |
484 | #endif | 443 | 0x81, &data, 1); |
485 | core99_init_caches(0); | 444 | if (rc != 0) |
445 | goto bail; | ||
446 | |||
447 | data = (data & 0xf3) | (freeze ? 0x00 : 0x0c); | ||
448 | |||
449 | pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_stdsub); | ||
450 | rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, | ||
451 | 0xd0 | pmac_low_i2c_write, | ||
452 | 0x81, &data, 1); | ||
453 | |||
454 | bail: | ||
455 | if (rc != 0) { | ||
456 | printk("Cypress Timebase %s rc: %d\n", | ||
457 | freeze ? "freeze" : "unfreeze", rc); | ||
458 | panic("Timebase freeze failed !\n"); | ||
486 | } | 459 | } |
487 | |||
488 | return ncpus; | ||
489 | } | 460 | } |
490 | 461 | ||
491 | static void __devinit smp_core99_kick_cpu(int nr) | ||
492 | { | ||
493 | unsigned long save_vector, new_vector; | ||
494 | unsigned long flags; | ||
495 | 462 | ||
496 | volatile unsigned long *vector | 463 | static void smp_core99_pulsar_tb_freeze(int freeze) |
497 | = ((volatile unsigned long *)(KERNELBASE+0x100)); | 464 | { |
498 | if (nr < 0 || nr > 3) | 465 | u8 data; |
499 | return; | 466 | int rc; |
500 | if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu", 0x346); | 467 | |
468 | pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_combined); | ||
469 | rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, | ||
470 | pmac_tb_pulsar_addr | pmac_low_i2c_read, | ||
471 | 0x2e, &data, 1); | ||
472 | if (rc != 0) | ||
473 | goto bail; | ||
474 | |||
475 | data = (data & 0x88) | (freeze ? 0x11 : 0x22); | ||
476 | |||
477 | pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_stdsub); | ||
478 | rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, | ||
479 | pmac_tb_pulsar_addr | pmac_low_i2c_write, | ||
480 | 0x2e, &data, 1); | ||
481 | bail: | ||
482 | if (rc != 0) { | ||
483 | printk(KERN_ERR "Pulsar Timebase %s rc: %d\n", | ||
484 | freeze ? "freeze" : "unfreeze", rc); | ||
485 | panic("Timebase freeze failed !\n"); | ||
486 | } | ||
487 | } | ||
501 | 488 | ||
502 | local_irq_save(flags); | ||
503 | local_irq_disable(); | ||
504 | 489 | ||
505 | /* Save reset vector */ | 490 | static void smp_core99_give_timebase(void) |
506 | save_vector = *vector; | 491 | { |
492 | /* Open i2c bus for synchronous access */ | ||
493 | if (pmac_low_i2c_open(pmac_tb_clock_chip_host, 0)) | ||
494 | panic("Can't open i2c for TB sync !\n"); | ||
507 | 495 | ||
508 | /* Setup fake reset vector that does | 496 | spin_lock(&timebase_lock); |
509 | * b __secondary_start_pmac_0 + nr*8 - KERNELBASE | 497 | (*pmac_tb_freeze)(1); |
510 | */ | 498 | mb(); |
511 | new_vector = (unsigned long) __secondary_start_pmac_0 + nr * 8; | 499 | timebase = get_tb(); |
512 | *vector = 0x48000002 + new_vector - KERNELBASE; | 500 | spin_unlock(&timebase_lock); |
513 | 501 | ||
514 | /* flush data cache and inval instruction cache */ | 502 | while (timebase) |
515 | flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); | 503 | barrier(); |
516 | 504 | ||
517 | /* Put some life in our friend */ | 505 | spin_lock(&timebase_lock); |
518 | pmac_call_feature(PMAC_FTR_RESET_CPU, NULL, nr, 0); | 506 | (*pmac_tb_freeze)(0); |
507 | spin_unlock(&timebase_lock); | ||
519 | 508 | ||
520 | /* FIXME: We wait a bit for the CPU to take the exception, I should | 509 | /* Close i2c bus */ |
521 | * instead wait for the entry code to set something for me. Well, | 510 | pmac_low_i2c_close(pmac_tb_clock_chip_host); |
522 | * ideally, all that crap will be done in prom.c and the CPU left | 511 | } |
523 | * in a RAM-based wait loop like CHRP. | ||
524 | */ | ||
525 | mdelay(1); | ||
526 | 512 | ||
527 | /* Restore our exception vector */ | ||
528 | *vector = save_vector; | ||
529 | flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); | ||
530 | 513 | ||
531 | local_irq_restore(flags); | 514 | static void __devinit smp_core99_take_timebase(void) |
532 | if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347); | 515 | { |
516 | while (!timebase) | ||
517 | barrier(); | ||
518 | spin_lock(&timebase_lock); | ||
519 | set_tb(timebase >> 32, timebase & 0xffffffff); | ||
520 | timebase = 0; | ||
521 | spin_unlock(&timebase_lock); | ||
533 | } | 522 | } |
534 | 523 | ||
535 | static void __devinit smp_core99_setup_cpu(int cpu_nr) | 524 | static void __init smp_core99_setup(int ncpus) |
536 | { | 525 | { |
537 | /* Setup L2/L3 */ | 526 | struct device_node *cc = NULL; |
538 | if (cpu_nr != 0) | 527 | struct device_node *p; |
539 | core99_init_caches(cpu_nr); | 528 | u32 *reg; |
529 | int ok; | ||
530 | |||
531 | /* HW sync only on these platforms */ | ||
532 | if (!machine_is_compatible("PowerMac7,2") && | ||
533 | !machine_is_compatible("PowerMac7,3") && | ||
534 | !machine_is_compatible("RackMac3,1")) | ||
535 | return; | ||
540 | 536 | ||
541 | /* Setup openpic */ | 537 | /* Look for the clock chip */ |
542 | mpic_setup_this_cpu(); | 538 | while ((cc = of_find_node_by_name(cc, "i2c-hwclock")) != NULL) { |
539 | p = of_get_parent(cc); | ||
540 | ok = p && device_is_compatible(p, "uni-n-i2c"); | ||
541 | of_node_put(p); | ||
542 | if (!ok) | ||
543 | continue; | ||
543 | 544 | ||
544 | if (cpu_nr == 0) { | 545 | reg = (u32 *)get_property(cc, "reg", NULL); |
545 | #ifdef CONFIG_POWER4 | 546 | if (reg == NULL) |
546 | extern void g5_phy_disable_cpu1(void); | 547 | continue; |
547 | 548 | ||
548 | /* If we didn't start the second CPU, we must take | 549 | switch (*reg) { |
549 | * it off the bus | 550 | case 0xd2: |
550 | */ | 551 | if (device_is_compatible(cc, "pulsar-legacy-slewing")) { |
551 | if (machine_is_compatible("MacRISC4") && | 552 | pmac_tb_freeze = smp_core99_pulsar_tb_freeze; |
552 | num_online_cpus() < 2) | 553 | pmac_tb_pulsar_addr = 0xd2; |
553 | g5_phy_disable_cpu1(); | 554 | printk(KERN_INFO "Timebase clock is Pulsar chip\n"); |
554 | #endif /* CONFIG_POWER4 */ | 555 | } else if (device_is_compatible(cc, "cy28508")) { |
555 | if (ppc_md.progress) ppc_md.progress("core99_setup_cpu 0 done", 0x349); | 556 | pmac_tb_freeze = smp_core99_cypress_tb_freeze; |
557 | printk(KERN_INFO "Timebase clock is Cypress chip\n"); | ||
558 | } | ||
559 | break; | ||
560 | case 0xd4: | ||
561 | pmac_tb_freeze = smp_core99_pulsar_tb_freeze; | ||
562 | pmac_tb_pulsar_addr = 0xd4; | ||
563 | printk(KERN_INFO "Timebase clock is Pulsar chip\n"); | ||
564 | break; | ||
565 | } | ||
566 | if (pmac_tb_freeze != NULL) { | ||
567 | pmac_tb_clock_chip_host = of_get_parent(cc); | ||
568 | of_node_put(cc); | ||
569 | break; | ||
570 | } | ||
571 | } | ||
572 | if (pmac_tb_freeze == NULL) { | ||
573 | smp_ops->give_timebase = smp_generic_give_timebase; | ||
574 | smp_ops->take_timebase = smp_generic_take_timebase; | ||
556 | } | 575 | } |
557 | } | 576 | } |
558 | 577 | ||
559 | /* not __init, called in sleep/wakeup code */ | 578 | /* nothing to do here, caches are already set up by service processor */ |
560 | void smp_core99_take_timebase(void) | 579 | static inline void __devinit core99_init_caches(int cpu) |
561 | { | 580 | { |
562 | unsigned long flags; | 581 | } |
563 | 582 | ||
564 | /* tell the primary we're here */ | 583 | #else /* CONFIG_PPC64 */ |
565 | sec_tb_reset = 1; | ||
566 | mb(); | ||
567 | 584 | ||
568 | /* wait for the primary to set pri_tb_hi/lo */ | 585 | /* |
569 | while (sec_tb_reset < 2) | 586 | * SMP G4 powermacs use a GPIO to enable/disable the timebase. |
570 | mb(); | 587 | */ |
571 | 588 | ||
572 | /* set our stuff the same as the primary */ | 589 | static unsigned int core99_tb_gpio; /* Timebase freeze GPIO */ |
573 | local_irq_save(flags); | ||
574 | set_dec(1); | ||
575 | set_tb(pri_tb_hi, pri_tb_lo); | ||
576 | last_jiffy_stamp(smp_processor_id()) = pri_tb_stamp; | ||
577 | mb(); | ||
578 | 590 | ||
579 | /* tell the primary we're done */ | 591 | static unsigned int pri_tb_hi, pri_tb_lo; |
580 | sec_tb_reset = 0; | 592 | static unsigned int pri_tb_stamp; |
581 | mb(); | ||
582 | local_irq_restore(flags); | ||
583 | } | ||
584 | 593 | ||
585 | /* not __init, called in sleep/wakeup code */ | 594 | /* not __init, called in sleep/wakeup code */ |
586 | void smp_core99_give_timebase(void) | 595 | void smp_core99_give_timebase(void) |
@@ -626,43 +635,184 @@ void smp_core99_give_timebase(void) | |||
626 | local_irq_restore(flags); | 635 | local_irq_restore(flags); |
627 | } | 636 | } |
628 | 637 | ||
629 | void smp_core99_message_pass(int target, int msg) | 638 | /* not __init, called in sleep/wakeup code */ |
639 | void smp_core99_take_timebase(void) | ||
640 | { | ||
641 | unsigned long flags; | ||
642 | |||
643 | /* tell the primary we're here */ | ||
644 | sec_tb_reset = 1; | ||
645 | mb(); | ||
646 | |||
647 | /* wait for the primary to set pri_tb_hi/lo */ | ||
648 | while (sec_tb_reset < 2) | ||
649 | mb(); | ||
650 | |||
651 | /* set our stuff the same as the primary */ | ||
652 | local_irq_save(flags); | ||
653 | set_dec(1); | ||
654 | set_tb(pri_tb_hi, pri_tb_lo); | ||
655 | last_jiffy_stamp(smp_processor_id()) = pri_tb_stamp; | ||
656 | mb(); | ||
657 | |||
658 | /* tell the primary we're done */ | ||
659 | sec_tb_reset = 0; | ||
660 | mb(); | ||
661 | local_irq_restore(flags); | ||
662 | } | ||
663 | |||
664 | /* L2 and L3 cache settings to pass from CPU0 to CPU1 on G4 cpus */ | ||
665 | volatile static long int core99_l2_cache; | ||
666 | volatile static long int core99_l3_cache; | ||
667 | |||
668 | static void __devinit core99_init_caches(int cpu) | ||
630 | { | 669 | { |
631 | cpumask_t mask = CPU_MASK_ALL; | 670 | if (!cpu_has_feature(CPU_FTR_L2CR)) |
632 | /* make sure we're sending something that translates to an IPI */ | ||
633 | if (msg > 0x3) { | ||
634 | printk("SMP %d: smp_message_pass: unknown msg %d\n", | ||
635 | smp_processor_id(), msg); | ||
636 | return; | 671 | return; |
672 | |||
673 | if (cpu == 0) { | ||
674 | core99_l2_cache = _get_L2CR(); | ||
675 | printk("CPU0: L2CR is %lx\n", core99_l2_cache); | ||
676 | } else { | ||
677 | printk("CPU%d: L2CR was %lx\n", cpu, _get_L2CR()); | ||
678 | _set_L2CR(0); | ||
679 | _set_L2CR(core99_l2_cache); | ||
680 | printk("CPU%d: L2CR set to %lx\n", cpu, core99_l2_cache); | ||
637 | } | 681 | } |
638 | switch (target) { | 682 | |
639 | case MSG_ALL: | 683 | if (!cpu_has_feature(CPU_FTR_L3CR)) |
640 | mpic_send_ipi(msg, cpus_addr(mask)[0]); | 684 | return; |
641 | break; | 685 | |
642 | case MSG_ALL_BUT_SELF: | 686 | if (cpu == 0){ |
643 | cpu_clear(smp_processor_id(), mask); | 687 | core99_l3_cache = _get_L3CR(); |
644 | mpic_send_ipi(msg, cpus_addr(mask)[0]); | 688 | printk("CPU0: L3CR is %lx\n", core99_l3_cache); |
645 | break; | 689 | } else { |
646 | default: | 690 | printk("CPU%d: L3CR was %lx\n", cpu, _get_L3CR()); |
647 | mpic_send_ipi(msg, 1 << target); | 691 | _set_L3CR(0); |
648 | break; | 692 | _set_L3CR(core99_l3_cache); |
693 | printk("CPU%d: L3CR set to %lx\n", cpu, core99_l3_cache); | ||
649 | } | 694 | } |
650 | } | 695 | } |
651 | 696 | ||
697 | static void __init smp_core99_setup(int ncpus) | ||
698 | { | ||
699 | struct device_node *cpu; | ||
700 | u32 *tbprop = NULL; | ||
701 | int i; | ||
702 | |||
703 | core99_tb_gpio = KL_GPIO_TB_ENABLE; /* default value */ | ||
704 | cpu = of_find_node_by_type(NULL, "cpu"); | ||
705 | if (cpu != NULL) { | ||
706 | tbprop = (u32 *)get_property(cpu, "timebase-enable", NULL); | ||
707 | if (tbprop) | ||
708 | core99_tb_gpio = *tbprop; | ||
709 | of_node_put(cpu); | ||
710 | } | ||
711 | |||
712 | /* XXX should get this from reg properties */ | ||
713 | for (i = 1; i < ncpus; ++i) | ||
714 | smp_hw_index[i] = i; | ||
715 | powersave_nap = 0; | ||
716 | } | ||
717 | #endif | ||
718 | |||
719 | static int __init smp_core99_probe(void) | ||
720 | { | ||
721 | struct device_node *cpus; | ||
722 | int ncpus = 0; | ||
723 | |||
724 | if (ppc_md.progress) ppc_md.progress("smp_core99_probe", 0x345); | ||
725 | |||
726 | /* Count CPUs in the device-tree */ | ||
727 | for (cpus = NULL; (cpus = of_find_node_by_type(cpus, "cpu")) != NULL;) | ||
728 | ++ncpus; | ||
729 | |||
730 | printk(KERN_INFO "PowerMac SMP probe found %d cpus\n", ncpus); | ||
731 | |||
732 | /* Nothing more to do if less than 2 of them */ | ||
733 | if (ncpus <= 1) | ||
734 | return 1; | ||
735 | |||
736 | smp_core99_setup(ncpus); | ||
737 | mpic_request_ipis(); | ||
738 | core99_init_caches(0); | ||
739 | |||
740 | return ncpus; | ||
741 | } | ||
742 | |||
743 | static void __devinit smp_core99_kick_cpu(int nr) | ||
744 | { | ||
745 | unsigned int save_vector; | ||
746 | unsigned long new_vector; | ||
747 | unsigned long flags; | ||
748 | volatile unsigned int *vector | ||
749 | = ((volatile unsigned int *)(KERNELBASE+0x100)); | ||
750 | |||
751 | if (nr < 0 || nr > 3) | ||
752 | return; | ||
753 | if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu", 0x346); | ||
754 | |||
755 | local_irq_save(flags); | ||
756 | local_irq_disable(); | ||
757 | |||
758 | /* Save reset vector */ | ||
759 | save_vector = *vector; | ||
760 | |||
761 | /* Setup fake reset vector that does | ||
762 | * b __secondary_start_pmac_0 + nr*8 - KERNELBASE | ||
763 | */ | ||
764 | new_vector = (unsigned long) __secondary_start_pmac_0 + nr * 8; | ||
765 | *vector = 0x48000002 + new_vector - KERNELBASE; | ||
766 | |||
767 | /* flush data cache and inval instruction cache */ | ||
768 | flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); | ||
769 | |||
770 | /* Put some life in our friend */ | ||
771 | pmac_call_feature(PMAC_FTR_RESET_CPU, NULL, nr, 0); | ||
772 | |||
773 | /* FIXME: We wait a bit for the CPU to take the exception, I should | ||
774 | * instead wait for the entry code to set something for me. Well, | ||
775 | * ideally, all that crap will be done in prom.c and the CPU left | ||
776 | * in a RAM-based wait loop like CHRP. | ||
777 | */ | ||
778 | mdelay(1); | ||
779 | |||
780 | /* Restore our exception vector */ | ||
781 | *vector = save_vector; | ||
782 | flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); | ||
783 | |||
784 | local_irq_restore(flags); | ||
785 | if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347); | ||
786 | } | ||
787 | |||
788 | static void __devinit smp_core99_setup_cpu(int cpu_nr) | ||
789 | { | ||
790 | /* Setup L2/L3 */ | ||
791 | if (cpu_nr != 0) | ||
792 | core99_init_caches(cpu_nr); | ||
793 | |||
794 | /* Setup openpic */ | ||
795 | mpic_setup_this_cpu(); | ||
796 | |||
797 | if (cpu_nr == 0) { | ||
798 | #ifdef CONFIG_POWER4 | ||
799 | extern void g5_phy_disable_cpu1(void); | ||
800 | |||
801 | /* If we didn't start the second CPU, we must take | ||
802 | * it off the bus | ||
803 | */ | ||
804 | if (machine_is_compatible("MacRISC4") && | ||
805 | num_online_cpus() < 2) | ||
806 | g5_phy_disable_cpu1(); | ||
807 | #endif /* CONFIG_POWER4 */ | ||
808 | if (ppc_md.progress) ppc_md.progress("core99_setup_cpu 0 done", 0x349); | ||
809 | } | ||
810 | } | ||
652 | 811 | ||
653 | /* PowerSurge-style Macs */ | ||
654 | struct smp_ops_t psurge_smp_ops = { | ||
655 | .message_pass = smp_psurge_message_pass, | ||
656 | .probe = smp_psurge_probe, | ||
657 | .kick_cpu = smp_psurge_kick_cpu, | ||
658 | .setup_cpu = smp_psurge_setup_cpu, | ||
659 | .give_timebase = smp_psurge_give_timebase, | ||
660 | .take_timebase = smp_psurge_take_timebase, | ||
661 | }; | ||
662 | 812 | ||
663 | /* Core99 Macs (dual G4s) */ | 813 | /* Core99 Macs (dual G4s and G5s) */ |
664 | struct smp_ops_t core99_smp_ops = { | 814 | struct smp_ops_t core99_smp_ops = { |
665 | .message_pass = smp_core99_message_pass, | 815 | .message_pass = smp_mpic_message_pass, |
666 | .probe = smp_core99_probe, | 816 | .probe = smp_core99_probe, |
667 | .kick_cpu = smp_core99_kick_cpu, | 817 | .kick_cpu = smp_core99_kick_cpu, |
668 | .setup_cpu = smp_core99_setup_cpu, | 818 | .setup_cpu = smp_core99_setup_cpu, |
@@ -670,7 +820,7 @@ struct smp_ops_t core99_smp_ops = { | |||
670 | .take_timebase = smp_core99_take_timebase, | 820 | .take_timebase = smp_core99_take_timebase, |
671 | }; | 821 | }; |
672 | 822 | ||
673 | #ifdef CONFIG_HOTPLUG_CPU | 823 | #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PPC32) |
674 | 824 | ||
675 | int __cpu_disable(void) | 825 | int __cpu_disable(void) |
676 | { | 826 | { |
@@ -685,7 +835,7 @@ int __cpu_disable(void) | |||
685 | return 0; | 835 | return 0; |
686 | } | 836 | } |
687 | 837 | ||
688 | extern void low_cpu_die(void) __attribute__((noreturn)); /* in pmac_sleep.S */ | 838 | extern void low_cpu_die(void) __attribute__((noreturn)); /* in sleep.S */ |
689 | static int cpu_dead[NR_CPUS]; | 839 | static int cpu_dead[NR_CPUS]; |
690 | 840 | ||
691 | void cpu_die(void) | 841 | void cpu_die(void) |