xref: /linux/drivers/thermal/gov_bang_bang.c (revision f898c16a0624e7f2dcb0b1cda6916c9be6489197)
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