2010-08-02 13:18:18 +03:00
/*
2012-04-12 16:51:47 +05:30
* OMAP4 + Power Management Routines
2010-08-02 13:18:18 +03:00
*
2012-04-12 16:51:47 +05:30
* Copyright ( C ) 2010 - 2013 Texas Instruments , Inc .
2010-08-02 13:18:18 +03:00
* Rajendra Nayak < rnayak @ ti . com >
2010-06-16 22:19:49 +05:30
* Santosh Shilimkar < santosh . shilimkar @ ti . com >
2010-08-02 13:18:18 +03:00
*
* 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/pm.h>
# include <linux/suspend.h>
# include <linux/module.h>
# include <linux/list.h>
# include <linux/err.h>
# include <linux/slab.h>
2012-03-28 18:30:01 +01:00
# include <asm/system_misc.h>
2010-08-02 13:18:18 +03:00
2012-10-05 13:25:59 -07:00
# include "soc.h"
2011-11-10 22:45:17 +01:00
# include "common.h"
2011-01-05 22:03:17 +05:30
# include "clockdomain.h"
2010-12-21 21:05:16 -07:00
# include "powerdomain.h"
2010-06-16 22:19:49 +05:30
# include "pm.h"
2010-08-02 13:18:18 +03:00
2014-01-20 14:06:37 -06:00
u16 pm44xx_errata ;
2010-08-02 13:18:18 +03:00
struct power_state {
struct powerdomain * pwrdm ;
u32 next_state ;
2014-06-05 21:40:39 -05:00
u32 next_logic_state ;
2010-08-02 13:18:18 +03:00
# ifdef CONFIG_SUSPEND
u32 saved_state ;
2011-06-06 14:33:29 +05:30
u32 saved_logic_state ;
2010-08-02 13:18:18 +03:00
# endif
struct list_head node ;
} ;
2014-10-21 15:22:29 -05:00
/**
* struct static_dep_map - Static dependency map
* @ from : from clockdomain
* @ to : to clockdomain
*/
struct static_dep_map {
const char * from ;
const char * to ;
} ;
2013-05-27 15:46:44 +05:30
static u32 cpu_suspend_state = PWRDM_POWER_OFF ;
2010-08-02 13:18:18 +03:00
static LIST_HEAD ( pwrst_list ) ;
# ifdef CONFIG_SUSPEND
static int omap4_pm_suspend ( void )
{
2010-06-16 22:19:49 +05:30
struct power_state * pwrst ;
int state , ret = 0 ;
u32 cpu_id = smp_processor_id ( ) ;
/* Save current powerdomain state */
list_for_each_entry ( pwrst , & pwrst_list , node ) {
pwrst - > saved_state = pwrdm_read_next_pwrst ( pwrst - > pwrdm ) ;
2011-06-06 14:33:29 +05:30
pwrst - > saved_logic_state = pwrdm_read_logic_retst ( pwrst - > pwrdm ) ;
2010-06-16 22:19:49 +05:30
}
/* Set targeted power domain states by suspend */
list_for_each_entry ( pwrst , & pwrst_list , node ) {
omap_set_pwrdm_state ( pwrst - > pwrdm , pwrst - > next_state ) ;
2014-06-05 21:40:39 -05:00
pwrdm_set_logic_retst ( pwrst - > pwrdm , pwrst - > next_logic_state ) ;
2010-06-16 22:19:49 +05:30
}
/*
* For MPUSS to hit power domain retention ( CSWR or OSWR ) ,
* CPU0 and CPU1 power domains need to be in OFF or DORMANT state ,
* since CPU power domain CSWR is not supported by hardware
* Only master CPU follows suspend path . All other CPUs follow
* CPU hotplug path in system wide suspend . On OMAP4 , CPU power
* domain CSWR is not supported by hardware .
* More details can be found in OMAP4430 TRM section 4.3 .4 .2 .
*/
2013-05-27 15:46:44 +05:30
omap4_enter_lowpower ( cpu_id , cpu_suspend_state ) ;
2010-06-16 22:19:49 +05:30
/* Restore next powerdomain state */
list_for_each_entry ( pwrst , & pwrst_list , node ) {
state = pwrdm_read_prev_pwrst ( pwrst - > pwrdm ) ;
if ( state > pwrst - > next_state ) {
2012-07-26 00:54:26 -06:00
pr_info ( " Powerdomain (%s) didn't enter target state %d \n " ,
pwrst - > pwrdm - > name , pwrst - > next_state ) ;
2010-06-16 22:19:49 +05:30
ret = - 1 ;
}
omap_set_pwrdm_state ( pwrst - > pwrdm , pwrst - > saved_state ) ;
2011-06-06 14:33:29 +05:30
pwrdm_set_logic_retst ( pwrst - > pwrdm , pwrst - > saved_logic_state ) ;
2010-06-16 22:19:49 +05:30
}
2013-02-04 17:54:43 +05:30
if ( ret ) {
2010-06-16 22:19:49 +05:30
pr_crit ( " Could not enter target state in pm_suspend \n " ) ;
2013-02-04 17:54:43 +05:30
/*
* OMAP4 chip PM currently works only with certain ( newer )
* versions of bootloaders . This is due to missing code in the
* kernel to properly reset and initialize some devices .
* Warn the user about the bootloader version being one of the
* possible causes .
* http : //www.spinics.net/lists/arm-kernel/msg218641.html
*/
pr_warn ( " A possible cause could be an old bootloader - try u-boot >= v2012.07 \n " ) ;
} else {
2010-06-16 22:19:49 +05:30
pr_info ( " Successfully put all powerdomains to target state \n " ) ;
2013-02-04 17:54:43 +05:30
}
2010-06-16 22:19:49 +05:30
2010-08-02 13:18:18 +03:00
return 0 ;
}
2014-05-12 13:33:21 -05:00
# else
# define omap4_pm_suspend NULL
2010-08-02 13:18:18 +03:00
# endif /* CONFIG_SUSPEND */
static int __init pwrdms_setup ( struct powerdomain * pwrdm , void * unused )
{
struct power_state * pwrst ;
if ( ! pwrdm - > pwrsts )
return 0 ;
2010-06-16 22:19:49 +05:30
/*
* Skip CPU0 and CPU1 power domains . CPU1 is programmed
* through hotplug path and CPU0 explicitly programmed
* further down in the code path
*/
2013-05-27 15:46:44 +05:30
if ( ! strncmp ( pwrdm - > name , " cpu " , 3 ) ) {
if ( IS_PM44XX_ERRATUM ( PM_OMAP4_CPU_OSWR_DISABLE ) )
cpu_suspend_state = PWRDM_POWER_RET ;
2010-06-16 22:19:49 +05:30
return 0 ;
2013-05-27 15:46:44 +05:30
}
2010-06-16 22:19:49 +05:30
2010-08-02 13:18:18 +03:00
pwrst = kmalloc ( sizeof ( struct power_state ) , GFP_ATOMIC ) ;
if ( ! pwrst )
return - ENOMEM ;
2010-06-16 22:19:49 +05:30
2010-08-02 13:18:18 +03:00
pwrst - > pwrdm = pwrdm ;
2014-06-06 01:17:37 -05:00
pwrst - > next_state = pwrdm_get_valid_lp_state ( pwrdm , false ,
PWRDM_POWER_RET ) ;
pwrst - > next_logic_state = pwrdm_get_valid_lp_state ( pwrdm , true ,
PWRDM_POWER_OFF ) ;
2014-06-05 21:40:39 -05:00
2010-08-02 13:18:18 +03:00
list_add ( & pwrst - > node , & pwrst_list ) ;
2010-06-16 22:19:49 +05:30
return omap_set_pwrdm_state ( pwrst - > pwrdm , pwrst - > next_state ) ;
2010-08-02 13:18:18 +03:00
}
2011-07-18 12:25:10 +05:30
/**
* omap_default_idle - OMAP4 default ilde routine . '
*
* Implements OMAP4 memory , IO ordering requirements which can ' t be addressed
2013-03-29 21:35:01 +01:00
* with default cpu_do_idle ( ) hook . Used by all CPUs with ! CONFIG_CPU_IDLE and
* by secondary CPU with CONFIG_CPU_IDLE .
2011-07-18 12:25:10 +05:30
*/
static void omap_default_idle ( void )
{
omap_do_wfi ( ) ;
}
2014-10-21 15:22:29 -05:00
/*
* The dynamic dependency between MPUSS - > MEMIF and
* MPUSS - > L4_PER / L3_ * and DUCATI - > L3_ * doesn ' t work as
* expected . The hardware recommendation is to enable static
* dependencies for these to avoid system lock ups or random crashes .
* The L4 wakeup depedency is added to workaround the OCP sync hardware
* BUG with 32 K synctimer which lead to incorrect timer value read
* from the 32 K counter . The BUG applies for GPTIMER1 and WDT2 which
* are part of L4 wakeup clockdomain .
2010-08-02 13:18:18 +03:00
*/
2014-10-21 15:22:29 -05:00
static const struct static_dep_map omap4_static_dep_map [ ] = {
{ . from = " mpuss_clkdm " , . to = " l3_emif_clkdm " } ,
{ . from = " mpuss_clkdm " , . to = " l3_1_clkdm " } ,
{ . from = " mpuss_clkdm " , . to = " l3_2_clkdm " } ,
{ . from = " ducati_clkdm " , . to = " l3_1_clkdm " } ,
{ . from = " ducati_clkdm " , . to = " l3_2_clkdm " } ,
{ . from = NULL } /* TERMINATION */
} ;
2012-04-12 16:51:47 +05:30
2014-10-21 15:22:29 -05:00
static const struct static_dep_map omap5_dra7_static_dep_map [ ] = {
{ . from = " mpu_clkdm " , . to = " emif_clkdm " } ,
{ . from = NULL } /* TERMINATION */
} ;
2014-01-20 14:06:37 -06:00
2013-02-06 15:51:45 +05:30
/**
2014-10-21 15:22:29 -05:00
* omap4plus_init_static_deps ( ) - Initialize a static dependency map
* @ map : Mapping of clock domains
2013-02-06 15:51:45 +05:30
*/
2014-10-21 15:22:29 -05:00
static inline int omap4plus_init_static_deps ( const struct static_dep_map * map )
2013-02-06 15:51:45 +05:30
{
int ret ;
2014-10-21 15:22:29 -05:00
struct clockdomain * from , * to ;
2013-02-06 15:51:45 +05:30
2014-10-21 15:22:29 -05:00
if ( ! map )
return 0 ;
while ( map - > from ) {
from = clkdm_lookup ( map - > from ) ;
to = clkdm_lookup ( map - > to ) ;
if ( ! from | | ! to ) {
pr_err ( " Failed lookup %s or %s for wakeup dependency \n " ,
map - > from , map - > to ) ;
return - EINVAL ;
}
ret = clkdm_add_wkdep ( from , to ) ;
if ( ret ) {
pr_err ( " Failed to add %s -> %s wakeup dependency(%d) \n " ,
map - > from , map - > to , ret ) ;
return ret ;
}
2013-02-06 15:51:45 +05:30
2014-10-21 15:22:29 -05:00
map + + ;
2015-09-17 15:38:05 +02:00
}
2013-02-06 15:51:45 +05:30
2014-10-21 15:22:29 -05:00
return 0 ;
2013-02-06 15:51:45 +05:30
}
2014-01-20 14:06:37 -06:00
/**
* omap4_pm_init_early - Does early initialization necessary for OMAP4 + devices
*
* Initializes basic stuff for power management functionality .
*/
int __init omap4_pm_init_early ( void )
{
if ( cpu_is_omap446x ( ) )
pm44xx_errata | = PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD ;
2013-05-27 15:46:44 +05:30
if ( soc_is_omap54xx ( ) | | soc_is_dra7xx ( ) )
pm44xx_errata | = PM_OMAP4_CPU_OSWR_DISABLE ;
2014-01-20 14:06:37 -06:00
return 0 ;
2012-04-12 16:51:47 +05:30
}
/**
* omap4_pm_init - Init routine for OMAP4 + devices
*
* Initializes all powerdomain and clockdomain target states
* and all PRCM settings .
* Return : Returns the error code returned by called functions .
*/
int __init omap4_pm_init ( void )
{
int ret = 0 ;
if ( omap_rev ( ) = = OMAP4430_REV_ES1_0 ) {
WARN ( 1 , " Power Management not supported on OMAP4430 ES1.0 \n " ) ;
return - ENODEV ;
}
pr_info ( " Power Management for TI OMAP4+ devices. \n " ) ;
2014-10-21 15:22:28 -05:00
/*
* OMAP4 chip PM currently works only with certain ( newer )
* versions of bootloaders . This is due to missing code in the
* kernel to properly reset and initialize some devices .
* http : //www.spinics.net/lists/arm-kernel/msg218641.html
*/
if ( cpu_is_omap44xx ( ) )
pr_warn ( " OMAP4 PM: u-boot >= v2012.07 is required for full PM support \n " ) ;
2012-04-12 16:51:47 +05:30
ret = pwrdm_for_each ( pwrdms_setup , NULL ) ;
if ( ret ) {
pr_err ( " Failed to setup powerdomains. \n " ) ;
2011-03-08 18:24:30 +05:30
goto err2 ;
}
2013-02-06 15:51:45 +05:30
if ( cpu_is_omap44xx ( ) )
2014-10-21 15:22:29 -05:00
ret = omap4plus_init_static_deps ( omap4_static_dep_map ) ;
2013-02-06 15:51:45 +05:30
else if ( soc_is_omap54xx ( ) | | soc_is_dra7xx ( ) )
2014-10-21 15:22:29 -05:00
ret = omap4plus_init_static_deps ( omap5_dra7_static_dep_map ) ;
2013-02-06 15:51:45 +05:30
if ( ret ) {
pr_err ( " Failed to initialise static dependencies. \n " ) ;
goto err2 ;
2012-04-12 16:51:47 +05:30
}
2010-06-16 22:19:48 +05:30
ret = omap4_mpuss_init ( ) ;
if ( ret ) {
pr_err ( " Failed to initialise OMAP4 MPUSS \n " ) ;
goto err2 ;
}
2012-02-02 02:38:50 -07:00
( void ) clkdm_for_each ( omap_pm_clkdms_setup , NULL ) ;
2011-01-05 22:03:17 +05:30
2014-05-12 13:33:21 -05:00
omap_common_suspend_init ( omap4_pm_suspend ) ;
2010-08-02 13:18:18 +03:00
2011-12-19 03:03:58 -05:00
/* Overwrite the default cpu_do_idle() */
2012-01-04 16:27:48 -05:00
arm_pm_idle = omap_default_idle ;
2011-07-18 12:25:10 +05:30
2012-04-12 16:51:47 +05:30
if ( cpu_is_omap44xx ( ) )
omap4_idle_init ( ) ;
2011-08-16 17:31:40 +05:30
2010-08-02 13:18:18 +03:00
err2 :
return ret ;
}