@ -14,6 +14,7 @@
# include <linux/smp.h>
# include <linux/interrupt.h>
# include <linux/kernel_stat.h>
# include <linux/of.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <linux/mm.h>
@ -29,6 +30,7 @@
# include <asm/ptrace.h>
# include <asm/atomic.h>
# include <asm/irq_regs.h>
# include <asm/traps.h>
# include <asm/delay.h>
# include <asm/irq.h>
@ -52,6 +54,10 @@ static int smp_processors_ready;
extern volatile unsigned long cpu_callin_map [ NR_CPUS ] ;
extern cpumask_t smp_commenced_mask ;
void __init leon_configure_cache_smp ( void ) ;
static void leon_ipi_init ( void ) ;
/* IRQ number of LEON IPIs */
int leon_ipi_irq = LEON3_IRQ_IPI_DEFAULT ;
static inline unsigned long do_swap ( volatile unsigned long * ptr ,
unsigned long val )
@ -176,13 +182,16 @@ void __init leon_boot_cpus(void)
int nrcpu = leon_smp_nrcpus ( ) ;
int me = smp_processor_id ( ) ;
/* Setup IPI */
leon_ipi_init ( ) ;
printk ( KERN_INFO " %d:(%d:%d) cpus mpirq at 0x%x \n " , ( unsigned int ) me ,
( unsigned int ) nrcpu , ( unsigned int ) NR_CPUS ,
( unsigned int ) & ( leon3_irqctrl_regs - > mpstatus ) ) ;
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 ( leon_ipi_irq , me ) ;
leon_smp_setbroadcast ( 1 < < LEON3_IRQ_TICKER ) ;
@ -237,7 +246,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 ( leon_ipi_irq , i ) ;
}
local_flush_cache_all ( ) ;
@ -293,6 +302,99 @@ 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 ) ;
/* Initialize IPIs on the LEON, in order to save IRQ resources only one IRQ
* is used for all three types of IPIs .
*/
static void __init leon_ipi_init ( void )
{
int cpu , len ;
struct leon_ipi_work * work ;
struct property * pp ;
struct device_node * rootnp ;
struct tt_entry * trap_table ;
unsigned long flags ;
/* Find IPI IRQ or stick with default value */
rootnp = of_find_node_by_path ( " /ambapp0 " ) ;
if ( rootnp ) {
pp = of_find_property ( rootnp , " ipi_num " , & len ) ;
if ( pp & & ( * ( int * ) pp - > value ) )
leon_ipi_irq = * ( int * ) pp - > value ;
}
printk ( KERN_INFO " leon: SMP IPIs at IRQ %d \n " , leon_ipi_irq ) ;
/* Adjust so that we jump directly to smpleon_ipi */
local_irq_save ( flags ) ;
trap_table = & sparc_ttable [ SP_TRAP_IRQ1 + ( leon_ipi_irq - 1 ) ] ;
trap_table - > inst_three + = smpleon_ipi - real_irq_entry ;
local_flush_cache_all ( ) ;
local_irq_restore ( flags ) ;
for_each_possible_cpu ( cpu ) {
work = & per_cpu ( leon_ipi_work , cpu ) ;
work - > single = work - > msk = work - > resched = 0 ;
}
}
static void leon_ipi_single ( int cpu )
{
struct leon_ipi_work * work = & per_cpu ( leon_ipi_work , cpu ) ;
/* Mark work */
work - > single = 1 ;
/* Generate IRQ on the CPU */
set_cpu_int ( cpu , leon_ipi_irq ) ;
}
static void leon_ipi_mask_one ( int cpu )
{
struct leon_ipi_work * work = & per_cpu ( leon_ipi_work , cpu ) ;
/* Mark work */
work - > msk = 1 ;
/* Generate IRQ on the CPU */
set_cpu_int ( cpu , leon_ipi_irq ) ;
}
static void leon_ipi_resched ( int cpu )
{
struct leon_ipi_work * work = & per_cpu ( leon_ipi_work , cpu ) ;
/* Mark work */
work - > resched = 1 ;
/* Generate IRQ on the CPU (any IRQ will cause resched) */
set_cpu_int ( cpu , leon_ipi_irq ) ;
}
void leonsmp_ipi_interrupt ( void )
{
struct leon_ipi_work * work = & __get_cpu_var ( leon_ipi_work ) ;
if ( work - > single ) {
work - > single = 0 ;
smp_call_function_single_interrupt ( ) ;
}
if ( work - > msk ) {
work - > msk = 0 ;
smp_call_function_interrupt ( ) ;
}
if ( work - > resched ) {
work - > resched = 0 ;
smp_resched_interrupt ( ) ;
}
}
static struct smp_funcall {
smpfunc_t func ;
unsigned long arg1 ;
@ -446,6 +548,9 @@ void __init leon_init_smp(void)
BTFIXUPSET_CALL ( smp_cross_call , leon_cross_call , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( __hard_smp_processor_id , __leon_processor_id ,
BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( smp_ipi_resched , leon_ipi_resched , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( smp_ipi_single , leon_ipi_single , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( smp_ipi_mask_one , leon_ipi_mask_one , BTFIXUPCALL_NORM ) ;
}
# endif /* CONFIG_SPARC_LEON */