2012-12-11 13:58:43 +09:00
/*
* Copyright ( C ) 2012 Samsung Electronics .
* Kyungmin Park < kyungmin . park @ samsung . com >
* Tomasz Figa < t . figa @ samsung . com >
*
* 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/kernel.h>
# include <linux/io.h>
# include <linux/init.h>
# include <linux/of.h>
# include <linux/of_address.h>
2014-09-24 01:24:39 +09:00
# include <asm/cacheflush.h>
# include <asm/cputype.h>
2012-12-11 13:58:43 +09:00
# include <asm/firmware.h>
2015-01-08 07:53:20 +01:00
# include <asm/hardware/cache-l2x0.h>
2014-09-24 01:24:39 +09:00
# include <asm/suspend.h>
2012-12-11 13:58:43 +09:00
# include <mach/map.h>
2014-05-13 07:13:44 +09:00
# include "common.h"
2012-12-11 13:58:43 +09:00
# include "smc.h"
2014-09-24 01:24:39 +09:00
# define EXYNOS_BOOT_ADDR 0x8
# define EXYNOS_BOOT_FLAG 0xc
2014-09-25 17:59:41 +09:00
static void exynos_save_cp15 ( void )
{
/* Save Power control and Diagnostic registers */
asm ( " mrc p15, 0, %0, c15, c0, 0 \n "
" mrc p15, 0, %1, c15, c0, 1 \n "
: " =r " ( cp15_save_power ) , " =r " ( cp15_save_diag )
: : " cc " ) ;
}
2014-09-25 17:59:41 +09:00
static int exynos_do_idle ( unsigned long mode )
2012-12-11 13:58:43 +09:00
{
2014-09-25 17:59:41 +09:00
switch ( mode ) {
case FW_DO_IDLE_AFTR :
2014-09-25 17:59:41 +09:00
if ( read_cpuid_part ( ) = = ARM_CPU_PART_CORTEX_A9 )
exynos_save_cp15 ( ) ;
__raw_writel ( virt_to_phys ( exynos_cpu_resume_ns ) ,
sysram_ns_base_addr + 0x24 ) ;
__raw_writel ( EXYNOS_AFTR_MAGIC , sysram_ns_base_addr + 0x20 ) ;
2015-03-27 02:35:48 +09:00
if ( soc_is_exynos3250 ( ) ) {
2015-03-18 14:09:57 +01:00
flush_cache_all ( ) ;
2015-03-27 02:35:48 +09:00
exynos_smc ( SMC_CMD_SAVE , OP_TYPE_CORE ,
SMC_POWERSTATE_IDLE , 0 ) ;
exynos_smc ( SMC_CMD_SHUTDOWN , OP_TYPE_CLUSTER ,
SMC_POWERSTATE_IDLE , 0 ) ;
} else
exynos_smc ( SMC_CMD_CPU0AFTR , 0 , 0 , 0 ) ;
2014-09-25 17:59:41 +09:00
break ;
case FW_DO_IDLE_SLEEP :
exynos_smc ( SMC_CMD_SLEEP , 0 , 0 , 0 ) ;
}
2012-12-11 13:58:43 +09:00
return 0 ;
}
static int exynos_cpu_boot ( int cpu )
{
2014-05-26 04:12:32 +09:00
/*
* Exynos3250 doesn ' t need to send smc command for secondary CPU boot
* because Exynos3250 removes WFE in secure mode .
*/
if ( soc_is_exynos3250 ( ) )
return 0 ;
2014-05-09 06:19:18 +09:00
/*
* The second parameter of SMC_CMD_CPU1BOOT command means CPU id .
* But , Exynos4212 has only one secondary CPU so second parameter
* isn ' t used for informing secure firmware about CPU id .
*/
if ( soc_is_exynos4212 ( ) )
cpu = 0 ;
2012-12-11 13:58:43 +09:00
exynos_smc ( SMC_CMD_CPU1BOOT , cpu , 0 , 0 ) ;
return 0 ;
}
static int exynos_set_cpu_boot_addr ( int cpu , unsigned long boot_addr )
{
2014-05-13 07:13:44 +09:00
void __iomem * boot_reg ;
if ( ! sysram_ns_base_addr )
return - ENODEV ;
2014-05-30 21:44:32 -07:00
boot_reg = sysram_ns_base_addr + 0x1c ;
2014-05-09 06:19:18 +09:00
2014-07-08 08:03:49 +09:00
/*
* Almost all Exynos - series of SoCs that run in secure mode don ' t need
* additional offset for every CPU , with Exynos4412 being the only
* exception .
*/
if ( soc_is_exynos4412 ( ) )
boot_reg + = 4 * cpu ;
2012-12-11 13:58:43 +09:00
__raw_writel ( boot_addr , boot_reg ) ;
return 0 ;
}
2015-03-18 14:09:56 +01:00
static int exynos_get_cpu_boot_addr ( int cpu , unsigned long * boot_addr )
{
void __iomem * boot_reg ;
if ( ! sysram_ns_base_addr )
return - ENODEV ;
boot_reg = sysram_ns_base_addr + 0x1c ;
if ( soc_is_exynos4412 ( ) )
boot_reg + = 4 * cpu ;
* boot_addr = __raw_readl ( boot_reg ) ;
return 0 ;
}
2014-09-24 01:24:39 +09:00
static int exynos_cpu_suspend ( unsigned long arg )
{
flush_cache_all ( ) ;
outer_flush_all ( ) ;
exynos_smc ( SMC_CMD_SLEEP , 0 , 0 , 0 ) ;
pr_info ( " Failed to suspend the system \n " ) ;
writel ( 0 , sysram_ns_base_addr + EXYNOS_BOOT_FLAG ) ;
return 1 ;
}
static int exynos_suspend ( void )
{
2014-09-25 17:59:41 +09:00
if ( read_cpuid_part ( ) = = ARM_CPU_PART_CORTEX_A9 )
exynos_save_cp15 ( ) ;
2014-09-24 01:24:39 +09:00
writel ( EXYNOS_SLEEP_MAGIC , sysram_ns_base_addr + EXYNOS_BOOT_FLAG ) ;
writel ( virt_to_phys ( exynos_cpu_resume_ns ) ,
sysram_ns_base_addr + EXYNOS_BOOT_ADDR ) ;
return cpu_suspend ( 0 , exynos_cpu_suspend ) ;
}
static int exynos_resume ( void )
{
writel ( 0 , sysram_ns_base_addr + EXYNOS_BOOT_FLAG ) ;
return 0 ;
}
2012-12-11 13:58:43 +09:00
static const struct firmware_ops exynos_firmware_ops = {
2014-10-28 08:10:21 +09:00
. do_idle = IS_ENABLED ( CONFIG_EXYNOS_CPU_SUSPEND ) ? exynos_do_idle : NULL ,
2012-12-11 13:58:43 +09:00
. set_cpu_boot_addr = exynos_set_cpu_boot_addr ,
2015-03-18 14:09:56 +01:00
. get_cpu_boot_addr = exynos_get_cpu_boot_addr ,
2012-12-11 13:58:43 +09:00
. cpu_boot = exynos_cpu_boot ,
2014-10-28 08:10:21 +09:00
. suspend = IS_ENABLED ( CONFIG_PM_SLEEP ) ? exynos_suspend : NULL ,
. resume = IS_ENABLED ( CONFIG_EXYNOS_CPU_SUSPEND ) ? exynos_resume : NULL ,
2012-12-11 13:58:43 +09:00
} ;
2015-01-08 07:53:20 +01:00
static void exynos_l2_write_sec ( unsigned long val , unsigned reg )
{
static int l2cache_enabled ;
switch ( reg ) {
case L2X0_CTRL :
if ( val & L2X0_CTRL_EN ) {
/*
* Before the cache can be enabled , due to firmware
* design , SMC_CMD_L2X0INVALL must be called .
*/
if ( ! l2cache_enabled ) {
exynos_smc ( SMC_CMD_L2X0INVALL , 0 , 0 , 0 ) ;
l2cache_enabled = 1 ;
}
} else {
l2cache_enabled = 0 ;
}
exynos_smc ( SMC_CMD_L2X0CTRL , val , 0 , 0 ) ;
break ;
case L2X0_DEBUG_CTRL :
exynos_smc ( SMC_CMD_L2X0DEBUG , val , 0 , 0 ) ;
break ;
default :
WARN_ONCE ( 1 , " %s: ignoring write to reg 0x%x \n " , __func__ , reg ) ;
}
}
static void exynos_l2_configure ( const struct l2x0_regs * regs )
{
exynos_smc ( SMC_CMD_L2X0SETUP1 , regs - > tag_latency , regs - > data_latency ,
regs - > prefetch_ctrl ) ;
exynos_smc ( SMC_CMD_L2X0SETUP2 , regs - > pwr_ctrl , regs - > aux_ctrl , 0 ) ;
}
2012-12-11 13:58:43 +09:00
void __init exynos_firmware_init ( void )
{
2013-06-15 09:13:25 +09:00
struct device_node * nd ;
const __be32 * addr ;
2012-12-11 13:58:43 +09:00
2013-06-15 09:13:25 +09:00
nd = of_find_compatible_node ( NULL , NULL ,
" samsung,secure-firmware " ) ;
if ( ! nd )
return ;
2012-12-11 13:58:43 +09:00
2013-06-15 09:13:25 +09:00
addr = of_get_address ( nd , 0 , NULL , NULL ) ;
if ( ! addr ) {
pr_err ( " %s: No address specified. \n " , __func__ ) ;
return ;
2012-12-11 13:58:43 +09:00
}
pr_info ( " Running under secure firmware. \n " ) ;
register_firmware_ops ( & exynos_firmware_ops ) ;
2015-01-08 07:53:20 +01:00
/*
* Exynos 4 SoCs ( based on Cortex A9 and equipped with L2C - 310 ) ,
* running under secure firmware , require certain registers of L2
* cache controller to be written in secure mode . Here . write_sec
* callback is provided to perform necessary SMC calls .
*/
if ( IS_ENABLED ( CONFIG_CACHE_L2X0 ) & &
read_cpuid_part ( ) = = ARM_CPU_PART_CORTEX_A9 ) {
outer_cache . write_sec = exynos_l2_write_sec ;
outer_cache . configure = exynos_l2_configure ;
}
2012-12-11 13:58:43 +09:00
}
2015-03-27 02:32:56 +09:00
# define REG_CPU_STATE_ADDR (sysram_ns_base_addr + 0x28)
# define BOOT_MODE_MASK 0x1f
void exynos_set_boot_flag ( unsigned int cpu , unsigned int mode )
{
unsigned int tmp ;
tmp = __raw_readl ( REG_CPU_STATE_ADDR + cpu * 4 ) ;
if ( mode & BOOT_MODE_MASK )
tmp & = ~ BOOT_MODE_MASK ;
tmp | = mode ;
__raw_writel ( tmp , REG_CPU_STATE_ADDR + cpu * 4 ) ;
}
void exynos_clear_boot_flag ( unsigned int cpu , unsigned int mode )
{
unsigned int tmp ;
tmp = __raw_readl ( REG_CPU_STATE_ADDR + cpu * 4 ) ;
tmp & = ~ mode ;
__raw_writel ( tmp , REG_CPU_STATE_ADDR + cpu * 4 ) ;
}