10015d9a2SAmit Kucheria // SPDX-License-Identifier: GPL-2.0-only
20015d9a2SAmit Kucheria /*
30015d9a2SAmit Kucheria * step_wise.c - A step-by-step Thermal throttling governor
40015d9a2SAmit Kucheria *
50015d9a2SAmit Kucheria * Copyright (C) 2012 Intel Corp
60015d9a2SAmit Kucheria * Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
70015d9a2SAmit Kucheria *
80015d9a2SAmit Kucheria * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
90015d9a2SAmit Kucheria *
100015d9a2SAmit Kucheria * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
110015d9a2SAmit Kucheria */
120015d9a2SAmit Kucheria
130015d9a2SAmit Kucheria #include <linux/thermal.h>
1425bff3edSDaniel Lezcano #include <linux/minmax.h>
1532a7a021SDaniel Lezcano #include "thermal_trace.h"
160015d9a2SAmit Kucheria
170015d9a2SAmit Kucheria #include "thermal_core.h"
180015d9a2SAmit Kucheria
190015d9a2SAmit Kucheria /*
200015d9a2SAmit Kucheria * If the temperature is higher than a trip point,
210015d9a2SAmit Kucheria * a. if the trend is THERMAL_TREND_RAISING, use higher cooling
220015d9a2SAmit Kucheria * state for this trip point
230015d9a2SAmit Kucheria * b. if the trend is THERMAL_TREND_DROPPING, do nothing
240015d9a2SAmit Kucheria * If the temperature is lower than a trip point,
250015d9a2SAmit Kucheria * a. if the trend is THERMAL_TREND_RAISING, do nothing
260015d9a2SAmit Kucheria * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling
270015d9a2SAmit Kucheria * state for this trip point, if the cooling state already
280015d9a2SAmit Kucheria * equals lower limit, deactivate the thermal instance
290015d9a2SAmit Kucheria */
get_target_state(struct thermal_instance * instance,enum thermal_trend trend,bool throttle)300015d9a2SAmit Kucheria static unsigned long get_target_state(struct thermal_instance *instance,
310015d9a2SAmit Kucheria enum thermal_trend trend, bool throttle)
320015d9a2SAmit Kucheria {
330015d9a2SAmit Kucheria struct thermal_cooling_device *cdev = instance->cdev;
340015d9a2SAmit Kucheria unsigned long cur_state;
350015d9a2SAmit Kucheria unsigned long next_target;
360015d9a2SAmit Kucheria
370015d9a2SAmit Kucheria /*
380015d9a2SAmit Kucheria * We keep this instance the way it is by default.
390015d9a2SAmit Kucheria * Otherwise, we use the current state of the
400015d9a2SAmit Kucheria * cdev in use to determine the next_target.
410015d9a2SAmit Kucheria */
420015d9a2SAmit Kucheria cdev->ops->get_cur_state(cdev, &cur_state);
430015d9a2SAmit Kucheria next_target = instance->target;
440015d9a2SAmit Kucheria dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state);
450015d9a2SAmit Kucheria
460015d9a2SAmit Kucheria if (!instance->initialized) {
470015d9a2SAmit Kucheria if (throttle) {
4825bff3edSDaniel Lezcano next_target = clamp((cur_state + 1), instance->lower, instance->upper);
490015d9a2SAmit Kucheria } else {
500015d9a2SAmit Kucheria next_target = THERMAL_NO_TARGET;
510015d9a2SAmit Kucheria }
520015d9a2SAmit Kucheria
530015d9a2SAmit Kucheria return next_target;
540015d9a2SAmit Kucheria }
550015d9a2SAmit Kucheria
560015d9a2SAmit Kucheria if (throttle) {
57e4006bfeSZhang Rui if (trend == THERMAL_TREND_RAISING)
5825bff3edSDaniel Lezcano next_target = clamp((cur_state + 1), instance->lower, instance->upper);
590015d9a2SAmit Kucheria } else {
60e4006bfeSZhang Rui if (trend == THERMAL_TREND_DROPPING) {
61e4006bfeSZhang Rui if (cur_state <= instance->lower)
62e4006bfeSZhang Rui next_target = THERMAL_NO_TARGET;
63e4006bfeSZhang Rui else
6425bff3edSDaniel Lezcano next_target = clamp((cur_state - 1), instance->lower, instance->upper);
650015d9a2SAmit Kucheria }
660015d9a2SAmit Kucheria }
670015d9a2SAmit Kucheria
680015d9a2SAmit Kucheria return next_target;
690015d9a2SAmit Kucheria }
700015d9a2SAmit Kucheria
update_passive_instance(struct thermal_zone_device * tz,enum thermal_trip_type type,int value)710015d9a2SAmit Kucheria static void update_passive_instance(struct thermal_zone_device *tz,
720015d9a2SAmit Kucheria enum thermal_trip_type type, int value)
730015d9a2SAmit Kucheria {
740015d9a2SAmit Kucheria /*
750015d9a2SAmit Kucheria * If value is +1, activate a passive instance.
760015d9a2SAmit Kucheria * If value is -1, deactivate a passive instance.
770015d9a2SAmit Kucheria */
78a7d6ba14SDaniel Lezcano if (type == THERMAL_TRIP_PASSIVE)
790015d9a2SAmit Kucheria tz->passive += value;
800015d9a2SAmit Kucheria }
810015d9a2SAmit Kucheria
thermal_zone_trip_update(struct thermal_zone_device * tz,int trip_id)827f725a23SDaniel Lezcano static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip_id)
830015d9a2SAmit Kucheria {
84*77451ef5SRafael J. Wysocki const struct thermal_trip *trip = &tz->trips[trip_id];
850015d9a2SAmit Kucheria enum thermal_trend trend;
860015d9a2SAmit Kucheria struct thermal_instance *instance;
870015d9a2SAmit Kucheria bool throttle = false;
880015d9a2SAmit Kucheria int old_target;
890015d9a2SAmit Kucheria
907f725a23SDaniel Lezcano trend = get_tz_trend(tz, trip_id);
910015d9a2SAmit Kucheria
92*77451ef5SRafael J. Wysocki if (tz->temperature >= trip->temperature) {
930015d9a2SAmit Kucheria throttle = true;
94*77451ef5SRafael J. Wysocki trace_thermal_zone_trip(tz, trip_id, trip->type);
950015d9a2SAmit Kucheria }
960015d9a2SAmit Kucheria
970015d9a2SAmit Kucheria dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
98*77451ef5SRafael J. Wysocki trip_id, trip->type, trip->temperature, trend, throttle);
990015d9a2SAmit Kucheria
1000015d9a2SAmit Kucheria list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
101*77451ef5SRafael J. Wysocki if (instance->trip != trip)
1020015d9a2SAmit Kucheria continue;
1030015d9a2SAmit Kucheria
1040015d9a2SAmit Kucheria old_target = instance->target;
1050015d9a2SAmit Kucheria instance->target = get_target_state(instance, trend, throttle);
1060015d9a2SAmit Kucheria dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
1070015d9a2SAmit Kucheria old_target, (int)instance->target);
1080015d9a2SAmit Kucheria
1090015d9a2SAmit Kucheria if (instance->initialized && old_target == instance->target)
1100015d9a2SAmit Kucheria continue;
1110015d9a2SAmit Kucheria
1120015d9a2SAmit Kucheria /* Activate a passive thermal instance */
1130015d9a2SAmit Kucheria if (old_target == THERMAL_NO_TARGET &&
1140015d9a2SAmit Kucheria instance->target != THERMAL_NO_TARGET)
115*77451ef5SRafael J. Wysocki update_passive_instance(tz, trip->type, 1);
1160015d9a2SAmit Kucheria /* Deactivate a passive thermal instance */
1170015d9a2SAmit Kucheria else if (old_target != THERMAL_NO_TARGET &&
1180015d9a2SAmit Kucheria instance->target == THERMAL_NO_TARGET)
119*77451ef5SRafael J. Wysocki update_passive_instance(tz, trip->type, -1);
1200015d9a2SAmit Kucheria
1210015d9a2SAmit Kucheria instance->initialized = true;
1220015d9a2SAmit Kucheria mutex_lock(&instance->cdev->lock);
1230015d9a2SAmit Kucheria instance->cdev->updated = false; /* cdev needs update */
1240015d9a2SAmit Kucheria mutex_unlock(&instance->cdev->lock);
1250015d9a2SAmit Kucheria }
1260015d9a2SAmit Kucheria }
1270015d9a2SAmit Kucheria
1280015d9a2SAmit Kucheria /**
1290015d9a2SAmit Kucheria * step_wise_throttle - throttles devices associated with the given zone
1300015d9a2SAmit Kucheria * @tz: thermal_zone_device
1310015d9a2SAmit Kucheria * @trip: trip point index
1320015d9a2SAmit Kucheria *
1330015d9a2SAmit Kucheria * Throttling Logic: This uses the trend of the thermal zone to throttle.
1340015d9a2SAmit Kucheria * If the thermal zone is 'heating up' this throttles all the cooling
1350015d9a2SAmit Kucheria * devices associated with the zone and its particular trip point, by one
1360015d9a2SAmit Kucheria * step. If the zone is 'cooling down' it brings back the performance of
1370015d9a2SAmit Kucheria * the devices by one step.
1380015d9a2SAmit Kucheria */
step_wise_throttle(struct thermal_zone_device * tz,int trip)1390015d9a2SAmit Kucheria static int step_wise_throttle(struct thermal_zone_device *tz, int trip)
1400015d9a2SAmit Kucheria {
1410015d9a2SAmit Kucheria struct thermal_instance *instance;
1420015d9a2SAmit Kucheria
143670a5e35SDaniel Lezcano lockdep_assert_held(&tz->lock);
1440015d9a2SAmit Kucheria
14563561fe3SDaniel Lezcano thermal_zone_trip_update(tz, trip);
14663561fe3SDaniel Lezcano
1470015d9a2SAmit Kucheria list_for_each_entry(instance, &tz->thermal_instances, tz_node)
1480015d9a2SAmit Kucheria thermal_cdev_update(instance->cdev);
1490015d9a2SAmit Kucheria
1500015d9a2SAmit Kucheria return 0;
1510015d9a2SAmit Kucheria }
1520015d9a2SAmit Kucheria
1530015d9a2SAmit Kucheria static struct thermal_governor thermal_gov_step_wise = {
1540015d9a2SAmit Kucheria .name = "step_wise",
1550015d9a2SAmit Kucheria .throttle = step_wise_throttle,
1560015d9a2SAmit Kucheria };
1570015d9a2SAmit Kucheria THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise);
158