2011-01-19 10:24:56 +00:00
/*
* linux / arch / arm / plat - versatile / platsmp . c
*
* Copyright ( C ) 2002 ARM Ltd .
* All Rights Reserved
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/jiffies.h>
# include <linux/smp.h>
# include <asm/cacheflush.h>
2012-01-20 12:01:12 +01:00
# include <asm/smp_plat.h>
2011-04-03 13:01:30 +01:00
# include <asm/hardware/gic.h>
2011-01-19 10:24:56 +00:00
/*
* control for which core is the next to come out of the secondary
* boot " holding pen "
*/
volatile int __cpuinitdata pen_release = - 1 ;
/*
* Write pen_release in a way that is guaranteed to be visible to all
* observers , irrespective of whether they ' re taking part in coherency
* or not . This is necessary for the hotplug code to work reliably .
*/
static void __cpuinit write_pen_release ( int val )
{
pen_release = val ;
smp_wmb ( ) ;
__cpuc_flush_dcache_area ( ( void * ) & pen_release , sizeof ( pen_release ) ) ;
outer_clean_range ( __pa ( & pen_release ) , __pa ( & pen_release + 1 ) ) ;
}
static DEFINE_SPINLOCK ( boot_lock ) ;
void __cpuinit platform_secondary_init ( unsigned int cpu )
{
/*
* if any interrupts are already enabled for the primary
* core ( e . g . timer irq ) , then they will not have been enabled
* for us : do so
*/
gic_secondary_init ( 0 ) ;
/*
* let the primary processor know we ' re out of the
* pen , then head off into the C entry point
*/
write_pen_release ( - 1 ) ;
/*
* Synchronise with the boot thread .
*/
spin_lock ( & boot_lock ) ;
spin_unlock ( & boot_lock ) ;
}
int __cpuinit boot_secondary ( unsigned int cpu , struct task_struct * idle )
{
unsigned long timeout ;
/*
* Set synchronisation state between this boot processor
* and the secondary one
*/
spin_lock ( & boot_lock ) ;
/*
* This is really belt and braces ; we hold unintended secondary
* CPUs in the holding pen until we ' re ready for them . However ,
* since we haven ' t sent them a soft interrupt , they shouldn ' t
* be there .
*/
2011-08-09 12:24:07 +01:00
write_pen_release ( cpu_logical_map ( cpu ) ) ;
2011-01-19 10:24:56 +00:00
/*
* Send the secondary CPU a soft interrupt , thereby causing
* the boot monitor to read the system wide flags register ,
* and branch to the address found there .
*/
2012-06-11 20:23:20 +01:00
gic_raise_softirq ( cpumask_of ( cpu ) , 0 ) ;
2011-01-19 10:24:56 +00:00
timeout = jiffies + ( 1 * HZ ) ;
while ( time_before ( jiffies , timeout ) ) {
smp_rmb ( ) ;
if ( pen_release = = - 1 )
break ;
udelay ( 10 ) ;
}
/*
* now the secondary core is starting up let it run its
* calibrations , then wait for it to finish
*/
spin_unlock ( & boot_lock ) ;
return pen_release ! = - 1 ? - ENOSYS : 0 ;
}