aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel
diff options
context:
space:
mode:
authorDaniel Hellstrom <daniel@gaisler.com>2011-05-01 20:08:52 -0400
committerDavid S. Miller <davem@davemloft.net>2011-05-16 16:07:43 -0400
commit1ca0c808c60f171c1949b0e7f3a4c0516855f7a0 (patch)
treef1183b243d2e8ed42006891d80e0d077cabaf5e1 /arch/sparc/kernel
parentd6d048192b1d22cb8f09da0cc936095ec2cb969c (diff)
sparc32,leon: Implemented SMP IPIs for LEON CPU
This patch implements SMP IPIs on LEON using software generated IRQs to signal between CPUs. The IPI IRQ number is set by using the ipi_num property in the device tree, or defaults to 13. LEON SMP systems should reserve IRQ 13 (and IRQ 15) to Linux in order for the defaults to work. Signed-off-by: Daniel Hellstrom <daniel@gaisler.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc/kernel')
-rw-r--r--arch/sparc/kernel/entry.S16
-rw-r--r--arch/sparc/kernel/leon_smp.c109
2 files changed, 123 insertions, 2 deletions
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
index 1879739c9588..d759cf31c8ee 100644
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -401,6 +401,22 @@ linux_trap_ipi15_sun4d:
4011: b,a 1b 4011: b,a 1b
402 402
403#ifdef CONFIG_SPARC_LEON 403#ifdef CONFIG_SPARC_LEON
404 .globl smpleon_ipi
405 .extern leon_ipi_interrupt
406 /* SMP per-cpu IPI interrupts are handled specially. */
407smpleon_ipi:
408 SAVE_ALL
409 or %l0, PSR_PIL, %g2
410 wr %g2, 0x0, %psr
411 WRITE_PAUSE
412 wr %g2, PSR_ET, %psr
413 WRITE_PAUSE
414 call leonsmp_ipi_interrupt
415 add %sp, STACKFRAME_SZ, %o1 ! pt_regs
416 wr %l0, PSR_ET, %psr
417 WRITE_PAUSE
418 RESTORE_ALL
419
404 .align 4 420 .align 4
405 .globl linux_trap_ipi15_leon 421 .globl linux_trap_ipi15_leon
406linux_trap_ipi15_leon: 422linux_trap_ipi15_leon:
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;
52extern volatile unsigned long cpu_callin_map[NR_CPUS]; 54extern volatile unsigned long cpu_callin_map[NR_CPUS];
53extern cpumask_t smp_commenced_mask; 55extern cpumask_t smp_commenced_mask;
54void __init leon_configure_cache_smp(void); 56void __init leon_configure_cache_smp(void);
57static void leon_ipi_init(void);
58
59/* IRQ number of LEON IPIs */
60int leon_ipi_irq = LEON3_IRQ_IPI_DEFAULT;
55 61
56static inline unsigned long do_swap(volatile unsigned long *ptr, 62static 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
305struct leon_ipi_work {
306 int single;
307 int msk;
308 int resched;
309};
310
311static 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 */
316static 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
347static 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
358static 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
369static 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
380void 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
296static struct smp_funcall { 398static 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 */