xref: /openbmc/linux/drivers/thermal/gov_step_wise.c (revision 7d7ae873b5e0f46d19e5dc818d1a7809e4b7cc81)
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