diff --git a/arch/ppc/kernel/open_pic.c b/arch/ppc/kernel/open_pic.c index 9727f59e8f954cb0aa0c93c2d18a24f0664aadd1..685cbf4637691a44abc06ebc46b6cf11a831a7a9 100644 --- a/arch/ppc/kernel/open_pic.c +++ b/arch/ppc/kernel/open_pic.c @@ -602,7 +602,7 @@ void openpic_request_IPIs(void) * -- Cort */ -void __init do_openpic_setup_cpu(void) +void __devinit do_openpic_setup_cpu(void) { int i; u32 msk = 1 << smp_hw_index[smp_processor_id()]; diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index 5ac73276f493191850b2e50e3e88ab83468ab483..52150568c47baa834ab1de00a091da22658242d0 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -48,15 +48,17 @@ struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 }; atomic_t ipi_recv; atomic_t ipi_sent; spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; -unsigned int prof_multiplier[NR_CPUS]; -unsigned int prof_counter[NR_CPUS]; -unsigned long cache_decay_ticks; -static int max_cpus __initdata = NR_CPUS; -unsigned long cpu_online_map; +unsigned int prof_multiplier[NR_CPUS] = { [1 ... NR_CPUS-1] = 1 }; +unsigned int prof_counter[NR_CPUS] = { [1 ... NR_CPUS-1] = 1 }; +unsigned long cache_decay_ticks = HZ/100; +unsigned long cpu_online_map = 1UL; +unsigned long cpu_possible_map = 1UL; int smp_hw_index[NR_CPUS]; -static struct smp_ops_t *smp_ops; struct thread_info *secondary_ti; +/* SMP operations for this machine */ +static struct smp_ops_t *smp_ops; + /* all cpu mappings are 1-1 -- Cort */ volatile unsigned long cpu_callin_map[NR_CPUS]; @@ -70,10 +72,6 @@ void smp_call_function_interrupt(void); static int __smp_call_function(void (*func) (void *info), void *info, int wait, int target); -#ifdef CONFIG_PPC_ISERIES -extern void smp_iSeries_space_timers( unsigned nr ); -#endif - /* Since OpenPIC has only 4 IPIs, we use slightly different message numbers. * * Make sure this matches openpic_request_IPIs in open_pic.c, or what shows up @@ -291,6 +289,7 @@ void smp_call_function_interrupt(void) atomic_inc(&call_data->finished); } +#if 0 /* Old boot code. */ void __init smp_boot_cpus(void) { int i, cpu_nr; @@ -556,3 +555,156 @@ static int __init maxcpus(char *str) } __setup("maxcpus=", maxcpus); +#else /* New boot code */ +/* FIXME: Do this properly for all archs --RR */ +static spinlock_t timebase_lock = SPIN_LOCK_UNLOCKED; +static unsigned int timebase_upper = 0, timebase_lower = 0; + +void __devinit +smp_generic_give_timebase(void) +{ + spin_lock(&timebase_lock); + do { + timebase_upper = get_tbu(); + timebase_lower = get_tbl(); + } while (timebase_upper != get_tbu()); + spin_unlock(&timebase_lock); + + while (timebase_upper || timebase_lower) + rmb(); +} + +void __devinit +smp_generic_take_timebase(void) +{ + int done = 0; + + while (!done) { + spin_lock(&timebase_lock); + if (timebase_upper || timebase_lower) { + set_tb(timebase_upper, timebase_lower); + timebase_upper = 0; + timebase_lower = 0; + done = 1; + } + spin_unlock(&timebase_lock); + } +} + +static void __devinit smp_store_cpu_info(int id) +{ + struct cpuinfo_PPC *c = &cpu_data[id]; + + /* assume bogomips are same for everything */ + c->loops_per_jiffy = loops_per_jiffy; + c->pvr = mfspr(PVR); +} + +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + int num_cpus; + + /* Fixup boot cpu */ + smp_store_cpu_info(smp_processor_id()); + cpu_callin_map[smp_processor_id()] = 1; + + smp_ops = ppc_md.smp_ops; + if (smp_ops == NULL) { + printk("SMP not supported on this machine.\n"); + return; + } + + /* Probe platform for CPUs: always linear. */ + num_cpus = smp_ops->probe(); + cpu_possible_map = (1 << num_cpus)-1; + + if (smp_ops->space_timers) + smp_ops->space_timers(num_cpus); +} + +int __init setup_profiling_timer(unsigned int multiplier) +{ + return 0; +} + +/* Processor coming up starts here */ +int __devinit start_secondary(void *unused) +{ + int cpu; + + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + + cpu = smp_processor_id(); + smp_store_cpu_info(cpu); + set_dec(tb_ticks_per_jiffy); + cpu_callin_map[cpu] = 1; + + printk("CPU %i done callin...\n", cpu); + smp_ops->setup_cpu(cpu); + printk("CPU %i done setup...\n", cpu); + smp_ops->take_timebase(); + printk("CPU %i done timebase take...\n", cpu); + + return cpu_idle(NULL); +} + +int __cpu_up(unsigned int cpu) +{ + struct pt_regs regs; + struct task_struct *p; + char buf[32]; + int c; + + /* create a process for the processor */ + /* only regs.msr is actually used, and 0 is OK for it */ + memset(®s, 0, sizeof(struct pt_regs)); + p = do_fork(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0); + if (IS_ERR(p)) + panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p)); + + init_idle(p, cpu); + unhash_process(p); + + secondary_ti = p->thread_info; + p->thread_info->cpu = cpu; + + /* + * There was a cache flush loop here to flush the cache + * to memory for the first 8MB of RAM. The cache flush + * has been pushed into the kick_cpu function for those + * platforms that need it. + */ + + /* wake up cpu */ + smp_ops->kick_cpu(cpu); + + /* + * wait to see if the cpu made a callin (is actually up). + * use this value that I found through experimentation. + * -- Cort + */ + for (c = 1000; c && !cpu_callin_map[cpu]; c--) + udelay(100); + + if (!cpu_callin_map[cpu]) { + sprintf(buf, "didn't find cpu %u", cpu); + if (ppc_md.progress) ppc_md.progress(buf, 0x360+cpu); + printk("Processor %u is stuck.\n", cpu); + return -ENOENT; + } + + sprintf(buf, "found cpu %u", cpu); + if (ppc_md.progress) ppc_md.progress(buf, 0x350+cpu); + printk("Processor %d found.\n", cpu); + + smp_ops->give_timebase(); + set_bit(cpu, &cpu_online_map); + return 0; +} + +void smp_cpus_done(unsigned int max_cpus) +{ + smp_ops->setup_cpu(0); +} +#endif diff --git a/arch/ppc/platforms/chrp_smp.c b/arch/ppc/platforms/chrp_smp.c index 4d28976651fe1a098a694e08068c2cacdcb672e2..80e6584c93d96ae62bf48f615300409858450db3 100644 --- a/arch/ppc/platforms/chrp_smp.c +++ b/arch/ppc/platforms/chrp_smp.c @@ -50,59 +50,61 @@ smp_chrp_probe(void) return smp_chrp_cpu_nr; } -static void __init +static void __devinit smp_chrp_kick_cpu(int nr) { *(unsigned long *)KERNELBASE = nr; asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); } -static void __init +static void __devinit smp_chrp_setup_cpu(int cpu_nr) { - static atomic_t ready = ATOMIC_INIT(1); - static volatile int frozen = 0; - - /* FIXME: Hotplug cpu breaks all this --RR */ - if (cpu_nr == 0) { - /* wait for all the others */ - while (atomic_read(&ready) < num_online_cpus()) - barrier(); - atomic_set(&ready, 1); - /* freeze the timebase */ - call_rtas("freeze-time-base", 0, 1, NULL); - mb(); - frozen = 1; - /* XXX assumes this is not a 601 */ - set_tb(0, 0); - last_jiffy_stamp(0) = 0; - while (atomic_read(&ready) < num_online_cpus()) - barrier(); - /* thaw the timebase again */ - call_rtas("thaw-time-base", 0, 1, NULL); - mb(); - frozen = 0; - smp_tb_synchronized = 1; - } else { - atomic_inc(&ready); - while (!frozen) - barrier(); - set_tb(0, 0); - last_jiffy_stamp(0) = 0; - mb(); - atomic_inc(&ready); - while (frozen) - barrier(); - } - if (OpenPIC_Addr) do_openpic_setup_cpu(); } +static spinlock_t timebase_lock = SPIN_LOCK_UNLOCKED; +static unsigned int timebase_upper = 0, timebase_lower = 0; + +void __devinit +smp_chrp_give_timebase(void) +{ + spin_lock(&timebase_lock); + call_rtas("freeze-time-base", 0, 1, NULL); + timebase_upper = get_tbu(); + timebase_lower = get_tbl(); + spin_unlock(&timebase_lock); + + while (timebase_upper || timebase_lower) + rmb(); + call_rtas("thaw-time-base", 0, 1, NULL); +} + +void __devinit +smp_chrp_take_timebase(void) +{ + int done = 0; + + while (!done) { + spin_lock(&timebase_lock); + if (timebase_upper || timebase_lower) { + set_tb(timebase_upper, timebase_lower); + timebase_upper = 0; + timebase_lower = 0; + done = 1; + } + spin_unlock(&timebase_lock); + } + printk("CPU %i taken timebase\n", smp_processor_id()); +} + /* CHRP with openpic */ struct smp_ops_t chrp_smp_ops __chrpdata = { - smp_openpic_message_pass, - smp_chrp_probe, - smp_chrp_kick_cpu, - smp_chrp_setup_cpu, + .message_pass = smp_openpic_message_pass, + .probe = smp_chrp_probe, + .kick_cpu = smp_chrp_kick_cpu, + .setup_cpu = smp_chrp_setup_cpu, + .give_timebase = smp_chrp_give_timebase, + .take_timebase = smp_chrp_take_timebase, }; diff --git a/arch/ppc/platforms/gemini_setup.c b/arch/ppc/platforms/gemini_setup.c index db86f459abd4c70abee86c5a361803e14f3bffa2..feeee01f669b736aa6face157caf4fec1ddd7591 100644 --- a/arch/ppc/platforms/gemini_setup.c +++ b/arch/ppc/platforms/gemini_setup.c @@ -528,6 +528,8 @@ static struct smp_ops_t gemini_smp_ops = { smp_gemini_probe, smp_gemini_kick_cpu, smp_gemini_setup_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, }; #endif /* CONFIG_SMP */ diff --git a/arch/ppc/platforms/iSeries_smp.c b/arch/ppc/platforms/iSeries_smp.c index 055a4f58ce50eeef620aa1c0117f388110f2f59b..1a22a035125e6492ec57f64eaf0a8e61bcf89ee6 100644 --- a/arch/ppc/platforms/iSeries_smp.c +++ b/arch/ppc/platforms/iSeries_smp.c @@ -117,7 +117,7 @@ static void smp_iSeries_setup_cpu(int nr) set_dec( xPaca[nr].default_decr ); } -void smp_iSeries_space_timers( unsigned nr ) +static void smp_iSeries_space_timers(unsigned nr) { unsigned offset,i; @@ -131,6 +131,9 @@ struct smp_ops_t iSeries_smp_ops = { smp_iSeries_message_pass, smp_iSeries_probe, smp_iSeries_kick_cpu, - smp_iSeries_setup_cpu + smp_iSeries_setup_cpu, + smp_iSeries_space_timers, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, }; diff --git a/arch/ppc/platforms/pmac_smp.c b/arch/ppc/platforms/pmac_smp.c index 0ca0e3af9f58107574cd2fdf5f1b8569b0e72c23..cd5804be4e88bdbdc12b42912704ad343a66eb50 100644 --- a/arch/ppc/platforms/pmac_smp.c +++ b/arch/ppc/platforms/pmac_smp.c @@ -612,6 +612,8 @@ struct smp_ops_t psurge_smp_ops __pmacdata = { smp_psurge_probe, smp_psurge_kick_cpu, smp_psurge_setup_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, }; /* Core99 Macs (dual G4s) */ @@ -620,4 +622,6 @@ struct smp_ops_t core99_smp_ops __pmacdata = { smp_core99_probe, smp_core99_kick_cpu, smp_core99_setup_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, }; diff --git a/arch/ppc/platforms/pplus_setup.c b/arch/ppc/platforms/pplus_setup.c index 640c6164fbd0ec9f226d6951e758a5600d4aacd7..73d74c2d0e9989e297d8d020b3d80e6b4ab38b5c 100644 --- a/arch/ppc/platforms/pplus_setup.c +++ b/arch/ppc/platforms/pplus_setup.c @@ -309,6 +309,8 @@ static struct smp_ops_t pplus_smp_ops = { smp_pplus_probe, smp_pplus_kick_cpu, smp_pplus_setup_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, }; #endif /* CONFIG_SMP */ diff --git a/arch/ppc/platforms/prep_setup.c b/arch/ppc/platforms/prep_setup.c index 9ab36cbf3f10f6a17daa1d4fb57bceb6caba6f9a..25721ad975b7b9a7b7bc68f52f26d41ac512a56e 100644 --- a/arch/ppc/platforms/prep_setup.c +++ b/arch/ppc/platforms/prep_setup.c @@ -756,6 +756,8 @@ static struct smp_ops_t prep_smp_ops __prepdata = { smp_prep_probe, smp_prep_kick_cpu, smp_prep_setup_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, }; #endif /* CONFIG_SMP */ diff --git a/include/asm-ppc/machdep.h b/include/asm-ppc/machdep.h index f5218fdb73c2c82aba737f4bfd380860e537f45b..2c32dc7aa949d09e3092882d707d3217235c1486 100644 --- a/include/asm-ppc/machdep.h +++ b/include/asm-ppc/machdep.h @@ -6,6 +6,7 @@ #define _PPC_MACHDEP_H #include <linux/config.h> +#include <linux/init.h> #ifdef CONFIG_APUS #include <asm-m68k/machdep.h> @@ -129,7 +130,14 @@ struct smp_ops_t { int (*probe)(void); void (*kick_cpu)(int nr); void (*setup_cpu)(int nr); + void (*space_timers)(int nr); + void (*take_timebase)(void); + void (*give_timebase)(void); }; + +/* Poor default implementations */ +extern void __devinit smp_generic_give_timebase(void); +extern void __devinit smp_generic_take_timebase(void); #endif /* CONFIG_SMP */ #endif /* _PPC_MACHDEP_H */ diff --git a/include/asm-ppc/smp.h b/include/asm-ppc/smp.h index 6c58f14684a1c86d23042913d9abb005e869fd18..f5e99d565d32deb20998503181e6081b57bbc9bb 100644 --- a/include/asm-ppc/smp.h +++ b/include/asm-ppc/smp.h @@ -16,6 +16,7 @@ #include <linux/config.h> #include <linux/kernel.h> #include <linux/bitops.h> +#include <linux/errno.h> #ifdef CONFIG_SMP @@ -31,11 +32,11 @@ struct cpuinfo_PPC { extern struct cpuinfo_PPC cpu_data[]; extern unsigned long cpu_online_map; +extern unsigned long cpu_possible_map; extern unsigned long smp_proc_in_lock[]; extern volatile unsigned long cpu_callin_map[]; extern int smp_tb_synchronized; -extern void smp_store_cpu_info(int id); extern void smp_send_tlb_invalidate(int); extern void smp_send_xmon_break(int cpu); struct pt_regs; @@ -48,6 +49,7 @@ extern void smp_local_timer_interrupt(struct pt_regs *); #define smp_processor_id() (current_thread_info()->cpu) #define cpu_online(cpu) (cpu_online_map & (1<<(cpu))) +#define cpu_possible(cpu) (cpu_possible_map & (1<<(cpu))) extern inline unsigned int num_online_cpus(void) { @@ -62,6 +64,8 @@ extern inline int any_online_cpu(unsigned int mask) return -1; } +extern int __cpu_up(unsigned int cpu); + extern int smp_hw_index[]; #define hard_smp_processor_id() (smp_hw_index[smp_processor_id()])