2010-08-02 14:18:18 +04:00
/*
2012-04-12 15:21:47 +04:00
* OMAP4 + Power Management Routines
2010-08-02 14:18:18 +04:00
*
2012-04-12 15:21:47 +04:00
* Copyright ( C ) 2010 - 2013 Texas Instruments , Inc .
2010-08-02 14:18:18 +04:00
* Rajendra Nayak < rnayak @ ti . com >
2010-06-16 20:49:49 +04:00
* Santosh Shilimkar < santosh . shilimkar @ ti . com >
2010-08-02 14:18:18 +04: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 21:30:01 +04:00
# include <asm/system_misc.h>
2010-08-02 14:18:18 +04:00
2012-10-06 00:25:59 +04:00
# include "soc.h"
2011-11-11 01:45:17 +04:00
# include "common.h"
2011-01-05 19:33:17 +03:00
# include "clockdomain.h"
2010-12-22 07:05:16 +03:00
# include "powerdomain.h"
2010-06-16 20:49:49 +04:00
# include "pm.h"
2010-08-02 14:18:18 +04:00
2014-01-21 00:06:37 +04:00
u16 pm44xx_errata ;
2010-08-02 14:18:18 +04:00
struct power_state {
struct powerdomain * pwrdm ;
u32 next_state ;
2014-06-06 06:40:39 +04:00
u32 next_logic_state ;
2010-08-02 14:18:18 +04:00
# ifdef CONFIG_SUSPEND
u32 saved_state ;
2011-06-06 13:03:29 +04:00
u32 saved_logic_state ;
2010-08-02 14:18:18 +04:00
# endif
struct list_head node ;
} ;
2014-10-22 00:22:29 +04: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 14:16:44 +04:00
static u32 cpu_suspend_state = PWRDM_POWER_OFF ;
2010-08-02 14:18:18 +04:00
static LIST_HEAD ( pwrst_list ) ;
# ifdef CONFIG_SUSPEND
static int omap4_pm_suspend ( void )
{
2010-06-16 20:49:49 +04:00
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 13:03:29 +04:00
pwrst - > saved_logic_state = pwrdm_read_logic_retst ( pwrst - > pwrdm ) ;
2010-06-16 20:49:49 +04:00
}
/* 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-06 06:40:39 +04:00
pwrdm_set_logic_retst ( pwrst - > pwrdm , pwrst - > next_logic_state ) ;
2010-06-16 20:49:49 +04:00
}
/*
* 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 14:16:44 +04:00
omap4_enter_lowpower ( cpu_id , cpu_suspend_state ) ;
2010-06-16 20:49:49 +04:00
/* 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 10:54:26 +04:00
pr_info ( " Powerdomain (%s) didn't enter target state %d \n " ,
pwrst - > pwrdm - > name , pwrst - > next_state ) ;
2010-06-16 20:49:49 +04:00
ret = - 1 ;
}
omap_set_pwrdm_state ( pwrst - > pwrdm , pwrst - > saved_state ) ;
2011-06-06 13:03:29 +04:00
pwrdm_set_logic_retst ( pwrst - > pwrdm , pwrst - > saved_logic_state ) ;
2010-06-16 20:49:49 +04:00
}
2013-02-04 16:24:43 +04:00
if ( ret ) {
2010-06-16 20:49:49 +04:00
pr_crit ( " Could not enter target state in pm_suspend \n " ) ;
2013-02-04 16:24:43 +04: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 .
* 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 20:49:49 +04:00
pr_info ( " Successfully put all powerdomains to target state \n " ) ;
2013-02-04 16:24:43 +04:00
}
2010-06-16 20:49:49 +04:00
2010-08-02 14:18:18 +04:00
return 0 ;
}
2014-05-12 22:33:21 +04:00
# else
# define omap4_pm_suspend NULL
2010-08-02 14:18:18 +04: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 20:49:49 +04:00
/*
* 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 14:16:44 +04:00
if ( ! strncmp ( pwrdm - > name , " cpu " , 3 ) ) {
if ( IS_PM44XX_ERRATUM ( PM_OMAP4_CPU_OSWR_DISABLE ) )
cpu_suspend_state = PWRDM_POWER_RET ;
2010-06-16 20:49:49 +04:00
return 0 ;
2013-05-27 14:16:44 +04:00
}
2010-06-16 20:49:49 +04:00
2010-08-02 14:18:18 +04:00
pwrst = kmalloc ( sizeof ( struct power_state ) , GFP_ATOMIC ) ;
if ( ! pwrst )
return - ENOMEM ;
2010-06-16 20:49:49 +04:00
2010-08-02 14:18:18 +04:00
pwrst - > pwrdm = pwrdm ;
2014-06-06 10:17:37 +04: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-06 06:40:39 +04:00
2010-08-02 14:18:18 +04:00
list_add ( & pwrst - > node , & pwrst_list ) ;
2010-06-16 20:49:49 +04:00
return omap_set_pwrdm_state ( pwrst - > pwrdm , pwrst - > next_state ) ;
2010-08-02 14:18:18 +04:00
}
2011-07-18 10:55:10 +04:00
/**
* omap_default_idle - OMAP4 default ilde routine . '
*
* Implements OMAP4 memory , IO ordering requirements which can ' t be addressed
2013-03-30 00:35:01 +04: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 10:55:10 +04:00
*/
static void omap_default_idle ( void )
{
omap_do_wfi ( ) ;
}
2014-10-22 00:22:29 +04: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 14:18:18 +04:00
*/
2014-10-22 00:22:29 +04: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 15:21:47 +04:00
2014-10-22 00:22:29 +04:00
static const struct static_dep_map omap5_dra7_static_dep_map [ ] = {
{ . from = " mpu_clkdm " , . to = " emif_clkdm " } ,
{ . from = NULL } /* TERMINATION */
} ;
2014-01-21 00:06:37 +04:00
2013-02-06 14:21:45 +04:00
/**
2014-10-22 00:22:29 +04:00
* omap4plus_init_static_deps ( ) - Initialize a static dependency map
* @ map : Mapping of clock domains
2013-02-06 14:21:45 +04:00
*/
2014-10-22 00:22:29 +04:00
static inline int omap4plus_init_static_deps ( const struct static_dep_map * map )
2013-02-06 14:21:45 +04:00
{
int ret ;
2014-10-22 00:22:29 +04:00
struct clockdomain * from , * to ;
2013-02-06 14:21:45 +04:00
2014-10-22 00:22:29 +04: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 14:21:45 +04:00
2014-10-22 00:22:29 +04:00
map + + ;
2015-09-17 16:38:05 +03:00
}
2013-02-06 14:21:45 +04:00
2014-10-22 00:22:29 +04:00
return 0 ;
2013-02-06 14:21:45 +04:00
}
2014-01-21 00:06:37 +04: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 14:16:44 +04:00
if ( soc_is_omap54xx ( ) | | soc_is_dra7xx ( ) )
pm44xx_errata | = PM_OMAP4_CPU_OSWR_DISABLE ;
2014-01-21 00:06:37 +04:00
return 0 ;
2012-04-12 15:21:47 +04:00
}
/**
* 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-22 00:22:28 +04: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 15:21:47 +04:00
ret = pwrdm_for_each ( pwrdms_setup , NULL ) ;
if ( ret ) {
pr_err ( " Failed to setup powerdomains. \n " ) ;
2011-03-08 15:54:30 +03:00
goto err2 ;
}
2013-02-06 14:21:45 +04:00
if ( cpu_is_omap44xx ( ) )
2014-10-22 00:22:29 +04:00
ret = omap4plus_init_static_deps ( omap4_static_dep_map ) ;
2013-02-06 14:21:45 +04:00
else if ( soc_is_omap54xx ( ) | | soc_is_dra7xx ( ) )
2014-10-22 00:22:29 +04:00
ret = omap4plus_init_static_deps ( omap5_dra7_static_dep_map ) ;
2013-02-06 14:21:45 +04:00
if ( ret ) {
pr_err ( " Failed to initialise static dependencies. \n " ) ;
goto err2 ;
2012-04-12 15:21:47 +04:00
}
2010-06-16 20:49:48 +04:00
ret = omap4_mpuss_init ( ) ;
if ( ret ) {
pr_err ( " Failed to initialise OMAP4 MPUSS \n " ) ;
goto err2 ;
}
2012-02-02 13:38:50 +04:00
( void ) clkdm_for_each ( omap_pm_clkdms_setup , NULL ) ;
2011-01-05 19:33:17 +03:00
2014-05-12 22:33:21 +04:00
omap_common_suspend_init ( omap4_pm_suspend ) ;
2010-08-02 14:18:18 +04:00
2011-12-19 12:03:58 +04:00
/* Overwrite the default cpu_do_idle() */
2012-01-05 01:27:48 +04:00
arm_pm_idle = omap_default_idle ;
2011-07-18 10:55:10 +04:00
2012-04-12 15:21:47 +04:00
if ( cpu_is_omap44xx ( ) )
omap4_idle_init ( ) ;
2011-08-16 16:01:40 +04:00
2010-08-02 14:18:18 +04:00
err2 :
return ret ;
}