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