mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-17 18:36:00 +00:00
Merge back earlier thermal core changes for v6.10.
This commit is contained in:
commit
8c882f172f
@ -13,60 +13,11 @@
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
static int thermal_zone_trip_update(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
{
|
||||
int trip_index = thermal_zone_trip_id(tz, trip);
|
||||
struct thermal_instance *instance;
|
||||
|
||||
if (!trip->hysteresis)
|
||||
dev_info_once(&tz->device,
|
||||
"Zero hysteresis value for thermal zone %s\n", tz->type);
|
||||
|
||||
dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n",
|
||||
trip_index, trip->temperature, tz->temperature,
|
||||
trip->hysteresis);
|
||||
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||||
if (instance->trip != trip)
|
||||
continue;
|
||||
|
||||
/* in case fan is in initial state, switch the fan off */
|
||||
if (instance->target == THERMAL_NO_TARGET)
|
||||
instance->target = 0;
|
||||
|
||||
/* in case fan is neither on nor off set the fan to active */
|
||||
if (instance->target != 0 && instance->target != 1) {
|
||||
pr_warn("Thermal instance %s controlled by bang-bang has unexpected state: %ld\n",
|
||||
instance->name, instance->target);
|
||||
instance->target = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* enable fan when temperature exceeds trip_temp and disable
|
||||
* the fan in case it falls below trip_temp minus hysteresis
|
||||
*/
|
||||
if (instance->target == 0 && tz->temperature >= trip->temperature)
|
||||
instance->target = 1;
|
||||
else if (instance->target == 1 &&
|
||||
tz->temperature < trip->temperature - trip->hysteresis)
|
||||
instance->target = 0;
|
||||
|
||||
dev_dbg(&instance->cdev->device, "target=%d\n",
|
||||
(int)instance->target);
|
||||
|
||||
mutex_lock(&instance->cdev->lock);
|
||||
instance->cdev->updated = false; /* cdev needs update */
|
||||
mutex_unlock(&instance->cdev->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bang_bang_control - controls devices associated with the given zone
|
||||
* @tz: thermal_zone_device
|
||||
* @trip: the trip point
|
||||
* @crossed_up: whether or not the trip has been crossed on the way up
|
||||
*
|
||||
* Regulation Logic: a two point regulation, deliver cooling state depending
|
||||
* on the previous state shown in this diagram:
|
||||
@ -90,26 +41,54 @@ static int thermal_zone_trip_update(struct thermal_zone_device *tz,
|
||||
* (trip_temp - hyst) so that the fan gets turned off again.
|
||||
*
|
||||
*/
|
||||
static int bang_bang_control(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
static void bang_bang_control(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
bool crossed_up)
|
||||
{
|
||||
struct thermal_instance *instance;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
ret = thermal_zone_trip_update(tz, trip);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n",
|
||||
thermal_zone_trip_id(tz, trip), trip->temperature,
|
||||
tz->temperature, trip->hysteresis);
|
||||
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||||
if (instance->trip != trip)
|
||||
continue;
|
||||
|
||||
if (instance->target == THERMAL_NO_TARGET)
|
||||
instance->target = 0;
|
||||
|
||||
if (instance->target != 0 && instance->target != 1) {
|
||||
pr_debug("Unexpected state %ld of thermal instance %s in bang-bang\n",
|
||||
instance->target, instance->name);
|
||||
|
||||
instance->target = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the fan when the trip is crossed on the way up and
|
||||
* disable it when the trip is crossed on the way down.
|
||||
*/
|
||||
if (instance->target == 0 && crossed_up)
|
||||
instance->target = 1;
|
||||
else if (instance->target == 1 && !crossed_up)
|
||||
instance->target = 0;
|
||||
|
||||
dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target);
|
||||
|
||||
mutex_lock(&instance->cdev->lock);
|
||||
instance->cdev->updated = false; /* cdev needs update */
|
||||
mutex_unlock(&instance->cdev->lock);
|
||||
}
|
||||
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
|
||||
thermal_cdev_update(instance->cdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_governor thermal_gov_bang_bang = {
|
||||
.name = "bang_bang",
|
||||
.throttle = bang_bang_control,
|
||||
.trip_crossed = bang_bang_control,
|
||||
};
|
||||
THERMAL_GOVERNOR_DECLARE(thermal_gov_bang_bang);
|
||||
|
@ -17,97 +17,111 @@
|
||||
|
||||
static int get_trip_level(struct thermal_zone_device *tz)
|
||||
{
|
||||
const struct thermal_trip *trip, *level_trip = NULL;
|
||||
const struct thermal_trip_desc *level_td = NULL;
|
||||
const struct thermal_trip_desc *td;
|
||||
int trip_level = -1;
|
||||
|
||||
for_each_trip(tz, trip) {
|
||||
if (trip->temperature >= tz->temperature)
|
||||
for_each_trip_desc(tz, td) {
|
||||
if (td->threshold > tz->temperature)
|
||||
continue;
|
||||
|
||||
trip_level++;
|
||||
|
||||
if (!level_trip || trip->temperature > level_trip->temperature)
|
||||
level_trip = trip;
|
||||
if (!level_td || td->threshold > level_td->threshold)
|
||||
level_td = td;
|
||||
}
|
||||
|
||||
/* Bail out if the temperature is not greater than any trips. */
|
||||
if (trip_level < 0)
|
||||
return 0;
|
||||
|
||||
trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, level_trip),
|
||||
level_trip->type);
|
||||
trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, &level_td->trip),
|
||||
level_td->trip.type);
|
||||
|
||||
return trip_level;
|
||||
}
|
||||
|
||||
static long get_target_state(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev, int percentage, int level)
|
||||
{
|
||||
return (long)(percentage * level * cdev->max_state) / (100 * tz->num_trips);
|
||||
}
|
||||
|
||||
/**
|
||||
* fair_share_throttle - throttles devices associated with the given zone
|
||||
* @tz: thermal_zone_device
|
||||
* @trip: trip point
|
||||
* @trip_level: number of trips crossed by the zone temperature
|
||||
*
|
||||
* Throttling Logic: This uses three parameters to calculate the new
|
||||
* throttle state of the cooling devices associated with the given zone.
|
||||
*
|
||||
* Parameters used for Throttling:
|
||||
* P1. max_state: Maximum throttle state exposed by the cooling device.
|
||||
* P2. percentage[i]/100:
|
||||
* P2. weight[i]/total_weight:
|
||||
* How 'effective' the 'i'th device is, in cooling the given zone.
|
||||
* P3. cur_trip_level/max_no_of_trips:
|
||||
* P3. trip_level/max_no_of_trips:
|
||||
* This describes the extent to which the devices should be throttled.
|
||||
* We do not want to throttle too much when we trip a lower temperature,
|
||||
* whereas the throttling is at full swing if we trip critical levels.
|
||||
* (Heavily assumes the trip points are in ascending order)
|
||||
* new_state of cooling device = P3 * P2 * P1
|
||||
*/
|
||||
static int fair_share_throttle(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
static void fair_share_throttle(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
int trip_level)
|
||||
{
|
||||
struct thermal_instance *instance;
|
||||
int total_weight = 0;
|
||||
int total_instance = 0;
|
||||
int cur_trip_level = get_trip_level(tz);
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
int nr_instances = 0;
|
||||
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||||
if (instance->trip != trip)
|
||||
continue;
|
||||
|
||||
total_weight += instance->weight;
|
||||
total_instance++;
|
||||
nr_instances++;
|
||||
}
|
||||
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||||
int percentage;
|
||||
struct thermal_cooling_device *cdev = instance->cdev;
|
||||
u64 dividend;
|
||||
u32 divisor;
|
||||
|
||||
if (instance->trip != trip)
|
||||
continue;
|
||||
|
||||
if (!total_weight)
|
||||
percentage = 100 / total_instance;
|
||||
else
|
||||
percentage = (instance->weight * 100) / total_weight;
|
||||
|
||||
instance->target = get_target_state(tz, cdev, percentage,
|
||||
cur_trip_level);
|
||||
dividend = trip_level;
|
||||
dividend *= cdev->max_state;
|
||||
divisor = tz->num_trips;
|
||||
if (total_weight) {
|
||||
dividend *= instance->weight;
|
||||
divisor *= total_weight;
|
||||
} else {
|
||||
divisor *= nr_instances;
|
||||
}
|
||||
instance->target = div_u64(dividend, divisor);
|
||||
|
||||
mutex_lock(&cdev->lock);
|
||||
__thermal_cdev_update(cdev);
|
||||
mutex_unlock(&cdev->lock);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
static void fair_share_manage(struct thermal_zone_device *tz)
|
||||
{
|
||||
int trip_level = get_trip_level(tz);
|
||||
const struct thermal_trip_desc *td;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
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;
|
||||
|
||||
fair_share_throttle(tz, trip, trip_level);
|
||||
}
|
||||
}
|
||||
|
||||
static struct thermal_governor thermal_gov_fair_share = {
|
||||
.name = "fair_share",
|
||||
.throttle = fair_share_throttle,
|
||||
.name = "fair_share",
|
||||
.manage = fair_share_manage,
|
||||
};
|
||||
THERMAL_GOVERNOR_DECLARE(thermal_gov_fair_share);
|
||||
|
@ -395,7 +395,7 @@ static void divvy_up_power(struct power_actor *power, int num_actors,
|
||||
}
|
||||
}
|
||||
|
||||
static int allocate_power(struct thermal_zone_device *tz, int control_temp)
|
||||
static void allocate_power(struct thermal_zone_device *tz, int control_temp)
|
||||
{
|
||||
struct power_allocator_params *params = tz->governor_data;
|
||||
unsigned int num_actors = params->num_actors;
|
||||
@ -410,7 +410,7 @@ static int allocate_power(struct thermal_zone_device *tz, int control_temp)
|
||||
int i = 0, ret;
|
||||
|
||||
if (!num_actors)
|
||||
return -ENODEV;
|
||||
return;
|
||||
|
||||
/* Clean all buffers for new power estimations */
|
||||
memset(power, 0, params->buffer_size);
|
||||
@ -471,8 +471,6 @@ static int allocate_power(struct thermal_zone_device *tz, int control_temp)
|
||||
num_actors, power_range,
|
||||
max_allocatable_power, tz->temperature,
|
||||
control_temp - tz->temperature);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -496,9 +494,11 @@ static void get_governor_trips(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *first_passive = NULL;
|
||||
const struct thermal_trip *last_passive = NULL;
|
||||
const struct thermal_trip *last_active = NULL;
|
||||
const struct thermal_trip *trip;
|
||||
const struct thermal_trip_desc *td;
|
||||
|
||||
for_each_trip_desc(tz, td) {
|
||||
const struct thermal_trip *trip = &td->trip;
|
||||
|
||||
for_each_trip(tz, trip) {
|
||||
switch (trip->type) {
|
||||
case THERMAL_TRIP_PASSIVE:
|
||||
if (!first_passive) {
|
||||
@ -743,40 +743,29 @@ static void power_allocator_unbind(struct thermal_zone_device *tz)
|
||||
tz->governor_data = NULL;
|
||||
}
|
||||
|
||||
static int power_allocator_throttle(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
static void power_allocator_manage(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct power_allocator_params *params = tz->governor_data;
|
||||
bool update;
|
||||
const struct thermal_trip *trip = params->trip_switch_on;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
/*
|
||||
* We get called for every trip point but we only need to do
|
||||
* our calculations once
|
||||
*/
|
||||
if (trip != params->trip_max)
|
||||
return 0;
|
||||
|
||||
trip = params->trip_switch_on;
|
||||
if (trip && tz->temperature < trip->temperature) {
|
||||
update = tz->passive;
|
||||
tz->passive = 0;
|
||||
reset_pid_controller(params);
|
||||
allow_maximum_power(tz, update);
|
||||
return 0;
|
||||
allow_maximum_power(tz, tz->passive);
|
||||
tz->passive = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
allocate_power(tz, params->trip_max->temperature);
|
||||
tz->passive = 1;
|
||||
|
||||
return allocate_power(tz, params->trip_max->temperature);
|
||||
}
|
||||
|
||||
static struct thermal_governor thermal_gov_power_allocator = {
|
||||
.name = "power_allocator",
|
||||
.bind_to_tz = power_allocator_bind,
|
||||
.unbind_from_tz = power_allocator_unbind,
|
||||
.throttle = power_allocator_throttle,
|
||||
.manage = power_allocator_manage,
|
||||
.update_tz = power_allocator_update_tz,
|
||||
};
|
||||
THERMAL_GOVERNOR_DECLARE(thermal_gov_power_allocator);
|
||||
|
@ -32,7 +32,6 @@ static unsigned long get_target_state(struct thermal_instance *instance,
|
||||
{
|
||||
struct thermal_cooling_device *cdev = instance->cdev;
|
||||
unsigned long cur_state;
|
||||
unsigned long next_target;
|
||||
|
||||
/*
|
||||
* We keep this instance the way it is by default.
|
||||
@ -40,112 +39,109 @@ static unsigned long get_target_state(struct thermal_instance *instance,
|
||||
* cdev in use to determine the next_target.
|
||||
*/
|
||||
cdev->ops->get_cur_state(cdev, &cur_state);
|
||||
next_target = instance->target;
|
||||
dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state);
|
||||
|
||||
if (!instance->initialized) {
|
||||
if (throttle) {
|
||||
next_target = clamp((cur_state + 1), instance->lower, instance->upper);
|
||||
} else {
|
||||
next_target = THERMAL_NO_TARGET;
|
||||
}
|
||||
if (throttle)
|
||||
return clamp(cur_state + 1, instance->lower, instance->upper);
|
||||
|
||||
return next_target;
|
||||
return THERMAL_NO_TARGET;
|
||||
}
|
||||
|
||||
if (throttle) {
|
||||
if (trend == THERMAL_TREND_RAISING)
|
||||
next_target = clamp((cur_state + 1), instance->lower, instance->upper);
|
||||
} else {
|
||||
if (trend == THERMAL_TREND_DROPPING) {
|
||||
if (cur_state <= instance->lower)
|
||||
next_target = THERMAL_NO_TARGET;
|
||||
else
|
||||
next_target = clamp((cur_state - 1), instance->lower, instance->upper);
|
||||
}
|
||||
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 next_target;
|
||||
return instance->target;
|
||||
}
|
||||
|
||||
static void thermal_zone_trip_update(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
const struct thermal_trip *trip,
|
||||
int trip_threshold)
|
||||
{
|
||||
enum thermal_trend trend = get_tz_trend(tz, trip);
|
||||
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->temperature) {
|
||||
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->temperature, trend, throttle);
|
||||
trip_id, trip->type, trip_threshold, trend, throttle);
|
||||
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||||
int old_target;
|
||||
|
||||
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);
|
||||
|
||||
dev_dbg(&instance->cdev->device, "old_target=%d, target=%ld\n",
|
||||
old_target, instance->target);
|
||||
|
||||
if (instance->initialized && old_target == instance->target)
|
||||
continue;
|
||||
|
||||
if (old_target == THERMAL_NO_TARGET &&
|
||||
instance->target != THERMAL_NO_TARGET) {
|
||||
/* Activate a passive thermal instance */
|
||||
if (trip->type == THERMAL_TRIP_PASSIVE)
|
||||
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) {
|
||||
/* Deactivate a passive thermal instance */
|
||||
if (trip->type == THERMAL_TRIP_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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* step_wise_throttle - throttles devices associated with the given zone
|
||||
* @tz: thermal_zone_device
|
||||
* @trip: trip point
|
||||
*
|
||||
* Throttling Logic: This uses the trend of the thermal zone to throttle.
|
||||
* If the thermal zone is 'heating up' this throttles all the cooling
|
||||
* devices associated with the zone and its particular trip point, by one
|
||||
* step. If the zone is 'cooling down' it brings back the performance of
|
||||
* the devices by one step.
|
||||
*/
|
||||
static int step_wise_throttle(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
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);
|
||||
|
||||
thermal_zone_trip_update(tz, trip);
|
||||
/*
|
||||
* 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_governor thermal_gov_step_wise = {
|
||||
.name = "step_wise",
|
||||
.throttle = step_wise_throttle,
|
||||
.name = "step_wise",
|
||||
.manage = step_wise_manage,
|
||||
};
|
||||
THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise);
|
||||
|
@ -26,11 +26,13 @@ static int user_space_bind(struct thermal_zone_device *tz)
|
||||
* notify_user_space - Notifies user space about thermal events
|
||||
* @tz: thermal_zone_device
|
||||
* @trip: trip point
|
||||
* @crossed_up: whether or not the trip has been crossed on the way up
|
||||
*
|
||||
* This function notifies the user space through UEvents.
|
||||
*/
|
||||
static int notify_user_space(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
static void notify_user_space(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
bool crossed_up)
|
||||
{
|
||||
char *thermal_prop[5];
|
||||
int i;
|
||||
@ -46,13 +48,11 @@ static int notify_user_space(struct thermal_zone_device *tz,
|
||||
kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop);
|
||||
for (i = 0; i < 4; ++i)
|
||||
kfree(thermal_prop[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_governor thermal_gov_user_space = {
|
||||
.name = "user_space",
|
||||
.throttle = notify_user_space,
|
||||
.trip_crossed = notify_user_space,
|
||||
.bind_to_tz = user_space_bind,
|
||||
};
|
||||
THERMAL_GOVERNOR_DECLARE(thermal_gov_user_space);
|
||||
|
@ -309,7 +309,7 @@ static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **ps
|
||||
|
||||
if (knob->type == ACPI_TYPE_STRING) {
|
||||
memset(&psvt->limit, 0, sizeof(u64));
|
||||
strncpy(psvt->limit.string, psvt_ptr->limit.str_ptr, knob->string.length);
|
||||
strscpy(psvt->limit.string, psvt_ptr->limit.str_ptr, ACPI_LIMIT_STR_MAX_LEN);
|
||||
} else {
|
||||
psvt->limit.integer = psvt_ptr->limit.integer;
|
||||
}
|
||||
@ -468,7 +468,7 @@ static int fill_psvt(char __user *ubuf)
|
||||
psvt_user[i].unlimit_coeff = psvts[i].unlimit_coeff;
|
||||
psvt_user[i].control_knob_type = psvts[i].control_knob_type;
|
||||
if (psvt_user[i].control_knob_type == ACPI_TYPE_STRING)
|
||||
strncpy(psvt_user[i].limit.string, psvts[i].limit.string,
|
||||
strscpy(psvt_user[i].limit.string, psvts[i].limit.string,
|
||||
ACPI_LIMIT_STR_MAX_LEN);
|
||||
else
|
||||
psvt_user[i].limit.integer = psvts[i].limit.integer;
|
||||
|
@ -159,6 +159,7 @@ struct hfi_cpu_info {
|
||||
static DEFINE_PER_CPU(struct hfi_cpu_info, hfi_cpu_info) = { .index = -1 };
|
||||
|
||||
static int max_hfi_instances;
|
||||
static int hfi_clients_nr;
|
||||
static struct hfi_instance *hfi_instances;
|
||||
|
||||
static struct hfi_features hfi_features;
|
||||
@ -477,8 +478,11 @@ void intel_hfi_online(unsigned int cpu)
|
||||
enable:
|
||||
cpumask_set_cpu(cpu, hfi_instance->cpus);
|
||||
|
||||
/* Enable this HFI instance if this is its first online CPU. */
|
||||
if (cpumask_weight(hfi_instance->cpus) == 1) {
|
||||
/*
|
||||
* Enable this HFI instance if this is its first online CPU and
|
||||
* there are user-space clients of thermal events.
|
||||
*/
|
||||
if (cpumask_weight(hfi_instance->cpus) == 1 && hfi_clients_nr > 0) {
|
||||
hfi_set_hw_table(hfi_instance);
|
||||
hfi_enable();
|
||||
}
|
||||
@ -573,18 +577,33 @@ static __init int hfi_parse_features(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hfi_do_enable(void)
|
||||
/*
|
||||
* If concurrency is not prevented by other means, the HFI enable/disable
|
||||
* routines must be called under hfi_instance_lock."
|
||||
*/
|
||||
static void hfi_enable_instance(void *ptr)
|
||||
{
|
||||
hfi_set_hw_table(ptr);
|
||||
hfi_enable();
|
||||
}
|
||||
|
||||
static void hfi_disable_instance(void *ptr)
|
||||
{
|
||||
hfi_disable();
|
||||
}
|
||||
|
||||
static void hfi_syscore_resume(void)
|
||||
{
|
||||
/* This code runs only on the boot CPU. */
|
||||
struct hfi_cpu_info *info = &per_cpu(hfi_cpu_info, 0);
|
||||
struct hfi_instance *hfi_instance = info->hfi_instance;
|
||||
|
||||
/* No locking needed. There is no concurrency with CPU online. */
|
||||
hfi_set_hw_table(hfi_instance);
|
||||
hfi_enable();
|
||||
if (hfi_clients_nr > 0)
|
||||
hfi_enable_instance(hfi_instance);
|
||||
}
|
||||
|
||||
static int hfi_do_disable(void)
|
||||
static int hfi_syscore_suspend(void)
|
||||
{
|
||||
/* No locking needed. There is no concurrency with CPU offline. */
|
||||
hfi_disable();
|
||||
@ -593,8 +612,58 @@ static int hfi_do_disable(void)
|
||||
}
|
||||
|
||||
static struct syscore_ops hfi_pm_ops = {
|
||||
.resume = hfi_do_enable,
|
||||
.suspend = hfi_do_disable,
|
||||
.resume = hfi_syscore_resume,
|
||||
.suspend = hfi_syscore_suspend,
|
||||
};
|
||||
|
||||
static int hfi_thermal_notify(struct notifier_block *nb, unsigned long state,
|
||||
void *_notify)
|
||||
{
|
||||
struct thermal_genl_notify *notify = _notify;
|
||||
struct hfi_instance *hfi_instance;
|
||||
smp_call_func_t func = NULL;
|
||||
unsigned int cpu;
|
||||
int i;
|
||||
|
||||
if (notify->mcgrp != THERMAL_GENL_EVENT_GROUP)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (state != THERMAL_NOTIFY_BIND && state != THERMAL_NOTIFY_UNBIND)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
mutex_lock(&hfi_instance_lock);
|
||||
|
||||
switch (state) {
|
||||
case THERMAL_NOTIFY_BIND:
|
||||
if (++hfi_clients_nr == 1)
|
||||
func = hfi_enable_instance;
|
||||
break;
|
||||
case THERMAL_NOTIFY_UNBIND:
|
||||
if (--hfi_clients_nr == 0)
|
||||
func = hfi_disable_instance;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!func)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < max_hfi_instances; i++) {
|
||||
hfi_instance = &hfi_instances[i];
|
||||
if (cpumask_empty(hfi_instance->cpus))
|
||||
continue;
|
||||
|
||||
cpu = cpumask_any(hfi_instance->cpus);
|
||||
smp_call_function_single(cpu, func, hfi_instance, true);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&hfi_instance_lock);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block hfi_thermal_nb = {
|
||||
.notifier_call = hfi_thermal_notify,
|
||||
};
|
||||
|
||||
void __init intel_hfi_init(void)
|
||||
@ -628,10 +697,22 @@ void __init intel_hfi_init(void)
|
||||
if (!hfi_updates_wq)
|
||||
goto err_nomem;
|
||||
|
||||
/*
|
||||
* Both thermal core and Intel HFI can not be build as modules.
|
||||
* As kernel build-in drivers they are initialized before user-space
|
||||
* starts, hence we can not miss BIND/UNBIND events when applications
|
||||
* add/remove thermal multicast group to/from a netlink socket.
|
||||
*/
|
||||
if (thermal_genl_register_notifier(&hfi_thermal_nb))
|
||||
goto err_nl_notif;
|
||||
|
||||
register_syscore_ops(&hfi_pm_ops);
|
||||
|
||||
return;
|
||||
|
||||
err_nl_notif:
|
||||
destroy_workqueue(hfi_updates_wq);
|
||||
|
||||
err_nomem:
|
||||
for (j = 0; j < i; ++j) {
|
||||
hfi_instance = &hfi_instances[j];
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/list_sort.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/string.h>
|
||||
@ -301,11 +302,12 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz)
|
||||
thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies);
|
||||
}
|
||||
|
||||
static void handle_non_critical_trips(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
static struct thermal_governor *thermal_get_tz_governor(struct thermal_zone_device *tz)
|
||||
{
|
||||
tz->governor ? tz->governor->throttle(tz, trip) :
|
||||
def_governor->throttle(tz, trip);
|
||||
if (tz->governor)
|
||||
return tz->governor;
|
||||
|
||||
return def_governor;
|
||||
}
|
||||
|
||||
void thermal_governor_update_tz(struct thermal_zone_device *tz,
|
||||
@ -348,10 +350,6 @@ void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz)
|
||||
static void handle_critical_trips(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
{
|
||||
/* If we have not crossed the trip_temp, we do not care. */
|
||||
if (trip->temperature <= 0 || tz->temperature < trip->temperature)
|
||||
return;
|
||||
|
||||
trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, trip), trip->type);
|
||||
|
||||
if (trip->type == THERMAL_TRIP_CRITICAL)
|
||||
@ -361,55 +359,53 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
|
||||
}
|
||||
|
||||
static void handle_thermal_trip(struct thermal_zone_device *tz,
|
||||
struct thermal_trip *trip)
|
||||
struct thermal_trip_desc *td,
|
||||
struct list_head *way_up_list,
|
||||
struct list_head *way_down_list)
|
||||
{
|
||||
const struct thermal_trip *trip = &td->trip;
|
||||
int old_threshold;
|
||||
|
||||
if (trip->temperature == THERMAL_TEMP_INVALID)
|
||||
return;
|
||||
|
||||
if (tz->last_temperature == THERMAL_TEMP_INVALID) {
|
||||
/* Initialization. */
|
||||
trip->threshold = trip->temperature;
|
||||
if (tz->temperature >= trip->threshold)
|
||||
trip->threshold -= trip->hysteresis;
|
||||
} else if (tz->last_temperature < trip->threshold) {
|
||||
/*
|
||||
* If the trip temperature or hysteresis has been updated recently,
|
||||
* the threshold needs to be computed again using the new values.
|
||||
* However, its initial value still reflects the old ones and that
|
||||
* is what needs to be compared with the previous zone temperature
|
||||
* to decide which action to take.
|
||||
*/
|
||||
old_threshold = td->threshold;
|
||||
td->threshold = trip->temperature;
|
||||
|
||||
if (tz->last_temperature >= old_threshold &&
|
||||
tz->last_temperature != THERMAL_TEMP_INVALID) {
|
||||
/*
|
||||
* The trip threshold is equal to the trip temperature, unless
|
||||
* the latter has changed in the meantime. In either case,
|
||||
* the trip is crossed if the current zone temperature is at
|
||||
* least equal to its temperature, but otherwise ensure that
|
||||
* the threshold and the trip temperature will be equal.
|
||||
*/
|
||||
if (tz->temperature >= trip->temperature) {
|
||||
thermal_notify_tz_trip_up(tz, trip);
|
||||
thermal_debug_tz_trip_up(tz, trip);
|
||||
trip->threshold = trip->temperature - trip->hysteresis;
|
||||
} else {
|
||||
trip->threshold = trip->temperature;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* The previous zone temperature was above or equal to the trip
|
||||
* threshold, which would be equal to the "low temperature" of
|
||||
* the trip (its temperature minus its hysteresis), unless the
|
||||
* trip temperature or hysteresis had changed. In either case,
|
||||
* the trip is crossed if the current zone temperature is below
|
||||
* the low temperature of the trip, but otherwise ensure that
|
||||
* the trip threshold will be equal to the low temperature of
|
||||
* the trip.
|
||||
* Mitigation is under way, so it needs to stop if the zone
|
||||
* temperature falls below the low temperature of the trip.
|
||||
* In that case, the trip temperature becomes the new threshold.
|
||||
*/
|
||||
if (tz->temperature < trip->temperature - trip->hysteresis) {
|
||||
thermal_notify_tz_trip_down(tz, trip);
|
||||
thermal_debug_tz_trip_down(tz, trip);
|
||||
trip->threshold = trip->temperature;
|
||||
list_add(&td->notify_list_node, way_down_list);
|
||||
td->notify_temp = trip->temperature - trip->hysteresis;
|
||||
} else {
|
||||
trip->threshold = trip->temperature - trip->hysteresis;
|
||||
td->threshold -= trip->hysteresis;
|
||||
}
|
||||
}
|
||||
} else if (tz->temperature >= trip->temperature) {
|
||||
/*
|
||||
* There is no mitigation under way, so it needs to be started
|
||||
* if the zone temperature exceeds the trip one. The new
|
||||
* threshold is then set to the low temperature of the trip.
|
||||
*/
|
||||
list_add_tail(&td->notify_list_node, way_up_list);
|
||||
td->notify_temp = trip->temperature;
|
||||
td->threshold -= trip->hysteresis;
|
||||
|
||||
if (trip->type == THERMAL_TRIP_CRITICAL || trip->type == THERMAL_TRIP_HOT)
|
||||
handle_critical_trips(tz, trip);
|
||||
else
|
||||
handle_non_critical_trips(tz, trip);
|
||||
if (trip->type == THERMAL_TRIP_CRITICAL ||
|
||||
trip->type == THERMAL_TRIP_HOT)
|
||||
handle_critical_trips(tz, trip);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_temperature(struct thermal_zone_device *tz)
|
||||
@ -431,7 +427,6 @@ static void update_temperature(struct thermal_zone_device *tz)
|
||||
trace_thermal_temperature(tz);
|
||||
|
||||
thermal_genl_sampling_temp(tz->id, temp);
|
||||
thermal_debug_update_temp(tz);
|
||||
}
|
||||
|
||||
static void thermal_zone_device_check(struct work_struct *work)
|
||||
@ -455,10 +450,34 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz)
|
||||
pos->initialized = false;
|
||||
}
|
||||
|
||||
static void thermal_governor_trip_crossed(struct thermal_governor *governor,
|
||||
struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
bool crossed_up)
|
||||
{
|
||||
if (governor->trip_crossed)
|
||||
governor->trip_crossed(tz, trip, crossed_up);
|
||||
}
|
||||
|
||||
static int thermal_trip_notify_cmp(void *ascending, const struct list_head *a,
|
||||
const struct list_head *b)
|
||||
{
|
||||
struct thermal_trip_desc *tda = container_of(a, struct thermal_trip_desc,
|
||||
notify_list_node);
|
||||
struct thermal_trip_desc *tdb = container_of(b, struct thermal_trip_desc,
|
||||
notify_list_node);
|
||||
int ret = tdb->notify_temp - tda->notify_temp;
|
||||
|
||||
return ascending ? ret : -ret;
|
||||
}
|
||||
|
||||
void __thermal_zone_device_update(struct thermal_zone_device *tz,
|
||||
enum thermal_notify_event event)
|
||||
{
|
||||
struct thermal_trip *trip;
|
||||
struct thermal_governor *governor = thermal_get_tz_governor(tz);
|
||||
struct thermal_trip_desc *td;
|
||||
LIST_HEAD(way_down_list);
|
||||
LIST_HEAD(way_up_list);
|
||||
|
||||
if (tz->suspended)
|
||||
return;
|
||||
@ -472,8 +491,27 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz,
|
||||
|
||||
tz->notify_event = event;
|
||||
|
||||
for_each_trip(tz, trip)
|
||||
handle_thermal_trip(tz, trip);
|
||||
for_each_trip_desc(tz, td)
|
||||
handle_thermal_trip(tz, td, &way_up_list, &way_down_list);
|
||||
|
||||
list_sort(&way_up_list, &way_up_list, thermal_trip_notify_cmp);
|
||||
list_for_each_entry(td, &way_up_list, notify_list_node) {
|
||||
thermal_notify_tz_trip_up(tz, &td->trip);
|
||||
thermal_debug_tz_trip_up(tz, &td->trip);
|
||||
thermal_governor_trip_crossed(governor, tz, &td->trip, true);
|
||||
}
|
||||
|
||||
list_sort(NULL, &way_down_list, thermal_trip_notify_cmp);
|
||||
list_for_each_entry(td, &way_down_list, notify_list_node) {
|
||||
thermal_notify_tz_trip_down(tz, &td->trip);
|
||||
thermal_debug_tz_trip_down(tz, &td->trip);
|
||||
thermal_governor_trip_crossed(governor, tz, &td->trip, false);
|
||||
}
|
||||
|
||||
if (governor->manage)
|
||||
governor->manage(tz);
|
||||
|
||||
thermal_debug_update_trip_stats(tz);
|
||||
|
||||
monitor_thermal_zone(tz);
|
||||
}
|
||||
@ -766,7 +804,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
|
||||
if (trip_index < 0 || trip_index >= tz->num_trips)
|
||||
return -EINVAL;
|
||||
|
||||
return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index], cdev,
|
||||
return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index].trip, cdev,
|
||||
upper, lower, weight);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device);
|
||||
@ -825,7 +863,7 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
|
||||
if (trip_index < 0 || trip_index >= tz->num_trips)
|
||||
return -EINVAL;
|
||||
|
||||
return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index], cdev);
|
||||
return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index].trip, cdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device);
|
||||
|
||||
@ -1221,16 +1259,19 @@ static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms
|
||||
|
||||
int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
int i, ret = -EINVAL;
|
||||
const struct thermal_trip_desc *td;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (tz->ops.get_crit_temp)
|
||||
return tz->ops.get_crit_temp(tz, temp);
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
for (i = 0; i < tz->num_trips; i++) {
|
||||
if (tz->trips[i].type == THERMAL_TRIP_CRITICAL) {
|
||||
*temp = tz->trips[i].temperature;
|
||||
for_each_trip_desc(tz, td) {
|
||||
const struct thermal_trip *trip = &td->trip;
|
||||
|
||||
if (trip->type == THERMAL_TRIP_CRITICAL) {
|
||||
*temp = trip->temperature;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
@ -1274,7 +1315,9 @@ thermal_zone_device_register_with_trips(const char *type,
|
||||
const struct thermal_zone_params *tzp,
|
||||
int passive_delay, int polling_delay)
|
||||
{
|
||||
const struct thermal_trip *trip = trips;
|
||||
struct thermal_zone_device *tz;
|
||||
struct thermal_trip_desc *td;
|
||||
int id;
|
||||
int result;
|
||||
struct thermal_governor *governor;
|
||||
@ -1339,7 +1382,8 @@ thermal_zone_device_register_with_trips(const char *type,
|
||||
tz->device.class = thermal_class;
|
||||
tz->devdata = devdata;
|
||||
tz->num_trips = num_trips;
|
||||
memcpy(tz->trips, trips, num_trips * sizeof(*trips));
|
||||
for_each_trip_desc(tz, td)
|
||||
td->trip = *trip++;
|
||||
|
||||
thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay);
|
||||
thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay);
|
||||
|
@ -15,6 +15,120 @@
|
||||
#include "thermal_netlink.h"
|
||||
#include "thermal_debugfs.h"
|
||||
|
||||
struct thermal_trip_desc {
|
||||
struct thermal_trip trip;
|
||||
struct list_head notify_list_node;
|
||||
int notify_temp;
|
||||
int threshold;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct thermal_governor - structure that holds thermal governor information
|
||||
* @name: name of the governor
|
||||
* @bind_to_tz: callback called when binding to a thermal zone. If it
|
||||
* returns 0, the governor is bound to the thermal zone,
|
||||
* otherwise it fails.
|
||||
* @unbind_from_tz: callback called when a governor is unbound from a
|
||||
* thermal zone.
|
||||
* @trip_crossed: called for trip points that have just been crossed
|
||||
* @manage: called on thermal zone temperature updates
|
||||
* @update_tz: callback called when thermal zone internals have changed, e.g.
|
||||
* thermal cooling instance was added/removed
|
||||
* @governor_list: node in thermal_governor_list (in thermal_core.c)
|
||||
*/
|
||||
struct thermal_governor {
|
||||
const char *name;
|
||||
int (*bind_to_tz)(struct thermal_zone_device *tz);
|
||||
void (*unbind_from_tz)(struct thermal_zone_device *tz);
|
||||
void (*trip_crossed)(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
bool crossed_up);
|
||||
void (*manage)(struct thermal_zone_device *tz);
|
||||
void (*update_tz)(struct thermal_zone_device *tz,
|
||||
enum thermal_notify_event reason);
|
||||
struct list_head governor_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct thermal_zone_device - structure for a thermal zone
|
||||
* @id: unique id number for each thermal zone
|
||||
* @type: the thermal zone device type
|
||||
* @device: &struct device for this thermal zone
|
||||
* @removal: removal completion
|
||||
* @trip_temp_attrs: attributes for trip points for sysfs: trip temperature
|
||||
* @trip_type_attrs: attributes for trip points for sysfs: trip type
|
||||
* @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
|
||||
* @mode: current mode of this thermal zone
|
||||
* @devdata: private pointer for device private data
|
||||
* @num_trips: number of trip points the thermal zone supports
|
||||
* @passive_delay_jiffies: number of jiffies to wait between polls when
|
||||
* performing passive cooling.
|
||||
* @polling_delay_jiffies: number of jiffies to wait between polls when
|
||||
* checking whether trip points have been crossed (0 for
|
||||
* interrupt driven systems)
|
||||
* @temperature: current temperature. This is only for core code,
|
||||
* drivers should use thermal_zone_get_temp() to get the
|
||||
* current temperature
|
||||
* @last_temperature: previous temperature read
|
||||
* @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION
|
||||
* @passive: 1 if you've crossed a passive trip point, 0 otherwise.
|
||||
* @prev_low_trip: the low current temperature if you've crossed a passive
|
||||
trip point.
|
||||
* @prev_high_trip: the above current temperature if you've crossed a
|
||||
passive trip point.
|
||||
* @need_update: if equals 1, thermal_zone_device_update needs to be invoked.
|
||||
* @ops: operations this &thermal_zone_device supports
|
||||
* @tzp: thermal zone parameters
|
||||
* @governor: pointer to the governor for this thermal zone
|
||||
* @governor_data: private pointer for governor data
|
||||
* @thermal_instances: list of &struct thermal_instance of this thermal zone
|
||||
* @ida: &struct ida to generate unique id for this zone's cooling
|
||||
* devices
|
||||
* @lock: lock to protect thermal_instances list
|
||||
* @node: node in thermal_tz_list (in thermal_core.c)
|
||||
* @poll_queue: delayed work for polling
|
||||
* @notify_event: Last notification event
|
||||
* @suspended: thermal zone suspend indicator
|
||||
* @trips: array of struct thermal_trip objects
|
||||
*/
|
||||
struct thermal_zone_device {
|
||||
int id;
|
||||
char type[THERMAL_NAME_LENGTH];
|
||||
struct device device;
|
||||
struct completion removal;
|
||||
struct attribute_group trips_attribute_group;
|
||||
struct thermal_attr *trip_temp_attrs;
|
||||
struct thermal_attr *trip_type_attrs;
|
||||
struct thermal_attr *trip_hyst_attrs;
|
||||
enum thermal_device_mode mode;
|
||||
void *devdata;
|
||||
int num_trips;
|
||||
unsigned long passive_delay_jiffies;
|
||||
unsigned long polling_delay_jiffies;
|
||||
int temperature;
|
||||
int last_temperature;
|
||||
int emul_temperature;
|
||||
int passive;
|
||||
int prev_low_trip;
|
||||
int prev_high_trip;
|
||||
atomic_t need_update;
|
||||
struct thermal_zone_device_ops ops;
|
||||
struct thermal_zone_params *tzp;
|
||||
struct thermal_governor *governor;
|
||||
void *governor_data;
|
||||
struct list_head thermal_instances;
|
||||
struct ida ida;
|
||||
struct mutex lock;
|
||||
struct list_head node;
|
||||
struct delayed_work poll_queue;
|
||||
enum thermal_notify_event notify_event;
|
||||
bool suspended;
|
||||
#ifdef CONFIG_THERMAL_DEBUGFS
|
||||
struct thermal_debugfs *debugfs;
|
||||
#endif
|
||||
struct thermal_trip_desc trips[] __counted_by(num_trips);
|
||||
};
|
||||
|
||||
/* Default Thermal Governor */
|
||||
#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)
|
||||
#define DEFAULT_THERMAL_GOVERNOR "step_wise"
|
||||
@ -120,8 +234,11 @@ void thermal_governor_update_tz(struct thermal_zone_device *tz,
|
||||
enum thermal_notify_event reason);
|
||||
|
||||
/* Helpers */
|
||||
#define for_each_trip(__tz, __trip) \
|
||||
for (__trip = __tz->trips; __trip - __tz->trips < __tz->num_trips; __trip++)
|
||||
#define for_each_trip_desc(__tz, __td) \
|
||||
for (__td = __tz->trips; __td - __tz->trips < __tz->num_trips; __td++)
|
||||
|
||||
#define trip_to_trip_desc(__trip) \
|
||||
container_of(__trip, struct thermal_trip_desc, trip)
|
||||
|
||||
void __thermal_zone_set_trips(struct thermal_zone_device *tz);
|
||||
int thermal_zone_trip_id(const struct thermal_zone_device *tz,
|
||||
|
@ -555,7 +555,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
|
||||
struct tz_episode *tze;
|
||||
struct tz_debugfs *tz_dbg;
|
||||
struct thermal_debugfs *thermal_dbg = tz->debugfs;
|
||||
int temperature = tz->temperature;
|
||||
int trip_id = thermal_zone_trip_id(tz, trip);
|
||||
ktime_t now = ktime_get();
|
||||
|
||||
@ -624,12 +623,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
|
||||
|
||||
tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
|
||||
tze->trip_stats[trip_id].timestamp = now;
|
||||
tze->trip_stats[trip_id].max = max(tze->trip_stats[trip_id].max, temperature);
|
||||
tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, temperature);
|
||||
tze->trip_stats[trip_id].count++;
|
||||
tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg +
|
||||
(temperature - tze->trip_stats[trip_id].avg) /
|
||||
tze->trip_stats[trip_id].count;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&thermal_dbg->lock);
|
||||
@ -693,12 +686,12 @@ out:
|
||||
mutex_unlock(&thermal_dbg->lock);
|
||||
}
|
||||
|
||||
void thermal_debug_update_temp(struct thermal_zone_device *tz)
|
||||
void thermal_debug_update_trip_stats(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_debugfs *thermal_dbg = tz->debugfs;
|
||||
struct tz_episode *tze;
|
||||
struct tz_debugfs *tz_dbg;
|
||||
int trip_id, i;
|
||||
struct tz_episode *tze;
|
||||
int i;
|
||||
|
||||
if (!thermal_dbg)
|
||||
return;
|
||||
@ -710,15 +703,16 @@ void thermal_debug_update_temp(struct thermal_zone_device *tz)
|
||||
if (!tz_dbg->nr_trips)
|
||||
goto out;
|
||||
|
||||
tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
|
||||
|
||||
for (i = 0; i < tz_dbg->nr_trips; i++) {
|
||||
trip_id = tz_dbg->trips_crossed[i];
|
||||
tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
|
||||
tze->trip_stats[trip_id].count++;
|
||||
tze->trip_stats[trip_id].max = max(tze->trip_stats[trip_id].max, tz->temperature);
|
||||
tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, tz->temperature);
|
||||
tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg +
|
||||
(tz->temperature - tze->trip_stats[trip_id].avg) /
|
||||
tze->trip_stats[trip_id].count;
|
||||
int trip_id = tz_dbg->trips_crossed[i];
|
||||
struct trip_stats *trip_stats = &tze->trip_stats[trip_id];
|
||||
|
||||
trip_stats->max = max(trip_stats->max, tz->temperature);
|
||||
trip_stats->min = min(trip_stats->min, tz->temperature);
|
||||
trip_stats->avg += (tz->temperature - trip_stats->avg) /
|
||||
++trip_stats->count;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&thermal_dbg->lock);
|
||||
@ -753,7 +747,7 @@ static int tze_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct thermal_debugfs *thermal_dbg = s->private;
|
||||
struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz;
|
||||
struct thermal_trip *trip;
|
||||
struct thermal_trip_desc *td;
|
||||
struct tz_episode *tze;
|
||||
const char *type;
|
||||
int trip_id;
|
||||
@ -766,7 +760,14 @@ static int tze_seq_show(struct seq_file *s, void *v)
|
||||
|
||||
seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n");
|
||||
|
||||
for_each_trip(tz, trip) {
|
||||
for_each_trip_desc(tz, td) {
|
||||
const struct thermal_trip *trip = &td->trip;
|
||||
struct trip_stats *trip_stats;
|
||||
|
||||
/* Skip invalid trips. */
|
||||
if (trip->temperature == THERMAL_TEMP_INVALID)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* There is no possible mitigation happening at the
|
||||
* critical trip point, so the stats will be always
|
||||
@ -775,6 +776,13 @@ static int tze_seq_show(struct seq_file *s, void *v)
|
||||
if (trip->type == THERMAL_TRIP_CRITICAL)
|
||||
continue;
|
||||
|
||||
trip_id = thermal_zone_trip_id(tz, trip);
|
||||
trip_stats = &tze->trip_stats[trip_id];
|
||||
|
||||
/* Skip trips without any stats. */
|
||||
if (trip_stats->min > trip_stats->max)
|
||||
continue;
|
||||
|
||||
if (trip->type == THERMAL_TRIP_PASSIVE)
|
||||
type = "passive";
|
||||
else if (trip->type == THERMAL_TRIP_ACTIVE)
|
||||
@ -782,17 +790,15 @@ static int tze_seq_show(struct seq_file *s, void *v)
|
||||
else
|
||||
type = "hot";
|
||||
|
||||
trip_id = thermal_zone_trip_id(tz, trip);
|
||||
|
||||
seq_printf(s, "| %*d | %*s | %*d | %*d | %*lld | %*d | %*d | %*d |\n",
|
||||
4 , trip_id,
|
||||
8, type,
|
||||
9, trip->temperature,
|
||||
9, trip->hysteresis,
|
||||
10, ktime_to_ms(tze->trip_stats[trip_id].duration),
|
||||
9, tze->trip_stats[trip_id].avg,
|
||||
9, tze->trip_stats[trip_id].min,
|
||||
9, tze->trip_stats[trip_id].max);
|
||||
10, ktime_to_ms(trip_stats->duration),
|
||||
9, trip_stats->avg,
|
||||
9, trip_stats->min,
|
||||
9, trip_stats->max);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -11,7 +11,7 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip);
|
||||
void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip);
|
||||
void thermal_debug_update_temp(struct thermal_zone_device *tz);
|
||||
void thermal_debug_update_trip_stats(struct thermal_zone_device *tz);
|
||||
#else
|
||||
static inline void thermal_debug_init(void) {}
|
||||
static inline void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) {}
|
||||
@ -24,5 +24,5 @@ static inline void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip) {};
|
||||
static inline void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip) {}
|
||||
static inline void thermal_debug_update_temp(struct thermal_zone_device *tz) {}
|
||||
static inline void thermal_debug_update_trip_stats(struct thermal_zone_device *tz) {}
|
||||
#endif /* CONFIG_THERMAL_DEBUGFS */
|
||||
|
@ -50,7 +50,7 @@ get_thermal_instance(struct thermal_zone_device *tz,
|
||||
mutex_lock(&tz->lock);
|
||||
mutex_lock(&cdev->lock);
|
||||
|
||||
trip = &tz->trips[trip_index];
|
||||
trip = &tz->trips[trip_index].trip;
|
||||
|
||||
list_for_each_entry(pos, &tz->thermal_instances, tz_node) {
|
||||
if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
|
||||
@ -82,7 +82,7 @@ EXPORT_SYMBOL(get_thermal_instance);
|
||||
*/
|
||||
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
const struct thermal_trip *trip;
|
||||
const struct thermal_trip_desc *td;
|
||||
int crit_temp = INT_MAX;
|
||||
int ret = -EINVAL;
|
||||
|
||||
@ -91,7 +91,9 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
ret = tz->ops.get_temp(tz, temp);
|
||||
|
||||
if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
|
||||
for_each_trip(tz, trip) {
|
||||
for_each_trip_desc(tz, td) {
|
||||
const struct thermal_trip *trip = &td->trip;
|
||||
|
||||
if (trip->type == THERMAL_TRIP_CRITICAL) {
|
||||
crit_temp = trip->temperature;
|
||||
break;
|
||||
|
@ -7,17 +7,13 @@
|
||||
* Generic netlink for thermal management framework
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <uapi/linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
enum thermal_genl_multicast_groups {
|
||||
THERMAL_GENL_SAMPLING_GROUP = 0,
|
||||
THERMAL_GENL_EVENT_GROUP = 1,
|
||||
};
|
||||
|
||||
static const struct genl_multicast_group thermal_genl_mcgrps[] = {
|
||||
[THERMAL_GENL_SAMPLING_GROUP] = { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
|
||||
[THERMAL_GENL_EVENT_GROUP] = { .name = THERMAL_GENL_EVENT_GROUP_NAME, },
|
||||
@ -74,11 +70,12 @@ struct param {
|
||||
|
||||
typedef int (*cb_t)(struct param *);
|
||||
|
||||
static struct genl_family thermal_gnl_family;
|
||||
static struct genl_family thermal_genl_family;
|
||||
static BLOCKING_NOTIFIER_HEAD(thermal_genl_chain);
|
||||
|
||||
static int thermal_group_has_listeners(enum thermal_genl_multicast_groups group)
|
||||
{
|
||||
return genl_has_listeners(&thermal_gnl_family, &init_net, group);
|
||||
return genl_has_listeners(&thermal_genl_family, &init_net, group);
|
||||
}
|
||||
|
||||
/************************** Sampling encoding *******************************/
|
||||
@ -95,7 +92,7 @@ int thermal_genl_sampling_temp(int id, int temp)
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
|
||||
hdr = genlmsg_put(skb, 0, 0, &thermal_genl_family, 0,
|
||||
THERMAL_GENL_SAMPLING_TEMP);
|
||||
if (!hdr)
|
||||
goto out_free;
|
||||
@ -108,7 +105,7 @@ int thermal_genl_sampling_temp(int id, int temp)
|
||||
|
||||
genlmsg_end(skb, hdr);
|
||||
|
||||
genlmsg_multicast(&thermal_gnl_family, skb, 0, THERMAL_GENL_SAMPLING_GROUP, GFP_KERNEL);
|
||||
genlmsg_multicast(&thermal_genl_family, skb, 0, THERMAL_GENL_SAMPLING_GROUP, GFP_KERNEL);
|
||||
|
||||
return 0;
|
||||
out_cancel:
|
||||
@ -282,7 +279,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event,
|
||||
return -ENOMEM;
|
||||
p->msg = msg;
|
||||
|
||||
hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
|
||||
hdr = genlmsg_put(msg, 0, 0, &thermal_genl_family, 0, event);
|
||||
if (!hdr)
|
||||
goto out_free_msg;
|
||||
|
||||
@ -292,7 +289,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event,
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
|
||||
genlmsg_multicast(&thermal_gnl_family, msg, 0, THERMAL_GENL_EVENT_GROUP, GFP_KERNEL);
|
||||
genlmsg_multicast(&thermal_genl_family, msg, 0, THERMAL_GENL_EVENT_GROUP, GFP_KERNEL);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -445,7 +442,7 @@ out_cancel_nest:
|
||||
static int thermal_genl_cmd_tz_get_trip(struct param *p)
|
||||
{
|
||||
struct sk_buff *msg = p->msg;
|
||||
const struct thermal_trip *trip;
|
||||
const struct thermal_trip_desc *td;
|
||||
struct thermal_zone_device *tz;
|
||||
struct nlattr *start_trip;
|
||||
int id;
|
||||
@ -465,7 +462,9 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p)
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
for_each_trip(tz, trip) {
|
||||
for_each_trip_desc(tz, td) {
|
||||
const struct thermal_trip *trip = &td->trip;
|
||||
|
||||
if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID,
|
||||
thermal_zone_trip_id(tz, trip)) ||
|
||||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) ||
|
||||
@ -593,7 +592,7 @@ static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
|
||||
int ret;
|
||||
void *hdr;
|
||||
|
||||
hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
|
||||
hdr = genlmsg_put(skb, 0, 0, &thermal_genl_family, 0, cmd);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
@ -625,7 +624,7 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb,
|
||||
return -ENOMEM;
|
||||
p.msg = msg;
|
||||
|
||||
hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
|
||||
hdr = genlmsg_put_reply(msg, info, &thermal_genl_family, 0, cmd);
|
||||
if (!hdr)
|
||||
goto out_free_msg;
|
||||
|
||||
@ -645,6 +644,27 @@ out_free_msg:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int thermal_genl_bind(int mcgrp)
|
||||
{
|
||||
struct thermal_genl_notify n = { .mcgrp = mcgrp };
|
||||
|
||||
if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP))
|
||||
return -EINVAL;
|
||||
|
||||
blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_BIND, &n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void thermal_genl_unbind(int mcgrp)
|
||||
{
|
||||
struct thermal_genl_notify n = { .mcgrp = mcgrp };
|
||||
|
||||
if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP))
|
||||
return;
|
||||
|
||||
blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_UNBIND, &n);
|
||||
}
|
||||
|
||||
static const struct genl_small_ops thermal_genl_ops[] = {
|
||||
{
|
||||
.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
|
||||
@ -673,12 +693,14 @@ static const struct genl_small_ops thermal_genl_ops[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_family thermal_gnl_family __ro_after_init = {
|
||||
static struct genl_family thermal_genl_family __ro_after_init = {
|
||||
.hdrsize = 0,
|
||||
.name = THERMAL_GENL_FAMILY_NAME,
|
||||
.version = THERMAL_GENL_VERSION,
|
||||
.maxattr = THERMAL_GENL_ATTR_MAX,
|
||||
.policy = thermal_genl_policy,
|
||||
.bind = thermal_genl_bind,
|
||||
.unbind = thermal_genl_unbind,
|
||||
.small_ops = thermal_genl_ops,
|
||||
.n_small_ops = ARRAY_SIZE(thermal_genl_ops),
|
||||
.resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1,
|
||||
@ -686,12 +708,22 @@ static struct genl_family thermal_gnl_family __ro_after_init = {
|
||||
.n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps),
|
||||
};
|
||||
|
||||
int thermal_genl_register_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&thermal_genl_chain, nb);
|
||||
}
|
||||
|
||||
int thermal_genl_unregister_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&thermal_genl_chain, nb);
|
||||
}
|
||||
|
||||
int __init thermal_netlink_init(void)
|
||||
{
|
||||
return genl_register_family(&thermal_gnl_family);
|
||||
return genl_register_family(&thermal_genl_family);
|
||||
}
|
||||
|
||||
void __init thermal_netlink_exit(void)
|
||||
{
|
||||
genl_unregister_family(&thermal_gnl_family);
|
||||
genl_unregister_family(&thermal_genl_family);
|
||||
}
|
||||
|
@ -10,6 +10,19 @@ struct thermal_genl_cpu_caps {
|
||||
int efficiency;
|
||||
};
|
||||
|
||||
enum thermal_genl_multicast_groups {
|
||||
THERMAL_GENL_SAMPLING_GROUP = 0,
|
||||
THERMAL_GENL_EVENT_GROUP = 1,
|
||||
THERMAL_GENL_MAX_GROUP = THERMAL_GENL_EVENT_GROUP,
|
||||
};
|
||||
|
||||
#define THERMAL_NOTIFY_BIND 0
|
||||
#define THERMAL_NOTIFY_UNBIND 1
|
||||
|
||||
struct thermal_genl_notify {
|
||||
int mcgrp;
|
||||
};
|
||||
|
||||
struct thermal_zone_device;
|
||||
struct thermal_trip;
|
||||
struct thermal_cooling_device;
|
||||
@ -18,6 +31,9 @@ struct thermal_cooling_device;
|
||||
#ifdef CONFIG_THERMAL_NETLINK
|
||||
int __init thermal_netlink_init(void);
|
||||
void __init thermal_netlink_exit(void);
|
||||
int thermal_genl_register_notifier(struct notifier_block *nb);
|
||||
int thermal_genl_unregister_notifier(struct notifier_block *nb);
|
||||
|
||||
int thermal_notify_tz_create(const struct thermal_zone_device *tz);
|
||||
int thermal_notify_tz_delete(const struct thermal_zone_device *tz);
|
||||
int thermal_notify_tz_enable(const struct thermal_zone_device *tz);
|
||||
@ -48,6 +64,16 @@ static inline int thermal_notify_tz_create(const struct thermal_zone_device *tz)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_genl_register_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_genl_unregister_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_tz_delete(const struct thermal_zone_device *tz)
|
||||
{
|
||||
return 0;
|
||||
|
@ -88,7 +88,7 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
switch (tz->trips[trip_id].type) {
|
||||
switch (tz->trips[trip_id].trip.type) {
|
||||
case THERMAL_TRIP_CRITICAL:
|
||||
return sprintf(buf, "critical\n");
|
||||
case THERMAL_TRIP_HOT:
|
||||
@ -120,7 +120,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
trip = &tz->trips[trip_id];
|
||||
trip = &tz->trips[trip_id].trip;
|
||||
|
||||
if (temp != trip->temperature) {
|
||||
if (tz->ops.set_trip_temp) {
|
||||
@ -150,7 +150,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%d\n", tz->trips[trip_id].temperature);
|
||||
return sprintf(buf, "%d\n", tz->trips[trip_id].trip.temperature);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -171,7 +171,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
trip = &tz->trips[trip_id];
|
||||
trip = &tz->trips[trip_id].trip;
|
||||
|
||||
if (hyst != trip->hysteresis) {
|
||||
trip->hysteresis = hyst;
|
||||
@ -194,7 +194,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%d\n", tz->trips[trip_id].hysteresis);
|
||||
return sprintf(buf, "%d\n", tz->trips[trip_id].trip.hysteresis);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -393,7 +393,7 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = {
|
||||
*/
|
||||
static int create_trip_attrs(struct thermal_zone_device *tz)
|
||||
{
|
||||
const struct thermal_trip *trip;
|
||||
const struct thermal_trip_desc *td;
|
||||
struct attribute **attrs;
|
||||
|
||||
/* This function works only for zones with at least one trip */
|
||||
@ -429,8 +429,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for_each_trip(tz, trip) {
|
||||
int indx = thermal_zone_trip_id(tz, trip);
|
||||
for_each_trip_desc(tz, td) {
|
||||
int indx = thermal_zone_trip_id(tz, &td->trip);
|
||||
|
||||
/* create trip type attribute */
|
||||
snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH,
|
||||
@ -452,7 +452,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz)
|
||||
tz->trip_temp_attrs[indx].name;
|
||||
tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO;
|
||||
tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show;
|
||||
if (trip->flags & THERMAL_TRIP_FLAG_RW_TEMP) {
|
||||
if (td->trip.flags & THERMAL_TRIP_FLAG_RW_TEMP) {
|
||||
tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR;
|
||||
tz->trip_temp_attrs[indx].attr.store =
|
||||
trip_point_temp_store;
|
||||
@ -467,7 +467,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz)
|
||||
tz->trip_hyst_attrs[indx].name;
|
||||
tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO;
|
||||
tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show;
|
||||
if (trip->flags & THERMAL_TRIP_FLAG_RW_HYST) {
|
||||
if (td->trip.flags & THERMAL_TRIP_FLAG_RW_HYST) {
|
||||
tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR;
|
||||
tz->trip_hyst_attrs[indx].attr.store =
|
||||
trip_point_hyst_store;
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
TRACE_DEFINE_ENUM(THERMAL_TRIP_CRITICAL);
|
||||
TRACE_DEFINE_ENUM(THERMAL_TRIP_HOT);
|
||||
TRACE_DEFINE_ENUM(THERMAL_TRIP_PASSIVE);
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
TRACE_EVENT(thermal_power_allocator,
|
||||
TP_PROTO(struct thermal_zone_device *tz, u32 total_req_power,
|
||||
u32 total_granted_power, int num_actors, u32 power_range,
|
||||
|
@ -13,11 +13,11 @@ int for_each_thermal_trip(struct thermal_zone_device *tz,
|
||||
int (*cb)(struct thermal_trip *, void *),
|
||||
void *data)
|
||||
{
|
||||
struct thermal_trip *trip;
|
||||
struct thermal_trip_desc *td;
|
||||
int ret;
|
||||
|
||||
for_each_trip(tz, trip) {
|
||||
ret = cb(trip, data);
|
||||
for_each_trip_desc(tz, td) {
|
||||
ret = cb(&td->trip, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -63,7 +63,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips);
|
||||
*/
|
||||
void __thermal_zone_set_trips(struct thermal_zone_device *tz)
|
||||
{
|
||||
const struct thermal_trip *trip;
|
||||
const struct thermal_trip_desc *td;
|
||||
int low = -INT_MAX, high = INT_MAX;
|
||||
int ret;
|
||||
|
||||
@ -72,7 +72,8 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz)
|
||||
if (!tz->ops.set_trips)
|
||||
return;
|
||||
|
||||
for_each_trip(tz, trip) {
|
||||
for_each_trip_desc(tz, td) {
|
||||
const struct thermal_trip *trip = &td->trip;
|
||||
int trip_low;
|
||||
|
||||
trip_low = trip->temperature - trip->hysteresis;
|
||||
@ -110,7 +111,7 @@ int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
|
||||
if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip)
|
||||
return -EINVAL;
|
||||
|
||||
*trip = tz->trips[trip_id];
|
||||
*trip = tz->trips[trip_id].trip;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__thermal_zone_get_trip);
|
||||
@ -135,7 +136,7 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz,
|
||||
* Assume the trip to be located within the bounds of the thermal
|
||||
* zone's trips[] table.
|
||||
*/
|
||||
return trip - tz->trips;
|
||||
return trip_to_trip_desc(trip) - tz->trips;
|
||||
}
|
||||
void thermal_zone_trip_updated(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
|
@ -61,7 +61,6 @@ enum thermal_notify_event {
|
||||
* struct thermal_trip - representation of a point in temperature domain
|
||||
* @temperature: temperature value in miliCelsius
|
||||
* @hysteresis: relative hysteresis in miliCelsius
|
||||
* @threshold: trip crossing notification threshold miliCelsius
|
||||
* @type: trip point type
|
||||
* @priv: pointer to driver data associated with this trip
|
||||
* @flags: flags representing binary properties of the trip
|
||||
@ -69,7 +68,6 @@ enum thermal_notify_event {
|
||||
struct thermal_trip {
|
||||
int temperature;
|
||||
int hysteresis;
|
||||
int threshold;
|
||||
enum thermal_trip_type type;
|
||||
u8 flags;
|
||||
void *priv;
|
||||
@ -81,6 +79,8 @@ struct thermal_trip {
|
||||
#define THERMAL_TRIP_FLAG_RW (THERMAL_TRIP_FLAG_RW_TEMP | \
|
||||
THERMAL_TRIP_FLAG_RW_HYST)
|
||||
|
||||
struct thermal_zone_device;
|
||||
|
||||
struct thermal_zone_device_ops {
|
||||
int (*bind) (struct thermal_zone_device *,
|
||||
struct thermal_cooling_device *);
|
||||
@ -126,111 +126,6 @@ struct thermal_cooling_device {
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* struct thermal_zone_device - structure for a thermal zone
|
||||
* @id: unique id number for each thermal zone
|
||||
* @type: the thermal zone device type
|
||||
* @device: &struct device for this thermal zone
|
||||
* @removal: removal completion
|
||||
* @trip_temp_attrs: attributes for trip points for sysfs: trip temperature
|
||||
* @trip_type_attrs: attributes for trip points for sysfs: trip type
|
||||
* @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
|
||||
* @mode: current mode of this thermal zone
|
||||
* @devdata: private pointer for device private data
|
||||
* @num_trips: number of trip points the thermal zone supports
|
||||
* @passive_delay_jiffies: number of jiffies to wait between polls when
|
||||
* performing passive cooling.
|
||||
* @polling_delay_jiffies: number of jiffies to wait between polls when
|
||||
* checking whether trip points have been crossed (0 for
|
||||
* interrupt driven systems)
|
||||
* @temperature: current temperature. This is only for core code,
|
||||
* drivers should use thermal_zone_get_temp() to get the
|
||||
* current temperature
|
||||
* @last_temperature: previous temperature read
|
||||
* @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION
|
||||
* @passive: 1 if you've crossed a passive trip point, 0 otherwise.
|
||||
* @prev_low_trip: the low current temperature if you've crossed a passive
|
||||
trip point.
|
||||
* @prev_high_trip: the above current temperature if you've crossed a
|
||||
passive trip point.
|
||||
* @need_update: if equals 1, thermal_zone_device_update needs to be invoked.
|
||||
* @ops: operations this &thermal_zone_device supports
|
||||
* @tzp: thermal zone parameters
|
||||
* @governor: pointer to the governor for this thermal zone
|
||||
* @governor_data: private pointer for governor data
|
||||
* @thermal_instances: list of &struct thermal_instance of this thermal zone
|
||||
* @ida: &struct ida to generate unique id for this zone's cooling
|
||||
* devices
|
||||
* @lock: lock to protect thermal_instances list
|
||||
* @node: node in thermal_tz_list (in thermal_core.c)
|
||||
* @poll_queue: delayed work for polling
|
||||
* @notify_event: Last notification event
|
||||
* @suspended: thermal zone suspend indicator
|
||||
* @trips: array of struct thermal_trip objects
|
||||
*/
|
||||
struct thermal_zone_device {
|
||||
int id;
|
||||
char type[THERMAL_NAME_LENGTH];
|
||||
struct device device;
|
||||
struct completion removal;
|
||||
struct attribute_group trips_attribute_group;
|
||||
struct thermal_attr *trip_temp_attrs;
|
||||
struct thermal_attr *trip_type_attrs;
|
||||
struct thermal_attr *trip_hyst_attrs;
|
||||
enum thermal_device_mode mode;
|
||||
void *devdata;
|
||||
int num_trips;
|
||||
unsigned long passive_delay_jiffies;
|
||||
unsigned long polling_delay_jiffies;
|
||||
int temperature;
|
||||
int last_temperature;
|
||||
int emul_temperature;
|
||||
int passive;
|
||||
int prev_low_trip;
|
||||
int prev_high_trip;
|
||||
atomic_t need_update;
|
||||
struct thermal_zone_device_ops ops;
|
||||
struct thermal_zone_params *tzp;
|
||||
struct thermal_governor *governor;
|
||||
void *governor_data;
|
||||
struct list_head thermal_instances;
|
||||
struct ida ida;
|
||||
struct mutex lock;
|
||||
struct list_head node;
|
||||
struct delayed_work poll_queue;
|
||||
enum thermal_notify_event notify_event;
|
||||
bool suspended;
|
||||
#ifdef CONFIG_THERMAL_DEBUGFS
|
||||
struct thermal_debugfs *debugfs;
|
||||
#endif
|
||||
struct thermal_trip trips[] __counted_by(num_trips);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct thermal_governor - structure that holds thermal governor information
|
||||
* @name: name of the governor
|
||||
* @bind_to_tz: callback called when binding to a thermal zone. If it
|
||||
* returns 0, the governor is bound to the thermal zone,
|
||||
* otherwise it fails.
|
||||
* @unbind_from_tz: callback called when a governor is unbound from a
|
||||
* thermal zone.
|
||||
* @throttle: callback called for every trip point even if temperature is
|
||||
* below the trip point temperature
|
||||
* @update_tz: callback called when thermal zone internals have changed, e.g.
|
||||
* thermal cooling instance was added/removed
|
||||
* @governor_list: node in thermal_governor_list (in thermal_core.c)
|
||||
*/
|
||||
struct thermal_governor {
|
||||
const char *name;
|
||||
int (*bind_to_tz)(struct thermal_zone_device *tz);
|
||||
void (*unbind_from_tz)(struct thermal_zone_device *tz);
|
||||
int (*throttle)(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip);
|
||||
void (*update_tz)(struct thermal_zone_device *tz,
|
||||
enum thermal_notify_event reason);
|
||||
struct list_head governor_list;
|
||||
};
|
||||
|
||||
/* Structure to define Thermal Zone parameters */
|
||||
struct thermal_zone_params {
|
||||
const char *governor_name;
|
||||
|
Loading…
x
Reference in New Issue
Block a user