Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.9 202 lines 6.0 kB view raw
1/* 2 * step_wise.c - A step-by-step Thermal throttling governor 3 * 4 * Copyright (C) 2012 Intel Corp 5 * Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com> 6 * 7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; version 2 of the License. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, write to the Free Software Foundation, Inc., 20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 * 22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 23 */ 24 25#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 26 27#include <linux/module.h> 28#include <linux/thermal.h> 29 30#include "thermal_core.h" 31 32/* 33 * If the temperature is higher than a trip point, 34 * a. if the trend is THERMAL_TREND_RAISING, use higher cooling 35 * state for this trip point 36 * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling 37 * state for this trip point 38 * c. if the trend is THERMAL_TREND_RAISE_FULL, use upper limit 39 * for this trip point 40 * d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit 41 * for this trip point 42 * If the temperature is lower than a trip point, 43 * a. if the trend is THERMAL_TREND_RAISING, do nothing 44 * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling 45 * state for this trip point, if the cooling state already 46 * equals lower limit, deactivate the thermal instance 47 * c. if the trend is THERMAL_TREND_RAISE_FULL, do nothing 48 * d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit, 49 * if the cooling state already equals lower limit, 50 * deactive the thermal instance 51 */ 52static unsigned long get_target_state(struct thermal_instance *instance, 53 enum thermal_trend trend, bool throttle) 54{ 55 struct thermal_cooling_device *cdev = instance->cdev; 56 unsigned long cur_state; 57 58 cdev->ops->get_cur_state(cdev, &cur_state); 59 60 switch (trend) { 61 case THERMAL_TREND_RAISING: 62 if (throttle) 63 cur_state = cur_state < instance->upper ? 64 (cur_state + 1) : instance->upper; 65 break; 66 case THERMAL_TREND_RAISE_FULL: 67 if (throttle) 68 cur_state = instance->upper; 69 break; 70 case THERMAL_TREND_DROPPING: 71 if (cur_state == instance->lower) { 72 if (!throttle) 73 cur_state = -1; 74 } else 75 cur_state -= 1; 76 break; 77 case THERMAL_TREND_DROP_FULL: 78 if (cur_state == instance->lower) { 79 if (!throttle) 80 cur_state = -1; 81 } else 82 cur_state = instance->lower; 83 break; 84 default: 85 break; 86 } 87 88 return cur_state; 89} 90 91static void update_passive_instance(struct thermal_zone_device *tz, 92 enum thermal_trip_type type, int value) 93{ 94 /* 95 * If value is +1, activate a passive instance. 96 * If value is -1, deactivate a passive instance. 97 */ 98 if (type == THERMAL_TRIP_PASSIVE || type == THERMAL_TRIPS_NONE) 99 tz->passive += value; 100} 101 102static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) 103{ 104 long trip_temp; 105 enum thermal_trip_type trip_type; 106 enum thermal_trend trend; 107 struct thermal_instance *instance; 108 bool throttle = false; 109 int old_target; 110 111 if (trip == THERMAL_TRIPS_NONE) { 112 trip_temp = tz->forced_passive; 113 trip_type = THERMAL_TRIPS_NONE; 114 } else { 115 tz->ops->get_trip_temp(tz, trip, &trip_temp); 116 tz->ops->get_trip_type(tz, trip, &trip_type); 117 } 118 119 trend = get_tz_trend(tz, trip); 120 121 if (tz->temperature >= trip_temp) 122 throttle = true; 123 124 mutex_lock(&tz->lock); 125 126 list_for_each_entry(instance, &tz->thermal_instances, tz_node) { 127 if (instance->trip != trip) 128 continue; 129 130 old_target = instance->target; 131 instance->target = get_target_state(instance, trend, throttle); 132 133 /* Activate a passive thermal instance */ 134 if (old_target == THERMAL_NO_TARGET && 135 instance->target != THERMAL_NO_TARGET) 136 update_passive_instance(tz, trip_type, 1); 137 /* Deactivate a passive thermal instance */ 138 else if (old_target != THERMAL_NO_TARGET && 139 instance->target == THERMAL_NO_TARGET) 140 update_passive_instance(tz, trip_type, -1); 141 142 143 instance->cdev->updated = false; /* cdev needs update */ 144 } 145 146 mutex_unlock(&tz->lock); 147} 148 149/** 150 * step_wise_throttle - throttles devices asscciated with the given zone 151 * @tz - thermal_zone_device 152 * @trip - the trip point 153 * @trip_type - type of the trip point 154 * 155 * Throttling Logic: This uses the trend of the thermal zone to throttle. 156 * If the thermal zone is 'heating up' this throttles all the cooling 157 * devices associated with the zone and its particular trip point, by one 158 * step. If the zone is 'cooling down' it brings back the performance of 159 * the devices by one step. 160 */ 161static int step_wise_throttle(struct thermal_zone_device *tz, int trip) 162{ 163 struct thermal_instance *instance; 164 165 thermal_zone_trip_update(tz, trip); 166 167 if (tz->forced_passive) 168 thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE); 169 170 mutex_lock(&tz->lock); 171 172 list_for_each_entry(instance, &tz->thermal_instances, tz_node) 173 thermal_cdev_update(instance->cdev); 174 175 mutex_unlock(&tz->lock); 176 177 return 0; 178} 179 180static struct thermal_governor thermal_gov_step_wise = { 181 .name = "step_wise", 182 .throttle = step_wise_throttle, 183 .owner = THIS_MODULE, 184}; 185 186static int __init thermal_gov_step_wise_init(void) 187{ 188 return thermal_register_governor(&thermal_gov_step_wise); 189} 190 191static void __exit thermal_gov_step_wise_exit(void) 192{ 193 thermal_unregister_governor(&thermal_gov_step_wise); 194} 195 196/* This should load after thermal framework */ 197fs_initcall(thermal_gov_step_wise_init); 198module_exit(thermal_gov_step_wise_exit); 199 200MODULE_AUTHOR("Durgadoss R"); 201MODULE_DESCRIPTION("A step-by-step thermal throttling governor"); 202MODULE_LICENSE("GPL");