2017-12-25 20:54:34 +01:00
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2010-2014 Samsung Electronics Co., Ltd.
// 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>
2010-10-02 19:13:42 +09:00
# 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 ;
2018-07-23 19:53:00 +02:00
static u32 s5pv210_read_eint_wakeup_mask ( void )
{
return __raw_readl ( S5P_EINT_WAKEUP_MASK ) ;
}
2014-07-02 19:37:45 +02:00
/*
* 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 ;
2018-07-23 19:53:00 +02:00
/*
* Set wake - up mask registers
* S5P_EINT_WAKEUP_MASK is set by pinctrl driver in late suspend .
*/
2014-07-02 19:37:45 +02:00
__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 )
{
2018-07-23 19:53:00 +02:00
u32 eint_wakeup_mask = s5pv210_read_eint_wakeup_mask ( ) ;
2014-07-02 19:37:45 +02:00
int ret ;
s3c_pm_debug_init ( ) ;
S3C_PMDBG ( " %s: suspending the system... \n " , __func__ ) ;
S3C_PMDBG ( " %s: wakeup masks: %08x,%08x \n " , __func__ ,
2018-07-23 19:53:00 +02:00
s5pv210_irqwake_intmask , eint_wakeup_mask ) ;
2014-07-02 19:37:45 +02:00
if ( s5pv210_irqwake_intmask = = - 1U
2018-07-23 19:53:00 +02:00
& & eint_wakeup_mask = = - 1U ) {
2014-07-02 19:37:45 +02:00
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
}