2012-12-20 15:37:32 +04:00
/*
* plat smp support for CSR Marco dual - core SMP SoCs
*
* Copyright ( c ) 2012 Cambridge Silicon Radio Limited , a CSR plc group company .
*
* Licensed under GPLv2 or later .
*/
# include <linux/init.h>
# include <linux/smp.h>
# include <linux/delay.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <asm/page.h>
# include <asm/mach/map.h>
# include <asm/smp_plat.h>
# include <asm/smp_scu.h>
# include <asm/cacheflush.h>
# include <asm/cputype.h>
# include "common.h"
2014-12-25 11:34:20 +03:00
static void __iomem * clk_base ;
2012-12-20 15:37:32 +04:00
static DEFINE_SPINLOCK ( boot_lock ) ;
2013-06-17 23:43:14 +04:00
static void sirfsoc_secondary_init ( unsigned int cpu )
2012-12-20 15:37:32 +04:00
{
/*
* let the primary processor know we ' re out of the
* pen , then head off into the C entry point
*/
pen_release = - 1 ;
smp_wmb ( ) ;
/*
* Synchronise with the boot thread .
*/
spin_lock ( & boot_lock ) ;
spin_unlock ( & boot_lock ) ;
}
2015-02-18 23:19:56 +03:00
static const struct of_device_id clk_ids [ ] = {
2014-12-25 11:34:20 +03:00
{ . compatible = " sirf,atlas7-clkc " } ,
2012-12-20 15:37:32 +04:00
{ } ,
} ;
2013-06-17 23:43:14 +04:00
static int sirfsoc_boot_secondary ( unsigned int cpu , struct task_struct * idle )
2012-12-20 15:37:32 +04:00
{
unsigned long timeout ;
struct device_node * np ;
2014-12-25 11:34:20 +03:00
np = of_find_matching_node ( NULL , clk_ids ) ;
2012-12-20 15:37:32 +04:00
if ( ! np )
return - ENODEV ;
2014-12-25 11:34:20 +03:00
clk_base = of_iomap ( np , 0 ) ;
if ( ! clk_base )
2012-12-20 15:37:32 +04:00
return - ENOMEM ;
/*
2014-12-25 11:34:20 +03:00
* write the address of secondary startup into the clkc register
* at offset 0x2bC , then write the magic number 0x3CAF5D62 to the
* clkc register at offset 0x2b8 , which is what boot rom code is
2012-12-20 15:37:32 +04:00
* waiting for . This would wake up the secondary core from WFE
*/
2014-12-25 11:34:20 +03:00
# define SIRFSOC_CPU1_JUMPADDR_OFFSET 0x2bc
2017-01-15 05:59:29 +03:00
__raw_writel ( __pa_symbol ( sirfsoc_secondary_startup ) ,
2014-12-25 11:34:20 +03:00
clk_base + SIRFSOC_CPU1_JUMPADDR_OFFSET ) ;
2012-12-20 15:37:32 +04:00
2014-12-25 11:34:20 +03:00
# define SIRFSOC_CPU1_WAKEMAGIC_OFFSET 0x2b8
2012-12-20 15:37:32 +04:00
__raw_writel ( 0x3CAF5D62 ,
2014-12-25 11:34:20 +03:00
clk_base + SIRFSOC_CPU1_WAKEMAGIC_OFFSET ) ;
2012-12-20 15:37:32 +04:00
/* make sure write buffer is drained */
mb ( ) ;
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_logical_map ( cpu ) ;
2013-12-05 23:26:16 +04:00
sync_cache_w ( & pen_release ) ;
2012-12-20 15:37:32 +04:00
/*
* Send the secondary CPU SEV , thereby causing the boot monitor to read
* the JUMPADDR and WAKEMAGIC , and branch to the address found there .
*/
dsb_sev ( ) ;
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 ;
}
2015-11-15 04:39:53 +03:00
const struct smp_operations sirfsoc_smp_ops __initconst = {
2014-02-18 10:46:28 +04:00
. smp_secondary_init = sirfsoc_secondary_init ,
. smp_boot_secondary = sirfsoc_boot_secondary ,
2012-12-20 15:37:32 +04:00
# ifdef CONFIG_HOTPLUG_CPU
. cpu_die = sirfsoc_cpu_die ,
# endif
} ;