2008-10-08 17:30:58 +05:30
/*
* 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 15:21:02 +02:00
# include <linux/sched.h>
2008-10-08 17:30:58 +05:30
# include <linux/cpuidle.h>
# include <plat/prcm.h>
2008-10-08 17:31:22 +05:30
# include <plat/irqs.h>
2010-12-21 21:05:16 -07:00
# include "powerdomain.h"
2010-12-21 21:05:15 -07:00
# include "clockdomain.h"
2008-10-28 17:32:11 -07:00
# include <plat/serial.h>
2008-10-08 17:30:58 +05:30
2008-10-28 17:30:07 -07:00
# include "pm.h"
2010-10-08 11:40:20 -06:00
# include "control.h"
2008-10-28 17:30:07 -07:00
2008-10-08 17:30:58 +05:30
# ifdef CONFIG_CPU_IDLE
2009-12-15 16:37:18 -08:00
/*
* The latencies / thresholds for various C states have
* to be configured from the respective board files .
* These are some default values ( which might not provide
* the best power savings ) used on boards which do not
* pass these details from the board file .
*/
static struct cpuidle_params cpuidle_params_table [ ] = {
/* C1 */
2011-05-09 12:02:13 +02:00
{ 2 + 2 , 5 , 1 } ,
2009-12-15 16:37:18 -08:00
/* C2 */
2011-05-09 12:02:13 +02:00
{ 10 + 10 , 30 , 1 } ,
2009-12-15 16:37:18 -08:00
/* C3 */
2011-05-09 12:02:13 +02:00
{ 50 + 50 , 300 , 1 } ,
2009-12-15 16:37:18 -08:00
/* C4 */
2011-05-09 12:02:13 +02:00
{ 1500 + 1800 , 4000 , 1 } ,
2009-12-15 16:37:18 -08:00
/* C5 */
2011-05-09 12:02:13 +02:00
{ 2500 + 7500 , 12000 , 1 } ,
2009-12-15 16:37:18 -08:00
/* C6 */
2011-05-09 12:02:13 +02:00
{ 3000 + 8500 , 15000 , 1 } ,
2009-12-15 16:37:18 -08:00
/* C7 */
2011-05-09 12:02:13 +02:00
{ 10000 + 30000 , 300000 , 1 } ,
2009-12-15 16:37:18 -08:00
} ;
2011-05-09 12:02:14 +02:00
# define OMAP3_NUM_STATES ARRAY_SIZE(cpuidle_params_table)
/* Mach specific information to be recorded in the C-state driver_data */
struct omap3_idle_statedata {
u32 mpu_state ;
u32 core_state ;
u8 valid ;
} ;
struct omap3_idle_statedata omap3_idle_data [ OMAP3_NUM_STATES ] ;
struct powerdomain * mpu_pd , * core_pd , * per_pd , * cam_pd ;
2009-12-15 16:37:18 -08:00
2009-03-13 18:19:16 +02:00
static int _cpuidle_allow_idle ( struct powerdomain * pwrdm ,
struct clockdomain * clkdm )
{
2011-02-25 16:06:48 -07:00
clkdm_allow_idle ( clkdm ) ;
2009-03-13 18:19:16 +02:00
return 0 ;
}
static int _cpuidle_deny_idle ( struct powerdomain * pwrdm ,
struct clockdomain * clkdm )
{
2011-02-25 16:06:48 -07:00
clkdm_deny_idle ( clkdm ) ;
2009-03-13 18:19:16 +02:00
return 0 ;
}
2008-10-08 17:30:58 +05:30
/**
* omap3_enter_idle - Programs OMAP3 to enter the specified state
* @ dev : cpuidle device
* @ state : The target state to be programmed
*
* Called from the CPUidle framework to program the device to the
* specified target state selected by the governor .
*/
static int omap3_enter_idle ( struct cpuidle_device * dev ,
struct cpuidle_state * state )
{
2011-05-09 12:02:14 +02:00
struct omap3_idle_statedata * cx = cpuidle_get_statedata ( state ) ;
2008-10-08 17:30:58 +05:30
struct timespec ts_preidle , ts_postidle , ts_idle ;
2008-10-28 17:30:07 -07:00
u32 mpu_state = cx - > mpu_state , core_state = cx - > core_state ;
2008-10-08 17:30:58 +05:30
/* Used to keep track of the total time in idle */
getnstimeofday ( & ts_preidle ) ;
local_irq_disable ( ) ;
local_fiq_disable ( ) ;
2008-10-28 10:59:05 +02:00
pwrdm_set_next_pwrst ( mpu_pd , mpu_state ) ;
pwrdm_set_next_pwrst ( core_pd , core_state ) ;
2008-10-08 17:31:22 +05:30
2009-03-20 15:21:02 +02:00
if ( omap_irq_pending ( ) | | need_resched ( ) )
2008-10-08 17:31:22 +05:30
goto return_sleep_time ;
2008-10-08 17:30:58 +05:30
2011-05-09 12:02:14 +02:00
/* Deny idle for C1 */
if ( state = = & dev - > states [ 0 ] ) {
2009-03-13 18:19:16 +02:00
pwrdm_for_each_clkdm ( mpu_pd , _cpuidle_deny_idle ) ;
pwrdm_for_each_clkdm ( core_pd , _cpuidle_deny_idle ) ;
}
2008-10-08 17:30:58 +05:30
/* Execute ARM wfi */
omap_sram_idle ( ) ;
2011-05-09 12:02:14 +02:00
/* Re-allow idle for C1 */
if ( state = = & dev - > states [ 0 ] ) {
2009-03-13 18:19:16 +02:00
pwrdm_for_each_clkdm ( mpu_pd , _cpuidle_allow_idle ) ;
pwrdm_for_each_clkdm ( core_pd , _cpuidle_allow_idle ) ;
}
2008-10-08 17:31:22 +05:30
return_sleep_time :
2008-10-08 17:30:58 +05:30
getnstimeofday ( & ts_postidle ) ;
ts_idle = timespec_sub ( ts_postidle , ts_preidle ) ;
local_irq_enable ( ) ;
local_fiq_enable ( ) ;
2009-10-26 15:10:40 +02:00
return ts_idle . tv_nsec / NSEC_PER_USEC + ts_idle . tv_sec * USEC_PER_SEC ;
2008-10-08 17:30:58 +05:30
}
2010-01-28 23:16:43 +05:30
/**
2011-05-09 12:02:16 +02:00
* next_valid_state - Find next valid C - state
2010-01-28 23:16:43 +05:30
* @ dev : cpuidle device
2011-05-09 12:02:16 +02:00
* @ state : Currently selected C - state
2010-01-28 23:16:43 +05:30
*
* If the current state is valid , it is returned back to the caller .
* Else , this function searches for a lower c - state which is still
2011-05-09 12:02:14 +02:00
* valid .
2011-05-09 12:02:16 +02:00
*
* A state is valid if the ' valid ' field is enabled and
* if it satisfies the enable_off_mode condition .
2010-01-28 23:16:43 +05:30
*/
static struct cpuidle_state * next_valid_state ( struct cpuidle_device * dev ,
2011-05-09 12:02:14 +02:00
struct cpuidle_state * curr )
2010-01-28 23:16:43 +05:30
{
struct cpuidle_state * next = NULL ;
2011-05-09 12:02:15 +02:00
struct omap3_idle_statedata * cx = cpuidle_get_statedata ( curr ) ;
2011-05-09 12:02:16 +02:00
u32 mpu_deepest_state = PWRDM_POWER_RET ;
u32 core_deepest_state = PWRDM_POWER_RET ;
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 23:16:43 +05:30
/* Check if current state is valid */
2011-05-09 12:02:16 +02:00
if ( ( cx - > valid ) & &
( cx - > mpu_state > = mpu_deepest_state ) & &
( cx - > core_state > = core_deepest_state ) ) {
2010-01-28 23:16:43 +05:30
return curr ;
} else {
2011-05-09 12:02:14 +02:00
int idx = OMAP3_NUM_STATES - 1 ;
2010-01-28 23:16:43 +05:30
2011-05-09 12:02:15 +02:00
/* Reach the current state starting at highest C-state */
2011-05-09 12:02:14 +02:00
for ( ; idx > = 0 ; idx - - ) {
2010-01-28 23:16:43 +05:30
if ( & dev - > states [ idx ] = = curr ) {
next = & dev - > states [ idx ] ;
break ;
}
}
2011-05-09 12:02:15 +02:00
/* Should never hit this condition */
2010-01-28 23:16:43 +05:30
WARN_ON ( next = = NULL ) ;
/*
* Drop to next valid state .
* Start search from the next ( lower ) state .
*/
idx - - ;
2011-05-09 12:02:14 +02:00
for ( ; idx > = 0 ; idx - - ) {
2010-01-28 23:16:43 +05:30
cx = cpuidle_get_statedata ( & dev - > states [ idx ] ) ;
2011-05-09 12:02:16 +02:00
if ( ( cx - > valid ) & &
( cx - > mpu_state > = mpu_deepest_state ) & &
( cx - > core_state > = core_deepest_state ) ) {
2010-01-28 23:16:43 +05:30
next = & dev - > states [ idx ] ;
break ;
}
}
/*
2011-05-09 12:02:14 +02:00
* C1 is always valid .
2010-01-28 23:16:43 +05:30
* So , no need to check for ' next = = NULL ' outside this loop .
*/
}
return next ;
}
2008-10-08 17:30:58 +05:30
/**
* omap3_enter_idle_bm - Checks for any bus activity
* @ dev : cpuidle device
* @ state : The target state to be programmed
*
2011-05-09 12:02:14 +02:00
* This function checks for any pending activity and then programs
* the device to the specified or a safer state .
2008-10-08 17:30:58 +05:30
*/
static int omap3_enter_idle_bm ( struct cpuidle_device * dev ,
struct cpuidle_state * state )
{
2011-05-09 12:02:15 +02:00
struct cpuidle_state * new_state ;
u32 core_next_state , per_next_state = 0 , per_saved_state = 0 , cam_state ;
2011-05-09 12:02:14 +02:00
struct omap3_idle_statedata * cx ;
2010-09-08 16:37:42 -07:00
int ret ;
2008-10-28 17:32:11 -07:00
2011-05-09 12:02:15 +02:00
if ( ! omap3_can_sleep ( ) ) {
2008-10-28 17:32:11 -07:00
new_state = dev - > safe_state ;
2010-09-08 16:37:42 -07:00
goto select_state ;
}
/*
* Prevent idle completely if CAM is active .
* CAM does not have wakeup capability in OMAP3 .
*/
cam_state = pwrdm_read_pwrst ( cam_pd ) ;
if ( cam_state = = PWRDM_POWER_ON ) {
new_state = dev - > safe_state ;
goto select_state ;
}
2011-05-09 12:02:15 +02: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 .
*/
2010-09-08 16:37:42 -07:00
/*
* Prevent PER off if CORE is not in retention or off as this
* would disable PER wakeups completely .
*/
2011-05-09 12:02:15 +02:00
cx = cpuidle_get_statedata ( state ) ;
core_next_state = cx - > core_state ;
2010-09-08 16:37:42 -07:00
per_next_state = per_saved_state = pwrdm_read_next_pwrst ( per_pd ) ;
if ( ( per_next_state = = PWRDM_POWER_OFF ) & &
2010-10-01 08:35:47 -07:00
( core_next_state > PWRDM_POWER_RET ) )
2010-09-08 16:37:42 -07:00
per_next_state = PWRDM_POWER_RET ;
2008-10-28 17:32:11 -07:00
2010-09-08 16:37:42 -07:00
/* Are we changing PER target state? */
if ( per_next_state ! = per_saved_state )
pwrdm_set_next_pwrst ( per_pd , per_next_state ) ;
2011-05-09 12:02:15 +02:00
new_state = next_valid_state ( dev , state ) ;
2010-09-08 16:37:42 -07:00
select_state :
2008-10-28 17:32:11 -07:00
dev - > last_state = new_state ;
2010-09-08 16:37:42 -07:00
ret = omap3_enter_idle ( dev , new_state ) ;
/* 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 17:30:58 +05:30
}
DEFINE_PER_CPU ( struct cpuidle_device , omap3_idle_dev ) ;
2009-12-15 16:37:18 -08:00
void omap3_pm_init_cpuidle ( struct cpuidle_params * cpuidle_board_params )
{
int i ;
if ( ! cpuidle_board_params )
return ;
2011-05-09 12:02:14 +02:00
for ( i = 0 ; i < OMAP3_NUM_STATES ; i + + ) {
cpuidle_params_table [ i ] . valid = cpuidle_board_params [ i ] . valid ;
2011-05-09 12:02:13 +02:00
cpuidle_params_table [ i ] . exit_latency =
cpuidle_board_params [ i ] . exit_latency ;
cpuidle_params_table [ i ] . target_residency =
cpuidle_board_params [ i ] . target_residency ;
2009-12-15 16:37:18 -08:00
}
return ;
}
2008-10-08 17:30:58 +05:30
struct cpuidle_driver omap3_idle_driver = {
. name = " omap3_idle " ,
. owner = THIS_MODULE ,
} ;
2011-05-09 12:02:15 +02:00
/* Helper to fill the C-state common data and register the driver_data */
2011-05-09 12:02:14 +02:00
static inline struct omap3_idle_statedata * _fill_cstate (
struct cpuidle_device * dev ,
int idx , const char * descr )
{
struct omap3_idle_statedata * cx = & omap3_idle_data [ idx ] ;
struct cpuidle_state * state = & dev - > states [ idx ] ;
state - > exit_latency = cpuidle_params_table [ idx ] . exit_latency ;
state - > target_residency = cpuidle_params_table [ idx ] . target_residency ;
state - > flags = CPUIDLE_FLAG_TIME_VALID ;
state - > enter = omap3_enter_idle_bm ;
cx - > valid = cpuidle_params_table [ idx ] . valid ;
sprintf ( state - > name , " C%d " , idx + 1 ) ;
strncpy ( state - > desc , descr , CPUIDLE_DESC_LEN ) ;
cpuidle_set_statedata ( state , cx ) ;
return cx ;
}
2008-10-08 17:30:58 +05:30
/**
* omap3_idle_init - Init routine for OMAP3 idle
*
2011-05-09 12:02:14 +02:00
* Registers the OMAP3 specific cpuidle driver to the cpuidle
2008-10-08 17:30:58 +05:30
* framework with the valid set of states .
*/
2008-09-26 11:04:20 +03:00
int __init omap3_idle_init ( void )
2008-10-08 17:30:58 +05:30
{
struct cpuidle_device * dev ;
2011-05-09 12:02:14 +02:00
struct omap3_idle_statedata * cx ;
2008-10-08 17:30:58 +05:30
mpu_pd = pwrdm_lookup ( " mpu_pwrdm " ) ;
2008-10-08 17:31:22 +05:30
core_pd = pwrdm_lookup ( " core_pwrdm " ) ;
2010-09-08 16:37:42 -07:00
per_pd = pwrdm_lookup ( " per_pwrdm " ) ;
cam_pd = pwrdm_lookup ( " cam_pwrdm " ) ;
2008-10-08 17:30:58 +05:30
cpuidle_register_driver ( & omap3_idle_driver ) ;
dev = & per_cpu ( omap3_idle_dev , smp_processor_id ( ) ) ;
2011-05-09 12:02:14 +02:00
/* C1 . MPU WFI + Core active */
cx = _fill_cstate ( dev , 0 , " MPU ON + CORE ON " ) ;
( & dev - > states [ 0 ] ) - > enter = omap3_enter_idle ;
dev - > safe_state = & dev - > states [ 0 ] ;
cx - > valid = 1 ; /* C1 is always valid */
cx - > mpu_state = PWRDM_POWER_ON ;
cx - > core_state = PWRDM_POWER_ON ;
/* C2 . MPU WFI + Core inactive */
cx = _fill_cstate ( dev , 1 , " MPU ON + CORE ON " ) ;
cx - > mpu_state = PWRDM_POWER_ON ;
cx - > core_state = PWRDM_POWER_ON ;
/* C3 . MPU CSWR + Core inactive */
cx = _fill_cstate ( dev , 2 , " MPU RET + CORE ON " ) ;
cx - > mpu_state = PWRDM_POWER_RET ;
cx - > core_state = PWRDM_POWER_ON ;
/* C4 . MPU OFF + Core inactive */
cx = _fill_cstate ( dev , 3 , " MPU OFF + CORE ON " ) ;
cx - > mpu_state = PWRDM_POWER_OFF ;
cx - > core_state = PWRDM_POWER_ON ;
/* C5 . MPU RET + Core RET */
cx = _fill_cstate ( dev , 4 , " MPU RET + CORE RET " ) ;
cx - > mpu_state = PWRDM_POWER_RET ;
cx - > core_state = PWRDM_POWER_RET ;
2008-10-08 17:30:58 +05:30
2011-05-09 12:02:14 +02:00
/* C6 . MPU OFF + Core RET */
cx = _fill_cstate ( dev , 5 , " MPU OFF + CORE RET " ) ;
cx - > mpu_state = PWRDM_POWER_OFF ;
cx - > core_state = PWRDM_POWER_RET ;
/* C7 . MPU OFF + Core OFF */
cx = _fill_cstate ( dev , 6 , " MPU OFF + CORE OFF " ) ;
/*
* Erratum i583 : implementation for ES rev < Es1 .2 on 3630. We cannot
* enable OFF mode in a stable form for previous revisions .
* We disable C7 state as a result .
*/
if ( IS_PM34XX_ERRATUM ( PM_SDRC_WAKEUP_ERRATUM_i583 ) ) {
cx - > valid = 0 ;
pr_warn ( " %s: core off state C7 disabled due to i583 \n " ,
__func__ ) ;
}
cx - > mpu_state = PWRDM_POWER_OFF ;
cx - > core_state = PWRDM_POWER_OFF ;
2008-10-08 17:30:58 +05:30
2011-05-09 12:02:14 +02:00
dev - > state_count = OMAP3_NUM_STATES ;
2008-10-08 17:30:58 +05:30
if ( cpuidle_register_device ( dev ) ) {
printk ( KERN_ERR " %s: CPUidle register device failed \n " ,
__func__ ) ;
return - EIO ;
}
return 0 ;
}
2008-09-26 11:04:20 +03:00
# else
int __init omap3_idle_init ( void )
{
return 0 ;
}
2008-10-08 17:30:58 +05:30
# endif /* CONFIG_CPU_IDLE */