2012-10-31 13:41:15 +04:00
/*
* CPU idle driver for Tegra CPUs
*
* Copyright ( c ) 2010 - 2012 , NVIDIA Corporation .
* Copyright ( c ) 2011 Google , Inc .
* Author : Colin Cross < ccross @ android . com >
* Gary King < gking @ nvidia . com >
*
* Rework for 3.3 by Peter De Schrijver < pdeschrijver @ nvidia . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*/
2014-07-11 11:44:49 +04:00
# include <linux/clk/tegra.h>
2015-04-03 03:32:14 +03:00
# include <linux/tick.h>
2012-10-31 13:41:15 +04:00
# include <linux/cpuidle.h>
2012-10-31 13:41:17 +04:00
# include <linux/cpu_pm.h>
2014-07-11 11:44:49 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
2012-10-31 13:41:15 +04:00
# include <asm/cpuidle.h>
2012-10-31 13:41:17 +04:00
# include <asm/smp_plat.h>
2014-07-11 11:44:49 +04:00
# include <asm/suspend.h>
2012-10-31 13:41:17 +04:00
2016-04-28 15:52:45 +03:00
# include "cpuidle.h"
2012-10-31 13:41:17 +04:00
# include "pm.h"
# include "sleep.h"
# ifdef CONFIG_PM_SLEEP
static int tegra30_idle_lp2 ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv ,
int index ) ;
# endif
2012-10-31 13:41:15 +04:00
static struct cpuidle_driver tegra_idle_driver = {
. name = " tegra_idle " ,
. owner = THIS_MODULE ,
2012-10-31 13:41:17 +04:00
# ifdef CONFIG_PM_SLEEP
. state_count = 2 ,
# else
2012-10-31 13:41:15 +04:00
. state_count = 1 ,
2012-10-31 13:41:17 +04:00
# endif
2012-10-31 13:41:15 +04:00
. states = {
[ 0 ] = ARM_CPUIDLE_WFI_STATE_PWR ( 600 ) ,
2012-10-31 13:41:17 +04:00
# ifdef CONFIG_PM_SLEEP
[ 1 ] = {
. enter = tegra30_idle_lp2 ,
. exit_latency = 2000 ,
. target_residency = 2200 ,
. power_usage = 0 ,
. name = " powered-down " ,
. desc = " CPU power gated " ,
} ,
# endif
2012-10-31 13:41:15 +04:00
} ,
} ;
2012-10-31 13:41:17 +04:00
# ifdef CONFIG_PM_SLEEP
2012-10-31 13:41:21 +04:00
static bool tegra30_cpu_cluster_power_down ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv ,
int index )
{
/* All CPUs entering LP2 is not working.
* Don ' t let CPU0 enter LP2 when any secondary CPU is online .
*/
if ( num_online_cpus ( ) > 1 | | ! tegra_cpu_rail_off_ready ( ) ) {
cpu_do_idle ( ) ;
return false ;
}
2015-04-03 03:32:14 +03:00
tick_broadcast_enter ( ) ;
2012-10-31 13:41:21 +04:00
2013-04-02 05:20:50 +04:00
tegra_idle_lp2_last ( ) ;
2012-10-31 13:41:21 +04:00
2015-04-03 03:32:14 +03:00
tick_broadcast_exit ( ) ;
2012-10-31 13:41:21 +04:00
return true ;
}
2012-10-31 13:41:17 +04:00
# ifdef CONFIG_SMP
static bool tegra30_cpu_core_power_down ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv ,
int index )
{
2015-04-03 03:32:14 +03:00
tick_broadcast_enter ( ) ;
2012-10-31 13:41:17 +04:00
smp_wmb ( ) ;
cpu_suspend ( 0 , tegra30_sleep_cpu_secondary_finish ) ;
2015-04-03 03:32:14 +03:00
tick_broadcast_exit ( ) ;
2012-10-31 13:41:17 +04:00
return true ;
}
# else
static inline bool tegra30_cpu_core_power_down ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv ,
int index )
{
return true ;
}
# endif
2013-01-04 13:32:21 +04:00
static int tegra30_idle_lp2 ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv ,
int index )
2012-10-31 13:41:17 +04:00
{
bool entered_lp2 = false ;
2012-10-31 13:41:21 +04:00
bool last_cpu ;
2012-10-31 13:41:17 +04:00
local_fiq_disable ( ) ;
2013-06-04 14:47:35 +04:00
last_cpu = tegra_set_cpu_in_lp2 ( ) ;
2012-10-31 13:41:17 +04:00
cpu_pm_enter ( ) ;
2013-06-04 14:47:35 +04:00
if ( dev - > cpu = = 0 ) {
2012-10-31 13:41:21 +04:00
if ( last_cpu )
entered_lp2 = tegra30_cpu_cluster_power_down ( dev , drv ,
index ) ;
else
cpu_do_idle ( ) ;
} else {
2012-10-31 13:41:17 +04:00
entered_lp2 = tegra30_cpu_core_power_down ( dev , drv , index ) ;
2012-10-31 13:41:21 +04:00
}
2012-10-31 13:41:17 +04:00
cpu_pm_exit ( ) ;
2013-06-04 14:47:35 +04:00
tegra_clear_cpu_in_lp2 ( ) ;
2012-10-31 13:41:17 +04:00
local_fiq_enable ( ) ;
smp_rmb ( ) ;
return ( entered_lp2 ) ? index : 0 ;
}
# endif
2012-10-31 13:41:15 +04:00
int __init tegra30_cpuidle_init ( void )
{
2013-04-23 12:54:41 +04:00
return cpuidle_register ( & tegra_idle_driver , NULL ) ;
2012-10-31 13:41:15 +04:00
}