2005-11-07 21:05:42 +00:00
/*
* linux / arch / arm / mach - realview / 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>
2009-01-08 09:58:51 +00:00
# include <linux/jiffies.h>
2005-11-07 21:05:42 +00:00
# include <linux/smp.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2005-11-07 21:05:42 +00:00
# include <asm/cacheflush.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2008-02-04 17:39:00 +01:00
# include <asm/mach-types.h>
2009-05-17 18:58:34 +01:00
# include <asm/localtimer.h>
2005-11-07 21:05:42 +00:00
2008-08-05 16:14:15 +01:00
# include <mach/board-eb.h>
# include <mach/board-pb11mp.h>
# include <mach/scu.h>
2008-04-18 22:43:08 +01:00
2008-12-01 14:54:58 +00:00
# include "core.h"
2005-11-07 21:05:42 +00:00
extern void realview_secondary_startup ( void ) ;
/*
* control for which core is the next to come out of the secondary
* boot " holding pen "
*/
volatile int __cpuinitdata pen_release = - 1 ;
2008-12-01 14:54:58 +00:00
static void __iomem * scu_base_addr ( void )
{
if ( machine_is_realview_eb_mp ( ) )
return __io_address ( REALVIEW_EB11MP_SCU_BASE ) ;
else if ( machine_is_realview_pb11mp ( ) )
return __io_address ( REALVIEW_TC11MP_SCU_BASE ) ;
else
return ( void __iomem * ) 0 ;
}
2005-11-07 21:05:42 +00:00
static unsigned int __init get_core_count ( void )
{
unsigned int ncores ;
2008-12-01 14:54:58 +00:00
void __iomem * scu_base = scu_base_addr ( ) ;
2005-11-07 21:05:42 +00:00
2008-04-18 22:43:08 +01:00
if ( scu_base ) {
ncores = __raw_readl ( scu_base + SCU_CONFIG ) ;
2008-02-04 17:39:00 +01:00
ncores = ( ncores & 0x03 ) + 1 ;
} else
ncores = 1 ;
2005-11-07 21:05:42 +00:00
2008-02-04 17:39:00 +01:00
return ncores ;
2005-11-07 21:05:42 +00:00
}
2008-04-18 22:43:08 +01:00
/*
* Setup the SCU
*/
static void scu_enable ( void )
{
u32 scu_ctrl ;
2008-12-01 14:54:58 +00:00
void __iomem * scu_base = scu_base_addr ( ) ;
2008-04-18 22:43:08 +01:00
scu_ctrl = __raw_readl ( scu_base + SCU_CTRL ) ;
scu_ctrl | = 1 ;
__raw_writel ( scu_ctrl , scu_base + SCU_CTRL ) ;
}
2005-11-07 21:05:42 +00:00
static DEFINE_SPINLOCK ( boot_lock ) ;
void __cpuinit platform_secondary_init ( unsigned int cpu )
{
2008-06-27 15:15:12 +01:00
trace_hardirqs_off ( ) ;
2005-11-07 21:05:42 +00:00
/*
* 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
*/
2008-12-01 14:54:58 +00:00
gic_cpu_init ( 0 , gic_cpu_base_addr ) ;
2005-11-07 21:05:42 +00:00
/*
* let the primary processor know we ' re out of the
* pen , then head off into the C entry point
*/
pen_release = - 1 ;
2007-02-15 19:05:29 +01:00
smp_wmb ( ) ;
2005-11-07 21:05:42 +00:00
/*
* 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 .
*
* Note that " pen_release " is the hardware CPU ID , whereas
* " cpu " is Linux ' s internal ID .
*/
pen_release = cpu ;
flush_cache_all ( ) ;
/*
* XXX
*
* This is a later addition to the booting protocol : the
* bootMonitor now puts secondary cores into WFI , so
* poke_milo ( ) no longer gets the cores moving ; we need
* to send a soft interrupt to wake the secondary core .
* Use smp_cross_call ( ) for this , since there ' s little
* point duplicating the code here
*/
2009-05-17 16:20:18 +01:00
smp_cross_call ( cpumask_of ( cpu ) ) ;
2005-11-07 21:05:42 +00:00
timeout = jiffies + ( 1 * HZ ) ;
while ( time_before ( jiffies , timeout ) ) {
2007-02-15 19:05:29 +01:00
smp_rmb ( ) ;
2005-11-07 21:05:42 +00:00
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 ;
}
static void __init poke_milo ( void )
{
extern void secondary_startup ( void ) ;
/* nobody is to be released from the pen yet */
pen_release = - 1 ;
/*
* write the address of secondary startup into the system - wide
* flags register , then clear the bottom two bits , which is what
* BootMonitor is waiting for
*/
# if 1
# define REALVIEW_SYS_FLAGSS_OFFSET 0x30
__raw_writel ( virt_to_phys ( realview_secondary_startup ) ,
2005-11-08 10:44:46 +00:00
__io_address ( REALVIEW_SYS_BASE ) +
REALVIEW_SYS_FLAGSS_OFFSET ) ;
2005-11-07 21:05:42 +00:00
# define REALVIEW_SYS_FLAGSC_OFFSET 0x34
__raw_writel ( 3 ,
2005-11-08 10:44:46 +00:00
__io_address ( REALVIEW_SYS_BASE ) +
REALVIEW_SYS_FLAGSC_OFFSET ) ;
2005-11-07 21:05:42 +00:00
# endif
mb ( ) ;
}
2006-02-16 11:08:09 +00:00
/*
* 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 )
{
unsigned int i , ncores = get_core_count ( ) ;
for ( i = 0 ; i < ncores ; i + + )
cpu_set ( i , cpu_possible_map ) ;
}
2005-11-07 21:05:42 +00:00
void __init smp_prepare_cpus ( unsigned int max_cpus )
{
unsigned int ncores = get_core_count ( ) ;
unsigned int cpu = smp_processor_id ( ) ;
int i ;
/* sanity check */
if ( ncores = = 0 ) {
printk ( KERN_ERR
" Realview: strange CM count of 0? Default to 1 \n " ) ;
ncores = 1 ;
}
if ( ncores > NR_CPUS ) {
printk ( KERN_WARNING
" Realview: no. of cores (%d) greater than configured "
" maximum of %d - clipping \n " ,
ncores , NR_CPUS ) ;
ncores = NR_CPUS ;
}
smp_store_cpu_info ( cpu ) ;
/*
* are we trying to boot more cores than exist ?
*/
if ( max_cpus > ncores )
max_cpus = ncores ;
/*
2006-02-16 11:08:09 +00:00
* Initialise the present map , which describes the set of CPUs
* actually populated at the present time .
2005-11-07 21:05:42 +00:00
*/
2006-02-16 11:08:09 +00:00
for ( i = 0 ; i < max_cpus ; i + + )
2005-11-07 21:05:42 +00:00
cpu_set ( i , cpu_present_map ) ;
/*
2008-04-18 22:43:08 +01:00
* Initialise the SCU if there are more than one CPU and let
* them know where to start . Note that , on modern versions of
* MILO , the " poke " doesn ' t actually do anything until each
* individual core is sent a soft interrupt to get it out of
* WFI
2005-11-07 21:05:42 +00:00
*/
2008-04-18 22:43:08 +01:00
if ( max_cpus > 1 ) {
2009-05-17 18:58:34 +01:00
/*
* Enable the local timer or broadcast device for the
* boot CPU , but only if we have more than one CPU .
*/
percpu_timer_setup ( ) ;
2008-04-18 22:43:08 +01:00
scu_enable ( ) ;
2005-11-07 21:05:42 +00:00
poke_milo ( ) ;
2008-04-18 22:43:08 +01:00
}
2005-11-07 21:05:42 +00:00
}