2010-12-14 16:57:11 +09:00
/*
* SMP support for R - Mobile / SH - Mobile - sh73a0 portion
*
* Copyright ( C ) 2010 Magnus Damm
* Copyright ( C ) 2010 Takashi Yoshii
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/smp.h>
# include <linux/spinlock.h>
# include <linux/io.h>
2011-09-08 13:15:22 +01:00
# include <linux/delay.h>
2010-12-14 16:57:11 +09:00
# include <mach/common.h>
2012-01-20 12:01:12 +01:00
# include <asm/smp_plat.h>
2011-09-08 13:15:22 +01:00
# include <mach/sh73a0.h>
2010-12-14 16:57:11 +09:00
# include <asm/smp_scu.h>
# include <asm/smp_twd.h>
# include <asm/hardware/gic.h>
2012-03-09 17:16:40 -06:00
# define WUPCR IOMEM(0xe6151010)
# define SRESCR IOMEM(0xe6151018)
# define PSTR IOMEM(0xe6151040)
# define SBAR IOMEM(0xe6180020)
# define APARMBAREA IOMEM(0xe6f10020)
2010-12-14 16:57:11 +09:00
static void __iomem * scu_base_addr ( void )
{
return ( void __iomem * ) 0xf0000000 ;
}
static DEFINE_SPINLOCK ( scu_lock ) ;
static unsigned long tmp ;
2012-05-10 00:26:58 -07:00
# ifdef CONFIG_HAVE_ARM_TWD
2012-01-10 19:44:19 +00:00
static DEFINE_TWD_LOCAL_TIMER ( twd_local_timer , 0xf0000600 , 29 ) ;
2012-05-10 00:26:58 -07:00
void __init sh73a0_register_twd ( void )
{
twd_local_timer_register ( & twd_local_timer ) ;
}
# endif
2012-01-10 19:44:19 +00:00
2010-12-14 16:57:11 +09:00
static void modify_scu_cpu_psr ( unsigned long set , unsigned long clr )
{
void __iomem * scu_base = scu_base_addr ( ) ;
spin_lock ( & scu_lock ) ;
tmp = __raw_readl ( scu_base + 8 ) ;
tmp & = ~ clr ;
tmp | = set ;
spin_unlock ( & scu_lock ) ;
/* disable cache coherency after releasing the lock */
__raw_writel ( tmp , scu_base + 8 ) ;
}
2011-09-08 13:15:22 +01:00
static unsigned int __init sh73a0_get_core_count ( void )
2010-12-14 16:57:11 +09:00
{
void __iomem * scu_base = scu_base_addr ( ) ;
return scu_get_core_count ( scu_base ) ;
}
2011-09-08 13:15:22 +01:00
static void __cpuinit sh73a0_secondary_init ( unsigned int cpu )
2010-12-14 16:57:11 +09:00
{
2011-01-07 12:02:11 +09:00
gic_secondary_init ( 0 ) ;
2010-12-14 16:57:11 +09:00
}
2011-09-08 13:15:22 +01:00
static int __cpuinit sh73a0_boot_secondary ( unsigned int cpu , struct task_struct * idle )
2010-12-14 16:57:11 +09:00
{
2011-08-09 12:13:53 +01:00
cpu = cpu_logical_map ( cpu ) ;
2010-12-14 16:57:11 +09:00
/* enable cache coherency */
modify_scu_cpu_psr ( 0 , 3 < < ( cpu * 8 ) ) ;
2012-03-29 18:02:10 -07:00
if ( ( ( __raw_readl ( PSTR ) > > ( 4 * cpu ) ) & 3 ) = = 3 )
2012-03-09 17:16:40 -06:00
__raw_writel ( 1 < < cpu , WUPCR ) ; /* wake up */
2010-12-14 16:57:11 +09:00
else
2012-03-09 17:16:40 -06:00
__raw_writel ( 1 < < cpu , SRESCR ) ; /* reset */
2010-12-14 16:57:11 +09:00
return 0 ;
}
2011-09-08 13:15:22 +01:00
static void __init sh73a0_smp_prepare_cpus ( unsigned int max_cpus )
2010-12-14 16:57:11 +09:00
{
2011-08-09 12:13:53 +01:00
int cpu = cpu_logical_map ( 0 ) ;
2010-12-14 16:57:11 +09:00
scu_enable ( scu_base_addr ( ) ) ;
/* Map the reset vector (in headsmp.S) */
2012-03-09 17:16:40 -06:00
__raw_writel ( 0 , APARMBAREA ) ; /* 4k */
__raw_writel ( __pa ( shmobile_secondary_vector ) , SBAR ) ;
2010-12-14 16:57:11 +09:00
/* enable cache coherency on CPU0 */
2011-08-09 12:13:53 +01:00
modify_scu_cpu_psr ( 0 , 3 < < ( cpu * 8 ) ) ;
2010-12-14 16:57:11 +09:00
}
2011-09-08 13:15:22 +01:00
static void __init sh73a0_smp_init_cpus ( void )
{
unsigned int ncores = sh73a0_get_core_count ( ) ;
shmobile_smp_init_cpus ( ncores ) ;
}
static int __maybe_unused sh73a0_cpu_kill ( unsigned int cpu )
{
int k ;
/* this function is running on another CPU than the offline target,
* here we need wait for shutdown code in platform_cpu_die ( ) to
* finish before asking SoC - specific code to power off the CPU core .
*/
for ( k = 0 ; k < 1000 ; k + + ) {
if ( shmobile_cpu_is_dead ( cpu ) )
return 1 ;
mdelay ( 1 ) ;
}
return 0 ;
}
struct smp_operations sh73a0_smp_ops __initdata = {
. smp_init_cpus = sh73a0_smp_init_cpus ,
. smp_prepare_cpus = sh73a0_smp_prepare_cpus ,
. smp_secondary_init = sh73a0_secondary_init ,
. smp_boot_secondary = sh73a0_boot_secondary ,
# ifdef CONFIG_HOTPLUG_CPU
. cpu_kill = sh73a0_cpu_kill ,
. cpu_die = shmobile_cpu_die ,
. cpu_disable = shmobile_cpu_disable ,
# endif
} ;