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 Kaestle <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 /** 17 * bang_bang_control - controls devices associated with the given zone 18 * @tz: thermal_zone_device 19 * @trip: the trip point 20 * @crossed_up: whether or not the trip has been crossed on the way up 21 * 22 * Regulation Logic: a two point regulation, deliver cooling state depending 23 * on the previous state shown in this diagram: 24 * 25 * Fan: OFF ON 26 * 27 * | 28 * | 29 * trip_temp: +---->+ 30 * | | ^ 31 * | | | 32 * | | Temperature 33 * (trip_temp - hyst): +<----+ 34 * | 35 * | 36 * | 37 * 38 * * If the fan is not running and temperature exceeds trip_temp, the fan 39 * gets turned on. 40 * * In case the fan is running, temperature must fall below 41 * (trip_temp - hyst) so that the fan gets turned off again. 42 * 43 */ 44 static void bang_bang_control(struct thermal_zone_device *tz, 45 const struct thermal_trip *trip, 46 bool crossed_up) 47 { 48 struct thermal_instance *instance; 49 50 lockdep_assert_held(&tz->lock); 51 52 dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n", 53 thermal_zone_trip_id(tz, trip), trip->temperature, 54 tz->temperature, trip->hysteresis); 55 56 list_for_each_entry(instance, &tz->thermal_instances, tz_node) { 57 if (instance->trip != trip) 58 continue; 59 60 if (instance->target == THERMAL_NO_TARGET) 61 instance->target = 0; 62 63 if (instance->target != 0 && instance->target != 1) { 64 pr_debug("Unexpected state %ld of thermal instance %s in bang-bang\n", 65 instance->target, instance->name); 66 67 instance->target = 1; 68 } 69 70 /* 71 * Enable the fan when the trip is crossed on the way up and 72 * disable it when the trip is crossed on the way down. 73 */ 74 if (instance->target == 0 && crossed_up) 75 instance->target = 1; 76 else if (instance->target == 1 && !crossed_up) 77 instance->target = 0; 78 79 dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target); 80 81 mutex_lock(&instance->cdev->lock); 82 instance->cdev->updated = false; /* cdev needs update */ 83 mutex_unlock(&instance->cdev->lock); 84 } 85 86 list_for_each_entry(instance, &tz->thermal_instances, tz_node) 87 thermal_cdev_update(instance->cdev); 88 } 89 90 static struct thermal_governor thermal_gov_bang_bang = { 91 .name = "bang_bang", 92 .trip_crossed = bang_bang_control, 93 }; 94 THERMAL_GOVERNOR_DECLARE(thermal_gov_bang_bang); 95