2012-09-21 10:36:04 +04:00
/*
* step_wise . c - A step - by - step Thermal throttling governor
*
* Copyright ( C ) 2012 Intel Corp
* Copyright ( C ) 2012 Durgadoss R < durgadoss . r @ intel . 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 ; version 2 of the License .
*
* 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 .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA .
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
# include <linux/thermal.h>
# include "thermal_core.h"
/*
* If the temperature is higher than a trip point ,
* a . if the trend is THERMAL_TREND_RAISING , use higher cooling
* state for this trip point
* b . if the trend is THERMAL_TREND_DROPPING , use lower cooling
* state for this trip point
2012-11-19 12:10:20 +04:00
* c . if the trend is THERMAL_TREND_RAISE_FULL , use upper limit
* for this trip point
* d . if the trend is THERMAL_TREND_DROP_FULL , use lower limit
* for this trip point
* If the temperature is lower than a trip point ,
* a . if the trend is THERMAL_TREND_RAISING , do nothing
* b . if the trend is THERMAL_TREND_DROPPING , use lower cooling
* state for this trip point , if the cooling state already
* equals lower limit , deactivate the thermal instance
* c . if the trend is THERMAL_TREND_RAISE_FULL , do nothing
* d . if the trend is THERMAL_TREND_DROP_FULL , use lower limit ,
* if the cooling state already equals lower limit ,
* deactive the thermal instance
2012-09-21 10:36:04 +04:00
*/
static unsigned long get_target_state ( struct thermal_instance * instance ,
2012-11-19 12:10:20 +04:00
enum thermal_trend trend , bool throttle )
2012-09-21 10:36:04 +04:00
{
struct thermal_cooling_device * cdev = instance - > cdev ;
unsigned long cur_state ;
2013-06-17 17:24:24 +04:00
unsigned long next_target ;
2012-09-21 10:36:04 +04:00
2013-06-17 17:24:24 +04:00
/*
* We keep this instance the way it is by default .
* Otherwise , we use the current state of the
* cdev in use to determine the next_target .
*/
2012-09-21 10:36:04 +04:00
cdev - > ops - > get_cur_state ( cdev , & cur_state ) ;
2013-06-17 17:24:24 +04:00
next_target = instance - > target ;
2012-09-21 10:36:04 +04:00
2012-11-19 12:10:20 +04:00
switch ( trend ) {
case THERMAL_TREND_RAISING :
2013-04-10 01:59:47 +04:00
if ( throttle ) {
2013-06-17 17:24:24 +04:00
next_target = cur_state < instance - > upper ?
2012-11-19 12:10:20 +04:00
( cur_state + 1 ) : instance - > upper ;
2013-06-17 17:24:24 +04:00
if ( next_target < instance - > lower )
next_target = instance - > lower ;
2013-04-10 01:59:47 +04:00
}
2012-11-19 12:10:20 +04:00
break ;
case THERMAL_TREND_RAISE_FULL :
if ( throttle )
2013-06-17 17:24:24 +04:00
next_target = instance - > upper ;
2012-11-19 12:10:20 +04:00
break ;
case THERMAL_TREND_DROPPING :
if ( cur_state = = instance - > lower ) {
if ( ! throttle )
2013-06-17 17:24:24 +04:00
next_target = THERMAL_NO_TARGET ;
2013-04-10 01:59:47 +04:00
} else {
2013-06-17 17:24:24 +04:00
next_target = cur_state - 1 ;
if ( next_target > instance - > upper )
next_target = instance - > upper ;
2013-04-10 01:59:47 +04:00
}
2012-11-19 12:10:20 +04:00
break ;
case THERMAL_TREND_DROP_FULL :
if ( cur_state = = instance - > lower ) {
if ( ! throttle )
2013-06-17 17:24:24 +04:00
next_target = THERMAL_NO_TARGET ;
2012-11-19 12:10:20 +04:00
} else
2013-06-17 17:24:24 +04:00
next_target = instance - > lower ;
2012-11-19 12:10:20 +04:00
break ;
default :
break ;
2012-09-21 10:36:04 +04:00
}
2013-06-17 17:24:24 +04:00
return next_target ;
2012-09-21 10:36:04 +04:00
}
static void update_passive_instance ( struct thermal_zone_device * tz ,
enum thermal_trip_type type , int value )
{
/*
* If value is + 1 , activate a passive instance .
* If value is - 1 , deactivate a passive instance .
*/
if ( type = = THERMAL_TRIP_PASSIVE | | type = = THERMAL_TRIPS_NONE )
tz - > passive + = value ;
}
static void thermal_zone_trip_update ( struct thermal_zone_device * tz , int trip )
{
long trip_temp ;
enum thermal_trip_type trip_type ;
enum thermal_trend trend ;
2012-11-22 11:45:02 +04:00
struct thermal_instance * instance ;
bool throttle = false ;
int old_target ;
2012-09-21 10:36:04 +04:00
if ( trip = = THERMAL_TRIPS_NONE ) {
trip_temp = tz - > forced_passive ;
trip_type = THERMAL_TRIPS_NONE ;
} else {
tz - > ops - > get_trip_temp ( tz , trip , & trip_temp ) ;
tz - > ops - > get_trip_type ( tz , trip , & trip_type ) ;
}
trend = get_tz_trend ( tz , trip ) ;
2012-11-22 11:45:02 +04:00
if ( tz - > temperature > = trip_temp )
throttle = true ;
2012-09-21 10:36:04 +04:00
mutex_lock ( & tz - > lock ) ;
2012-11-22 11:45:02 +04:00
list_for_each_entry ( instance , & tz - > thermal_instances , tz_node ) {
if ( instance - > trip ! = trip )
continue ;
old_target = instance - > target ;
instance - > target = get_target_state ( instance , trend , throttle ) ;
2013-06-17 17:24:23 +04:00
if ( old_target = = instance - > target )
continue ;
2012-11-22 11:45:02 +04:00
/* Activate a passive thermal instance */
if ( old_target = = THERMAL_NO_TARGET & &
instance - > target ! = THERMAL_NO_TARGET )
update_passive_instance ( tz , trip_type , 1 ) ;
/* Deactivate a passive thermal instance */
else if ( old_target ! = THERMAL_NO_TARGET & &
instance - > target = = THERMAL_NO_TARGET )
update_passive_instance ( tz , trip_type , - 1 ) ;
instance - > cdev - > updated = false ; /* cdev needs update */
}
2012-09-21 10:36:04 +04:00
mutex_unlock ( & tz - > lock ) ;
}
/**
* step_wise_throttle - throttles devices asscciated with the given zone
* @ tz - thermal_zone_device
* @ trip - the trip point
* @ trip_type - type of the trip point
*
* Throttling Logic : This uses the trend of the thermal zone to throttle .
* If the thermal zone is ' heating up ' this throttles all the cooling
* devices associated with the zone and its particular trip point , by one
* step . If the zone is ' cooling down ' it brings back the performance of
* the devices by one step .
*/
2012-09-27 14:58:12 +04:00
static int step_wise_throttle ( struct thermal_zone_device * tz , int trip )
2012-09-21 10:36:04 +04:00
{
struct thermal_instance * instance ;
thermal_zone_trip_update ( tz , trip ) ;
if ( tz - > forced_passive )
thermal_zone_trip_update ( tz , THERMAL_TRIPS_NONE ) ;
mutex_lock ( & tz - > lock ) ;
list_for_each_entry ( instance , & tz - > thermal_instances , tz_node )
thermal_cdev_update ( instance - > cdev ) ;
mutex_unlock ( & tz - > lock ) ;
return 0 ;
}
2012-09-27 14:58:12 +04:00
static struct thermal_governor thermal_gov_step_wise = {
2012-12-12 11:31:37 +04:00
. name = " step_wise " ,
2012-09-21 10:36:04 +04:00
. throttle = step_wise_throttle ,
} ;
2013-03-26 12:38:29 +04:00
int thermal_gov_step_wise_register ( void )
2012-09-21 10:36:04 +04:00
{
return thermal_register_governor ( & thermal_gov_step_wise ) ;
}
2013-03-26 12:38:29 +04:00
void thermal_gov_step_wise_unregister ( void )
2012-09-21 10:36:04 +04:00
{
thermal_unregister_governor ( & thermal_gov_step_wise ) ;
}