2008-10-08 16:00:58 +04:00
/*
* linux / arch / arm / mach - omap2 / cpuidle34xx . c
*
* OMAP3 CPU IDLE Routines
*
* Copyright ( C ) 2008 Texas Instruments , Inc .
* Rajendra Nayak < rnayak @ ti . com >
*
* Copyright ( C ) 2007 Texas Instruments , Inc .
* Karthik Dasu < karthik - dp @ ti . com >
*
* Copyright ( C ) 2006 Nokia Corporation
* Tony Lindgren < tony @ atomide . com >
*
* Copyright ( C ) 2005 Texas Instruments , Inc .
* Richard Woodruff < r - woodruff2 @ ti . com >
*
* Based on pm . c for omap2
*
* 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 .
*/
2009-03-20 16:21:02 +03:00
# include <linux/sched.h>
2008-10-08 16:00:58 +04:00
# include <linux/cpuidle.h>
2011-11-08 03:58:40 +04:00
# include <linux/export.h>
2011-09-03 21:08:27 +04:00
# include <linux/cpu_pm.h>
2013-04-23 12:54:36 +04:00
# include <asm/cpuidle.h>
2008-10-08 16:00:58 +04:00
2010-12-22 07:05:16 +03:00
# include "powerdomain.h"
2010-12-22 07:05:15 +03:00
# include "clockdomain.h"
2008-10-08 16:00:58 +04:00
2008-10-29 03:30:07 +03:00
# include "pm.h"
2010-10-08 21:40:20 +04:00
# include "control.h"
2011-12-05 12:46:24 +04:00
# include "common.h"
2016-02-19 21:35:39 +03:00
# include "soc.h"
2008-10-29 03:30:07 +03:00
2011-05-09 14:02:14 +04:00
/* Mach specific information to be recorded in the C-state driver_data */
struct omap3_idle_statedata {
2013-01-26 11:58:12 +04:00
u8 mpu_state ;
u8 core_state ;
u8 per_min_state ;
2013-01-26 11:58:13 +04:00
u8 flags ;
2011-05-09 14:02:14 +04:00
} ;
2012-04-24 18:05:33 +04:00
2012-12-15 12:39:19 +04:00
static struct powerdomain * mpu_pd , * core_pd , * per_pd , * cam_pd ;
2013-01-26 11:58:13 +04:00
/*
* Possible flag bits for struct omap3_idle_statedata . flags :
*
* OMAP_CPUIDLE_CX_NO_CLKDM_IDLE : don ' t allow the MPU clockdomain to go
* inactive . This in turn prevents the MPU DPLL from entering autoidle
* mode , so wakeup latency is greatly reduced , at the cost of additional
* energy consumption . This also prevents the CORE clockdomain from
* entering idle .
*/
# define OMAP_CPUIDLE_CX_NO_CLKDM_IDLE BIT(0)
2013-01-26 11:58:12 +04:00
/*
* Prevent PER OFF if CORE is not in RETention or OFF as this would
* disable PER wakeups completely .
*/
2012-04-24 18:05:37 +04:00
static struct omap3_idle_statedata omap3_idle_data [ ] = {
2012-04-24 18:05:34 +04:00
{
. mpu_state = PWRDM_POWER_ON ,
. core_state = PWRDM_POWER_ON ,
2013-01-26 11:58:12 +04:00
/* In C1 do not allow PER state lower than CORE state */
. per_min_state = PWRDM_POWER_ON ,
2013-01-26 11:58:13 +04:00
. flags = OMAP_CPUIDLE_CX_NO_CLKDM_IDLE ,
2012-04-24 18:05:34 +04:00
} ,
{
. mpu_state = PWRDM_POWER_ON ,
. core_state = PWRDM_POWER_ON ,
2013-01-26 11:58:12 +04:00
. per_min_state = PWRDM_POWER_RET ,
2012-04-24 18:05:34 +04:00
} ,
{
. mpu_state = PWRDM_POWER_RET ,
. core_state = PWRDM_POWER_ON ,
2013-01-26 11:58:12 +04:00
. per_min_state = PWRDM_POWER_RET ,
2012-04-24 18:05:34 +04:00
} ,
{
. mpu_state = PWRDM_POWER_OFF ,
. core_state = PWRDM_POWER_ON ,
2013-01-26 11:58:12 +04:00
. per_min_state = PWRDM_POWER_RET ,
2012-04-24 18:05:34 +04:00
} ,
{
. mpu_state = PWRDM_POWER_RET ,
. core_state = PWRDM_POWER_RET ,
2013-01-26 11:58:12 +04:00
. per_min_state = PWRDM_POWER_OFF ,
2012-04-24 18:05:34 +04:00
} ,
{
. mpu_state = PWRDM_POWER_OFF ,
. core_state = PWRDM_POWER_RET ,
2013-01-26 11:58:12 +04:00
. per_min_state = PWRDM_POWER_OFF ,
2012-04-24 18:05:34 +04:00
} ,
{
. mpu_state = PWRDM_POWER_OFF ,
. core_state = PWRDM_POWER_OFF ,
2013-01-26 11:58:12 +04:00
. per_min_state = PWRDM_POWER_OFF ,
2012-04-24 18:05:34 +04:00
} ,
} ;
2011-05-09 14:02:14 +04:00
2013-04-12 16:35:49 +04:00
/**
* omap3_enter_idle - Programs OMAP3 to enter the specified state
* @ dev : cpuidle device
* @ drv : cpuidle driver
* @ index : the index of state to be entered
*/
static int omap3_enter_idle ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv ,
int index )
2008-10-08 16:00:58 +04:00
{
2012-04-24 18:05:35 +04:00
struct omap3_idle_statedata * cx = & omap3_idle_data [ index ] ;
2008-10-08 16:00:58 +04:00
2009-03-20 16:21:02 +03:00
if ( omap_irq_pending ( ) | | need_resched ( ) )
2008-10-08 16:01:22 +04:00
goto return_sleep_time ;
2008-10-08 16:00:58 +04:00
2011-05-09 14:02:14 +04:00
/* Deny idle for C1 */
2013-01-26 11:58:13 +04:00
if ( cx - > flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE ) {
2012-06-01 19:11:08 +04:00
clkdm_deny_idle ( mpu_pd - > pwrdm_clkdms [ 0 ] ) ;
2013-01-26 11:58:13 +04:00
} else {
pwrdm_set_next_pwrst ( mpu_pd , cx - > mpu_state ) ;
pwrdm_set_next_pwrst ( core_pd , cx - > core_state ) ;
2009-03-13 19:19:16 +03:00
}
2011-09-03 21:08:27 +04:00
/*
* Call idle CPU PM enter notifier chain so that
* VFP context is saved .
*/
2013-01-26 11:58:13 +04:00
if ( cx - > mpu_state = = PWRDM_POWER_OFF )
2011-09-03 21:08:27 +04:00
cpu_pm_enter ( ) ;
2008-10-08 16:00:58 +04:00
/* Execute ARM wfi */
omap_sram_idle ( ) ;
2011-09-03 21:08:27 +04:00
/*
* Call idle CPU PM enter notifier chain to restore
* VFP context .
*/
2013-01-26 11:58:13 +04:00
if ( cx - > mpu_state = = PWRDM_POWER_OFF & &
pwrdm_read_prev_pwrst ( mpu_pd ) = = PWRDM_POWER_OFF )
2011-09-03 21:08:27 +04:00
cpu_pm_exit ( ) ;
2011-05-09 14:02:14 +04:00
/* Re-allow idle for C1 */
2013-01-26 11:58:13 +04:00
if ( cx - > flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE )
2012-06-01 19:11:08 +04:00
clkdm_allow_idle ( mpu_pd - > pwrdm_clkdms [ 0 ] ) ;
2009-03-13 19:19:16 +03:00
2008-10-08 16:01:22 +04:00
return_sleep_time :
2008-10-08 16:00:58 +04:00
2011-10-28 14:50:09 +04:00
return index ;
2008-10-08 16:00:58 +04:00
}
2010-01-28 20:46:43 +03:00
/**
2011-05-09 14:02:16 +04:00
* next_valid_state - Find next valid C - state
2010-01-28 20:46:43 +03:00
* @ dev : cpuidle device
2011-10-28 14:50:42 +04:00
* @ drv : cpuidle driver
2011-10-28 14:50:09 +04:00
* @ index : Index of currently selected c - state
2010-01-28 20:46:43 +03:00
*
2011-10-28 14:50:09 +04:00
* If the state corresponding to index is valid , index is returned back
* to the caller . Else , this function searches for a lower c - state which is
* still valid ( as defined in omap3_power_states [ ] ) and returns its index .
2011-05-09 14:02:16 +04:00
*
* A state is valid if the ' valid ' field is enabled and
* if it satisfies the enable_off_mode condition .
2010-01-28 20:46:43 +03:00
*/
2011-10-28 14:50:09 +04:00
static int next_valid_state ( struct cpuidle_device * dev ,
2012-04-24 18:05:36 +04:00
struct cpuidle_driver * drv , int index )
2010-01-28 20:46:43 +03:00
{
2012-04-24 18:05:35 +04:00
struct omap3_idle_statedata * cx = & omap3_idle_data [ index ] ;
2011-05-09 14:02:16 +04:00
u32 mpu_deepest_state = PWRDM_POWER_RET ;
u32 core_deepest_state = PWRDM_POWER_RET ;
2012-04-24 18:05:36 +04:00
int idx ;
2012-06-01 19:11:06 +04:00
int next_index = 0 ; /* C1 is the default value */
2011-05-09 14:02:16 +04:00
if ( enable_off_mode ) {
mpu_deepest_state = PWRDM_POWER_OFF ;
/*
* Erratum i583 : valable for ES rev < Es1 .2 on 3630.
* CORE OFF mode is not supported in a stable form , restrict
* instead the CORE state to RET .
*/
if ( ! IS_PM34XX_ERRATUM ( PM_SDRC_WAKEUP_ERRATUM_i583 ) )
core_deepest_state = PWRDM_POWER_OFF ;
}
2010-01-28 20:46:43 +03:00
/* Check if current state is valid */
2012-04-24 18:05:32 +04:00
if ( ( cx - > mpu_state > = mpu_deepest_state ) & &
2012-04-24 18:05:36 +04:00
( cx - > core_state > = core_deepest_state ) )
2011-10-28 14:50:09 +04:00
return index ;
2010-01-28 20:46:43 +03:00
2012-04-24 18:05:36 +04:00
/*
* Drop to next valid state .
* Start search from the next ( lower ) state .
*/
for ( idx = index - 1 ; idx > = 0 ; idx - - ) {
2013-01-26 11:58:13 +04:00
cx = & omap3_idle_data [ idx ] ;
2012-04-24 18:05:36 +04:00
if ( ( cx - > mpu_state > = mpu_deepest_state ) & &
( cx - > core_state > = core_deepest_state ) ) {
next_index = idx ;
break ;
2010-01-28 20:46:43 +03:00
}
}
2011-10-28 14:50:09 +04:00
return next_index ;
2010-01-28 20:46:43 +03:00
}
2008-10-08 16:00:58 +04:00
/**
* omap3_enter_idle_bm - Checks for any bus activity
* @ dev : cpuidle device
2011-10-28 14:50:42 +04:00
* @ drv : cpuidle driver
2011-10-28 14:50:09 +04:00
* @ index : array index of target state to be programmed
2008-10-08 16:00:58 +04:00
*
2011-05-09 14:02:14 +04:00
* This function checks for any pending activity and then programs
* the device to the specified or a safer state .
2008-10-08 16:00:58 +04:00
*/
static int omap3_enter_idle_bm ( struct cpuidle_device * dev ,
2012-06-01 19:11:07 +04:00
struct cpuidle_driver * drv ,
2011-10-28 14:50:09 +04:00
int index )
2008-10-08 16:00:58 +04:00
{
2013-01-26 11:58:12 +04:00
int new_state_idx , ret ;
u8 per_next_state , per_saved_state ;
2011-05-09 14:02:14 +04:00
struct omap3_idle_statedata * cx ;
2008-10-29 03:32:11 +03:00
2010-09-09 03:37:42 +04:00
/*
2012-06-01 19:11:07 +04:00
* Use only C1 if CAM is active .
2010-09-09 03:37:42 +04:00
* CAM does not have wakeup capability in OMAP3 .
*/
2012-06-01 19:11:07 +04:00
if ( pwrdm_read_pwrst ( cam_pd ) = = PWRDM_POWER_ON )
2011-10-28 14:50:42 +04:00
new_state_idx = drv - > safe_state_index ;
2012-06-01 19:11:07 +04:00
else
new_state_idx = next_valid_state ( dev , drv , index ) ;
2010-09-09 03:37:42 +04:00
2011-05-09 14:02:15 +04:00
/*
* FIXME : we currently manage device - specific idle states
* for PER and CORE in combination with CPU - specific
* idle states . This is wrong , and device - specific
* idle management needs to be separated out into
* its own code .
*/
2012-06-01 19:11:07 +04:00
/* Program PER state */
cx = & omap3_idle_data [ new_state_idx ] ;
2008-10-29 03:32:11 +03:00
2013-01-26 11:58:12 +04:00
per_next_state = pwrdm_read_next_pwrst ( per_pd ) ;
per_saved_state = per_next_state ;
if ( per_next_state < cx - > per_min_state ) {
per_next_state = cx - > per_min_state ;
2010-09-09 03:37:42 +04:00
pwrdm_set_next_pwrst ( per_pd , per_next_state ) ;
2013-01-26 11:58:12 +04:00
}
2010-09-09 03:37:42 +04:00
2011-10-28 14:50:42 +04:00
ret = omap3_enter_idle ( dev , drv , new_state_idx ) ;
2010-09-09 03:37:42 +04:00
/* Restore original PER state if it was modified */
if ( per_next_state ! = per_saved_state )
pwrdm_set_next_pwrst ( per_pd , per_saved_state ) ;
return ret ;
2008-10-08 16:00:58 +04:00
}
2012-12-15 12:39:19 +04:00
static struct cpuidle_driver omap3_idle_driver = {
2013-03-29 14:31:35 +04:00
. name = " omap3_idle " ,
. owner = THIS_MODULE ,
2012-04-24 18:05:30 +04:00
. states = {
{
2012-06-01 19:11:07 +04:00
. enter = omap3_enter_idle_bm ,
2012-04-24 18:05:30 +04:00
. exit_latency = 2 + 2 ,
. target_residency = 5 ,
. name = " C1 " ,
. desc = " MPU ON + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 10 + 10 ,
. target_residency = 30 ,
. name = " C2 " ,
. desc = " MPU ON + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 50 + 50 ,
. target_residency = 300 ,
. name = " C3 " ,
. desc = " MPU RET + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 1500 + 1800 ,
. target_residency = 4000 ,
. name = " C4 " ,
. desc = " MPU OFF + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 2500 + 7500 ,
. target_residency = 12000 ,
. name = " C5 " ,
. desc = " MPU RET + CORE RET " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 3000 + 8500 ,
. target_residency = 15000 ,
. name = " C6 " ,
. desc = " MPU OFF + CORE RET " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 10000 + 30000 ,
. target_residency = 30000 ,
. name = " C7 " ,
. desc = " MPU OFF + CORE OFF " ,
} ,
} ,
2012-04-24 18:05:34 +04:00
. state_count = ARRAY_SIZE ( omap3_idle_data ) ,
2012-04-24 18:05:30 +04:00
. safe_state_index = 0 ,
2008-10-08 16:00:58 +04:00
} ;
2016-02-19 21:35:39 +03:00
/*
* Numbers based on measurements made in October 2009 for PM optimized kernel
* with CPU freq enabled on device Nokia N900 . Assumes OPP2 ( main idle OPP ,
* and worst case latencies ) .
*/
static struct cpuidle_driver omap3430_idle_driver = {
. name = " omap3430_idle " ,
. owner = THIS_MODULE ,
. states = {
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 110 + 162 ,
. target_residency = 5 ,
. name = " C1 " ,
. desc = " MPU ON + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 106 + 180 ,
. target_residency = 309 ,
. name = " C2 " ,
. desc = " MPU ON + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 107 + 410 ,
. target_residency = 46057 ,
. name = " C3 " ,
. desc = " MPU RET + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 121 + 3374 ,
. target_residency = 46057 ,
. name = " C4 " ,
. desc = " MPU OFF + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 855 + 1146 ,
. target_residency = 46057 ,
. name = " C5 " ,
. desc = " MPU RET + CORE RET " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 7580 + 4134 ,
. target_residency = 484329 ,
. name = " C6 " ,
. desc = " MPU OFF + CORE RET " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 7505 + 15274 ,
. target_residency = 484329 ,
. name = " C7 " ,
. desc = " MPU OFF + CORE OFF " ,
} ,
} ,
. state_count = ARRAY_SIZE ( omap3_idle_data ) ,
. safe_state_index = 0 ,
} ;
2012-12-15 12:39:19 +04:00
/* Public functions */
2008-10-08 16:00:58 +04:00
/**
* omap3_idle_init - Init routine for OMAP3 idle
*
2011-05-09 14:02:14 +04:00
* Registers the OMAP3 specific cpuidle driver to the cpuidle
2008-10-08 16:00:58 +04:00
* framework with the valid set of states .
*/
2008-09-26 12:04:20 +04:00
int __init omap3_idle_init ( void )
2008-10-08 16:00:58 +04:00
{
mpu_pd = pwrdm_lookup ( " mpu_pwrdm " ) ;
2008-10-08 16:01:22 +04:00
core_pd = pwrdm_lookup ( " core_pwrdm " ) ;
2010-09-09 03:37:42 +04:00
per_pd = pwrdm_lookup ( " per_pwrdm " ) ;
cam_pd = pwrdm_lookup ( " cam_pwrdm " ) ;
2008-10-08 16:00:58 +04:00
2012-05-04 21:18:40 +04:00
if ( ! mpu_pd | | ! core_pd | | ! per_pd | | ! cam_pd )
return - ENODEV ;
2016-02-19 21:35:39 +03:00
if ( cpu_is_omap3430 ( ) )
return cpuidle_register ( & omap3430_idle_driver , NULL ) ;
else
return cpuidle_register ( & omap3_idle_driver , NULL ) ;
2008-10-08 16:00:58 +04:00
}