Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 *  step_wise.c - A step-by-step Thermal throttling governor
  4 *
  5 *  Copyright (C) 2012 Intel Corp
  6 *  Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
  7 *
  8 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  9 *
 10 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 11 */
 12
 13#include <linux/thermal.h>
 14#include <linux/minmax.h>
 15#include "thermal_trace.h"
 16
 17#include "thermal_core.h"
 18
 19/*
 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
 29 */
 30static unsigned long get_target_state(struct thermal_instance *instance,
 31				enum thermal_trend trend, bool throttle)
 32{
 33	struct thermal_cooling_device *cdev = instance->cdev;
 34	unsigned long cur_state;
 35	unsigned long next_target;
 36
 37	/*
 38	 * We keep this instance the way it is by default.
 39	 * Otherwise, we use the current state of the
 40	 * cdev in use to determine the next_target.
 41	 */
 42	cdev->ops->get_cur_state(cdev, &cur_state);
 43	next_target = instance->target;
 44	dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state);
 45
 46	if (!instance->initialized) {
 47		if (throttle) {
 48			next_target = clamp((cur_state + 1), instance->lower, instance->upper);
 49		} else {
 50			next_target = THERMAL_NO_TARGET;
 51		}
 52
 53		return next_target;
 54	}
 55
 56	if (throttle) {
 57		if (trend == THERMAL_TREND_RAISING)
 58			next_target = clamp((cur_state + 1), instance->lower, instance->upper);
 59	} else {
 60		if (trend == THERMAL_TREND_DROPPING) {
 61			if (cur_state <= instance->lower)
 62				next_target = THERMAL_NO_TARGET;
 63			else
 64				next_target = clamp((cur_state - 1), instance->lower, instance->upper);
 65		}
 66	}
 67
 68	return next_target;
 69}
 70
 71static void thermal_zone_trip_update(struct thermal_zone_device *tz,
 72				     const struct thermal_trip *trip)
 73{
 74	int trip_id = thermal_zone_trip_id(tz, trip);
 75	enum thermal_trend trend;
 76	struct thermal_instance *instance;
 77	bool throttle = false;
 78	int old_target;
 79
 80	trend = get_tz_trend(tz, trip);
 81
 82	if (tz->temperature >= trip->temperature) {
 83		throttle = true;
 84		trace_thermal_zone_trip(tz, trip_id, trip->type);
 85	}
 86
 87	dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
 88		trip_id, trip->type, trip->temperature, trend, throttle);
 89
 90	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
 91		if (instance->trip != trip)
 92			continue;
 93
 94		old_target = instance->target;
 95		instance->target = get_target_state(instance, trend, throttle);
 96		dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
 97					old_target, (int)instance->target);
 98
 99		if (instance->initialized && old_target == instance->target)
100			continue;
101
102		if (old_target == THERMAL_NO_TARGET &&
103		    instance->target != THERMAL_NO_TARGET) {
104			/* Activate a passive thermal instance */
105			if (trip->type == THERMAL_TRIP_PASSIVE)
106				tz->passive++;
107		} else if (old_target != THERMAL_NO_TARGET &&
108			   instance->target == THERMAL_NO_TARGET) {
109			/* Deactivate a passive thermal instance */
110			if (trip->type == THERMAL_TRIP_PASSIVE)
111				tz->passive--;
112		}
113
114		instance->initialized = true;
115		mutex_lock(&instance->cdev->lock);
116		instance->cdev->updated = false; /* cdev needs update */
117		mutex_unlock(&instance->cdev->lock);
118	}
119}
120
121/**
122 * step_wise_throttle - throttles devices associated with the given zone
123 * @tz: thermal_zone_device
124 * @trip: trip point
125 *
126 * Throttling Logic: This uses the trend of the thermal zone to throttle.
127 * If the thermal zone is 'heating up' this throttles all the cooling
128 * devices associated with the zone and its particular trip point, by one
129 * step. If the zone is 'cooling down' it brings back the performance of
130 * the devices by one step.
131 */
132static int step_wise_throttle(struct thermal_zone_device *tz,
133			      const struct thermal_trip *trip)
134{
135	struct thermal_instance *instance;
136
137	lockdep_assert_held(&tz->lock);
138
139	thermal_zone_trip_update(tz, trip);
140
141	list_for_each_entry(instance, &tz->thermal_instances, tz_node)
142		thermal_cdev_update(instance->cdev);
143
144	return 0;
145}
146
147static struct thermal_governor thermal_gov_step_wise = {
148	.name		= "step_wise",
149	.throttle	= step_wise_throttle,
150};
151THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise);