1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * gov_bang_bang.c - A simple thermal throttling governor using hysteresis 4 * 5 * Copyright (C) 2014 Peter Feuerer <peter@piie.net> 6 * 7 * Based on step_wise.c with following Copyrights: 8 * Copyright (C) 2012 Intel Corp 9 * Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com> 10 */ 11 12 #include <linux/thermal.h> 13 14 #include "thermal_core.h" 15 16 static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) 17 { 18 int trip_temp, trip_hyst; 19 struct thermal_instance *instance; 20 21 tz->ops->get_trip_temp(tz, trip, &trip_temp); 22 23 if (!tz->ops->get_trip_hyst) { 24 pr_warn_once("Undefined get_trip_hyst for thermal zone %s - " 25 "running with default hysteresis zero\n", tz->type); 26 trip_hyst = 0; 27 } else 28 tz->ops->get_trip_hyst(tz, trip, &trip_hyst); 29 30 dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n", 31 trip, trip_temp, tz->temperature, 32 trip_hyst); 33 34 mutex_lock(&tz->lock); 35 36 list_for_each_entry(instance, &tz->thermal_instances, tz_node) { 37 if (instance->trip != trip) 38 continue; 39 40 /* in case fan is in initial state, switch the fan off */ 41 if (instance->target == THERMAL_NO_TARGET) 42 instance->target = 0; 43 44 /* in case fan is neither on nor off set the fan to active */ 45 if (instance->target != 0 && instance->target != 1) { 46 pr_warn("Thermal instance %s controlled by bang-bang has unexpected state: %ld\n", 47 instance->name, instance->target); 48 instance->target = 1; 49 } 50 51 /* 52 * enable fan when temperature exceeds trip_temp and disable 53 * the fan in case it falls below trip_temp minus hysteresis 54 */ 55 if (instance->target == 0 && tz->temperature >= trip_temp) 56 instance->target = 1; 57 else if (instance->target == 1 && 58 tz->temperature <= trip_temp - trip_hyst) 59 instance->target = 0; 60 61 dev_dbg(&instance->cdev->device, "target=%d\n", 62 (int)instance->target); 63 64 mutex_lock(&instance->cdev->lock); 65 instance->cdev->updated = false; /* cdev needs update */ 66 mutex_unlock(&instance->cdev->lock); 67 } 68 69 mutex_unlock(&tz->lock); 70 } 71 72 /** 73 * bang_bang_control - controls devices associated with the given zone 74 * @tz - thermal_zone_device 75 * @trip - the trip point 76 * 77 * Regulation Logic: a two point regulation, deliver cooling state depending 78 * on the previous state shown in this diagram: 79 * 80 * Fan: OFF ON 81 * 82 * | 83 * | 84 * trip_temp: +---->+ 85 * | | ^ 86 * | | | 87 * | | Temperature 88 * (trip_temp - hyst): +<----+ 89 * | 90 * | 91 * | 92 * 93 * * If the fan is not running and temperature exceeds trip_temp, the fan 94 * gets turned on. 95 * * In case the fan is running, temperature must fall below 96 * (trip_temp - hyst) so that the fan gets turned off again. 97 * 98 */ 99 static int bang_bang_control(struct thermal_zone_device *tz, int trip) 100 { 101 struct thermal_instance *instance; 102 103 thermal_zone_trip_update(tz, trip); 104 105 mutex_lock(&tz->lock); 106 107 list_for_each_entry(instance, &tz->thermal_instances, tz_node) 108 thermal_cdev_update(instance->cdev); 109 110 mutex_unlock(&tz->lock); 111 112 return 0; 113 } 114 115 static struct thermal_governor thermal_gov_bang_bang = { 116 .name = "bang_bang", 117 .throttle = bang_bang_control, 118 }; 119 120 int thermal_gov_bang_bang_register(void) 121 { 122 return thermal_register_governor(&thermal_gov_bang_bang); 123 } 124 125 void thermal_gov_bang_bang_unregister(void) 126 { 127 thermal_unregister_governor(&thermal_gov_bang_bang); 128 } 129