diff options
Diffstat (limited to 'arch/sparc/kernel/leon_smp.c')
-rw-r--r-- | arch/sparc/kernel/leon_smp.c | 148 |
1 files changed, 125 insertions, 23 deletions
diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c index 8f5de4aa3c0a..fe8fb44c609c 100644 --- a/arch/sparc/kernel/leon_smp.c +++ b/arch/sparc/kernel/leon_smp.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/smp.h> | 14 | #include <linux/smp.h> |
15 | #include <linux/interrupt.h> | 15 | #include <linux/interrupt.h> |
16 | #include <linux/kernel_stat.h> | 16 | #include <linux/kernel_stat.h> |
17 | #include <linux/of.h> | ||
17 | #include <linux/init.h> | 18 | #include <linux/init.h> |
18 | #include <linux/spinlock.h> | 19 | #include <linux/spinlock.h> |
19 | #include <linux/mm.h> | 20 | #include <linux/mm.h> |
@@ -29,6 +30,7 @@ | |||
29 | #include <asm/ptrace.h> | 30 | #include <asm/ptrace.h> |
30 | #include <asm/atomic.h> | 31 | #include <asm/atomic.h> |
31 | #include <asm/irq_regs.h> | 32 | #include <asm/irq_regs.h> |
33 | #include <asm/traps.h> | ||
32 | 34 | ||
33 | #include <asm/delay.h> | 35 | #include <asm/delay.h> |
34 | #include <asm/irq.h> | 36 | #include <asm/irq.h> |
@@ -50,9 +52,12 @@ | |||
50 | extern ctxd_t *srmmu_ctx_table_phys; | 52 | extern ctxd_t *srmmu_ctx_table_phys; |
51 | static int smp_processors_ready; | 53 | static int smp_processors_ready; |
52 | extern volatile unsigned long cpu_callin_map[NR_CPUS]; | 54 | extern volatile unsigned long cpu_callin_map[NR_CPUS]; |
53 | extern unsigned char boot_cpu_id; | ||
54 | extern cpumask_t smp_commenced_mask; | 55 | extern cpumask_t smp_commenced_mask; |
55 | void __init leon_configure_cache_smp(void); | 56 | void __init leon_configure_cache_smp(void); |
57 | static void leon_ipi_init(void); | ||
58 | |||
59 | /* IRQ number of LEON IPIs */ | ||
60 | int leon_ipi_irq = LEON3_IRQ_IPI_DEFAULT; | ||
56 | 61 | ||
57 | static inline unsigned long do_swap(volatile unsigned long *ptr, | 62 | static inline unsigned long do_swap(volatile unsigned long *ptr, |
58 | unsigned long val) | 63 | unsigned long val) |
@@ -94,8 +99,6 @@ void __cpuinit leon_callin(void) | |||
94 | local_flush_cache_all(); | 99 | local_flush_cache_all(); |
95 | local_flush_tlb_all(); | 100 | local_flush_tlb_all(); |
96 | 101 | ||
97 | cpu_probe(); | ||
98 | |||
99 | /* Fix idle thread fields. */ | 102 | /* Fix idle thread fields. */ |
100 | __asm__ __volatile__("ld [%0], %%g6\n\t" : : "r"(¤t_set[cpuid]) | 103 | __asm__ __volatile__("ld [%0], %%g6\n\t" : : "r"(¤t_set[cpuid]) |
101 | : "memory" /* paranoid */); | 104 | : "memory" /* paranoid */); |
@@ -104,11 +107,11 @@ void __cpuinit leon_callin(void) | |||
104 | atomic_inc(&init_mm.mm_count); | 107 | atomic_inc(&init_mm.mm_count); |
105 | current->active_mm = &init_mm; | 108 | current->active_mm = &init_mm; |
106 | 109 | ||
107 | while (!cpu_isset(cpuid, smp_commenced_mask)) | 110 | while (!cpumask_test_cpu(cpuid, &smp_commenced_mask)) |
108 | mb(); | 111 | mb(); |
109 | 112 | ||
110 | local_irq_enable(); | 113 | local_irq_enable(); |
111 | cpu_set(cpuid, cpu_online_map); | 114 | set_cpu_online(cpuid, true); |
112 | } | 115 | } |
113 | 116 | ||
114 | /* | 117 | /* |
@@ -179,13 +182,16 @@ void __init leon_boot_cpus(void) | |||
179 | int nrcpu = leon_smp_nrcpus(); | 182 | int nrcpu = leon_smp_nrcpus(); |
180 | int me = smp_processor_id(); | 183 | int me = smp_processor_id(); |
181 | 184 | ||
185 | /* Setup IPI */ | ||
186 | leon_ipi_init(); | ||
187 | |||
182 | printk(KERN_INFO "%d:(%d:%d) cpus mpirq at 0x%x\n", (unsigned int)me, | 188 | printk(KERN_INFO "%d:(%d:%d) cpus mpirq at 0x%x\n", (unsigned int)me, |
183 | (unsigned int)nrcpu, (unsigned int)NR_CPUS, | 189 | (unsigned int)nrcpu, (unsigned int)NR_CPUS, |
184 | (unsigned int)&(leon3_irqctrl_regs->mpstatus)); | 190 | (unsigned int)&(leon3_irqctrl_regs->mpstatus)); |
185 | 191 | ||
186 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, me); | 192 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, me); |
187 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, me); | 193 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, me); |
188 | leon_enable_irq_cpu(LEON3_IRQ_RESCHEDULE, me); | 194 | leon_enable_irq_cpu(leon_ipi_irq, me); |
189 | 195 | ||
190 | leon_smp_setbroadcast(1 << LEON3_IRQ_TICKER); | 196 | leon_smp_setbroadcast(1 << LEON3_IRQ_TICKER); |
191 | 197 | ||
@@ -220,6 +226,10 @@ int __cpuinit leon_boot_one_cpu(int i) | |||
220 | (unsigned int)&leon3_irqctrl_regs->mpstatus); | 226 | (unsigned int)&leon3_irqctrl_regs->mpstatus); |
221 | local_flush_cache_all(); | 227 | local_flush_cache_all(); |
222 | 228 | ||
229 | /* Make sure all IRQs are of from the start for this new CPU */ | ||
230 | LEON_BYPASS_STORE_PA(&leon3_irqctrl_regs->mask[i], 0); | ||
231 | |||
232 | /* Wake one CPU */ | ||
223 | LEON_BYPASS_STORE_PA(&(leon3_irqctrl_regs->mpstatus), 1 << i); | 233 | LEON_BYPASS_STORE_PA(&(leon3_irqctrl_regs->mpstatus), 1 << i); |
224 | 234 | ||
225 | /* wheee... it's going... */ | 235 | /* wheee... it's going... */ |
@@ -236,7 +246,7 @@ int __cpuinit leon_boot_one_cpu(int i) | |||
236 | } else { | 246 | } else { |
237 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, i); | 247 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, i); |
238 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, i); | 248 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, i); |
239 | leon_enable_irq_cpu(LEON3_IRQ_RESCHEDULE, i); | 249 | leon_enable_irq_cpu(leon_ipi_irq, i); |
240 | } | 250 | } |
241 | 251 | ||
242 | local_flush_cache_all(); | 252 | local_flush_cache_all(); |
@@ -262,21 +272,21 @@ void __init leon_smp_done(void) | |||
262 | local_flush_cache_all(); | 272 | local_flush_cache_all(); |
263 | 273 | ||
264 | /* Free unneeded trap tables */ | 274 | /* Free unneeded trap tables */ |
265 | if (!cpu_isset(1, cpu_present_map)) { | 275 | if (!cpu_present(1)) { |
266 | ClearPageReserved(virt_to_page(&trapbase_cpu1)); | 276 | ClearPageReserved(virt_to_page(&trapbase_cpu1)); |
267 | init_page_count(virt_to_page(&trapbase_cpu1)); | 277 | init_page_count(virt_to_page(&trapbase_cpu1)); |
268 | free_page((unsigned long)&trapbase_cpu1); | 278 | free_page((unsigned long)&trapbase_cpu1); |
269 | totalram_pages++; | 279 | totalram_pages++; |
270 | num_physpages++; | 280 | num_physpages++; |
271 | } | 281 | } |
272 | if (!cpu_isset(2, cpu_present_map)) { | 282 | if (!cpu_present(2)) { |
273 | ClearPageReserved(virt_to_page(&trapbase_cpu2)); | 283 | ClearPageReserved(virt_to_page(&trapbase_cpu2)); |
274 | init_page_count(virt_to_page(&trapbase_cpu2)); | 284 | init_page_count(virt_to_page(&trapbase_cpu2)); |
275 | free_page((unsigned long)&trapbase_cpu2); | 285 | free_page((unsigned long)&trapbase_cpu2); |
276 | totalram_pages++; | 286 | totalram_pages++; |
277 | num_physpages++; | 287 | num_physpages++; |
278 | } | 288 | } |
279 | if (!cpu_isset(3, cpu_present_map)) { | 289 | if (!cpu_present(3)) { |
280 | ClearPageReserved(virt_to_page(&trapbase_cpu3)); | 290 | ClearPageReserved(virt_to_page(&trapbase_cpu3)); |
281 | init_page_count(virt_to_page(&trapbase_cpu3)); | 291 | init_page_count(virt_to_page(&trapbase_cpu3)); |
282 | free_page((unsigned long)&trapbase_cpu3); | 292 | free_page((unsigned long)&trapbase_cpu3); |
@@ -292,6 +302,99 @@ void leon_irq_rotate(int cpu) | |||
292 | { | 302 | { |
293 | } | 303 | } |
294 | 304 | ||
305 | struct leon_ipi_work { | ||
306 | int single; | ||
307 | int msk; | ||
308 | int resched; | ||
309 | }; | ||
310 | |||
311 | static DEFINE_PER_CPU_SHARED_ALIGNED(struct leon_ipi_work, leon_ipi_work); | ||
312 | |||
313 | /* Initialize IPIs on the LEON, in order to save IRQ resources only one IRQ | ||
314 | * is used for all three types of IPIs. | ||
315 | */ | ||
316 | static void __init leon_ipi_init(void) | ||
317 | { | ||
318 | int cpu, len; | ||
319 | struct leon_ipi_work *work; | ||
320 | struct property *pp; | ||
321 | struct device_node *rootnp; | ||
322 | struct tt_entry *trap_table; | ||
323 | unsigned long flags; | ||
324 | |||
325 | /* Find IPI IRQ or stick with default value */ | ||
326 | rootnp = of_find_node_by_path("/ambapp0"); | ||
327 | if (rootnp) { | ||
328 | pp = of_find_property(rootnp, "ipi_num", &len); | ||
329 | if (pp && (*(int *)pp->value)) | ||
330 | leon_ipi_irq = *(int *)pp->value; | ||
331 | } | ||
332 | printk(KERN_INFO "leon: SMP IPIs at IRQ %d\n", leon_ipi_irq); | ||
333 | |||
334 | /* Adjust so that we jump directly to smpleon_ipi */ | ||
335 | local_irq_save(flags); | ||
336 | trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (leon_ipi_irq - 1)]; | ||
337 | trap_table->inst_three += smpleon_ipi - real_irq_entry; | ||
338 | local_flush_cache_all(); | ||
339 | local_irq_restore(flags); | ||
340 | |||
341 | for_each_possible_cpu(cpu) { | ||
342 | work = &per_cpu(leon_ipi_work, cpu); | ||
343 | work->single = work->msk = work->resched = 0; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | static void leon_ipi_single(int cpu) | ||
348 | { | ||
349 | struct leon_ipi_work *work = &per_cpu(leon_ipi_work, cpu); | ||
350 | |||
351 | /* Mark work */ | ||
352 | work->single = 1; | ||
353 | |||
354 | /* Generate IRQ on the CPU */ | ||
355 | set_cpu_int(cpu, leon_ipi_irq); | ||
356 | } | ||
357 | |||
358 | static void leon_ipi_mask_one(int cpu) | ||
359 | { | ||
360 | struct leon_ipi_work *work = &per_cpu(leon_ipi_work, cpu); | ||
361 | |||
362 | /* Mark work */ | ||
363 | work->msk = 1; | ||
364 | |||
365 | /* Generate IRQ on the CPU */ | ||
366 | set_cpu_int(cpu, leon_ipi_irq); | ||
367 | } | ||
368 | |||
369 | static void leon_ipi_resched(int cpu) | ||
370 | { | ||
371 | struct leon_ipi_work *work = &per_cpu(leon_ipi_work, cpu); | ||
372 | |||
373 | /* Mark work */ | ||
374 | work->resched = 1; | ||
375 | |||
376 | /* Generate IRQ on the CPU (any IRQ will cause resched) */ | ||
377 | set_cpu_int(cpu, leon_ipi_irq); | ||
378 | } | ||
379 | |||
380 | void leonsmp_ipi_interrupt(void) | ||
381 | { | ||
382 | struct leon_ipi_work *work = &__get_cpu_var(leon_ipi_work); | ||
383 | |||
384 | if (work->single) { | ||
385 | work->single = 0; | ||
386 | smp_call_function_single_interrupt(); | ||
387 | } | ||
388 | if (work->msk) { | ||
389 | work->msk = 0; | ||
390 | smp_call_function_interrupt(); | ||
391 | } | ||
392 | if (work->resched) { | ||
393 | work->resched = 0; | ||
394 | smp_resched_interrupt(); | ||
395 | } | ||
396 | } | ||
397 | |||
295 | static struct smp_funcall { | 398 | static struct smp_funcall { |
296 | smpfunc_t func; | 399 | smpfunc_t func; |
297 | unsigned long arg1; | 400 | unsigned long arg1; |
@@ -337,10 +440,10 @@ static void leon_cross_call(smpfunc_t func, cpumask_t mask, unsigned long arg1, | |||
337 | { | 440 | { |
338 | register int i; | 441 | register int i; |
339 | 442 | ||
340 | cpu_clear(smp_processor_id(), mask); | 443 | cpumask_clear_cpu(smp_processor_id(), &mask); |
341 | cpus_and(mask, cpu_online_map, mask); | 444 | cpumask_and(&mask, cpu_online_mask, &mask); |
342 | for (i = 0; i <= high; i++) { | 445 | for (i = 0; i <= high; i++) { |
343 | if (cpu_isset(i, mask)) { | 446 | if (cpumask_test_cpu(i, &mask)) { |
344 | ccall_info.processors_in[i] = 0; | 447 | ccall_info.processors_in[i] = 0; |
345 | ccall_info.processors_out[i] = 0; | 448 | ccall_info.processors_out[i] = 0; |
346 | set_cpu_int(i, LEON3_IRQ_CROSS_CALL); | 449 | set_cpu_int(i, LEON3_IRQ_CROSS_CALL); |
@@ -354,7 +457,7 @@ static void leon_cross_call(smpfunc_t func, cpumask_t mask, unsigned long arg1, | |||
354 | 457 | ||
355 | i = 0; | 458 | i = 0; |
356 | do { | 459 | do { |
357 | if (!cpu_isset(i, mask)) | 460 | if (!cpumask_test_cpu(i, &mask)) |
358 | continue; | 461 | continue; |
359 | 462 | ||
360 | while (!ccall_info.processors_in[i]) | 463 | while (!ccall_info.processors_in[i]) |
@@ -363,7 +466,7 @@ static void leon_cross_call(smpfunc_t func, cpumask_t mask, unsigned long arg1, | |||
363 | 466 | ||
364 | i = 0; | 467 | i = 0; |
365 | do { | 468 | do { |
366 | if (!cpu_isset(i, mask)) | 469 | if (!cpumask_test_cpu(i, &mask)) |
367 | continue; | 470 | continue; |
368 | 471 | ||
369 | while (!ccall_info.processors_out[i]) | 472 | while (!ccall_info.processors_out[i]) |
@@ -386,27 +489,23 @@ void leon_cross_call_irq(void) | |||
386 | ccall_info.processors_out[i] = 1; | 489 | ccall_info.processors_out[i] = 1; |
387 | } | 490 | } |
388 | 491 | ||
389 | void leon_percpu_timer_interrupt(struct pt_regs *regs) | 492 | irqreturn_t leon_percpu_timer_interrupt(int irq, void *unused) |
390 | { | 493 | { |
391 | struct pt_regs *old_regs; | ||
392 | int cpu = smp_processor_id(); | 494 | int cpu = smp_processor_id(); |
393 | 495 | ||
394 | old_regs = set_irq_regs(regs); | ||
395 | |||
396 | leon_clear_profile_irq(cpu); | 496 | leon_clear_profile_irq(cpu); |
397 | 497 | ||
398 | profile_tick(CPU_PROFILING); | 498 | profile_tick(CPU_PROFILING); |
399 | 499 | ||
400 | if (!--prof_counter(cpu)) { | 500 | if (!--prof_counter(cpu)) { |
401 | int user = user_mode(regs); | 501 | int user = user_mode(get_irq_regs()); |
402 | 502 | ||
403 | irq_enter(); | ||
404 | update_process_times(user); | 503 | update_process_times(user); |
405 | irq_exit(); | ||
406 | 504 | ||
407 | prof_counter(cpu) = prof_multiplier(cpu); | 505 | prof_counter(cpu) = prof_multiplier(cpu); |
408 | } | 506 | } |
409 | set_irq_regs(old_regs); | 507 | |
508 | return IRQ_HANDLED; | ||
410 | } | 509 | } |
411 | 510 | ||
412 | static void __init smp_setup_percpu_timer(void) | 511 | static void __init smp_setup_percpu_timer(void) |
@@ -449,6 +548,9 @@ void __init leon_init_smp(void) | |||
449 | BTFIXUPSET_CALL(smp_cross_call, leon_cross_call, BTFIXUPCALL_NORM); | 548 | BTFIXUPSET_CALL(smp_cross_call, leon_cross_call, BTFIXUPCALL_NORM); |
450 | BTFIXUPSET_CALL(__hard_smp_processor_id, __leon_processor_id, | 549 | BTFIXUPSET_CALL(__hard_smp_processor_id, __leon_processor_id, |
451 | BTFIXUPCALL_NORM); | 550 | BTFIXUPCALL_NORM); |
551 | BTFIXUPSET_CALL(smp_ipi_resched, leon_ipi_resched, BTFIXUPCALL_NORM); | ||
552 | BTFIXUPSET_CALL(smp_ipi_single, leon_ipi_single, BTFIXUPCALL_NORM); | ||
553 | BTFIXUPSET_CALL(smp_ipi_mask_one, leon_ipi_mask_one, BTFIXUPCALL_NORM); | ||
452 | } | 554 | } |
453 | 555 | ||
454 | #endif /* CONFIG_SPARC_LEON */ | 556 | #endif /* CONFIG_SPARC_LEON */ |