1 // SPDX-License-Identifier: GPL-2.0-only
3 * gov_bang_bang.c - A simple thermal throttling governor using hysteresis
5 * Copyright (C) 2014 Peter Kaestle <peter@piie.net>
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>
12 #include <linux/thermal.h>
14 #include "thermal_core.h"
16 static void bang_bang_set_instance_target(struct thermal_instance
*instance
,
19 if (instance
->target
!= 0 && instance
->target
!= 1 &&
20 instance
->target
!= THERMAL_NO_TARGET
)
21 pr_debug("Unexpected state %ld of thermal instance %s in bang-bang\n",
22 instance
->target
, instance
->name
);
25 * Enable the fan when the trip is crossed on the way up and disable it
26 * when the trip is crossed on the way down.
28 instance
->target
= target
;
29 instance
->initialized
= true;
31 dev_dbg(&instance
->cdev
->device
, "target=%ld\n", instance
->target
);
33 thermal_cdev_update_nocheck(instance
->cdev
);
37 * bang_bang_control - controls devices associated with the given zone
38 * @tz: thermal_zone_device
39 * @trip: the trip point
40 * @crossed_up: whether or not the trip has been crossed on the way up
42 * Regulation Logic: a two point regulation, deliver cooling state depending
43 * on the previous state shown in this diagram:
53 * (trip_temp - hyst): +<----+
58 * * If the fan is not running and temperature exceeds trip_temp, the fan
60 * * In case the fan is running, temperature must fall below
61 * (trip_temp - hyst) so that the fan gets turned off again.
64 static void bang_bang_control(struct thermal_zone_device
*tz
,
65 const struct thermal_trip
*trip
,
68 const struct thermal_trip_desc
*td
= trip_to_trip_desc(trip
);
69 struct thermal_instance
*instance
;
71 lockdep_assert_held(&tz
->lock
);
73 dev_dbg(&tz
->device
, "Trip%d[temp=%d]:temp=%d:hyst=%d\n",
74 thermal_zone_trip_id(tz
, trip
), trip
->temperature
,
75 tz
->temperature
, trip
->hysteresis
);
77 list_for_each_entry(instance
, &td
->thermal_instances
, trip_node
)
78 bang_bang_set_instance_target(instance
, crossed_up
);
81 static void bang_bang_manage(struct thermal_zone_device
*tz
)
83 const struct thermal_trip_desc
*td
;
84 struct thermal_instance
*instance
;
86 /* If the code below has run already, nothing needs to be done. */
87 if (tz
->governor_data
)
90 for_each_trip_desc(tz
, td
) {
91 const struct thermal_trip
*trip
= &td
->trip
;
94 if (trip
->temperature
== THERMAL_TEMP_INVALID
||
95 trip
->type
== THERMAL_TRIP_CRITICAL
||
96 trip
->type
== THERMAL_TRIP_HOT
)
100 * Adjust the target states for uninitialized thermal instances
101 * to the thermal zone temperature and the trip point threshold.
103 turn_on
= tz
->temperature
>= td
->threshold
;
104 list_for_each_entry(instance
, &td
->thermal_instances
, trip_node
) {
105 if (!instance
->initialized
)
106 bang_bang_set_instance_target(instance
, turn_on
);
110 tz
->governor_data
= (void *)true;
113 static void bang_bang_update_tz(struct thermal_zone_device
*tz
,
114 enum thermal_notify_event reason
)
117 * Let bang_bang_manage() know that it needs to walk trips after binding
118 * a new cdev and after system resume.
120 if (reason
== THERMAL_TZ_BIND_CDEV
|| reason
== THERMAL_TZ_RESUME
)
121 tz
->governor_data
= NULL
;
124 static struct thermal_governor thermal_gov_bang_bang
= {
126 .trip_crossed
= bang_bang_control
,
127 .manage
= bang_bang_manage
,
128 .update_tz
= bang_bang_update_tz
,
130 THERMAL_GOVERNOR_DECLARE(thermal_gov_bang_bang
);