2013-06-25 15:15:10 +04:00
/*
* arch / arm / mach - sti / platsmp . c
*
* Copyright ( C ) 2013 STMicroelectronics ( R & D ) Limited .
* http : //www.st.com
*
* Cloned from linux / arch / arm / mach - vexpress / 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/smp.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_address.h>
2015-06-09 16:33:00 +03:00
# include <linux/memblock.h>
2013-06-25 15:15:10 +04:00
# include <asm/cacheflush.h>
# include <asm/smp_plat.h>
# include <asm/smp_scu.h>
# include "smp.h"
2013-06-17 23:43:14 +04:00
static void write_pen_release ( int val )
2013-06-25 15:15:10 +04:00
{
pen_release = val ;
smp_wmb ( ) ;
2013-12-05 23:26:16 +04:00
sync_cache_w ( & pen_release ) ;
2013-06-25 15:15:10 +04:00
}
static DEFINE_SPINLOCK ( boot_lock ) ;
2014-06-24 15:43:48 +04:00
static void sti_secondary_init ( unsigned int cpu )
2013-06-25 15:15:10 +04:00
{
/*
* 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 ) ;
}
2014-06-24 15:43:48 +04:00
static int sti_boot_secondary ( unsigned int cpu , struct task_struct * idle )
2013-06-25 15:15:10 +04:00
{
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 .
*/
write_pen_release ( cpu_logical_map ( cpu ) ) ;
/*
* Send the secondary CPU a soft interrupt , thereby causing
* it to jump to the secondary entrypoint .
*/
arch_send_wakeup_ipi_mask ( cpumask_of ( cpu ) ) ;
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 ;
}
2014-06-24 15:43:48 +04:00
static void __init sti_smp_prepare_cpus ( unsigned int max_cpus )
2013-06-25 15:15:10 +04:00
{
2015-06-09 16:33:00 +03:00
struct device_node * np ;
void __iomem * scu_base ;
u32 __iomem * cpu_strt_ptr ;
u32 release_phys ;
int cpu ;
2017-01-15 05:59:29 +03:00
unsigned long entry_pa = __pa_symbol ( sti_secondary_startup ) ;
2015-06-09 16:33:00 +03:00
np = of_find_compatible_node ( NULL , NULL , " arm,cortex-a9-scu " ) ;
2013-06-25 15:15:10 +04:00
if ( np ) {
scu_base = of_iomap ( np , 0 ) ;
scu_enable ( scu_base ) ;
of_node_put ( np ) ;
}
2015-06-09 16:33:00 +03:00
if ( max_cpus < = 1 )
return ;
for_each_possible_cpu ( cpu ) {
np = of_get_cpu_node ( cpu , NULL ) ;
if ( ! np )
continue ;
if ( of_property_read_u32 ( np , " cpu-release-addr " ,
& release_phys ) ) {
pr_err ( " CPU %d: missing or invalid cpu-release-addr "
" property \n " , cpu ) ;
continue ;
}
/*
* holding pen is usually configured in SBC DMEM but can also be
* in RAM .
*/
if ( ! memblock_is_memory ( release_phys ) )
cpu_strt_ptr =
ioremap ( release_phys , sizeof ( release_phys ) ) ;
else
cpu_strt_ptr =
( u32 __iomem * ) phys_to_virt ( release_phys ) ;
__raw_writel ( entry_pa , cpu_strt_ptr ) ;
/*
* wmb so that data is actually written
* before cache flush is done
*/
smp_wmb ( ) ;
sync_cache_w ( cpu_strt_ptr ) ;
if ( ! memblock_is_memory ( release_phys ) )
iounmap ( cpu_strt_ptr ) ;
}
2013-06-25 15:15:10 +04:00
}
2015-11-15 04:39:53 +03:00
const struct smp_operations sti_smp_ops __initconst = {
2013-06-25 15:15:10 +04:00
. smp_prepare_cpus = sti_smp_prepare_cpus ,
. smp_secondary_init = sti_secondary_init ,
. smp_boot_secondary = sti_boot_secondary ,
} ;