linux-stable/drivers/thermal/gov_step_wise.c
Rafael J. Wysocki e4065f144f thermal: gov_step_wise: Use trip thresholds instead of trip temperatures
In principle, the Step-Wise governor should take trip hysteresis into
account.  After all, once a trip has been crossed on the way up,
mitigation is still needed until it is crossed on the way down.

For this reason, make it use trip thresholds that are computed by
the core when trips are crossed, so as to apply mitigations in the
hysteresis rages of trips that were crossed on the way up, but have
not been crossed on the way down yet.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
2024-04-24 10:14:33 +02:00

147 lines
4.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* step_wise.c - A step-by-step Thermal throttling governor
*
* Copyright (C) 2012 Intel Corp
* Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/thermal.h>
#include <linux/minmax.h>
#include "thermal_trace.h"
#include "thermal_core.h"
/*
* If the temperature is higher than a trip point,
* a. if the trend is THERMAL_TREND_RAISING, use higher cooling
* state for this trip point
* b. if the trend is THERMAL_TREND_DROPPING, do nothing
* If the temperature is lower than a trip point,
* a. if the trend is THERMAL_TREND_RAISING, do nothing
* b. if the trend is THERMAL_TREND_DROPPING, use lower cooling
* state for this trip point, if the cooling state already
* equals lower limit, deactivate the thermal instance
*/
static unsigned long get_target_state(struct thermal_instance *instance,
enum thermal_trend trend, bool throttle)
{
struct thermal_cooling_device *cdev = instance->cdev;
unsigned long cur_state;
/*
* We keep this instance the way it is by default.
* Otherwise, we use the current state of the
* cdev in use to determine the next_target.
*/
cdev->ops->get_cur_state(cdev, &cur_state);
dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state);
if (!instance->initialized) {
if (throttle)
return clamp(cur_state + 1, instance->lower, instance->upper);
return THERMAL_NO_TARGET;
}
if (throttle) {
if (trend == THERMAL_TREND_RAISING)
return clamp(cur_state + 1, instance->lower, instance->upper);
} else if (trend == THERMAL_TREND_DROPPING) {
if (cur_state <= instance->lower)
return THERMAL_NO_TARGET;
return clamp(cur_state - 1, instance->lower, instance->upper);
}
return instance->target;
}
static void thermal_zone_trip_update(struct thermal_zone_device *tz,
const struct thermal_trip *trip,
int trip_threshold)
{
int trip_id = thermal_zone_trip_id(tz, trip);
enum thermal_trend trend;
struct thermal_instance *instance;
bool throttle = false;
int old_target;
trend = get_tz_trend(tz, trip);
if (tz->temperature >= trip_threshold) {
throttle = true;
trace_thermal_zone_trip(tz, trip_id, trip->type);
}
dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
trip_id, trip->type, trip_threshold, trend, throttle);
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != trip)
continue;
old_target = instance->target;
instance->target = get_target_state(instance, trend, throttle);
dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
old_target, (int)instance->target);
if (instance->initialized && old_target == instance->target)
continue;
if (trip->type == THERMAL_TRIP_PASSIVE) {
/* If needed, update the status of passive polling. */
if (old_target == THERMAL_NO_TARGET &&
instance->target != THERMAL_NO_TARGET)
tz->passive++;
else if (old_target != THERMAL_NO_TARGET &&
instance->target == THERMAL_NO_TARGET)
tz->passive--;
}
instance->initialized = true;
mutex_lock(&instance->cdev->lock);
instance->cdev->updated = false; /* cdev needs update */
mutex_unlock(&instance->cdev->lock);
}
}
static void step_wise_manage(struct thermal_zone_device *tz)
{
const struct thermal_trip_desc *td;
struct thermal_instance *instance;
lockdep_assert_held(&tz->lock);
/*
* Throttling Logic: Use the trend of the thermal zone to throttle.
* If the thermal zone is 'heating up', throttle all of the cooling
* devices associated with each trip point by one step. If the zone
* is 'cooling down', it brings back the performance of the devices
* by one step.
*/
for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
if (trip->temperature == THERMAL_TEMP_INVALID ||
trip->type == THERMAL_TRIP_CRITICAL ||
trip->type == THERMAL_TRIP_HOT)
continue;
thermal_zone_trip_update(tz, trip, td->threshold);
}
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
thermal_cdev_update(instance->cdev);
}
static struct thermal_governor thermal_gov_step_wise = {
.name = "step_wise",
.manage = step_wise_manage,
};
THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise);