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>
2008-10-08 16:00:58 +04:00
# include <plat/prcm.h>
2008-10-08 16:01:22 +04:00
# include <plat/irqs.h>
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"
2008-10-29 03:30:07 +03:00
2008-10-08 16:00:58 +04:00
# ifdef CONFIG_CPU_IDLE
2011-05-09 14:02:14 +04:00
/* Mach specific information to be recorded in the C-state driver_data */
struct omap3_idle_statedata {
u32 mpu_state ;
u32 core_state ;
} ;
2012-04-24 18:05:33 +04:00
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 ,
} ,
{
. mpu_state = PWRDM_POWER_ON ,
. core_state = PWRDM_POWER_ON ,
} ,
{
. mpu_state = PWRDM_POWER_RET ,
. core_state = PWRDM_POWER_ON ,
} ,
{
. mpu_state = PWRDM_POWER_OFF ,
. core_state = PWRDM_POWER_ON ,
} ,
{
. mpu_state = PWRDM_POWER_RET ,
. core_state = PWRDM_POWER_RET ,
} ,
{
. mpu_state = PWRDM_POWER_OFF ,
. core_state = PWRDM_POWER_RET ,
} ,
{
. mpu_state = PWRDM_POWER_OFF ,
. core_state = PWRDM_POWER_OFF ,
} ,
} ;
2011-05-09 14:02:14 +04:00
2012-04-24 18:05:39 +04:00
static struct powerdomain * mpu_pd , * core_pd , * per_pd , * cam_pd ;
2009-12-16 03:37:18 +03:00
2009-03-13 19:19:16 +03:00
static int _cpuidle_allow_idle ( struct powerdomain * pwrdm ,
struct clockdomain * clkdm )
{
2011-02-26 02:06:48 +03:00
clkdm_allow_idle ( clkdm ) ;
2009-03-13 19:19:16 +03:00
return 0 ;
}
static int _cpuidle_deny_idle ( struct powerdomain * pwrdm ,
struct clockdomain * clkdm )
{
2011-02-26 02:06:48 +03:00
clkdm_deny_idle ( clkdm ) ;
2009-03-13 19:19:16 +03:00
return 0 ;
}
2012-03-21 00:22:46 +04:00
static int __omap3_enter_idle ( struct cpuidle_device * dev ,
2011-10-28 14:50:42 +04:00
struct cpuidle_driver * drv ,
2011-10-28 14:50:09 +04:00
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-29 03:30:07 +03:00
u32 mpu_state = cx - > mpu_state , core_state = cx - > core_state ;
2008-10-08 16:00:58 +04:00
local_fiq_disable ( ) ;
2008-10-28 11:59:05 +03:00
pwrdm_set_next_pwrst ( mpu_pd , mpu_state ) ;
pwrdm_set_next_pwrst ( core_pd , core_state ) ;
2008-10-08 16:01:22 +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 */
2011-10-28 14:50:09 +04:00
if ( index = = 0 ) {
2009-03-13 19:19:16 +03:00
pwrdm_for_each_clkdm ( mpu_pd , _cpuidle_deny_idle ) ;
pwrdm_for_each_clkdm ( core_pd , _cpuidle_deny_idle ) ;
}
2011-09-03 21:08:27 +04:00
/*
* Call idle CPU PM enter notifier chain so that
* VFP context is saved .
*/
if ( mpu_state = = PWRDM_POWER_OFF )
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 .
*/
if ( pwrdm_read_prev_pwrst ( mpu_pd ) = = PWRDM_POWER_OFF )
cpu_pm_exit ( ) ;
2011-05-09 14:02:14 +04:00
/* Re-allow idle for C1 */
2011-10-28 14:50:09 +04:00
if ( index = = 0 ) {
2009-03-13 19:19:16 +03:00
pwrdm_for_each_clkdm ( mpu_pd , _cpuidle_allow_idle ) ;
pwrdm_for_each_clkdm ( core_pd , _cpuidle_allow_idle ) ;
}
2008-10-08 16:01:22 +04:00
return_sleep_time :
2008-10-08 16:00:58 +04:00
local_fiq_enable ( ) ;
2011-10-28 14:50:09 +04:00
return index ;
2008-10-08 16:00:58 +04:00
}
2012-03-21 00:22:46 +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
*
* Called from the CPUidle framework to program the device to the
* specified target state selected by the governor .
*/
static inline int omap3_enter_idle ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv ,
int index )
{
return cpuidle_wrap_enter ( dev , drv , index , __omap3_enter_idle ) ;
}
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 - - ) {
cx = & omap3_idle_data [ idx ] ;
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
{
2011-10-28 14:50:09 +04:00
int new_state_idx ;
2012-06-01 19:11:07 +04:00
u32 core_next_state , per_next_state = 0 , per_saved_state = 0 ;
2011-05-09 14:02:14 +04:00
struct omap3_idle_statedata * cx ;
2010-09-09 03:37:42 +04:00
int ret ;
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 ] ;
2011-05-09 14:02:15 +04:00
core_next_state = cx - > core_state ;
2010-09-09 03:37:42 +04:00
per_next_state = per_saved_state = pwrdm_read_next_pwrst ( per_pd ) ;
2012-06-01 19:11:07 +04:00
if ( new_state_idx = = 0 ) {
/* In C1 do not allow PER state lower than CORE state */
if ( per_next_state < core_next_state )
per_next_state = core_next_state ;
} else {
/*
* Prevent PER OFF if CORE is not in RETention or OFF as this
* would disable PER wakeups completely .
*/
if ( ( per_next_state = = PWRDM_POWER_OFF ) & &
( core_next_state > PWRDM_POWER_RET ) )
per_next_state = PWRDM_POWER_RET ;
}
2008-10-29 03:32:11 +03:00
2010-09-09 03:37:42 +04:00
/* Are we changing PER target state? */
if ( per_next_state ! = per_saved_state )
pwrdm_set_next_pwrst ( per_pd , per_next_state ) ;
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
}
DEFINE_PER_CPU ( struct cpuidle_device , omap3_idle_dev ) ;
struct cpuidle_driver omap3_idle_driver = {
. 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 ,
. flags = CPUIDLE_FLAG_TIME_VALID ,
. name = " C1 " ,
. desc = " MPU ON + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 10 + 10 ,
. target_residency = 30 ,
. flags = CPUIDLE_FLAG_TIME_VALID ,
. name = " C2 " ,
. desc = " MPU ON + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 50 + 50 ,
. target_residency = 300 ,
. flags = CPUIDLE_FLAG_TIME_VALID ,
. name = " C3 " ,
. desc = " MPU RET + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 1500 + 1800 ,
. target_residency = 4000 ,
. flags = CPUIDLE_FLAG_TIME_VALID ,
. name = " C4 " ,
. desc = " MPU OFF + CORE ON " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 2500 + 7500 ,
. target_residency = 12000 ,
. flags = CPUIDLE_FLAG_TIME_VALID ,
. name = " C5 " ,
. desc = " MPU RET + CORE RET " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 3000 + 8500 ,
. target_residency = 15000 ,
. flags = CPUIDLE_FLAG_TIME_VALID ,
. name = " C6 " ,
. desc = " MPU OFF + CORE RET " ,
} ,
{
. enter = omap3_enter_idle_bm ,
. exit_latency = 10000 + 30000 ,
. target_residency = 30000 ,
. flags = CPUIDLE_FLAG_TIME_VALID ,
. 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
} ;
/**
* 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
{
struct cpuidle_device * dev ;
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 ;
2012-04-24 18:05:35 +04:00
cpuidle_register_driver ( & omap3_idle_driver ) ;
2011-10-28 14:50:42 +04:00
2008-10-08 16:00:58 +04:00
dev = & per_cpu ( omap3_idle_dev , smp_processor_id ( ) ) ;
2012-04-24 18:05:35 +04:00
dev - > cpu = 0 ;
2011-10-28 14:50:42 +04:00
2008-10-08 16:00:58 +04:00
if ( cpuidle_register_device ( dev ) ) {
printk ( KERN_ERR " %s: CPUidle register device failed \n " ,
__func__ ) ;
return - EIO ;
}
return 0 ;
}
2008-09-26 12:04:20 +04:00
# else
int __init omap3_idle_init ( void )
{
return 0 ;
}
2008-10-08 16:00:58 +04:00
# endif /* CONFIG_CPU_IDLE */