diff options
Diffstat (limited to 'arch/powerpc/platforms/cell/smp.c')
-rw-r--r-- | arch/powerpc/platforms/cell/smp.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/cell/smp.c b/arch/powerpc/platforms/cell/smp.c new file mode 100644 index 000000000000..de96eadf419d --- /dev/null +++ b/arch/powerpc/platforms/cell/smp.c | |||
@@ -0,0 +1,230 @@ | |||
1 | /* | ||
2 | * SMP support for BPA machines. | ||
3 | * | ||
4 | * Dave Engebretsen, Peter Bergner, and | ||
5 | * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com | ||
6 | * | ||
7 | * Plus various changes from other IBM teams... | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version | ||
12 | * 2 of the License, or (at your option) any later version. | ||
13 | */ | ||
14 | |||
15 | #undef DEBUG | ||
16 | |||
17 | #include <linux/config.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/sched.h> | ||
21 | #include <linux/smp.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/spinlock.h> | ||
26 | #include <linux/cache.h> | ||
27 | #include <linux/err.h> | ||
28 | #include <linux/sysdev.h> | ||
29 | #include <linux/cpu.h> | ||
30 | |||
31 | #include <asm/ptrace.h> | ||
32 | #include <asm/atomic.h> | ||
33 | #include <asm/irq.h> | ||
34 | #include <asm/page.h> | ||
35 | #include <asm/pgtable.h> | ||
36 | #include <asm/io.h> | ||
37 | #include <asm/prom.h> | ||
38 | #include <asm/smp.h> | ||
39 | #include <asm/paca.h> | ||
40 | #include <asm/time.h> | ||
41 | #include <asm/machdep.h> | ||
42 | #include <asm/cputable.h> | ||
43 | #include <asm/firmware.h> | ||
44 | #include <asm/system.h> | ||
45 | #include <asm/rtas.h> | ||
46 | |||
47 | #include "interrupt.h" | ||
48 | |||
49 | #ifdef DEBUG | ||
50 | #define DBG(fmt...) udbg_printf(fmt) | ||
51 | #else | ||
52 | #define DBG(fmt...) | ||
53 | #endif | ||
54 | |||
55 | /* | ||
56 | * The primary thread of each non-boot processor is recorded here before | ||
57 | * smp init. | ||
58 | */ | ||
59 | static cpumask_t of_spin_map; | ||
60 | |||
61 | extern void pSeries_secondary_smp_init(unsigned long); | ||
62 | |||
63 | /** | ||
64 | * smp_startup_cpu() - start the given cpu | ||
65 | * | ||
66 | * At boot time, there is nothing to do for primary threads which were | ||
67 | * started from Open Firmware. For anything else, call RTAS with the | ||
68 | * appropriate start location. | ||
69 | * | ||
70 | * Returns: | ||
71 | * 0 - failure | ||
72 | * 1 - success | ||
73 | */ | ||
74 | static inline int __devinit smp_startup_cpu(unsigned int lcpu) | ||
75 | { | ||
76 | int status; | ||
77 | unsigned long start_here = __pa((u32)*((unsigned long *) | ||
78 | pSeries_secondary_smp_init)); | ||
79 | unsigned int pcpu; | ||
80 | int start_cpu; | ||
81 | |||
82 | if (cpu_isset(lcpu, of_spin_map)) | ||
83 | /* Already started by OF and sitting in spin loop */ | ||
84 | return 1; | ||
85 | |||
86 | pcpu = get_hard_smp_processor_id(lcpu); | ||
87 | |||
88 | /* Fixup atomic count: it exited inside IRQ handler. */ | ||
89 | paca[lcpu].__current->thread_info->preempt_count = 0; | ||
90 | |||
91 | /* | ||
92 | * If the RTAS start-cpu token does not exist then presume the | ||
93 | * cpu is already spinning. | ||
94 | */ | ||
95 | start_cpu = rtas_token("start-cpu"); | ||
96 | if (start_cpu == RTAS_UNKNOWN_SERVICE) | ||
97 | return 1; | ||
98 | |||
99 | status = rtas_call(start_cpu, 3, 1, NULL, pcpu, start_here, lcpu); | ||
100 | if (status != 0) { | ||
101 | printk(KERN_ERR "start-cpu failed: %i\n", status); | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | return 1; | ||
106 | } | ||
107 | |||
108 | static void smp_iic_message_pass(int target, int msg) | ||
109 | { | ||
110 | unsigned int i; | ||
111 | |||
112 | if (target < NR_CPUS) { | ||
113 | iic_cause_IPI(target, msg); | ||
114 | } else { | ||
115 | for_each_online_cpu(i) { | ||
116 | if (target == MSG_ALL_BUT_SELF | ||
117 | && i == smp_processor_id()) | ||
118 | continue; | ||
119 | iic_cause_IPI(i, msg); | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | |||
124 | static int __init smp_iic_probe(void) | ||
125 | { | ||
126 | iic_request_IPIs(); | ||
127 | |||
128 | return cpus_weight(cpu_possible_map); | ||
129 | } | ||
130 | |||
131 | static void __devinit smp_iic_setup_cpu(int cpu) | ||
132 | { | ||
133 | if (cpu != boot_cpuid) | ||
134 | iic_setup_cpu(); | ||
135 | } | ||
136 | |||
137 | static DEFINE_SPINLOCK(timebase_lock); | ||
138 | static unsigned long timebase = 0; | ||
139 | |||
140 | static void __devinit cell_give_timebase(void) | ||
141 | { | ||
142 | spin_lock(&timebase_lock); | ||
143 | rtas_call(rtas_token("freeze-time-base"), 0, 1, NULL); | ||
144 | timebase = get_tb(); | ||
145 | spin_unlock(&timebase_lock); | ||
146 | |||
147 | while (timebase) | ||
148 | barrier(); | ||
149 | rtas_call(rtas_token("thaw-time-base"), 0, 1, NULL); | ||
150 | } | ||
151 | |||
152 | static void __devinit cell_take_timebase(void) | ||
153 | { | ||
154 | while (!timebase) | ||
155 | barrier(); | ||
156 | spin_lock(&timebase_lock); | ||
157 | set_tb(timebase >> 32, timebase & 0xffffffff); | ||
158 | timebase = 0; | ||
159 | spin_unlock(&timebase_lock); | ||
160 | } | ||
161 | |||
162 | static void __devinit smp_cell_kick_cpu(int nr) | ||
163 | { | ||
164 | BUG_ON(nr < 0 || nr >= NR_CPUS); | ||
165 | |||
166 | if (!smp_startup_cpu(nr)) | ||
167 | return; | ||
168 | |||
169 | /* | ||
170 | * The processor is currently spinning, waiting for the | ||
171 | * cpu_start field to become non-zero After we set cpu_start, | ||
172 | * the processor will continue on to secondary_start | ||
173 | */ | ||
174 | paca[nr].cpu_start = 1; | ||
175 | } | ||
176 | |||
177 | static int smp_cell_cpu_bootable(unsigned int nr) | ||
178 | { | ||
179 | /* Special case - we inhibit secondary thread startup | ||
180 | * during boot if the user requests it. Odd-numbered | ||
181 | * cpus are assumed to be secondary threads. | ||
182 | */ | ||
183 | if (system_state < SYSTEM_RUNNING && | ||
184 | cpu_has_feature(CPU_FTR_SMT) && | ||
185 | !smt_enabled_at_boot && nr % 2 != 0) | ||
186 | return 0; | ||
187 | |||
188 | return 1; | ||
189 | } | ||
190 | static struct smp_ops_t bpa_iic_smp_ops = { | ||
191 | .message_pass = smp_iic_message_pass, | ||
192 | .probe = smp_iic_probe, | ||
193 | .kick_cpu = smp_cell_kick_cpu, | ||
194 | .setup_cpu = smp_iic_setup_cpu, | ||
195 | .cpu_bootable = smp_cell_cpu_bootable, | ||
196 | }; | ||
197 | |||
198 | /* This is called very early */ | ||
199 | void __init smp_init_cell(void) | ||
200 | { | ||
201 | int i; | ||
202 | |||
203 | DBG(" -> smp_init_cell()\n"); | ||
204 | |||
205 | smp_ops = &bpa_iic_smp_ops; | ||
206 | |||
207 | /* Mark threads which are still spinning in hold loops. */ | ||
208 | if (cpu_has_feature(CPU_FTR_SMT)) { | ||
209 | for_each_present_cpu(i) { | ||
210 | if (i % 2 == 0) | ||
211 | /* | ||
212 | * Even-numbered logical cpus correspond to | ||
213 | * primary threads. | ||
214 | */ | ||
215 | cpu_set(i, of_spin_map); | ||
216 | } | ||
217 | } else { | ||
218 | of_spin_map = cpu_present_map; | ||
219 | } | ||
220 | |||
221 | cpu_clear(boot_cpuid, of_spin_map); | ||
222 | |||
223 | /* Non-lpar has additional take/give timebase */ | ||
224 | if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) { | ||
225 | smp_ops->give_timebase = cell_give_timebase; | ||
226 | smp_ops->take_timebase = cell_take_timebase; | ||
227 | } | ||
228 | |||
229 | DBG(" <- smp_init_cell()\n"); | ||
230 | } | ||