2017-12-25 20:54:33 +01:00
// SPDX-License-Identifier: GPL-2.0
//
// Copyright 2008 Openmoko, Inc.
// Copyright 2008 Simtec Electronics
// Ben Dooks <ben@simtec.co.uk>
// http://armlinux.simtec.co.uk/
//
// S3C64XX CPU PM support.
2009-03-10 18:19:35 +00:00
# include <linux/init.h>
# include <linux/suspend.h>
# include <linux/serial_core.h>
# include <linux/io.h>
2011-08-13 10:40:52 +09:00
# include <linux/gpio.h>
2011-12-08 23:27:48 +01:00
# include <linux/pm_domain.h>
2009-03-10 18:19:35 +00:00
2019-09-02 18:37:30 +02:00
# include "map.h"
2022-04-02 15:28:18 +02:00
# include "irqs.h"
2009-03-10 18:19:35 +00:00
2019-09-02 18:37:30 +02:00
# include "cpu.h"
# include "devs.h"
# include "pm.h"
# include "wakeup-mask.h"
2010-05-20 12:56:45 +09:00
2019-09-02 18:37:30 +02:00
# include "regs-gpio.h"
# include "regs-clock.h"
# include "gpio-samsung.h"
2009-03-10 18:19:35 +00:00
2019-09-02 17:47:55 +02:00
# include "regs-gpio-memport-s3c64xx.h"
# include "regs-modem-s3c64xx.h"
# include "regs-sys-s3c64xx.h"
# include "regs-syscon-power-s3c64xx.h"
2009-03-10 18:19:35 +00:00
2011-12-08 23:27:48 +01:00
struct s3c64xx_pm_domain {
char * const name ;
u32 ena ;
u32 pwr_stat ;
struct generic_pm_domain pd ;
} ;
static int s3c64xx_pd_off ( struct generic_pm_domain * domain )
{
struct s3c64xx_pm_domain * pd ;
u32 val ;
pd = container_of ( domain , struct s3c64xx_pm_domain , pd ) ;
val = __raw_readl ( S3C64XX_NORMAL_CFG ) ;
val & = ~ ( pd - > ena ) ;
__raw_writel ( val , S3C64XX_NORMAL_CFG ) ;
return 0 ;
}
static int s3c64xx_pd_on ( struct generic_pm_domain * domain )
{
struct s3c64xx_pm_domain * pd ;
u32 val ;
long retry = 1000000L ;
pd = container_of ( domain , struct s3c64xx_pm_domain , pd ) ;
val = __raw_readl ( S3C64XX_NORMAL_CFG ) ;
val | = pd - > ena ;
__raw_writel ( val , S3C64XX_NORMAL_CFG ) ;
/* Not all domains provide power status readback */
if ( pd - > pwr_stat ) {
do {
cpu_relax ( ) ;
if ( __raw_readl ( S3C64XX_BLK_PWR_STAT ) & pd - > pwr_stat )
break ;
} while ( retry - - ) ;
if ( ! retry ) {
pr_err ( " Failed to start domain %s \n " , pd - > name ) ;
return - EBUSY ;
}
}
return 0 ;
}
static struct s3c64xx_pm_domain s3c64xx_pm_irom = {
. name = " IROM " ,
. ena = S3C64XX_NORMALCFG_IROM_ON ,
. pd = {
. power_off = s3c64xx_pd_off ,
. power_on = s3c64xx_pd_on ,
} ,
} ;
static struct s3c64xx_pm_domain s3c64xx_pm_etm = {
. name = " ETM " ,
. ena = S3C64XX_NORMALCFG_DOMAIN_ETM_ON ,
. pwr_stat = S3C64XX_BLKPWRSTAT_ETM ,
. pd = {
. power_off = s3c64xx_pd_off ,
. power_on = s3c64xx_pd_on ,
} ,
} ;
static struct s3c64xx_pm_domain s3c64xx_pm_s = {
. name = " S " ,
. ena = S3C64XX_NORMALCFG_DOMAIN_S_ON ,
. pwr_stat = S3C64XX_BLKPWRSTAT_S ,
. pd = {
. power_off = s3c64xx_pd_off ,
. power_on = s3c64xx_pd_on ,
} ,
} ;
static struct s3c64xx_pm_domain s3c64xx_pm_f = {
. name = " F " ,
. ena = S3C64XX_NORMALCFG_DOMAIN_F_ON ,
. pwr_stat = S3C64XX_BLKPWRSTAT_F ,
. pd = {
. power_off = s3c64xx_pd_off ,
. power_on = s3c64xx_pd_on ,
} ,
} ;
static struct s3c64xx_pm_domain s3c64xx_pm_p = {
. name = " P " ,
. ena = S3C64XX_NORMALCFG_DOMAIN_P_ON ,
. pwr_stat = S3C64XX_BLKPWRSTAT_P ,
. pd = {
. power_off = s3c64xx_pd_off ,
. power_on = s3c64xx_pd_on ,
} ,
} ;
static struct s3c64xx_pm_domain s3c64xx_pm_i = {
. name = " I " ,
. ena = S3C64XX_NORMALCFG_DOMAIN_I_ON ,
. pwr_stat = S3C64XX_BLKPWRSTAT_I ,
. pd = {
. power_off = s3c64xx_pd_off ,
. power_on = s3c64xx_pd_on ,
} ,
} ;
static struct s3c64xx_pm_domain s3c64xx_pm_g = {
. name = " G " ,
. ena = S3C64XX_NORMALCFG_DOMAIN_G_ON ,
. pd = {
. power_off = s3c64xx_pd_off ,
. power_on = s3c64xx_pd_on ,
} ,
} ;
static struct s3c64xx_pm_domain s3c64xx_pm_v = {
. name = " V " ,
. ena = S3C64XX_NORMALCFG_DOMAIN_V_ON ,
. pwr_stat = S3C64XX_BLKPWRSTAT_V ,
. pd = {
. power_off = s3c64xx_pd_off ,
. power_on = s3c64xx_pd_on ,
} ,
} ;
static struct s3c64xx_pm_domain * s3c64xx_always_on_pm_domains [ ] = {
& s3c64xx_pm_irom ,
} ;
static struct s3c64xx_pm_domain * s3c64xx_pm_domains [ ] = {
& s3c64xx_pm_etm ,
& s3c64xx_pm_g ,
& s3c64xx_pm_v ,
& s3c64xx_pm_i ,
& s3c64xx_pm_p ,
& s3c64xx_pm_s ,
& s3c64xx_pm_f ,
} ;
2015-02-27 05:50:22 +09:00
# ifdef CONFIG_PM_SLEEP
2009-03-10 18:19:35 +00:00
static struct sleep_save core_save [ ] = {
SAVE_ITEM ( S3C64XX_MEM0DRVCON ) ,
SAVE_ITEM ( S3C64XX_MEM1DRVCON ) ,
} ;
static struct sleep_save misc_save [ ] = {
SAVE_ITEM ( S3C64XX_AHB_CON0 ) ,
SAVE_ITEM ( S3C64XX_AHB_CON1 ) ,
SAVE_ITEM ( S3C64XX_AHB_CON2 ) ,
SAVE_ITEM ( S3C64XX_SPCON ) ,
SAVE_ITEM ( S3C64XX_MEM0CONSTOP ) ,
SAVE_ITEM ( S3C64XX_MEM1CONSTOP ) ,
SAVE_ITEM ( S3C64XX_MEM0CONSLP0 ) ,
SAVE_ITEM ( S3C64XX_MEM0CONSLP1 ) ,
SAVE_ITEM ( S3C64XX_MEM1CONSLP ) ,
2011-08-23 11:33:08 +09:00
SAVE_ITEM ( S3C64XX_SDMA_SEL ) ,
2011-08-23 11:33:08 +09:00
SAVE_ITEM ( S3C64XX_MODEM_MIFPCON ) ,
2011-12-08 23:27:48 +01:00
SAVE_ITEM ( S3C64XX_NORMAL_CFG ) ,
2009-03-10 18:19:35 +00:00
} ;
void s3c_pm_configure_extint ( void )
{
__raw_writel ( s3c_irqwake_eintmask , S3C64XX_EINT_MASK ) ;
}
void s3c_pm_restore_core ( void )
{
__raw_writel ( 0 , S3C64XX_EINT_MASK ) ;
s3c_pm_do_restore_core ( core_save , ARRAY_SIZE ( core_save ) ) ;
s3c_pm_do_restore ( misc_save , ARRAY_SIZE ( misc_save ) ) ;
}
void s3c_pm_save_core ( void )
{
s3c_pm_do_save ( misc_save , ARRAY_SIZE ( misc_save ) ) ;
s3c_pm_do_save ( core_save , ARRAY_SIZE ( core_save ) ) ;
}
2015-02-27 05:50:22 +09:00
# endif
2009-03-10 18:19:35 +00:00
/* since both s3c6400 and s3c6410 share the same sleep pm calls, we
* put the per - cpu code in here until any new cpu comes along and changes
* this .
*/
2011-07-02 09:54:01 +01:00
static int s3c64xx_cpu_suspend ( unsigned long arg )
2009-03-10 18:19:35 +00:00
{
unsigned long tmp ;
/* set our standby method to sleep */
tmp = __raw_readl ( S3C64XX_PWR_CFG ) ;
tmp & = ~ S3C64XX_PWRCFG_CFG_WFI_MASK ;
tmp | = S3C64XX_PWRCFG_CFG_WFI_SLEEP ;
__raw_writel ( tmp , S3C64XX_PWR_CFG ) ;
/* clear any old wakeup */
__raw_writel ( __raw_readl ( S3C64XX_WAKEUP_STAT ) ,
S3C64XX_WAKEUP_STAT ) ;
/* 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 "
" mcr p15, 0, %0, c7, c0, 4 " : : " r " ( tmp ) ) ;
/* we should never get past here */
2013-01-25 10:40:19 -08:00
pr_info ( " Failed to suspend the system \n " ) ;
return 1 ; /* Aborting suspend */
2009-03-10 18:19:35 +00:00
}
2010-05-20 12:56:45 +09:00
/* mapping of interrupts to parts of the wakeup mask */
2016-12-10 15:47:38 +02:00
static const struct samsung_wakeup_mask wake_irqs [ ] = {
2010-05-20 12:56:45 +09:00
{ . irq = IRQ_RTC_ALARM , . bit = S3C64XX_PWRCFG_RTC_ALARM_DISABLE , } ,
{ . irq = IRQ_RTC_TIC , . bit = S3C64XX_PWRCFG_RTC_TICK_DISABLE , } ,
{ . irq = IRQ_PENDN , . bit = S3C64XX_PWRCFG_TS_DISABLE , } ,
{ . irq = IRQ_HSMMC0 , . bit = S3C64XX_PWRCFG_MMC0_DISABLE , } ,
{ . irq = IRQ_HSMMC1 , . bit = S3C64XX_PWRCFG_MMC1_DISABLE , } ,
{ . irq = IRQ_HSMMC2 , . bit = S3C64XX_PWRCFG_MMC2_DISABLE , } ,
{ . irq = NO_WAKEUP_IRQ , . bit = S3C64XX_PWRCFG_BATF_DISABLE } ,
{ . irq = NO_WAKEUP_IRQ , . bit = S3C64XX_PWRCFG_MSM_DISABLE } ,
{ . irq = NO_WAKEUP_IRQ , . bit = S3C64XX_PWRCFG_HSI_DISABLE } ,
{ . irq = NO_WAKEUP_IRQ , . bit = S3C64XX_PWRCFG_MSM_DISABLE } ,
} ;
2009-03-10 18:19:35 +00:00
static void s3c64xx_pm_prepare ( void )
{
2010-05-20 12:56:45 +09:00
samsung_sync_wakemask ( S3C64XX_PWR_CFG ,
wake_irqs , ARRAY_SIZE ( wake_irqs ) ) ;
2009-03-10 18:19:35 +00:00
/* store address of resume. */
2017-01-15 03:59:29 +01:00
__raw_writel ( __pa_symbol ( s3c_cpu_resume ) , S3C64XX_INFORM0 ) ;
2009-03-10 18:19:35 +00:00
/* ensure previous wakeup state is cleared before sleeping */
__raw_writel ( __raw_readl ( S3C64XX_WAKEUP_STAT ) , S3C64XX_WAKEUP_STAT ) ;
}
2011-12-08 23:27:48 +01:00
int __init s3c64xx_pm_init ( void )
{
int i ;
s3c_pm_init ( ) ;
for ( i = 0 ; i < ARRAY_SIZE ( s3c64xx_always_on_pm_domains ) ; i + + )
pm_genpd_init ( & s3c64xx_always_on_pm_domains [ i ] - > pd ,
& pm_domain_always_on_gov , false ) ;
for ( i = 0 ; i < ARRAY_SIZE ( s3c64xx_pm_domains ) ; i + + )
pm_genpd_init ( & s3c64xx_pm_domains [ i ] - > pd , NULL , false ) ;
2013-01-09 17:13:23 -08:00
# ifdef CONFIG_S3C_DEV_FB
2011-12-08 23:27:48 +01:00
if ( dev_get_platdata ( & s3c_device_fb . dev ) )
pm_genpd_add_device ( & s3c64xx_pm_f . pd , & s3c_device_fb . dev ) ;
2013-01-09 17:13:23 -08:00
# endif
2011-12-08 23:27:48 +01:00
return 0 ;
}
static __init int s3c64xx_pm_initcall ( void )
2009-03-10 18:19:35 +00:00
{
2015-02-27 20:31:51 +01:00
if ( ! soc_is_s3c64xx ( ) )
return 0 ;
2009-03-10 18:19:35 +00:00
pm_cpu_prep = s3c64xx_pm_prepare ;
pm_cpu_sleep = s3c64xx_cpu_suspend ;
2011-05-06 09:37:17 +09:00
2009-03-10 18:19:35 +00:00
return 0 ;
}
2011-12-08 23:27:48 +01:00
arch_initcall ( s3c64xx_pm_initcall ) ;