xref: /linux/drivers/thermal/gov_fair_share.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
10015d9a2SAmit Kucheria // SPDX-License-Identifier: GPL-2.0-only
20015d9a2SAmit Kucheria /*
30015d9a2SAmit Kucheria  *  fair_share.c - A simple weight based Thermal 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>
1432a7a021SDaniel Lezcano #include "thermal_trace.h"
150015d9a2SAmit Kucheria 
160015d9a2SAmit Kucheria #include "thermal_core.h"
170015d9a2SAmit Kucheria 
get_trip_level(struct thermal_zone_device * tz)180015d9a2SAmit Kucheria static int get_trip_level(struct thermal_zone_device *tz)
190015d9a2SAmit Kucheria {
200292991cSRafael J. Wysocki 	const struct thermal_trip_desc *level_td = NULL;
21daeeb032SRafael J. Wysocki 	const struct thermal_trip_desc *td;
22f2675e58SRafael J. Wysocki 	int trip_level = -1;
230015d9a2SAmit Kucheria 
24daeeb032SRafael J. Wysocki 	for_each_trip_desc(tz, td) {
250292991cSRafael J. Wysocki 		if (td->threshold > tz->temperature)
26f2675e58SRafael J. Wysocki 			continue;
27276f1edeSRafael J. Wysocki 
28f2675e58SRafael J. Wysocki 		trip_level++;
29f2675e58SRafael J. Wysocki 
300292991cSRafael J. Wysocki 		if (!level_td || td->threshold > level_td->threshold)
310292991cSRafael J. Wysocki 			level_td = td;
320015d9a2SAmit Kucheria 	}
330015d9a2SAmit Kucheria 
34276f1edeSRafael J. Wysocki 	/*  Bail out if the temperature is not greater than any trips. */
35f2675e58SRafael J. Wysocki 	if (trip_level < 0)
36276f1edeSRafael J. Wysocki 		return 0;
370015d9a2SAmit Kucheria 
380292991cSRafael J. Wysocki 	trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, &level_td->trip),
390292991cSRafael J. Wysocki 				level_td->trip.type);
40276f1edeSRafael J. Wysocki 
41276f1edeSRafael J. Wysocki 	return trip_level;
420015d9a2SAmit Kucheria }
430015d9a2SAmit Kucheria 
440015d9a2SAmit Kucheria /**
450015d9a2SAmit Kucheria  * fair_share_throttle - throttles devices associated with the given zone
460015d9a2SAmit Kucheria  * @tz: thermal_zone_device
478c35b1f4SRafael J. Wysocki  * @trip: trip point
48bec55332SRafael J. Wysocki  * @trip_level: number of trips crossed by the zone temperature
490015d9a2SAmit Kucheria  *
500015d9a2SAmit Kucheria  * Throttling Logic: This uses three parameters to calculate the new
510015d9a2SAmit Kucheria  * throttle state of the cooling devices associated with the given zone.
520015d9a2SAmit Kucheria  *
530015d9a2SAmit Kucheria  * Parameters used for Throttling:
540015d9a2SAmit Kucheria  * P1. max_state: Maximum throttle state exposed by the cooling device.
55*c98e2479SRafael J. Wysocki  * P2. weight[i]/total_weight:
560015d9a2SAmit Kucheria  *	How 'effective' the 'i'th device is, in cooling the given zone.
57bec55332SRafael J. Wysocki  * P3. trip_level/max_no_of_trips:
580015d9a2SAmit Kucheria  *	This describes the extent to which the devices should be throttled.
590015d9a2SAmit Kucheria  *	We do not want to throttle too much when we trip a lower temperature,
600015d9a2SAmit Kucheria  *	whereas the throttling is at full swing if we trip critical levels.
610015d9a2SAmit Kucheria  * new_state of cooling device = P3 * P2 * P1
620015d9a2SAmit Kucheria  */
fair_share_throttle(struct thermal_zone_device * tz,const struct thermal_trip * trip,int trip_level)63bec55332SRafael J. Wysocki static void fair_share_throttle(struct thermal_zone_device *tz,
64bec55332SRafael J. Wysocki 				const struct thermal_trip *trip,
65bec55332SRafael J. Wysocki 				int trip_level)
660015d9a2SAmit Kucheria {
670015d9a2SAmit Kucheria 	struct thermal_instance *instance;
680015d9a2SAmit Kucheria 	int total_weight = 0;
69*c98e2479SRafael J. Wysocki 	int nr_instances = 0;
70fef05776SLukasz Luba 
710015d9a2SAmit Kucheria 	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
720015d9a2SAmit Kucheria 		if (instance->trip != trip)
730015d9a2SAmit Kucheria 			continue;
740015d9a2SAmit Kucheria 
750015d9a2SAmit Kucheria 		total_weight += instance->weight;
76*c98e2479SRafael J. Wysocki 		nr_instances++;
770015d9a2SAmit Kucheria 	}
780015d9a2SAmit Kucheria 
790015d9a2SAmit Kucheria 	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
800015d9a2SAmit Kucheria 		struct thermal_cooling_device *cdev = instance->cdev;
81*c98e2479SRafael J. Wysocki 		u64 dividend;
82*c98e2479SRafael J. Wysocki 		u32 divisor;
830015d9a2SAmit Kucheria 
840015d9a2SAmit Kucheria 		if (instance->trip != trip)
850015d9a2SAmit Kucheria 			continue;
860015d9a2SAmit Kucheria 
87*c98e2479SRafael J. Wysocki 		dividend = trip_level;
88*c98e2479SRafael J. Wysocki 		dividend *= cdev->max_state;
89*c98e2479SRafael J. Wysocki 		divisor = tz->num_trips;
90*c98e2479SRafael J. Wysocki 		if (total_weight) {
91*c98e2479SRafael J. Wysocki 			dividend *= instance->weight;
92*c98e2479SRafael J. Wysocki 			divisor *= total_weight;
93*c98e2479SRafael J. Wysocki 		} else {
94*c98e2479SRafael J. Wysocki 			divisor *= nr_instances;
95*c98e2479SRafael J. Wysocki 		}
96*c98e2479SRafael J. Wysocki 		instance->target = div_u64(dividend, divisor);
970015d9a2SAmit Kucheria 
981a933698SLukasz Luba 		mutex_lock(&cdev->lock);
991a933698SLukasz Luba 		__thermal_cdev_update(cdev);
1001a933698SLukasz Luba 		mutex_unlock(&cdev->lock);
1010015d9a2SAmit Kucheria 	}
102bec55332SRafael J. Wysocki }
103fef05776SLukasz Luba 
fair_share_manage(struct thermal_zone_device * tz)104bec55332SRafael J. Wysocki static void fair_share_manage(struct thermal_zone_device *tz)
105bec55332SRafael J. Wysocki {
106bec55332SRafael J. Wysocki 	int trip_level = get_trip_level(tz);
107bec55332SRafael J. Wysocki 	const struct thermal_trip_desc *td;
108bec55332SRafael J. Wysocki 
109bec55332SRafael J. Wysocki 	lockdep_assert_held(&tz->lock);
110bec55332SRafael J. Wysocki 
111bec55332SRafael J. Wysocki 	for_each_trip_desc(tz, td) {
112bec55332SRafael J. Wysocki 		const struct thermal_trip *trip = &td->trip;
113bec55332SRafael J. Wysocki 
114bec55332SRafael J. Wysocki 		if (trip->temperature == THERMAL_TEMP_INVALID ||
115bec55332SRafael J. Wysocki 		    trip->type == THERMAL_TRIP_CRITICAL ||
116bec55332SRafael J. Wysocki 		    trip->type == THERMAL_TRIP_HOT)
117bec55332SRafael J. Wysocki 			continue;
118bec55332SRafael J. Wysocki 
119bec55332SRafael J. Wysocki 		fair_share_throttle(tz, trip, trip_level);
120bec55332SRafael J. Wysocki 	}
1210015d9a2SAmit Kucheria }
1220015d9a2SAmit Kucheria 
1230015d9a2SAmit Kucheria static struct thermal_governor thermal_gov_fair_share = {
1240015d9a2SAmit Kucheria 	.name	= "fair_share",
125bec55332SRafael J. Wysocki 	.manage	= fair_share_manage,
1260015d9a2SAmit Kucheria };
1270015d9a2SAmit Kucheria THERMAL_GOVERNOR_DECLARE(thermal_gov_fair_share);
128