2018-02-23 09:43:56 -06:00
// SPDX-License-Identifier: GPL-2.0
/*
* AM33XX Arch Power Management Routines
*
* Copyright ( C ) 2016 - 2018 Texas Instruments Incorporated - http : //www.ti.com/
* Dave Gerlach
*/
# include <asm/smp_scu.h>
# include <asm/suspend.h>
# include <linux/errno.h>
# include <linux/platform_data/pm33xx.h>
2019-04-03 10:27:40 +05:30
# include <linux/clk.h>
# include <linux/platform_data/gpio-omap.h>
# include <linux/pinctrl/pinmux.h>
# include <linux/wkup_m3_ipc.h>
# include <linux/of.h>
# include <linux/rtc.h>
2018-02-23 09:43:56 -06:00
# include "cm33xx.h"
# include "common.h"
# include "control.h"
# include "clockdomain.h"
# include "iomap.h"
# include "omap_hwmod.h"
# include "pm.h"
# include "powerdomain.h"
# include "prm33xx.h"
# include "soc.h"
# include "sram.h"
2020-01-02 16:19:43 -05:00
# include "omap-secure.h"
2018-02-23 09:43:56 -06:00
static struct powerdomain * cefuse_pwrdm , * gfx_pwrdm , * per_pwrdm , * mpu_pwrdm ;
static struct clockdomain * gfx_l4ls_clkdm ;
static void __iomem * scu_base ;
2018-07-09 13:03:17 +05:30
static struct omap_hwmod * rtc_oh ;
2018-02-23 09:43:56 -06:00
2018-12-10 22:58:36 +01:00
static int am43xx_map_scu ( void )
2018-02-23 09:43:56 -06:00
{
scu_base = ioremap ( scu_a9_get_base ( ) , SZ_256 ) ;
if ( ! scu_base )
return - ENOMEM ;
return 0 ;
}
2019-04-03 10:27:40 +05:30
static int am33xx_check_off_mode_enable ( void )
{
if ( enable_off_mode )
pr_warn ( " WARNING: This platform does not support off-mode, entering DeepSleep suspend. \n " ) ;
/* off mode not supported on am335x so return 0 always */
return 0 ;
}
static int am43xx_check_off_mode_enable ( void )
{
/*
* Check for am437x - gp - evm which has the right Hardware design to
* support this mode reliably .
*/
if ( of_machine_is_compatible ( " ti,am437x-gp-evm " ) & & enable_off_mode )
return enable_off_mode ;
else if ( enable_off_mode )
pr_warn ( " WARNING: This platform does not support off-mode, entering DeepSleep suspend. \n " ) ;
return 0 ;
}
2018-02-23 09:43:56 -06:00
static int amx3_common_init ( void )
{
gfx_pwrdm = pwrdm_lookup ( " gfx_pwrdm " ) ;
per_pwrdm = pwrdm_lookup ( " per_pwrdm " ) ;
mpu_pwrdm = pwrdm_lookup ( " mpu_pwrdm " ) ;
if ( ( ! gfx_pwrdm ) | | ( ! per_pwrdm ) | | ( ! mpu_pwrdm ) )
return - ENODEV ;
( void ) clkdm_for_each ( omap_pm_clkdms_setup , NULL ) ;
/* CEFUSE domain can be turned off post bootup */
cefuse_pwrdm = pwrdm_lookup ( " cefuse_pwrdm " ) ;
2019-04-09 08:05:17 -07:00
if ( ! cefuse_pwrdm )
2018-02-23 09:43:56 -06:00
pr_err ( " PM: Failed to get cefuse_pwrdm \n " ) ;
2019-04-09 08:05:17 -07:00
else if ( omap_type ( ) ! = OMAP2_DEVICE_TYPE_GP )
pr_info ( " PM: Leaving EFUSE power domain active \n " ) ;
else
omap_set_pwrdm_state ( cefuse_pwrdm , PWRDM_POWER_OFF ) ;
2018-02-23 09:43:56 -06:00
return 0 ;
}
static int am33xx_suspend_init ( void )
{
int ret ;
gfx_l4ls_clkdm = clkdm_lookup ( " gfx_l4ls_gfx_clkdm " ) ;
if ( ! gfx_l4ls_clkdm ) {
pr_err ( " PM: Cannot lookup gfx_l4ls_clkdm clockdomains \n " ) ;
return - ENODEV ;
}
ret = amx3_common_init ( ) ;
return ret ;
}
static int am43xx_suspend_init ( void )
{
int ret = 0 ;
ret = am43xx_map_scu ( ) ;
if ( ret ) {
pr_err ( " PM: Could not ioremap SCU \n " ) ;
return ret ;
}
ret = amx3_common_init ( ) ;
return ret ;
}
static void amx3_pre_suspend_common ( void )
{
omap_set_pwrdm_state ( gfx_pwrdm , PWRDM_POWER_OFF ) ;
}
static void amx3_post_suspend_common ( void )
{
int status ;
/*
* Because gfx_pwrdm is the only one under MPU control ,
* comment on transition status
*/
status = pwrdm_read_pwrst ( gfx_pwrdm ) ;
if ( status ! = PWRDM_POWER_OFF )
pr_err ( " PM: GFX domain did not transition: %x \n " , status ) ;
}
2018-07-09 13:03:16 +05:30
static int am33xx_suspend ( unsigned int state , int ( * fn ) ( unsigned long ) ,
unsigned long args )
2018-02-23 09:43:56 -06:00
{
int ret = 0 ;
amx3_pre_suspend_common ( ) ;
2018-07-09 13:03:16 +05:30
ret = cpu_suspend ( args , fn ) ;
2018-02-23 09:43:56 -06:00
amx3_post_suspend_common ( ) ;
/*
* BUG : GFX_L4LS clock domain needs to be woken up to
* ensure thet L4LS clock domain does not get stuck in
* transition . If that happens L3 module does not get
* disabled , thereby leading to PER power domain
* transition failing
*/
clkdm_wakeup ( gfx_l4ls_clkdm ) ;
clkdm_sleep ( gfx_l4ls_clkdm ) ;
return ret ;
}
2018-07-09 13:03:16 +05:30
static int am43xx_suspend ( unsigned int state , int ( * fn ) ( unsigned long ) ,
unsigned long args )
2018-02-23 09:43:56 -06:00
{
int ret = 0 ;
2020-01-02 16:19:43 -05:00
/* Suspend secure side on HS devices */
if ( omap_type ( ) ! = OMAP2_DEVICE_TYPE_GP ) {
if ( optee_available )
omap_smccc_smc ( AM43xx_PPA_SVC_PM_SUSPEND , 0 ) ;
else
omap_secure_dispatcher ( AM43xx_PPA_SVC_PM_SUSPEND ,
FLAG_START_CRITICAL ,
0 , 0 , 0 , 0 , 0 ) ;
}
2018-02-23 09:43:56 -06:00
amx3_pre_suspend_common ( ) ;
scu_power_mode ( scu_base , SCU_PM_POWEROFF ) ;
2018-07-09 13:03:16 +05:30
ret = cpu_suspend ( args , fn ) ;
2018-02-23 09:43:56 -06:00
scu_power_mode ( scu_base , SCU_PM_NORMAL ) ;
2019-04-03 10:27:40 +05:30
if ( ! am43xx_check_off_mode_enable ( ) )
amx3_post_suspend_common ( ) ;
2018-02-23 09:43:56 -06:00
2020-01-02 16:19:43 -05:00
/*
* Resume secure side on HS devices .
*
* Note that even on systems with OP - TEE available this resume call is
* issued to the ROM . This is because upon waking from suspend the ROM
* is restored as the secure monitor . On systems with OP - TEE ROM will
* restore OP - TEE during this call .
*/
if ( omap_type ( ) ! = OMAP2_DEVICE_TYPE_GP )
omap_secure_dispatcher ( AM43xx_PPA_SVC_PM_RESUME ,
FLAG_START_CRITICAL ,
0 , 0 , 0 , 0 , 0 ) ;
2018-02-23 09:43:56 -06:00
return ret ;
}
static struct am33xx_pm_sram_addr * amx3_get_sram_addrs ( void )
{
if ( soc_is_am33xx ( ) )
return & am33xx_pm_sram ;
else if ( soc_is_am437x ( ) )
return & am43xx_pm_sram ;
else
return NULL ;
}
2018-07-09 13:03:17 +05:30
void __iomem * am43xx_get_rtc_base_addr ( void )
{
rtc_oh = omap_hwmod_lookup ( " rtc " ) ;
return omap_hwmod_get_mpu_rt_va ( rtc_oh ) ;
}
2019-04-03 10:27:40 +05:30
static void am43xx_save_context ( void )
{
}
static void am33xx_save_context ( void )
{
omap_intc_save_context ( ) ;
}
static void am33xx_restore_context ( void )
{
omap_intc_restore_context ( ) ;
}
static void am43xx_restore_context ( void )
{
/*
* HACK : restore dpll_per_clkdcoldo register contents , to avoid
* breaking suspend - resume
*/
writel_relaxed ( 0x0 , AM33XX_L4_WK_IO_ADDRESS ( 0x44df2e14 ) ) ;
}
static void am43xx_prepare_rtc_suspend ( void )
{
omap_hwmod_enable ( rtc_oh ) ;
}
static void am43xx_prepare_rtc_resume ( void )
{
omap_hwmod_idle ( rtc_oh ) ;
}
2018-02-23 09:43:56 -06:00
static struct am33xx_pm_platform_data am33xx_ops = {
. init = am33xx_suspend_init ,
. soc_suspend = am33xx_suspend ,
. get_sram_addrs = amx3_get_sram_addrs ,
2019-04-03 10:27:40 +05:30
. save_context = am33xx_save_context ,
. restore_context = am33xx_restore_context ,
. prepare_rtc_suspend = am43xx_prepare_rtc_suspend ,
. prepare_rtc_resume = am43xx_prepare_rtc_resume ,
. check_off_mode_enable = am33xx_check_off_mode_enable ,
2018-07-09 13:03:17 +05:30
. get_rtc_base_addr = am43xx_get_rtc_base_addr ,
2018-02-23 09:43:56 -06:00
} ;
static struct am33xx_pm_platform_data am43xx_ops = {
. init = am43xx_suspend_init ,
. soc_suspend = am43xx_suspend ,
. get_sram_addrs = amx3_get_sram_addrs ,
2019-04-03 10:27:40 +05:30
. save_context = am43xx_save_context ,
. restore_context = am43xx_restore_context ,
. prepare_rtc_suspend = am43xx_prepare_rtc_suspend ,
. prepare_rtc_resume = am43xx_prepare_rtc_resume ,
. check_off_mode_enable = am43xx_check_off_mode_enable ,
2018-07-09 13:03:17 +05:30
. get_rtc_base_addr = am43xx_get_rtc_base_addr ,
2018-02-23 09:43:56 -06:00
} ;
static struct am33xx_pm_platform_data * am33xx_pm_get_pdata ( void )
{
if ( soc_is_am33xx ( ) )
return & am33xx_ops ;
else if ( soc_is_am437x ( ) )
return & am43xx_ops ;
else
return NULL ;
}
2018-04-16 10:23:46 -07:00
int __init amx3_common_pm_init ( void )
2018-02-23 09:43:56 -06:00
{
struct am33xx_pm_platform_data * pdata ;
struct platform_device_info devinfo ;
pdata = am33xx_pm_get_pdata ( ) ;
memset ( & devinfo , 0 , sizeof ( devinfo ) ) ;
devinfo . name = " pm33xx " ;
devinfo . data = pdata ;
devinfo . size_data = sizeof ( * pdata ) ;
devinfo . id = - 1 ;
platform_device_register_full ( & devinfo ) ;
2018-04-16 10:23:46 -07:00
return 0 ;
2018-02-23 09:43:56 -06:00
}