2009-11-28 08:17:18 +01:00
/*
* Copyright ( C ) 2002 ARM Ltd .
* Copyright ( C ) 2008 STMicroelctronics .
* Copyright ( C ) 2009 ST - Ericsson .
* Author : Srinidhi Kasagar < srinidhi . kasagar @ stericsson . com >
*
* This file is based on arm realview platform
*
* 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/smp.h>
# include <linux/io.h>
# include <asm/cacheflush.h>
# include <asm/smp_scu.h>
# include <mach/hardware.h>
/*
* control for which core is the next to come out of the secondary
* boot " holding pen "
*/
volatile int __cpuinitdata 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
*/
2010-05-03 08:31:35 +01:00
gic_cpu_init ( 0 , __io_address ( UX500_GIC_CPU_BASE ) ) ;
2009-11-28 08:17:18 +01:00
/*
* let the primary processor know we ' re out of the
* pen , then head off into the C entry point
*/
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 ) ;
/*
* The secondary processor is waiting to be released from
* the holding pen - release it , then wait for it to flag
* that it has been released by resetting pen_release .
*/
pen_release = cpu ;
2010-04-03 19:10:45 +01:00
__cpuc_flush_dcache_area ( ( void * ) & pen_release , sizeof ( pen_release ) ) ;
outer_clean_range ( __pa ( & pen_release ) , __pa ( & pen_release ) + 1 ) ;
2009-11-28 08:17:18 +01:00
2010-11-15 09:42:08 +00:00
smp_cross_call ( cpumask_of ( cpu ) , 1 ) ;
2010-09-15 10:45:51 +01:00
2009-11-28 08:17:18 +01:00
timeout = jiffies + ( 1 * HZ ) ;
while ( time_before ( jiffies , timeout ) ) {
if ( pen_release = = - 1 )
break ;
}
/*
* 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 ;
}
static void __init wakeup_secondary ( void )
{
/* nobody is to be released from the pen yet */
pen_release = - 1 ;
/*
* write the address of secondary startup into the backup ram register
* at offset 0x1FF4 , then write the magic number 0xA1FEED01 to the
* backup ram register at offset 0x1FF0 , which is what boot rom code
* is waiting for . This would wake up the secondary core from WFE
*/
# define U8500_CPU1_JUMPADDR_OFFSET 0x1FF4
__raw_writel ( virt_to_phys ( u8500_secondary_startup ) ,
2010-05-03 08:31:35 +01:00
__io_address ( UX500_BACKUPRAM0_BASE ) +
2009-11-28 08:17:18 +01:00
U8500_CPU1_JUMPADDR_OFFSET ) ;
# define U8500_CPU1_WAKEMAGIC_OFFSET 0x1FF0
__raw_writel ( 0xA1FEED01 ,
2010-05-03 08:31:35 +01:00
__io_address ( UX500_BACKUPRAM0_BASE ) +
2009-11-28 08:17:18 +01:00
U8500_CPU1_WAKEMAGIC_OFFSET ) ;
/* make sure write buffer is drained */
mb ( ) ;
}
/*
* Initialise the CPU possible map early - this describes the CPUs
* which may be present or become present in the system .
*/
void __init smp_init_cpus ( void )
{
2010-12-02 18:09:37 +00:00
unsigned int i , ncores ;
ncores = scu_get_core_count ( __io_address ( UX500_SCU_BASE ) ) ;
2009-11-28 08:17:18 +01:00
/* sanity check */
2010-12-03 10:42:58 +00:00
if ( ncores > NR_CPUS ) {
2009-11-28 08:17:18 +01:00
printk ( KERN_WARNING
" U8500: no. of cores (%d) greater than configured "
" maximum of %d - clipping \n " ,
2010-12-03 10:42:58 +00:00
ncores , NR_CPUS ) ;
ncores = NR_CPUS ;
2009-11-28 08:17:18 +01:00
}
2010-12-03 10:42:58 +00:00
for ( i = 0 ; i < ncores ; i + + )
set_cpu_possible ( i , true ) ;
}
2010-12-03 11:09:48 +00:00
void __init platform_smp_prepare_cpus ( unsigned int max_cpus )
2010-12-03 10:42:58 +00:00
{
int i ;
2009-11-28 08:17:18 +01:00
/*
* Initialise the present map , which describes the set of CPUs
* actually populated at the present time .
*/
for ( i = 0 ; i < max_cpus ; i + + )
set_cpu_present ( i , true ) ;
2010-12-03 11:09:48 +00:00
scu_enable ( __io_address ( UX500_SCU_BASE ) ) ;
wakeup_secondary ( ) ;
2009-11-28 08:17:18 +01:00
}