1 // SPDX-License-Identifier: GPL-2.0-only
3 * step_wise.c - A step-by-step Thermal throttling governor
5 * Copyright (C) 2012 Intel Corp
6 * Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13 #include <linux/thermal.h>
14 #include <linux/minmax.h>
15 #include "thermal_trace.h"
17 #include "thermal_core.h"
20 * If the temperature is higher than a trip point,
21 * a. if the trend is THERMAL_TREND_RAISING, use higher cooling
22 * state for this trip point
23 * b. if the trend is THERMAL_TREND_DROPPING, do nothing
24 * If the temperature is lower than a trip point,
25 * a. if the trend is THERMAL_TREND_RAISING, do nothing
26 * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling
27 * state for this trip point, if the cooling state already
28 * equals lower limit, deactivate the thermal instance
30 static unsigned long get_target_state(struct thermal_instance
*instance
,
31 enum thermal_trend trend
, bool throttle
)
33 struct thermal_cooling_device
*cdev
= instance
->cdev
;
34 unsigned long cur_state
;
37 * We keep this instance the way it is by default.
38 * Otherwise, we use the current state of the
39 * cdev in use to determine the next_target.
41 cdev
->ops
->get_cur_state(cdev
, &cur_state
);
42 dev_dbg(&cdev
->device
, "cur_state=%ld\n", cur_state
);
44 if (!instance
->initialized
) {
46 return clamp(cur_state
+ 1, instance
->lower
, instance
->upper
);
48 return THERMAL_NO_TARGET
;
52 if (trend
== THERMAL_TREND_RAISING
)
53 return clamp(cur_state
+ 1, instance
->lower
, instance
->upper
);
54 } else if (trend
== THERMAL_TREND_DROPPING
) {
55 if (cur_state
<= instance
->lower
)
56 return THERMAL_NO_TARGET
;
59 * If 'throttle' is false, no mitigation is necessary, so
60 * request the lower state for this instance.
62 return instance
->lower
;
65 return instance
->target
;
68 static void thermal_zone_trip_update(struct thermal_zone_device
*tz
,
69 const struct thermal_trip_desc
*td
,
72 const struct thermal_trip
*trip
= &td
->trip
;
73 enum thermal_trend trend
= get_tz_trend(tz
, trip
);
74 int trip_id
= thermal_zone_trip_id(tz
, trip
);
75 struct thermal_instance
*instance
;
76 bool throttle
= false;
78 if (tz
->temperature
>= trip_threshold
) {
80 trace_thermal_zone_trip(tz
, trip_id
, trip
->type
);
83 dev_dbg(&tz
->device
, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
84 trip_id
, trip
->type
, trip_threshold
, trend
, throttle
);
86 list_for_each_entry(instance
, &td
->thermal_instances
, trip_node
) {
89 old_target
= instance
->target
;
90 instance
->target
= get_target_state(instance
, trend
, throttle
);
92 dev_dbg(&instance
->cdev
->device
, "old_target=%d, target=%ld\n",
93 old_target
, instance
->target
);
95 if (instance
->initialized
&& old_target
== instance
->target
)
98 instance
->initialized
= true;
100 scoped_guard(cooling_dev
, instance
->cdev
) {
101 instance
->cdev
->updated
= false; /* cdev needs update */
106 static void step_wise_manage(struct thermal_zone_device
*tz
)
108 const struct thermal_trip_desc
*td
;
109 struct thermal_instance
*instance
;
111 lockdep_assert_held(&tz
->lock
);
114 * Throttling Logic: Use the trend of the thermal zone to throttle.
115 * If the thermal zone is 'heating up', throttle all of the cooling
116 * devices associated with each trip point by one step. If the zone
117 * is 'cooling down', it brings back the performance of the devices
120 for_each_trip_desc(tz
, td
) {
121 const struct thermal_trip
*trip
= &td
->trip
;
123 if (trip
->temperature
== THERMAL_TEMP_INVALID
||
124 trip
->type
== THERMAL_TRIP_CRITICAL
||
125 trip
->type
== THERMAL_TRIP_HOT
)
128 thermal_zone_trip_update(tz
, td
, td
->threshold
);
131 for_each_trip_desc(tz
, td
) {
132 list_for_each_entry(instance
, &td
->thermal_instances
, trip_node
)
133 thermal_cdev_update(instance
->cdev
);
137 static struct thermal_governor thermal_gov_step_wise
= {
139 .manage
= step_wise_manage
,
141 THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise
);