@@ -239,7 +239,7 @@ static inline int sparc_leon3_cpuid(void)
#endif /*!__ASSEMBLY__*/
#ifdef CONFIG_SMP
-# define LEON3_IRQ_RESCHEDULE 13
+# define LEON3_IRQ_IPI 13
# define LEON3_IRQ_TICKER (leon_percpu_timer_dev[0].irq)
# define LEON3_IRQ_CROSS_CALL 15
#endif
@@ -366,6 +366,9 @@ extern void leon_smp_done(void);
extern void leon_boot_cpus(void);
extern int leon_boot_one_cpu(int i);
void leon_init_smp(void);
+extern void leon_send_call_function_ipi_mask(const struct cpumask *mask);
+extern void leon_send_call_function_single_ipi(int cpu);
+extern void leon_send_resched(int cpu);
extern void cpu_probe(void);
extern void cpu_idle(void);
extern void init_IRQ(void);
@@ -374,6 +377,7 @@ extern int __leon_processor_id(void);
void leon_enable_irq_cpu(unsigned int irq_nr, unsigned int cpu);
extern unsigned int real_irq_entry[], smpleon_ticker[];
+extern unsigned int smpleon_ipi[];
extern unsigned int patchme_maybe_smp_msg[];
extern unsigned long trapbase_cpu1[];
extern unsigned long trapbase_cpu2[];
@@ -417,6 +417,22 @@ smpleon_ticker:
WRITE_PAUSE
RESTORE_ALL
+ .globl smpleon_ipi
+ .extern leon_ipi_interrupt
+ /* SMP per-cpu IPI interrupts are handled specially. */
+smpleon_ipi:
+ SAVE_ALL
+ or %l0, PSR_PIL, %g2
+ wr %g2, 0x0, %psr
+ WRITE_PAUSE
+ wr %g2, PSR_ET, %psr
+ WRITE_PAUSE
+ call leonsmp_ipi_interrupt
+ add %sp, STACKFRAME_SZ, %o1 ! pt_regs
+ wr %l0, PSR_ET, %psr
+ WRITE_PAUSE
+ RESTORE_ALL
+
.align 4
.globl linux_trap_ipi15_leon
linux_trap_ipi15_leon:
@@ -245,6 +245,11 @@ void __init leon_init_timers(irq_handler_t counter_fn)
/* Adjust so that we jump directly to smpleon_ticker */
trap_table->inst_three += smpleon_ticker - real_irq_entry;
+ /* Adjust so that we jump directly to smpleon_ipi */
+ trap_table = &sparc_ttable[SP_TRAP_IRQ1 +
+ (LEON3_IRQ_IPI - 1)];
+ trap_table->inst_three += smpleon_ipi - real_irq_entry;
+
local_flush_cache_all();
local_irq_restore(flags);
}
@@ -183,7 +183,7 @@ void __init leon_boot_cpus(void)
leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, me);
leon_enable_irq_cpu(LEON3_IRQ_TICKER, me);
- leon_enable_irq_cpu(LEON3_IRQ_RESCHEDULE, me);
+ leon_enable_irq_cpu(LEON3_IRQ_IPI, me);
leon_smp_setbroadcast(1 << LEON3_IRQ_TICKER);
@@ -234,7 +234,7 @@ int __cpuinit leon_boot_one_cpu(int i)
} else {
leon_enable_irq_cpu(LEON3_IRQ_CROSS_CALL, i);
leon_enable_irq_cpu(LEON3_IRQ_TICKER, i);
- leon_enable_irq_cpu(LEON3_IRQ_RESCHEDULE, i);
+ leon_enable_irq_cpu(LEON3_IRQ_IPI, i);
}
local_flush_cache_all();
@@ -290,6 +290,79 @@ void leon_irq_rotate(int cpu)
{
}
+struct leon_ipi_work {
+ int single;
+ int msk;
+ int resched;
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct leon_ipi_work, leon_ipi_work);
+
+void leon_send_call_function_single_ipi(int cpu)
+{
+ struct leon_ipi_work *work = &per_cpu(leon_ipi_work, cpu);
+
+ /* Mark work */
+ work->single = 1;
+ mb();
+
+ /* Generate IRQ on the CPU */
+ set_cpu_int(cpu, LEON3_IRQ_IPI);
+}
+
+void leon_send_call_function_ipi_mask(const struct cpumask *mask)
+{
+ struct leon_ipi_work *work;
+ int i;
+
+ for_each_cpu(i, mask) {
+ /* Toggle work */
+ work = &per_cpu(leon_ipi_work, i);
+ work->msk = 1;
+ mb();
+
+ /* Generate IRQ on the CPU */
+ set_cpu_int(i, LEON3_IRQ_IPI);
+ }
+}
+
+void leon_send_resched(int cpu)
+{
+ struct leon_ipi_work *work = &per_cpu(leon_ipi_work, cpu);
+
+ /* Mark work... pointless really */
+ work->resched = 1;
+ mb();
+
+ /* Generate IRQ on the CPU */
+ set_cpu_int(cpu, LEON3_IRQ_IPI);
+}
+
+void leonsmp_ipi_interrupt(void)
+{
+ struct leon_ipi_work *work = &__get_cpu_var(leon_ipi_work);
+
+ if (work->single) {
+ work->single = 0;
+ mb();
+ generic_smp_call_function_single_interrupt();
+ }
+
+ if (work->msk) {
+ work->msk = 0;
+ mb();
+ generic_smp_call_function_interrupt();
+ }
+
+ if (work->resched) {
+ work->resched = 0;
+ mb();
+ /* do nothing, since it all was about calling re-schedule
+ * routine called by interrupt return code.
+ */
+ }
+}
+
static struct smp_funcall {
smpfunc_t func;
unsigned long arg1;
@@ -131,6 +131,9 @@ void smp_send_reschedule(int cpu)
* to call schedule.
*/
switch (sparc_cpu_model) {
+ case sparc_leon:
+ leon_send_resched(cpu);
+ break;
default:
BUG();
}
@@ -147,6 +150,9 @@ void arch_send_call_function_single_ipi(int cpu)
* a single CPU
*/
switch (sparc_cpu_model) {
+ case sparc_leon:
+ leon_send_call_function_single_ipi(cpu);
+ break;
default:
BUG();
}
@@ -159,6 +165,9 @@ void arch_send_call_function_ipi_mask(const struct cpumask *mask)
* a set of CPUs
*/
switch (sparc_cpu_model) {
+ case sparc_leon:
+ leon_send_call_function_ipi_mask(mask);
+ break;
default:
BUG();
}
This patch implements SMP IPIs on LEON using software generated IRQs to signal between CPUs. Signed-off-by: Daniel Hellstrom <daniel@gaisler.com> --- arch/sparc/include/asm/leon.h | 6 +++- arch/sparc/kernel/entry.S | 16 ++++++++ arch/sparc/kernel/leon_kernel.c | 5 +++ arch/sparc/kernel/leon_smp.c | 77 ++++++++++++++++++++++++++++++++++++++- arch/sparc/kernel/smp_32.c | 9 +++++ 5 files changed, 110 insertions(+), 3 deletions(-)