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>
2012-12-27 13:10:24 -06:00
# include <linux/irqchip/arm-gic.h>
2010-12-14 16:57:11 +09:00
# include <mach/common.h>
2013-01-09 19:41:52 +00:00
# include <asm/cacheflush.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>
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
2013-01-09 19:41:52 +00:00
# define PSTR_SHUTDOWN_MODE 3
2013-02-13 22:46:38 +09:00
# define SH73A0_SCU_BASE IOMEM(0xf0000000)
2012-05-10 00:26:58 -07:00
# ifdef CONFIG_HAVE_ARM_TWD
2013-02-13 22:46:38 +09:00
static DEFINE_TWD_LOCAL_TIMER ( twd_local_timer , SH73A0_SCU_BASE + 0x600 , 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
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 ) ;
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
{
2013-02-13 22:46:38 +09:00
scu_enable ( shmobile_scu_base ) ;
2010-12-14 16:57:11 +09:00
2013-02-13 22:47:07 +09:00
/* Map the reset vector (in headsmp-scu.S) */
2012-03-09 17:16:40 -06:00
__raw_writel ( 0 , APARMBAREA ) ; /* 4k */
2013-02-13 22:47:07 +09:00
__raw_writel ( __pa ( shmobile_secondary_vector_scu ) , SBAR ) ;
2010-12-14 16:57:11 +09:00
2013-01-09 19:41:51 +00:00
/* enable cache coherency on booting CPU */
2013-02-13 22:46:38 +09:00
scu_power_mode ( shmobile_scu_base , SCU_PM_NORMAL ) ;
2010-12-14 16:57:11 +09:00
}
2011-09-08 13:15:22 +01:00
static void __init sh73a0_smp_init_cpus ( void )
{
2013-02-13 22:46:38 +09:00
/* setup sh73a0 specific SCU base */
shmobile_scu_base = SH73A0_SCU_BASE ;
2011-09-08 13:15:22 +01:00
2013-02-13 22:46:38 +09:00
shmobile_smp_init_cpus ( scu_get_core_count ( shmobile_scu_base ) ) ;
2011-09-08 13:15:22 +01:00
}
2013-01-09 19:41:52 +00:00
# ifdef CONFIG_HOTPLUG_CPU
static int sh73a0_cpu_kill ( unsigned int cpu )
2011-09-08 13:15:22 +01:00
{
2013-01-09 19:41:52 +00:00
2011-09-08 13:15:22 +01:00
int k ;
2013-01-09 19:41:52 +00:00
u32 pstr ;
2011-09-08 13:15:22 +01:00
2013-01-09 19:41:52 +00:00
/*
* wait until the power status register confirms the shutdown of the
* offline target
2011-09-08 13:15:22 +01:00
*/
for ( k = 0 ; k < 1000 ; k + + ) {
2013-01-09 19:41:52 +00:00
pstr = ( __raw_readl ( PSTR ) > > ( 4 * cpu ) ) & 3 ;
if ( pstr = = PSTR_SHUTDOWN_MODE )
2011-09-08 13:15:22 +01:00
return 1 ;
mdelay ( 1 ) ;
}
return 0 ;
}
2013-01-09 19:41:52 +00:00
static void sh73a0_cpu_die ( unsigned int cpu )
{
/*
* The ARM MPcore does not issue a cache coherency request for the L1
* cache when powering off single CPUs . We must take care of this and
* further caches .
*/
dsb ( ) ;
flush_cache_all ( ) ;
/* Set power off mode. This takes the CPU out of the MP cluster */
2013-02-13 22:46:38 +09:00
scu_power_mode ( shmobile_scu_base , SCU_PM_POWEROFF ) ;
2013-01-09 19:41:52 +00:00
/* Enter shutdown mode */
cpu_do_idle ( ) ;
}
# endif /* CONFIG_HOTPLUG_CPU */
2011-09-08 13:15:22 +01:00
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 ,
2013-01-09 19:41:52 +00:00
. cpu_die = sh73a0_cpu_die ,
2013-01-10 11:16:44 +01:00
. cpu_disable = shmobile_cpu_disable_any ,
2011-09-08 13:15:22 +01:00
# endif
} ;