2010-10-02 19:13:42 +09:00
/* linux/arch/arm/mach-s5pv210/pm.c
*
2014-07-02 19:37:45 +02:00
* Copyright ( c ) 2010 - 2014 Samsung Electronics Co . , Ltd .
2010-10-02 19:13:42 +09:00
* http : //www.samsung.com
*
* S5PV210 - Power Management support
*
* Based on arch / arm / mach - s3c2410 / pm . c
* Copyright ( c ) 2006 Simtec Electronics
* Ben Dooks < ben @ simtec . co . uk >
*
* 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/suspend.h>
2011-04-22 22:03:21 +02:00
# include <linux/syscore_ops.h>
2010-10-02 19:13:42 +09:00
# include <linux/io.h>
2014-07-02 19:37:45 +02:00
# include <asm/cacheflush.h>
# include <asm/suspend.h>
# include <plat/pm-common.h>
2010-10-02 19:13:42 +09:00
2014-07-02 19:37:45 +02:00
# include "common.h"
2014-08-01 03:22:04 +09:00
# include "regs-clock.h"
2014-07-02 19:37:45 +02:00
2010-10-02 19:13:42 +09:00
static struct sleep_save s5pv210_core_save [ ] = {
/* Clock ETC */
SAVE_ITEM ( S5P_MDNIE_SEL ) ,
} ;
2014-07-02 19:37:45 +02:00
/*
* VIC wake - up support ( TODO )
*/
static u32 s5pv210_irqwake_intmask = 0xffffffff ;
/*
* Suspend helpers .
*/
2011-08-13 10:34:56 +09:00
static int s5pv210_cpu_suspend ( unsigned long arg )
2010-10-02 19:13:42 +09:00
{
unsigned long tmp ;
/* issue the standby signal into the pm unit. Note, we
* issue a write - buffer drain just in case */
tmp = 0 ;
asm ( " b 1f \n \t "
" .align 5 \n \t "
" 1: \n \t "
" mcr p15, 0, %0, c7, c10, 5 \n \t "
" mcr p15, 0, %0, c7, c10, 4 \n \t "
" wfi " : : " r " ( tmp ) ) ;
2013-01-25 10:40:19 -08:00
pr_info ( " Failed to suspend the system \n " ) ;
return 1 ; /* Aborting suspend */
2010-10-02 19:13:42 +09:00
}
static void s5pv210_pm_prepare ( void )
{
unsigned int tmp ;
2014-07-02 19:37:45 +02:00
/* Set wake-up mask registers */
__raw_writel ( exynos_get_eint_wake_mask ( ) , S5P_EINT_WAKEUP_MASK ) ;
__raw_writel ( s5pv210_irqwake_intmask , S5P_WAKEUP_MASK ) ;
2010-10-02 19:13:42 +09:00
/* ensure at least INFORM0 has the resume address */
2017-01-15 03:59:29 +01:00
__raw_writel ( __pa_symbol ( s5pv210_cpu_resume ) , S5P_INFORM0 ) ;
2010-10-02 19:13:42 +09:00
tmp = __raw_readl ( S5P_SLEEP_CFG ) ;
tmp & = ~ ( S5P_SLEEP_CFG_OSC_EN | S5P_SLEEP_CFG_USBOSC_EN ) ;
__raw_writel ( tmp , S5P_SLEEP_CFG ) ;
/* WFI for SLEEP mode configuration by SYSCON */
tmp = __raw_readl ( S5P_PWR_CFG ) ;
tmp & = S5P_CFG_WFI_CLEAN ;
tmp | = S5P_CFG_WFI_SLEEP ;
__raw_writel ( tmp , S5P_PWR_CFG ) ;
/* SYSCON interrupt handling disable */
tmp = __raw_readl ( S5P_OTHERS ) ;
tmp | = S5P_OTHER_SYSC_INTOFF ;
__raw_writel ( tmp , S5P_OTHERS ) ;
s3c_pm_do_save ( s5pv210_core_save , ARRAY_SIZE ( s5pv210_core_save ) ) ;
}
2014-07-02 19:37:45 +02:00
/*
* Suspend operations .
*/
static int s5pv210_suspend_enter ( suspend_state_t state )
{
int ret ;
s3c_pm_debug_init ( ) ;
S3C_PMDBG ( " %s: suspending the system... \n " , __func__ ) ;
S3C_PMDBG ( " %s: wakeup masks: %08x,%08x \n " , __func__ ,
s5pv210_irqwake_intmask , exynos_get_eint_wake_mask ( ) ) ;
if ( s5pv210_irqwake_intmask = = - 1U
& & exynos_get_eint_wake_mask ( ) = = - 1U ) {
pr_err ( " %s: No wake-up sources! \n " , __func__ ) ;
pr_err ( " %s: Aborting sleep \n " , __func__ ) ;
return - EINVAL ;
}
s3c_pm_save_uarts ( ) ;
s5pv210_pm_prepare ( ) ;
flush_cache_all ( ) ;
s3c_pm_check_store ( ) ;
ret = cpu_suspend ( 0 , s5pv210_cpu_suspend ) ;
if ( ret )
return ret ;
s3c_pm_restore_uarts ( ) ;
S3C_PMDBG ( " %s: wakeup stat: %08x \n " , __func__ ,
__raw_readl ( S5P_WAKEUP_STAT ) ) ;
s3c_pm_check_restore ( ) ;
S3C_PMDBG ( " %s: resuming the system... \n " , __func__ ) ;
return 0 ;
}
static int s5pv210_suspend_prepare ( void )
{
s3c_pm_check_prepare ( ) ;
return 0 ;
}
static void s5pv210_suspend_finish ( void )
{
s3c_pm_check_cleanup ( ) ;
}
static const struct platform_suspend_ops s5pv210_suspend_ops = {
. enter = s5pv210_suspend_enter ,
. prepare = s5pv210_suspend_prepare ,
. finish = s5pv210_suspend_finish ,
. valid = suspend_valid_only_mem ,
} ;
/*
* Syscore operations used to delay restore of certain registers .
*/
2011-04-22 22:03:21 +02:00
static void s5pv210_pm_resume ( void )
2010-10-02 19:13:42 +09:00
{
s3c_pm_do_restore_core ( s5pv210_core_save , ARRAY_SIZE ( s5pv210_core_save ) ) ;
}
2011-04-22 22:03:21 +02:00
static struct syscore_ops s5pv210_pm_syscore_ops = {
2010-10-02 19:13:42 +09:00
. resume = s5pv210_pm_resume ,
} ;
2014-07-02 19:37:45 +02:00
/*
* Initialization entry point .
*/
void __init s5pv210_pm_init ( void )
2010-10-02 19:13:42 +09:00
{
2011-04-22 22:03:21 +02:00
register_syscore_ops ( & s5pv210_pm_syscore_ops ) ;
2014-07-02 19:37:45 +02:00
suspend_set_ops ( & s5pv210_suspend_ops ) ;
2010-10-02 19:13:42 +09:00
}