diff options
Diffstat (limited to 'arch/sparc/kernel/leon_smp.c')
-rw-r--r-- | arch/sparc/kernel/leon_smp.c | 189 |
1 files changed, 138 insertions, 51 deletions
diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c index e1656fc41ccb..fe8fb44c609c 100644 --- a/arch/sparc/kernel/leon_smp.c +++ b/arch/sparc/kernel/leon_smp.c | |||
@@ -12,9 +12,9 @@ | |||
12 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
13 | #include <linux/threads.h> | 13 | #include <linux/threads.h> |
14 | #include <linux/smp.h> | 14 | #include <linux/smp.h> |
15 | #include <linux/smp_lock.h> | ||
16 | #include <linux/interrupt.h> | 15 | #include <linux/interrupt.h> |
17 | #include <linux/kernel_stat.h> | 16 | #include <linux/kernel_stat.h> |
17 | #include <linux/of.h> | ||
18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
19 | #include <linux/spinlock.h> | 19 | #include <linux/spinlock.h> |
20 | #include <linux/mm.h> | 20 | #include <linux/mm.h> |
@@ -30,6 +30,7 @@ | |||
30 | #include <asm/ptrace.h> | 30 | #include <asm/ptrace.h> |
31 | #include <asm/atomic.h> | 31 | #include <asm/atomic.h> |
32 | #include <asm/irq_regs.h> | 32 | #include <asm/irq_regs.h> |
33 | #include <asm/traps.h> | ||
33 | 34 | ||
34 | #include <asm/delay.h> | 35 | #include <asm/delay.h> |
35 | #include <asm/irq.h> | 36 | #include <asm/irq.h> |
@@ -42,6 +43,8 @@ | |||
42 | #include <asm/leon.h> | 43 | #include <asm/leon.h> |
43 | #include <asm/leon_amba.h> | 44 | #include <asm/leon_amba.h> |
44 | 45 | ||
46 | #include "kernel.h" | ||
47 | |||
45 | #ifdef CONFIG_SPARC_LEON | 48 | #ifdef CONFIG_SPARC_LEON |
46 | 49 | ||
47 | #include "irq.h" | 50 | #include "irq.h" |
@@ -49,15 +52,18 @@ | |||
49 | extern ctxd_t *srmmu_ctx_table_phys; | 52 | extern ctxd_t *srmmu_ctx_table_phys; |
50 | static int smp_processors_ready; | 53 | static int smp_processors_ready; |
51 | extern volatile unsigned long cpu_callin_map[NR_CPUS]; | 54 | extern volatile unsigned long cpu_callin_map[NR_CPUS]; |
52 | extern unsigned char boot_cpu_id; | ||
53 | extern cpumask_t smp_commenced_mask; | 55 | extern cpumask_t smp_commenced_mask; |
54 | 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; | ||
55 | 61 | ||
56 | static inline unsigned long do_swap(volatile unsigned long *ptr, | 62 | static inline unsigned long do_swap(volatile unsigned long *ptr, |
57 | unsigned long val) | 63 | unsigned long val) |
58 | { | 64 | { |
59 | __asm__ __volatile__("swapa [%1] %2, %0\n\t" : "=&r"(val) | 65 | __asm__ __volatile__("swapa [%2] %3, %0\n\t" : "=&r"(val) |
60 | : "r"(ptr), "i"(ASI_LEON_DCACHE_MISS) | 66 | : "0"(val), "r"(ptr), "i"(ASI_LEON_DCACHE_MISS) |
61 | : "memory"); | 67 | : "memory"); |
62 | return val; | 68 | return val; |
63 | } | 69 | } |
@@ -93,8 +99,6 @@ void __cpuinit leon_callin(void) | |||
93 | local_flush_cache_all(); | 99 | local_flush_cache_all(); |
94 | local_flush_tlb_all(); | 100 | local_flush_tlb_all(); |
95 | 101 | ||
96 | cpu_probe(); | ||
97 | |||
98 | /* Fix idle thread fields. */ | 102 | /* Fix idle thread fields. */ |
99 | __asm__ __volatile__("ld [%0], %%g6\n\t" : : "r"(¤t_set[cpuid]) | 103 | __asm__ __volatile__("ld [%0], %%g6\n\t" : : "r"(¤t_set[cpuid]) |
100 | : "memory" /* paranoid */); | 104 | : "memory" /* paranoid */); |
@@ -103,11 +107,11 @@ void __cpuinit leon_callin(void) | |||
103 | atomic_inc(&init_mm.mm_count); | 107 | atomic_inc(&init_mm.mm_count); |
104 | current->active_mm = &init_mm; | 108 | current->active_mm = &init_mm; |
105 | 109 | ||
106 | while (!cpu_isset(cpuid, smp_commenced_mask)) | 110 | while (!cpumask_test_cpu(cpuid, &smp_commenced_mask)) |
107 | mb(); | 111 | mb(); |
108 | 112 | ||
109 | local_irq_enable(); | 113 | local_irq_enable(); |
110 | cpu_set(cpuid, cpu_online_map); | 114 | set_cpu_online(cpuid, true); |
111 | } | 115 | } |
112 | 116 | ||
113 | /* | 117 | /* |
@@ -178,13 +182,16 @@ void __init leon_boot_cpus(void) | |||
178 | int nrcpu = leon_smp_nrcpus(); | 182 | int nrcpu = leon_smp_nrcpus(); |
179 | int me = smp_processor_id(); | 183 | int me = smp_processor_id(); |
180 | 184 | ||
185 | /* Setup IPI */ | ||
186 | leon_ipi_init(); | ||
187 | |||
181 | 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, |
182 | (unsigned int)nrcpu, (unsigned int)NR_CPUS, | 189 | (unsigned int)nrcpu, (unsigned int)NR_CPUS, |
183 | (unsigned int)&(leon3_irqctrl_regs->mpstatus)); | 190 | (unsigned int)&(leon3_irqctrl_regs->mpstatus)); |
184 | 191 | ||
185 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, me); | 192 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, me); |
186 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, me); | 193 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, me); |
187 | leon_enable_irq_cpu(LEON3_IRQ_RESCHEDULE, me); | 194 | leon_enable_irq_cpu(leon_ipi_irq, me); |
188 | 195 | ||
189 | leon_smp_setbroadcast(1 << LEON3_IRQ_TICKER); | 196 | leon_smp_setbroadcast(1 << LEON3_IRQ_TICKER); |
190 | 197 | ||
@@ -219,6 +226,10 @@ int __cpuinit leon_boot_one_cpu(int i) | |||
219 | (unsigned int)&leon3_irqctrl_regs->mpstatus); | 226 | (unsigned int)&leon3_irqctrl_regs->mpstatus); |
220 | local_flush_cache_all(); | 227 | local_flush_cache_all(); |
221 | 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 */ | ||
222 | LEON_BYPASS_STORE_PA(&(leon3_irqctrl_regs->mpstatus), 1 << i); | 233 | LEON_BYPASS_STORE_PA(&(leon3_irqctrl_regs->mpstatus), 1 << i); |
223 | 234 | ||
224 | /* wheee... it's going... */ | 235 | /* wheee... it's going... */ |
@@ -235,7 +246,7 @@ int __cpuinit leon_boot_one_cpu(int i) | |||
235 | } else { | 246 | } else { |
236 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, i); | 247 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, i); |
237 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, i); | 248 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, i); |
238 | leon_enable_irq_cpu(LEON3_IRQ_RESCHEDULE, i); | 249 | leon_enable_irq_cpu(leon_ipi_irq, i); |
239 | } | 250 | } |
240 | 251 | ||
241 | local_flush_cache_all(); | 252 | local_flush_cache_all(); |
@@ -261,24 +272,24 @@ void __init leon_smp_done(void) | |||
261 | local_flush_cache_all(); | 272 | local_flush_cache_all(); |
262 | 273 | ||
263 | /* Free unneeded trap tables */ | 274 | /* Free unneeded trap tables */ |
264 | if (!cpu_isset(1, cpu_present_map)) { | 275 | if (!cpu_present(1)) { |
265 | ClearPageReserved(virt_to_page(trapbase_cpu1)); | 276 | ClearPageReserved(virt_to_page(&trapbase_cpu1)); |
266 | init_page_count(virt_to_page(trapbase_cpu1)); | 277 | init_page_count(virt_to_page(&trapbase_cpu1)); |
267 | free_page((unsigned long)trapbase_cpu1); | 278 | free_page((unsigned long)&trapbase_cpu1); |
268 | totalram_pages++; | 279 | totalram_pages++; |
269 | num_physpages++; | 280 | num_physpages++; |
270 | } | 281 | } |
271 | if (!cpu_isset(2, cpu_present_map)) { | 282 | if (!cpu_present(2)) { |
272 | ClearPageReserved(virt_to_page(trapbase_cpu2)); | 283 | ClearPageReserved(virt_to_page(&trapbase_cpu2)); |
273 | init_page_count(virt_to_page(trapbase_cpu2)); | 284 | init_page_count(virt_to_page(&trapbase_cpu2)); |
274 | free_page((unsigned long)trapbase_cpu2); | 285 | free_page((unsigned long)&trapbase_cpu2); |
275 | totalram_pages++; | 286 | totalram_pages++; |
276 | num_physpages++; | 287 | num_physpages++; |
277 | } | 288 | } |
278 | if (!cpu_isset(3, cpu_present_map)) { | 289 | if (!cpu_present(3)) { |
279 | ClearPageReserved(virt_to_page(trapbase_cpu3)); | 290 | ClearPageReserved(virt_to_page(&trapbase_cpu3)); |
280 | init_page_count(virt_to_page(trapbase_cpu3)); | 291 | init_page_count(virt_to_page(&trapbase_cpu3)); |
281 | free_page((unsigned long)trapbase_cpu3); | 292 | free_page((unsigned long)&trapbase_cpu3); |
282 | totalram_pages++; | 293 | totalram_pages++; |
283 | num_physpages++; | 294 | num_physpages++; |
284 | } | 295 | } |
@@ -291,6 +302,99 @@ void leon_irq_rotate(int cpu) | |||
291 | { | 302 | { |
292 | } | 303 | } |
293 | 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 | |||
294 | static struct smp_funcall { | 398 | static struct smp_funcall { |
295 | smpfunc_t func; | 399 | smpfunc_t func; |
296 | unsigned long arg1; | 400 | unsigned long arg1; |
@@ -336,10 +440,10 @@ static void leon_cross_call(smpfunc_t func, cpumask_t mask, unsigned long arg1, | |||
336 | { | 440 | { |
337 | register int i; | 441 | register int i; |
338 | 442 | ||
339 | cpu_clear(smp_processor_id(), mask); | 443 | cpumask_clear_cpu(smp_processor_id(), &mask); |
340 | cpus_and(mask, cpu_online_map, mask); | 444 | cpumask_and(&mask, cpu_online_mask, &mask); |
341 | for (i = 0; i <= high; i++) { | 445 | for (i = 0; i <= high; i++) { |
342 | if (cpu_isset(i, mask)) { | 446 | if (cpumask_test_cpu(i, &mask)) { |
343 | ccall_info.processors_in[i] = 0; | 447 | ccall_info.processors_in[i] = 0; |
344 | ccall_info.processors_out[i] = 0; | 448 | ccall_info.processors_out[i] = 0; |
345 | set_cpu_int(i, LEON3_IRQ_CROSS_CALL); | 449 | set_cpu_int(i, LEON3_IRQ_CROSS_CALL); |
@@ -353,7 +457,7 @@ static void leon_cross_call(smpfunc_t func, cpumask_t mask, unsigned long arg1, | |||
353 | 457 | ||
354 | i = 0; | 458 | i = 0; |
355 | do { | 459 | do { |
356 | if (!cpu_isset(i, mask)) | 460 | if (!cpumask_test_cpu(i, &mask)) |
357 | continue; | 461 | continue; |
358 | 462 | ||
359 | while (!ccall_info.processors_in[i]) | 463 | while (!ccall_info.processors_in[i]) |
@@ -362,7 +466,7 @@ static void leon_cross_call(smpfunc_t func, cpumask_t mask, unsigned long arg1, | |||
362 | 466 | ||
363 | i = 0; | 467 | i = 0; |
364 | do { | 468 | do { |
365 | if (!cpu_isset(i, mask)) | 469 | if (!cpumask_test_cpu(i, &mask)) |
366 | continue; | 470 | continue; |
367 | 471 | ||
368 | while (!ccall_info.processors_out[i]) | 472 | while (!ccall_info.processors_out[i]) |
@@ -385,27 +489,23 @@ void leon_cross_call_irq(void) | |||
385 | ccall_info.processors_out[i] = 1; | 489 | ccall_info.processors_out[i] = 1; |
386 | } | 490 | } |
387 | 491 | ||
388 | void leon_percpu_timer_interrupt(struct pt_regs *regs) | 492 | irqreturn_t leon_percpu_timer_interrupt(int irq, void *unused) |
389 | { | 493 | { |
390 | struct pt_regs *old_regs; | ||
391 | int cpu = smp_processor_id(); | 494 | int cpu = smp_processor_id(); |
392 | 495 | ||
393 | old_regs = set_irq_regs(regs); | ||
394 | |||
395 | leon_clear_profile_irq(cpu); | 496 | leon_clear_profile_irq(cpu); |
396 | 497 | ||
397 | profile_tick(CPU_PROFILING); | 498 | profile_tick(CPU_PROFILING); |
398 | 499 | ||
399 | if (!--prof_counter(cpu)) { | 500 | if (!--prof_counter(cpu)) { |
400 | int user = user_mode(regs); | 501 | int user = user_mode(get_irq_regs()); |
401 | 502 | ||
402 | irq_enter(); | ||
403 | update_process_times(user); | 503 | update_process_times(user); |
404 | irq_exit(); | ||
405 | 504 | ||
406 | prof_counter(cpu) = prof_multiplier(cpu); | 505 | prof_counter(cpu) = prof_multiplier(cpu); |
407 | } | 506 | } |
408 | set_irq_regs(old_regs); | 507 | |
508 | return IRQ_HANDLED; | ||
409 | } | 509 | } |
410 | 510 | ||
411 | static void __init smp_setup_percpu_timer(void) | 511 | static void __init smp_setup_percpu_timer(void) |
@@ -438,15 +538,6 @@ void __init leon_blackbox_current(unsigned *addr) | |||
438 | 538 | ||
439 | } | 539 | } |
440 | 540 | ||
441 | /* | ||
442 | * CPU idle callback function | ||
443 | * See .../arch/sparc/kernel/process.c | ||
444 | */ | ||
445 | void pmc_leon_idle(void) | ||
446 | { | ||
447 | __asm__ volatile ("mov %g0, %asr19"); | ||
448 | } | ||
449 | |||
450 | void __init leon_init_smp(void) | 541 | void __init leon_init_smp(void) |
451 | { | 542 | { |
452 | /* Patch ipi15 trap table */ | 543 | /* Patch ipi15 trap table */ |
@@ -457,13 +548,9 @@ void __init leon_init_smp(void) | |||
457 | BTFIXUPSET_CALL(smp_cross_call, leon_cross_call, BTFIXUPCALL_NORM); | 548 | BTFIXUPSET_CALL(smp_cross_call, leon_cross_call, BTFIXUPCALL_NORM); |
458 | BTFIXUPSET_CALL(__hard_smp_processor_id, __leon_processor_id, | 549 | BTFIXUPSET_CALL(__hard_smp_processor_id, __leon_processor_id, |
459 | BTFIXUPCALL_NORM); | 550 | BTFIXUPCALL_NORM); |
460 | 551 | BTFIXUPSET_CALL(smp_ipi_resched, leon_ipi_resched, BTFIXUPCALL_NORM); | |
461 | #ifndef PMC_NO_IDLE | 552 | BTFIXUPSET_CALL(smp_ipi_single, leon_ipi_single, BTFIXUPCALL_NORM); |
462 | /* Assign power management IDLE handler */ | 553 | BTFIXUPSET_CALL(smp_ipi_mask_one, leon_ipi_mask_one, BTFIXUPCALL_NORM); |
463 | pm_idle = pmc_leon_idle; | ||
464 | printk(KERN_INFO "leon: power management initialized\n"); | ||
465 | #endif | ||
466 | |||
467 | } | 554 | } |
468 | 555 | ||
469 | #endif /* CONFIG_SPARC_LEON */ | 556 | #endif /* CONFIG_SPARC_LEON */ |