diff options
Diffstat (limited to 'arch/sparc/kernel/leon_smp.c')
-rw-r--r-- | arch/sparc/kernel/leon_smp.c | 109 |
1 files changed, 107 insertions, 2 deletions
diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c index de9506d9ad7d..d95e456a04b6 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> |
@@ -52,6 +54,10 @@ 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 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) |
@@ -176,13 +182,16 @@ void __init leon_boot_cpus(void) | |||
176 | int nrcpu = leon_smp_nrcpus(); | 182 | int nrcpu = leon_smp_nrcpus(); |
177 | int me = smp_processor_id(); | 183 | int me = smp_processor_id(); |
178 | 184 | ||
185 | /* Setup IPI */ | ||
186 | leon_ipi_init(); | ||
187 | |||
179 | 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, |
180 | (unsigned int)nrcpu, (unsigned int)NR_CPUS, | 189 | (unsigned int)nrcpu, (unsigned int)NR_CPUS, |
181 | (unsigned int)&(leon3_irqctrl_regs->mpstatus)); | 190 | (unsigned int)&(leon3_irqctrl_regs->mpstatus)); |
182 | 191 | ||
183 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, me); | 192 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, me); |
184 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, me); | 193 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, me); |
185 | leon_enable_irq_cpu(LEON3_IRQ_RESCHEDULE, me); | 194 | leon_enable_irq_cpu(leon_ipi_irq, me); |
186 | 195 | ||
187 | leon_smp_setbroadcast(1 << LEON3_IRQ_TICKER); | 196 | leon_smp_setbroadcast(1 << LEON3_IRQ_TICKER); |
188 | 197 | ||
@@ -237,7 +246,7 @@ int __cpuinit leon_boot_one_cpu(int i) | |||
237 | } else { | 246 | } else { |
238 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, i); | 247 | leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, i); |
239 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, i); | 248 | leon_enable_irq_cpu(LEON3_IRQ_TICKER, i); |
240 | leon_enable_irq_cpu(LEON3_IRQ_RESCHEDULE, i); | 249 | leon_enable_irq_cpu(leon_ipi_irq, i); |
241 | } | 250 | } |
242 | 251 | ||
243 | local_flush_cache_all(); | 252 | local_flush_cache_all(); |
@@ -293,6 +302,99 @@ void leon_irq_rotate(int cpu) | |||
293 | { | 302 | { |
294 | } | 303 | } |
295 | 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 | |||
296 | static struct smp_funcall { | 398 | static struct smp_funcall { |
297 | smpfunc_t func; | 399 | smpfunc_t func; |
298 | unsigned long arg1; | 400 | unsigned long arg1; |
@@ -446,6 +548,9 @@ void __init leon_init_smp(void) | |||
446 | BTFIXUPSET_CALL(smp_cross_call, leon_cross_call, BTFIXUPCALL_NORM); | 548 | BTFIXUPSET_CALL(smp_cross_call, leon_cross_call, BTFIXUPCALL_NORM); |
447 | BTFIXUPSET_CALL(__hard_smp_processor_id, __leon_processor_id, | 549 | BTFIXUPSET_CALL(__hard_smp_processor_id, __leon_processor_id, |
448 | 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); | ||
449 | } | 554 | } |
450 | 555 | ||
451 | #endif /* CONFIG_SPARC_LEON */ | 556 | #endif /* CONFIG_SPARC_LEON */ |