Commit 8c882f17 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge back earlier thermal core changes for v6.10.

parents d351eb0a f831892e
...@@ -13,60 +13,11 @@ ...@@ -13,60 +13,11 @@
#include "thermal_core.h" #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 * bang_bang_control - controls devices associated with the given zone
* @tz: thermal_zone_device * @tz: thermal_zone_device
* @trip: the trip point * @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 * Regulation Logic: a two point regulation, deliver cooling state depending
* on the previous state shown in this diagram: * on the previous state shown in this diagram:
...@@ -90,26 +41,54 @@ static int thermal_zone_trip_update(struct thermal_zone_device *tz, ...@@ -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. * (trip_temp - hyst) so that the fan gets turned off again.
* *
*/ */
static int bang_bang_control(struct thermal_zone_device *tz, static void bang_bang_control(struct thermal_zone_device *tz,
const struct thermal_trip *trip) const struct thermal_trip *trip,
bool crossed_up)
{ {
struct thermal_instance *instance; struct thermal_instance *instance;
int ret;
lockdep_assert_held(&tz->lock); lockdep_assert_held(&tz->lock);
ret = thermal_zone_trip_update(tz, trip); dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n",
if (ret) thermal_zone_trip_id(tz, trip), trip->temperature,
return ret; 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) list_for_each_entry(instance, &tz->thermal_instances, tz_node)
thermal_cdev_update(instance->cdev); thermal_cdev_update(instance->cdev);
return 0;
} }
static struct thermal_governor thermal_gov_bang_bang = { static struct thermal_governor thermal_gov_bang_bang = {
.name = "bang_bang", .name = "bang_bang",
.throttle = bang_bang_control, .trip_crossed = bang_bang_control,
}; };
THERMAL_GOVERNOR_DECLARE(thermal_gov_bang_bang); THERMAL_GOVERNOR_DECLARE(thermal_gov_bang_bang);
...@@ -17,97 +17,111 @@ ...@@ -17,97 +17,111 @@
static int get_trip_level(struct thermal_zone_device *tz) 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; int trip_level = -1;
for_each_trip(tz, trip) { for_each_trip_desc(tz, td) {
if (trip->temperature >= tz->temperature) if (td->threshold > tz->temperature)
continue; continue;
trip_level++; trip_level++;
if (!level_trip || trip->temperature > level_trip->temperature) if (!level_td || td->threshold > level_td->threshold)
level_trip = trip; level_td = td;
} }
/* Bail out if the temperature is not greater than any trips. */ /* Bail out if the temperature is not greater than any trips. */
if (trip_level < 0) if (trip_level < 0)
return 0; return 0;
trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, level_trip), trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, &level_td->trip),
level_trip->type); level_td->trip.type);
return trip_level; 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 * fair_share_throttle - throttles devices associated with the given zone
* @tz: thermal_zone_device * @tz: thermal_zone_device
* @trip: trip point * @trip: trip point
* @trip_level: number of trips crossed by the zone temperature
* *
* Throttling Logic: This uses three parameters to calculate the new * Throttling Logic: This uses three parameters to calculate the new
* throttle state of the cooling devices associated with the given zone. * throttle state of the cooling devices associated with the given zone.
* *
* Parameters used for Throttling: * Parameters used for Throttling:
* P1. max_state: Maximum throttle state exposed by the cooling device. * 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. * 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. * 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, * 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. * 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 * new_state of cooling device = P3 * P2 * P1
*/ */
static int fair_share_throttle(struct thermal_zone_device *tz, static void fair_share_throttle(struct thermal_zone_device *tz,
const struct thermal_trip *trip) const struct thermal_trip *trip,
int trip_level)
{ {
struct thermal_instance *instance; struct thermal_instance *instance;
int total_weight = 0; int total_weight = 0;
int total_instance = 0; int nr_instances = 0;
int cur_trip_level = get_trip_level(tz);
lockdep_assert_held(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node) { list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != trip) if (instance->trip != trip)
continue; continue;
total_weight += instance->weight; total_weight += instance->weight;
total_instance++; nr_instances++;
} }
list_for_each_entry(instance, &tz->thermal_instances, tz_node) { list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
int percentage;
struct thermal_cooling_device *cdev = instance->cdev; struct thermal_cooling_device *cdev = instance->cdev;
u64 dividend;
u32 divisor;
if (instance->trip != trip) if (instance->trip != trip)
continue; continue;
if (!total_weight) dividend = trip_level;
percentage = 100 / total_instance; dividend *= cdev->max_state;
else divisor = tz->num_trips;
percentage = (instance->weight * 100) / total_weight; if (total_weight) {
dividend *= instance->weight;
instance->target = get_target_state(tz, cdev, percentage, divisor *= total_weight;
cur_trip_level); } else {
divisor *= nr_instances;
}
instance->target = div_u64(dividend, divisor);
mutex_lock(&cdev->lock); mutex_lock(&cdev->lock);
__thermal_cdev_update(cdev); __thermal_cdev_update(cdev);
mutex_unlock(&cdev->lock); 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 = { static struct thermal_governor thermal_gov_fair_share = {
.name = "fair_share", .name = "fair_share",
.throttle = fair_share_throttle, .manage = fair_share_manage,
}; };
THERMAL_GOVERNOR_DECLARE(thermal_gov_fair_share); THERMAL_GOVERNOR_DECLARE(thermal_gov_fair_share);
...@@ -395,7 +395,7 @@ static void divvy_up_power(struct power_actor *power, int num_actors, ...@@ -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; struct power_allocator_params *params = tz->governor_data;
unsigned int num_actors = params->num_actors; unsigned int num_actors = params->num_actors;
...@@ -410,7 +410,7 @@ static int allocate_power(struct thermal_zone_device *tz, int control_temp) ...@@ -410,7 +410,7 @@ static int allocate_power(struct thermal_zone_device *tz, int control_temp)
int i = 0, ret; int i = 0, ret;
if (!num_actors) if (!num_actors)
return -ENODEV; return;
/* Clean all buffers for new power estimations */ /* Clean all buffers for new power estimations */
memset(power, 0, params->buffer_size); memset(power, 0, params->buffer_size);
...@@ -471,8 +471,6 @@ static int allocate_power(struct thermal_zone_device *tz, int control_temp) ...@@ -471,8 +471,6 @@ static int allocate_power(struct thermal_zone_device *tz, int control_temp)
num_actors, power_range, num_actors, power_range,
max_allocatable_power, tz->temperature, max_allocatable_power, tz->temperature,
control_temp - tz->temperature); control_temp - tz->temperature);
return 0;
} }
/** /**
...@@ -496,9 +494,11 @@ static void get_governor_trips(struct thermal_zone_device *tz, ...@@ -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 *first_passive = NULL;
const struct thermal_trip *last_passive = NULL; const struct thermal_trip *last_passive = NULL;
const struct thermal_trip *last_active = 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) { switch (trip->type) {
case THERMAL_TRIP_PASSIVE: case THERMAL_TRIP_PASSIVE:
if (!first_passive) { if (!first_passive) {
...@@ -743,40 +743,29 @@ static void power_allocator_unbind(struct thermal_zone_device *tz) ...@@ -743,40 +743,29 @@ static void power_allocator_unbind(struct thermal_zone_device *tz)
tz->governor_data = NULL; tz->governor_data = NULL;
} }
static int power_allocator_throttle(struct thermal_zone_device *tz, static void power_allocator_manage(struct thermal_zone_device *tz)
const struct thermal_trip *trip)
{ {
struct power_allocator_params *params = tz->governor_data; struct power_allocator_params *params = tz->governor_data;
bool update; const struct thermal_trip *trip = params->trip_switch_on;
lockdep_assert_held(&tz->lock); 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) { if (trip && tz->temperature < trip->temperature) {
update = tz->passive;
tz->passive = 0;
reset_pid_controller(params); reset_pid_controller(params);
allow_maximum_power(tz, update); allow_maximum_power(tz, tz->passive);
return 0; tz->passive = 0;
return;
} }
allocate_power(tz, params->trip_max->temperature);
tz->passive = 1; tz->passive = 1;
return allocate_power(tz, params->trip_max->temperature);
} }
static struct thermal_governor thermal_gov_power_allocator = { static struct thermal_governor thermal_gov_power_allocator = {
.name = "power_allocator", .name = "power_allocator",
.bind_to_tz = power_allocator_bind, .bind_to_tz = power_allocator_bind,
.unbind_from_tz = power_allocator_unbind, .unbind_from_tz = power_allocator_unbind,
.throttle = power_allocator_throttle, .manage = power_allocator_manage,
.update_tz = power_allocator_update_tz, .update_tz = power_allocator_update_tz,
}; };
THERMAL_GOVERNOR_DECLARE(thermal_gov_power_allocator); THERMAL_GOVERNOR_DECLARE(thermal_gov_power_allocator);
...@@ -32,7 +32,6 @@ static unsigned long get_target_state(struct thermal_instance *instance, ...@@ -32,7 +32,6 @@ static unsigned long get_target_state(struct thermal_instance *instance,
{ {
struct thermal_cooling_device *cdev = instance->cdev; struct thermal_cooling_device *cdev = instance->cdev;
unsigned long cur_state; unsigned long cur_state;
unsigned long next_target;
/* /*
* We keep this instance the way it is by default. * We keep this instance the way it is by default.
...@@ -40,112 +39,109 @@ static unsigned long get_target_state(struct thermal_instance *instance, ...@@ -40,112 +39,109 @@ static unsigned long get_target_state(struct thermal_instance *instance,
* cdev in use to determine the next_target. * cdev in use to determine the next_target.
*/ */
cdev->ops->get_cur_state(cdev, &cur_state); cdev->ops->get_cur_state(cdev, &cur_state);
next_target = instance->target;
dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state); dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state);
if (!instance->initialized) { if (!instance->initialized) {
if (throttle) { if (throttle)
next_target = clamp((cur_state + 1), instance->lower, instance->upper); return clamp(cur_state + 1, instance->lower, instance->upper);
} else {
next_target = THERMAL_NO_TARGET;
}
return next_target; return THERMAL_NO_TARGET;
} }
if (throttle) { if (throttle) {
if (trend == THERMAL_TREND_RAISING) if (trend == THERMAL_TREND_RAISING)
next_target = clamp((cur_state + 1), instance->lower, instance->upper); return clamp(cur_state + 1, instance->lower, instance->upper);
} else { } else if (trend == THERMAL_TREND_DROPPING) {
if (trend == THERMAL_TREND_DROPPING) {
if (cur_state <= instance->lower) if (cur_state <= instance->lower)
next_target = THERMAL_NO_TARGET; return THERMAL_NO_TARGET;
else
next_target = clamp((cur_state - 1), instance->lower, instance->upper); 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, 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); int trip_id = thermal_zone_trip_id(tz, trip);
enum thermal_trend trend;
struct thermal_instance *instance; struct thermal_instance *instance;
bool throttle = false; bool throttle = false;
int old_target;
trend = get_tz_trend(tz, trip); if (tz->temperature >= trip_threshold) {
if (tz->temperature >= trip->temperature) {
throttle = true; throttle = true;
trace_thermal_zone_trip(tz, trip_id, trip->type); trace_thermal_zone_trip(tz, trip_id, trip->type);
} }
dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n", 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) { list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
int old_target;
if (instance->trip != trip) if (instance->trip != trip)
continue; continue;
old_target = instance->target; old_target = instance->target;
instance->target = get_target_state(instance, trend, throttle); 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) if (instance->initialized && old_target == instance->target)
continue; continue;
if (trip->type == THERMAL_TRIP_PASSIVE) {
/* If needed, update the status of passive polling. */
if (old_target == THERMAL_NO_TARGET && if (old_target == THERMAL_NO_TARGET &&
instance->target != THERMAL_NO_TARGET) { instance->target != THERMAL_NO_TARGET)
/* Activate a passive thermal instance */
if (trip->type == THERMAL_TRIP_PASSIVE)
tz->passive++; tz->passive++;
} else if (old_target != THERMAL_NO_TARGET && else if (old_target != THERMAL_NO_TARGET &&
instance->target == THERMAL_NO_TARGET) { instance->target == THERMAL_NO_TARGET)
/* Deactivate a passive thermal instance */
if (trip->type == THERMAL_TRIP_PASSIVE)
tz->passive--; tz->passive--;
} }
instance->initialized = true; instance->initialized = true;
mutex_lock(&instance->cdev->lock); mutex_lock(&instance->cdev->lock);
instance->cdev->updated = false; /* cdev needs update */ instance->cdev->updated = false; /* cdev needs update */
mutex_unlock(&instance->cdev->lock); mutex_unlock(&instance->cdev->lock);
} }
} }
/** static void step_wise_manage(struct thermal_zone_device *tz)
* 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)
{ {
const struct thermal_trip_desc *td;
struct thermal_instance *instance; struct thermal_instance *instance;
lockdep_assert_held(&tz->lock); 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) list_for_each_entry(instance, &tz->thermal_instances, tz_node)
thermal_cdev_update(instance->cdev); thermal_cdev_update(instance->cdev);
return 0;
} }
static struct thermal_governor thermal_gov_step_wise = { static struct thermal_governor thermal_gov_step_wise = {
.name = "step_wise", .name = "step_wise",
.throttle = step_wise_throttle, .manage = step_wise_manage,
}; };
THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise); THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise);
...@@ -26,11 +26,13 @@ static int user_space_bind(struct thermal_zone_device *tz) ...@@ -26,11 +26,13 @@ static int user_space_bind(struct thermal_zone_device *tz)
* notify_user_space - Notifies user space about thermal events * notify_user_space - Notifies user space about thermal events
* @tz: thermal_zone_device * @tz: thermal_zone_device
* @trip: trip point * @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. * This function notifies the user space through UEvents.
*/ */
static int notify_user_space(struct thermal_zone_device *tz, static void notify_user_space(struct thermal_zone_device *tz,
const struct thermal_trip *trip) const struct thermal_trip *trip,
bool crossed_up)
{ {
char *thermal_prop[5]; char *thermal_prop[5];
int i; int i;
...@@ -46,13 +48,11 @@ static int notify_user_space(struct thermal_zone_device *tz, ...@@ -46,13 +48,11 @@ static int notify_user_space(struct thermal_zone_device *tz,
kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop); kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop);
for (i = 0; i < 4; ++i) for (i = 0; i < 4; ++i)
kfree(thermal_prop[i]); kfree(thermal_prop[i]);
return 0;
} }
static struct thermal_governor thermal_gov_user_space = { static struct thermal_governor thermal_gov_user_space = {
.name = "user_space", .name = "user_space",
.throttle = notify_user_space, .trip_crossed = notify_user_space,
.bind_to_tz = user_space_bind, .bind_to_tz = user_space_bind,
}; };
THERMAL_GOVERNOR_DECLARE(thermal_gov_user_space); 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 ...@@ -309,7 +309,7 @@ static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **ps
if (knob->type == ACPI_TYPE_STRING) { if (knob->type == ACPI_TYPE_STRING) {
memset(&psvt->limit, 0, sizeof(u64)); 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 { } else {
psvt->limit.integer = psvt_ptr->limit.integer; psvt->limit.integer = psvt_ptr->limit.integer;
} }
...@@ -468,7 +468,7 @@ static int fill_psvt(char __user *ubuf) ...@@ -468,7 +468,7 @@ static int fill_psvt(char __user *ubuf)
psvt_user[i].unlimit_coeff = psvts[i].unlimit_coeff; psvt_user[i].unlimit_coeff = psvts[i].unlimit_coeff;
psvt_user[i].control_knob_type = psvts[i].control_knob_type; psvt_user[i].control_knob_type = psvts[i].control_knob_type;
if (psvt_user[i].control_knob_type == ACPI_TYPE_STRING) 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); ACPI_LIMIT_STR_MAX_LEN);
else else
psvt_user[i].limit.integer = psvts[i].limit.integer; psvt_user[i].limit.integer = psvts[i].limit.integer;
......
...@@ -159,6 +159,7 @@ struct hfi_cpu_info { ...@@ -159,6 +159,7 @@ struct hfi_cpu_info {
static DEFINE_PER_CPU(struct hfi_cpu_info, hfi_cpu_info) = { .index = -1 }; static DEFINE_PER_CPU(struct hfi_cpu_info, hfi_cpu_info) = { .index = -1 };
static int max_hfi_instances; static int max_hfi_instances;
static int hfi_clients_nr;
static struct hfi_instance *hfi_instances; static struct hfi_instance *hfi_instances;
static struct hfi_features hfi_features; static struct hfi_features hfi_features;
...@@ -477,8 +478,11 @@ void intel_hfi_online(unsigned int cpu) ...@@ -477,8 +478,11 @@ void intel_hfi_online(unsigned int cpu)
enable: enable:
cpumask_set_cpu(cpu, hfi_instance->cpus); 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_set_hw_table(hfi_instance);
hfi_enable(); hfi_enable();
} }
...@@ -573,18 +577,33 @@ static __init int hfi_parse_features(void) ...@@ -573,18 +577,33 @@ static __init int hfi_parse_features(void)
return 0; 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. */ /* This code runs only on the boot CPU. */
struct hfi_cpu_info *info = &per_cpu(hfi_cpu_info, 0); struct hfi_cpu_info *info = &per_cpu(hfi_cpu_info, 0);
struct hfi_instance *hfi_instance = info->hfi_instance; struct hfi_instance *hfi_instance = info->hfi_instance;
/* No locking needed. There is no concurrency with CPU online. */ /* No locking needed. There is no concurrency with CPU online. */
hfi_set_hw_table(hfi_instance); if (hfi_clients_nr > 0)
hfi_enable(); 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. */ /* No locking needed. There is no concurrency with CPU offline. */
hfi_disable(); hfi_disable();
...@@ -593,8 +612,58 @@ static int hfi_do_disable(void) ...@@ -593,8 +612,58 @@ static int hfi_do_disable(void)
} }
static struct syscore_ops hfi_pm_ops = { static struct syscore_ops hfi_pm_ops = {
.resume = hfi_do_enable, .resume = hfi_syscore_resume,
.suspend = hfi_do_disable, .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) void __init intel_hfi_init(void)
...@@ -628,10 +697,22 @@ void __init intel_hfi_init(void) ...@@ -628,10 +697,22 @@ void __init intel_hfi_init(void)
if (!hfi_updates_wq) if (!hfi_updates_wq)
goto err_nomem; 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); register_syscore_ops(&hfi_pm_ops);
return; return;
err_nl_notif:
destroy_workqueue(hfi_updates_wq);
err_nomem: err_nomem:
for (j = 0; j < i; ++j) { for (j = 0; j < i; ++j) {
hfi_instance = &hfi_instances[j]; hfi_instance = &hfi_instances[j];
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/list_sort.h>
#include <linux/thermal.h> #include <linux/thermal.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/string.h> #include <linux/string.h>
...@@ -301,11 +302,12 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz) ...@@ -301,11 +302,12 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz)
thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies); thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies);
} }
static void handle_non_critical_trips(struct thermal_zone_device *tz, static struct thermal_governor *thermal_get_tz_governor(struct thermal_zone_device *tz)
const struct thermal_trip *trip)
{ {
tz->governor ? tz->governor->throttle(tz, trip) : if (tz->governor)
def_governor->throttle(tz, trip); return tz->governor;
return def_governor;
} }
void thermal_governor_update_tz(struct thermal_zone_device *tz, 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) ...@@ -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, static void handle_critical_trips(struct thermal_zone_device *tz,
const struct thermal_trip *trip) 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); trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, trip), trip->type);
if (trip->type == THERMAL_TRIP_CRITICAL) if (trip->type == THERMAL_TRIP_CRITICAL)
...@@ -361,55 +359,53 @@ static void handle_critical_trips(struct thermal_zone_device *tz, ...@@ -361,55 +359,53 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
} }
static void handle_thermal_trip(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) if (trip->temperature == THERMAL_TEMP_INVALID)
return; 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) {
/* /*
* The trip threshold is equal to the trip temperature, unless * If the trip temperature or hysteresis has been updated recently,
* the latter has changed in the meantime. In either case, * the threshold needs to be computed again using the new values.
* the trip is crossed if the current zone temperature is at * However, its initial value still reflects the old ones and that
* least equal to its temperature, but otherwise ensure that * is what needs to be compared with the previous zone temperature
* the threshold and the trip temperature will be equal. * to decide which action to take.
*/ */
if (tz->temperature >= trip->temperature) { old_threshold = td->threshold;
thermal_notify_tz_trip_up(tz, trip); td->threshold = trip->temperature;
thermal_debug_tz_trip_up(tz, trip);
trip->threshold = trip->temperature - trip->hysteresis; if (tz->last_temperature >= old_threshold &&
} else { tz->last_temperature != THERMAL_TEMP_INVALID) {
trip->threshold = trip->temperature;
}
} else {
/* /*
* The previous zone temperature was above or equal to the trip * Mitigation is under way, so it needs to stop if the zone
* threshold, which would be equal to the "low temperature" of * temperature falls below the low temperature of the trip.
* the trip (its temperature minus its hysteresis), unless the * In that case, the trip temperature becomes the new threshold.
* 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.
*/ */
if (tz->temperature < trip->temperature - trip->hysteresis) { if (tz->temperature < trip->temperature - trip->hysteresis) {
thermal_notify_tz_trip_down(tz, trip); list_add(&td->notify_list_node, way_down_list);
thermal_debug_tz_trip_down(tz, trip); td->notify_temp = trip->temperature - trip->hysteresis;
trip->threshold = trip->temperature;
} else { } 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) if (trip->type == THERMAL_TRIP_CRITICAL ||
trip->type == THERMAL_TRIP_HOT)
handle_critical_trips(tz, trip); handle_critical_trips(tz, trip);
else }
handle_non_critical_trips(tz, trip);
} }
static void update_temperature(struct thermal_zone_device *tz) static void update_temperature(struct thermal_zone_device *tz)
...@@ -431,7 +427,6 @@ 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); trace_thermal_temperature(tz);
thermal_genl_sampling_temp(tz->id, temp); thermal_genl_sampling_temp(tz->id, temp);
thermal_debug_update_temp(tz);
} }
static void thermal_zone_device_check(struct work_struct *work) 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) ...@@ -455,10 +450,34 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz)
pos->initialized = false; 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, void __thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event) 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) if (tz->suspended)
return; return;
...@@ -472,8 +491,27 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, ...@@ -472,8 +491,27 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz,
tz->notify_event = event; tz->notify_event = event;
for_each_trip(tz, trip) for_each_trip_desc(tz, td)
handle_thermal_trip(tz, trip); 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); monitor_thermal_zone(tz);
} }
...@@ -766,7 +804,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *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) if (trip_index < 0 || trip_index >= tz->num_trips)
return -EINVAL; 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); upper, lower, weight);
} }
EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device);
...@@ -825,7 +863,7 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, ...@@ -825,7 +863,7 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
if (trip_index < 0 || trip_index >= tz->num_trips) if (trip_index < 0 || trip_index >= tz->num_trips)
return -EINVAL; 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); 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 ...@@ -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 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) if (tz->ops.get_crit_temp)
return tz->ops.get_crit_temp(tz, temp); return tz->ops.get_crit_temp(tz, temp);
mutex_lock(&tz->lock); mutex_lock(&tz->lock);
for (i = 0; i < tz->num_trips; i++) { for_each_trip_desc(tz, td) {
if (tz->trips[i].type == THERMAL_TRIP_CRITICAL) { const struct thermal_trip *trip = &td->trip;
*temp = tz->trips[i].temperature;
if (trip->type == THERMAL_TRIP_CRITICAL) {
*temp = trip->temperature;
ret = 0; ret = 0;
break; break;
} }
...@@ -1274,7 +1315,9 @@ thermal_zone_device_register_with_trips(const char *type, ...@@ -1274,7 +1315,9 @@ thermal_zone_device_register_with_trips(const char *type,
const struct thermal_zone_params *tzp, const struct thermal_zone_params *tzp,
int passive_delay, int polling_delay) int passive_delay, int polling_delay)
{ {
const struct thermal_trip *trip = trips;
struct thermal_zone_device *tz; struct thermal_zone_device *tz;
struct thermal_trip_desc *td;
int id; int id;
int result; int result;
struct thermal_governor *governor; struct thermal_governor *governor;
...@@ -1339,7 +1382,8 @@ thermal_zone_device_register_with_trips(const char *type, ...@@ -1339,7 +1382,8 @@ thermal_zone_device_register_with_trips(const char *type,
tz->device.class = thermal_class; tz->device.class = thermal_class;
tz->devdata = devdata; tz->devdata = devdata;
tz->num_trips = num_trips; 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->passive_delay_jiffies, passive_delay);
thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay); thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay);
......
...@@ -15,6 +15,120 @@ ...@@ -15,6 +15,120 @@
#include "thermal_netlink.h" #include "thermal_netlink.h"
#include "thermal_debugfs.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 */ /* Default Thermal Governor */
#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE) #if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)
#define DEFAULT_THERMAL_GOVERNOR "step_wise" #define DEFAULT_THERMAL_GOVERNOR "step_wise"
...@@ -120,8 +234,11 @@ void thermal_governor_update_tz(struct thermal_zone_device *tz, ...@@ -120,8 +234,11 @@ void thermal_governor_update_tz(struct thermal_zone_device *tz,
enum thermal_notify_event reason); enum thermal_notify_event reason);
/* Helpers */ /* Helpers */
#define for_each_trip(__tz, __trip) \ #define for_each_trip_desc(__tz, __td) \
for (__trip = __tz->trips; __trip - __tz->trips < __tz->num_trips; __trip++) 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); void __thermal_zone_set_trips(struct thermal_zone_device *tz);
int thermal_zone_trip_id(const 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, ...@@ -555,7 +555,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
struct tz_episode *tze; struct tz_episode *tze;
struct tz_debugfs *tz_dbg; struct tz_debugfs *tz_dbg;
struct thermal_debugfs *thermal_dbg = tz->debugfs; struct thermal_debugfs *thermal_dbg = tz->debugfs;
int temperature = tz->temperature;
int trip_id = thermal_zone_trip_id(tz, trip); int trip_id = thermal_zone_trip_id(tz, trip);
ktime_t now = ktime_get(); ktime_t now = ktime_get();
...@@ -624,12 +623,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, ...@@ -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 = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
tze->trip_stats[trip_id].timestamp = now; 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: unlock:
mutex_unlock(&thermal_dbg->lock); mutex_unlock(&thermal_dbg->lock);
...@@ -693,12 +686,12 @@ void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, ...@@ -693,12 +686,12 @@ void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
mutex_unlock(&thermal_dbg->lock); 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 thermal_debugfs *thermal_dbg = tz->debugfs;
struct tz_episode *tze;
struct tz_debugfs *tz_dbg; struct tz_debugfs *tz_dbg;
int trip_id, i; struct tz_episode *tze;
int i;
if (!thermal_dbg) if (!thermal_dbg)
return; return;
...@@ -710,15 +703,16 @@ void thermal_debug_update_temp(struct thermal_zone_device *tz) ...@@ -710,15 +703,16 @@ void thermal_debug_update_temp(struct thermal_zone_device *tz)
if (!tz_dbg->nr_trips) if (!tz_dbg->nr_trips)
goto out; goto out;
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 = 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); for (i = 0; i < tz_dbg->nr_trips; i++) {
tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, tz->temperature); int trip_id = tz_dbg->trips_crossed[i];
tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg + struct trip_stats *trip_stats = &tze->trip_stats[trip_id];
(tz->temperature - tze->trip_stats[trip_id].avg) /
tze->trip_stats[trip_id].count; 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: out:
mutex_unlock(&thermal_dbg->lock); mutex_unlock(&thermal_dbg->lock);
...@@ -753,7 +747,7 @@ static int tze_seq_show(struct seq_file *s, void *v) ...@@ -753,7 +747,7 @@ static int tze_seq_show(struct seq_file *s, void *v)
{ {
struct thermal_debugfs *thermal_dbg = s->private; struct thermal_debugfs *thermal_dbg = s->private;
struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz; struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz;
struct thermal_trip *trip; struct thermal_trip_desc *td;
struct tz_episode *tze; struct tz_episode *tze;
const char *type; const char *type;
int trip_id; int trip_id;
...@@ -766,7 +760,14 @@ static int tze_seq_show(struct seq_file *s, void *v) ...@@ -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"); 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 * There is no possible mitigation happening at the
* critical trip point, so the stats will be always * critical trip point, so the stats will be always
...@@ -775,6 +776,13 @@ static int tze_seq_show(struct seq_file *s, void *v) ...@@ -775,6 +776,13 @@ static int tze_seq_show(struct seq_file *s, void *v)
if (trip->type == THERMAL_TRIP_CRITICAL) if (trip->type == THERMAL_TRIP_CRITICAL)
continue; 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) if (trip->type == THERMAL_TRIP_PASSIVE)
type = "passive"; type = "passive";
else if (trip->type == THERMAL_TRIP_ACTIVE) else if (trip->type == THERMAL_TRIP_ACTIVE)
...@@ -782,17 +790,15 @@ static int tze_seq_show(struct seq_file *s, void *v) ...@@ -782,17 +790,15 @@ static int tze_seq_show(struct seq_file *s, void *v)
else else
type = "hot"; type = "hot";
trip_id = thermal_zone_trip_id(tz, trip);
seq_printf(s, "| %*d | %*s | %*d | %*d | %*lld | %*d | %*d | %*d |\n", seq_printf(s, "| %*d | %*s | %*d | %*d | %*lld | %*d | %*d | %*d |\n",
4 , trip_id, 4 , trip_id,
8, type, 8, type,
9, trip->temperature, 9, trip->temperature,
9, trip->hysteresis, 9, trip->hysteresis,
10, ktime_to_ms(tze->trip_stats[trip_id].duration), 10, ktime_to_ms(trip_stats->duration),
9, tze->trip_stats[trip_id].avg, 9, trip_stats->avg,
9, tze->trip_stats[trip_id].min, 9, trip_stats->min,
9, tze->trip_stats[trip_id].max); 9, trip_stats->max);
} }
return 0; return 0;
......
...@@ -11,7 +11,7 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, ...@@ -11,7 +11,7 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
const struct thermal_trip *trip); const struct thermal_trip *trip);
void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
const struct thermal_trip *trip); 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 #else
static inline void thermal_debug_init(void) {} static inline void thermal_debug_init(void) {}
static inline void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) {} 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, ...@@ -24,5 +24,5 @@ static inline void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
const struct thermal_trip *trip) {}; const struct thermal_trip *trip) {};
static inline void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, static inline void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
const struct thermal_trip *trip) {} 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 */ #endif /* CONFIG_THERMAL_DEBUGFS */
...@@ -50,7 +50,7 @@ get_thermal_instance(struct thermal_zone_device *tz, ...@@ -50,7 +50,7 @@ get_thermal_instance(struct thermal_zone_device *tz,
mutex_lock(&tz->lock); mutex_lock(&tz->lock);
mutex_lock(&cdev->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) { list_for_each_entry(pos, &tz->thermal_instances, tz_node) {
if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
...@@ -82,7 +82,7 @@ EXPORT_SYMBOL(get_thermal_instance); ...@@ -82,7 +82,7 @@ EXPORT_SYMBOL(get_thermal_instance);
*/ */
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) 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 crit_temp = INT_MAX;
int ret = -EINVAL; int ret = -EINVAL;
...@@ -91,7 +91,9 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) ...@@ -91,7 +91,9 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
ret = tz->ops.get_temp(tz, temp); ret = tz->ops.get_temp(tz, temp);
if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) { 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) { if (trip->type == THERMAL_TRIP_CRITICAL) {
crit_temp = trip->temperature; crit_temp = trip->temperature;
break; break;
......
...@@ -7,17 +7,13 @@ ...@@ -7,17 +7,13 @@
* Generic netlink for thermal management framework * Generic netlink for thermal management framework
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/notifier.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <net/genetlink.h> #include <net/genetlink.h>
#include <uapi/linux/thermal.h> #include <uapi/linux/thermal.h>
#include "thermal_core.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[] = { static const struct genl_multicast_group thermal_genl_mcgrps[] = {
[THERMAL_GENL_SAMPLING_GROUP] = { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, }, [THERMAL_GENL_SAMPLING_GROUP] = { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
[THERMAL_GENL_EVENT_GROUP] = { .name = THERMAL_GENL_EVENT_GROUP_NAME, }, [THERMAL_GENL_EVENT_GROUP] = { .name = THERMAL_GENL_EVENT_GROUP_NAME, },
...@@ -74,11 +70,12 @@ struct param { ...@@ -74,11 +70,12 @@ struct param {
typedef int (*cb_t)(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) 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 *******************************/ /************************** Sampling encoding *******************************/
...@@ -95,7 +92,7 @@ int thermal_genl_sampling_temp(int id, int temp) ...@@ -95,7 +92,7 @@ int thermal_genl_sampling_temp(int id, int temp)
if (!skb) if (!skb)
return -ENOMEM; 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); THERMAL_GENL_SAMPLING_TEMP);
if (!hdr) if (!hdr)
goto out_free; goto out_free;
...@@ -108,7 +105,7 @@ int thermal_genl_sampling_temp(int id, int temp) ...@@ -108,7 +105,7 @@ int thermal_genl_sampling_temp(int id, int temp)
genlmsg_end(skb, hdr); 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; return 0;
out_cancel: out_cancel:
...@@ -282,7 +279,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event, ...@@ -282,7 +279,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event,
return -ENOMEM; return -ENOMEM;
p->msg = msg; 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) if (!hdr)
goto out_free_msg; goto out_free_msg;
...@@ -292,7 +289,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event, ...@@ -292,7 +289,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event,
genlmsg_end(msg, hdr); 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; return 0;
...@@ -445,7 +442,7 @@ static int thermal_genl_cmd_tz_get_id(struct param *p) ...@@ -445,7 +442,7 @@ static int thermal_genl_cmd_tz_get_id(struct param *p)
static int thermal_genl_cmd_tz_get_trip(struct param *p) static int thermal_genl_cmd_tz_get_trip(struct param *p)
{ {
struct sk_buff *msg = p->msg; struct sk_buff *msg = p->msg;
const struct thermal_trip *trip; const struct thermal_trip_desc *td;
struct thermal_zone_device *tz; struct thermal_zone_device *tz;
struct nlattr *start_trip; struct nlattr *start_trip;
int id; int id;
...@@ -465,7 +462,9 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p) ...@@ -465,7 +462,9 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p)
mutex_lock(&tz->lock); 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, if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID,
thermal_zone_trip_id(tz, trip)) || thermal_zone_trip_id(tz, trip)) ||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) || 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, ...@@ -593,7 +592,7 @@ static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
int ret; int ret;
void *hdr; 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) if (!hdr)
return -EMSGSIZE; return -EMSGSIZE;
...@@ -625,7 +624,7 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb, ...@@ -625,7 +624,7 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb,
return -ENOMEM; return -ENOMEM;
p.msg = msg; 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) if (!hdr)
goto out_free_msg; goto out_free_msg;
...@@ -645,6 +644,27 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb, ...@@ -645,6 +644,27 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb,
return ret; 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[] = { static const struct genl_small_ops thermal_genl_ops[] = {
{ {
.cmd = THERMAL_GENL_CMD_TZ_GET_ID, .cmd = THERMAL_GENL_CMD_TZ_GET_ID,
...@@ -673,12 +693,14 @@ static const struct genl_small_ops thermal_genl_ops[] = { ...@@ -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, .hdrsize = 0,
.name = THERMAL_GENL_FAMILY_NAME, .name = THERMAL_GENL_FAMILY_NAME,
.version = THERMAL_GENL_VERSION, .version = THERMAL_GENL_VERSION,
.maxattr = THERMAL_GENL_ATTR_MAX, .maxattr = THERMAL_GENL_ATTR_MAX,
.policy = thermal_genl_policy, .policy = thermal_genl_policy,
.bind = thermal_genl_bind,
.unbind = thermal_genl_unbind,
.small_ops = thermal_genl_ops, .small_ops = thermal_genl_ops,
.n_small_ops = ARRAY_SIZE(thermal_genl_ops), .n_small_ops = ARRAY_SIZE(thermal_genl_ops),
.resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1, .resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1,
...@@ -686,12 +708,22 @@ static struct genl_family thermal_gnl_family __ro_after_init = { ...@@ -686,12 +708,22 @@ static struct genl_family thermal_gnl_family __ro_after_init = {
.n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps), .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) 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) 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 { ...@@ -10,6 +10,19 @@ struct thermal_genl_cpu_caps {
int efficiency; 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_zone_device;
struct thermal_trip; struct thermal_trip;
struct thermal_cooling_device; struct thermal_cooling_device;
...@@ -18,6 +31,9 @@ struct thermal_cooling_device; ...@@ -18,6 +31,9 @@ struct thermal_cooling_device;
#ifdef CONFIG_THERMAL_NETLINK #ifdef CONFIG_THERMAL_NETLINK
int __init thermal_netlink_init(void); int __init thermal_netlink_init(void);
void __init thermal_netlink_exit(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_create(const struct thermal_zone_device *tz);
int thermal_notify_tz_delete(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); 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) ...@@ -48,6 +64,16 @@ static inline int thermal_notify_tz_create(const struct thermal_zone_device *tz)
return 0; 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) static inline int thermal_notify_tz_delete(const struct thermal_zone_device *tz)
{ {
return 0; return 0;
......
...@@ -88,7 +88,7 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr, ...@@ -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) if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1)
return -EINVAL; return -EINVAL;
switch (tz->trips[trip_id].type) { switch (tz->trips[trip_id].trip.type) {
case THERMAL_TRIP_CRITICAL: case THERMAL_TRIP_CRITICAL:
return sprintf(buf, "critical\n"); return sprintf(buf, "critical\n");
case THERMAL_TRIP_HOT: case THERMAL_TRIP_HOT:
...@@ -120,7 +120,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr, ...@@ -120,7 +120,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
mutex_lock(&tz->lock); mutex_lock(&tz->lock);
trip = &tz->trips[trip_id]; trip = &tz->trips[trip_id].trip;
if (temp != trip->temperature) { if (temp != trip->temperature) {
if (tz->ops.set_trip_temp) { if (tz->ops.set_trip_temp) {
...@@ -150,7 +150,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr, ...@@ -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) if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
return -EINVAL; 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 static ssize_t
...@@ -171,7 +171,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, ...@@ -171,7 +171,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
mutex_lock(&tz->lock); mutex_lock(&tz->lock);
trip = &tz->trips[trip_id]; trip = &tz->trips[trip_id].trip;
if (hyst != trip->hysteresis) { if (hyst != trip->hysteresis) {
trip->hysteresis = hyst; trip->hysteresis = hyst;
...@@ -194,7 +194,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr, ...@@ -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) if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
return -EINVAL; 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 static ssize_t
...@@ -393,7 +393,7 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = { ...@@ -393,7 +393,7 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = {
*/ */
static int create_trip_attrs(struct thermal_zone_device *tz) static int create_trip_attrs(struct thermal_zone_device *tz)
{ {
const struct thermal_trip *trip; const struct thermal_trip_desc *td;
struct attribute **attrs; struct attribute **attrs;
/* This function works only for zones with at least one trip */ /* 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) ...@@ -429,8 +429,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz)
return -ENOMEM; return -ENOMEM;
} }
for_each_trip(tz, trip) { for_each_trip_desc(tz, td) {
int indx = thermal_zone_trip_id(tz, trip); int indx = thermal_zone_trip_id(tz, &td->trip);
/* create trip type attribute */ /* create trip type attribute */
snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH,
...@@ -452,7 +452,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz) ...@@ -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].name;
tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO;
tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; 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.attr.mode |= S_IWUSR;
tz->trip_temp_attrs[indx].attr.store = tz->trip_temp_attrs[indx].attr.store =
trip_point_temp_store; trip_point_temp_store;
...@@ -467,7 +467,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz) ...@@ -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].name;
tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO;
tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; 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.attr.mode |= S_IWUSR;
tz->trip_hyst_attrs[indx].attr.store = tz->trip_hyst_attrs[indx].attr.store =
trip_point_hyst_store; trip_point_hyst_store;
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include <linux/thermal.h> #include <linux/thermal.h>
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include "thermal_core.h"
TRACE_DEFINE_ENUM(THERMAL_TRIP_CRITICAL); TRACE_DEFINE_ENUM(THERMAL_TRIP_CRITICAL);
TRACE_DEFINE_ENUM(THERMAL_TRIP_HOT); TRACE_DEFINE_ENUM(THERMAL_TRIP_HOT);
TRACE_DEFINE_ENUM(THERMAL_TRIP_PASSIVE); TRACE_DEFINE_ENUM(THERMAL_TRIP_PASSIVE);
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include "thermal_core.h"
TRACE_EVENT(thermal_power_allocator, TRACE_EVENT(thermal_power_allocator,
TP_PROTO(struct thermal_zone_device *tz, u32 total_req_power, TP_PROTO(struct thermal_zone_device *tz, u32 total_req_power,
u32 total_granted_power, int num_actors, u32 power_range, u32 total_granted_power, int num_actors, u32 power_range,
......
...@@ -13,11 +13,11 @@ int for_each_thermal_trip(struct thermal_zone_device *tz, ...@@ -13,11 +13,11 @@ int for_each_thermal_trip(struct thermal_zone_device *tz,
int (*cb)(struct thermal_trip *, void *), int (*cb)(struct thermal_trip *, void *),
void *data) void *data)
{ {
struct thermal_trip *trip; struct thermal_trip_desc *td;
int ret; int ret;
for_each_trip(tz, trip) { for_each_trip_desc(tz, td) {
ret = cb(trip, data); ret = cb(&td->trip, data);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -63,7 +63,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips); ...@@ -63,7 +63,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips);
*/ */
void __thermal_zone_set_trips(struct thermal_zone_device *tz) 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 low = -INT_MAX, high = INT_MAX;
int ret; int ret;
...@@ -72,7 +72,8 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz) ...@@ -72,7 +72,8 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz)
if (!tz->ops.set_trips) if (!tz->ops.set_trips)
return; return;
for_each_trip(tz, trip) { for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
int trip_low; int trip_low;
trip_low = trip->temperature - trip->hysteresis; trip_low = trip->temperature - trip->hysteresis;
...@@ -110,7 +111,7 @@ int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, ...@@ -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) if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip)
return -EINVAL; return -EINVAL;
*trip = tz->trips[trip_id]; *trip = tz->trips[trip_id].trip;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(__thermal_zone_get_trip); EXPORT_SYMBOL_GPL(__thermal_zone_get_trip);
...@@ -135,7 +136,7 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, ...@@ -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 * Assume the trip to be located within the bounds of the thermal
* zone's trips[] table. * 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, void thermal_zone_trip_updated(struct thermal_zone_device *tz,
const struct thermal_trip *trip) const struct thermal_trip *trip)
......
...@@ -61,7 +61,6 @@ enum thermal_notify_event { ...@@ -61,7 +61,6 @@ enum thermal_notify_event {
* struct thermal_trip - representation of a point in temperature domain * struct thermal_trip - representation of a point in temperature domain
* @temperature: temperature value in miliCelsius * @temperature: temperature value in miliCelsius
* @hysteresis: relative hysteresis in miliCelsius * @hysteresis: relative hysteresis in miliCelsius
* @threshold: trip crossing notification threshold miliCelsius
* @type: trip point type * @type: trip point type
* @priv: pointer to driver data associated with this trip * @priv: pointer to driver data associated with this trip
* @flags: flags representing binary properties of the trip * @flags: flags representing binary properties of the trip
...@@ -69,7 +68,6 @@ enum thermal_notify_event { ...@@ -69,7 +68,6 @@ enum thermal_notify_event {
struct thermal_trip { struct thermal_trip {
int temperature; int temperature;
int hysteresis; int hysteresis;
int threshold;
enum thermal_trip_type type; enum thermal_trip_type type;
u8 flags; u8 flags;
void *priv; void *priv;
...@@ -81,6 +79,8 @@ struct thermal_trip { ...@@ -81,6 +79,8 @@ struct thermal_trip {
#define THERMAL_TRIP_FLAG_RW (THERMAL_TRIP_FLAG_RW_TEMP | \ #define THERMAL_TRIP_FLAG_RW (THERMAL_TRIP_FLAG_RW_TEMP | \
THERMAL_TRIP_FLAG_RW_HYST) THERMAL_TRIP_FLAG_RW_HYST)
struct thermal_zone_device;
struct thermal_zone_device_ops { struct thermal_zone_device_ops {
int (*bind) (struct thermal_zone_device *, int (*bind) (struct thermal_zone_device *,
struct thermal_cooling_device *); struct thermal_cooling_device *);
...@@ -126,111 +126,6 @@ struct thermal_cooling_device { ...@@ -126,111 +126,6 @@ struct thermal_cooling_device {
#endif #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 */ /* Structure to define Thermal Zone parameters */
struct thermal_zone_params { struct thermal_zone_params {
const char *governor_name; const char *governor_name;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment