drivers: thermal: snapshot of thermal core from msm-4.14

Add a snapshot of thermal core framework changes from msm-4.14.
This is snapshot as of 'commit <0a56f56a528f> ("drivers: thermal:
virtual-sensor: Add new virtual sensors for SDMMAGPIE")'.

Change-Id: Ib0c7a15fadc095fe97c1d7efb4ea7527384c2782
Signed-off-by: Ram Chandrasekar <rkumbako@codeaurora.org>
This commit is contained in:
Ram Chandrasekar 2018-10-08 14:49:17 -06:00 committed by Manaf Meethalavalappu Pallikunhi
parent aabf400fc4
commit 8a12149c26
12 changed files with 1231 additions and 108 deletions

View file

@ -142,6 +142,16 @@ config THERMAL_GOV_USER_SPACE
help
Enable this to let the user space manage the platform thermals.
config THERMAL_GOV_LOW_LIMITS
bool "Low limits mitigation governor"
help
Enable this to manage platform limits using low limits
governor.
Enable this governor to monitor and trigger floor mitigation.
This governor will monitor the limits going below a
trip threshold to trigger a floor mitigation.
config THERMAL_GOV_POWER_ALLOCATOR
bool "Power allocator thermal governor"
help

View file

@ -16,6 +16,7 @@ thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG) += gov_bang_bang.o
thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o
thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o
thermal_sys-$(CONFIG_THERMAL_GOV_LOW_LIMITS) += gov_low_limits.o
thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += power_allocator.o
# cpufreq cooling
@ -54,7 +55,7 @@ obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o
obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o
obj-$(CONFIG_ST_THERMAL) += st/
obj-$(CONFIG_QCOM_TSENS) += qcom/
obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-y += tegra/
obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o

View file

@ -0,0 +1,130 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Intel Corp
* Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*/
#include <linux/thermal.h>
#include <trace/events/thermal.h>
#include "thermal_core.h"
static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
{
int trip_temp, trip_hyst;
enum thermal_trip_type trip_type;
struct thermal_instance *instance;
bool throttle;
int old_target;
tz->ops->get_trip_temp(tz, trip, &trip_temp);
tz->ops->get_trip_type(tz, trip, &trip_type);
if (tz->ops->get_trip_hyst) {
tz->ops->get_trip_hyst(tz, trip, &trip_hyst);
trip_hyst = trip_temp + trip_hyst;
} else {
trip_hyst = trip_temp;
}
mutex_lock(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != trip)
continue;
if ((tz->temperature <= trip_temp) ||
(instance->target != THERMAL_NO_TARGET
&& tz->temperature < trip_hyst))
throttle = true;
else
throttle = false;
dev_dbg(&tz->device,
"Trip%d[type=%d,temp=%d,hyst=%d],throttle=%d\n",
trip, trip_type, trip_temp, trip_hyst, throttle);
old_target = instance->target;
instance->target = (throttle) ? instance->upper
: THERMAL_NO_TARGET;
dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
old_target, (int)instance->target);
if (instance->initialized && old_target == instance->target)
continue;
if (!instance->initialized) {
if (instance->target != THERMAL_NO_TARGET) {
trace_thermal_zone_trip(tz, trip, trip_type,
true);
tz->passive += 1;
}
} else {
if (old_target == THERMAL_NO_TARGET &&
instance->target != THERMAL_NO_TARGET) {
trace_thermal_zone_trip(tz, trip, trip_type,
true);
tz->passive += 1;
} else if (old_target != THERMAL_NO_TARGET &&
instance->target == THERMAL_NO_TARGET) {
trace_thermal_zone_trip(tz, trip, trip_type,
false);
tz->passive -= 1;
}
}
instance->initialized = true;
instance->cdev->updated = false; /* cdev needs update */
}
mutex_unlock(&tz->lock);
}
/**
* low_limits_throttle - throttles devices associated with the given zone
* @tz - thermal_zone_device
* @trip - the trip point
*
* Throttling Logic: If the sensor reading goes below a trip point, the
* pre-defined mitigation will be applied for the cooling device.
* If the sensor reading goes above the trip hysteresis, the
* mitigation will be removed.
*/
static int low_limits_throttle(struct thermal_zone_device *tz, int trip)
{
struct thermal_instance *instance;
thermal_zone_trip_update(tz, trip);
mutex_lock(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
thermal_cdev_update(instance->cdev);
mutex_unlock(&tz->lock);
return 0;
}
static struct thermal_governor thermal_gov_low_limits_floor = {
.name = "low_limits_floor",
.throttle = low_limits_throttle,
.min_state_throttle = 1,
};
static struct thermal_governor thermal_gov_low_limits_cap = {
.name = "low_limits_cap",
.throttle = low_limits_throttle,
};
int thermal_gov_low_limits_register(void)
{
thermal_register_governor(&thermal_gov_low_limits_cap);
return thermal_register_governor(&thermal_gov_low_limits_floor);
}
void thermal_gov_low_limits_unregister(void)
{
thermal_unregister_governor(&thermal_gov_low_limits_cap);
thermal_unregister_governor(&thermal_gov_low_limits_floor);
}

View file

@ -13,6 +13,10 @@
#include <linux/err.h>
#include <linux/export.h>
#include <linux/string.h>
#include <linux/list.h>
#define CREATE_TRACE_POINTS
#include <trace/events/thermal_virtual.h>
#include "thermal_core.h"
@ -35,6 +39,23 @@ struct __thermal_bind_params {
unsigned long max;
};
/**
* struct __sensor_param - Holds individual sensor data
* @sensor_data: sensor driver private data passed as input argument
* @ops: sensor driver ops
* @trip_high: last trip high value programmed in the sensor driver
* @trip_low: last trip low value programmed in the sensor driver
* @lock: mutex lock acquired before updating the trip temperatures
* @first_tz: list head pointing the first thermal zone
*/
struct __sensor_param {
void *sensor_data;
const struct thermal_zone_of_device_ops *ops;
int trip_high, trip_low;
struct mutex lock;
struct list_head first_tz;
};
/**
* struct __thermal_zone - internal representation of a thermal zone
* @mode: current thermal zone device mode (enabled/disabled)
@ -42,12 +63,14 @@ struct __thermal_bind_params {
* @polling_delay: zone polling interval
* @slope: slope of the temperature adjustment curve
* @offset: offset of the temperature adjustment curve
* @default_disable: Keep the thermal zone disabled by default
* @tzd: thermal zone device pointer for this sensor
* @ntrips: number of trip points
* @trips: an array of trip points (0..ntrips - 1)
* @num_tbps: number of thermal bind params
* @tbps: an array of thermal bind params (0..num_tbps - 1)
* @sensor_data: sensor private data used while reading temperature and trend
* @ops: set of callbacks to handle the thermal zone based on DT
* @list: sibling thermal zone pointer
* @senps: sensor related parameters
*/
struct __thermal_zone {
@ -56,6 +79,8 @@ struct __thermal_zone {
int polling_delay;
int slope;
int offset;
struct thermal_zone_device *tzd;
bool default_disable;
/* trip data */
int ntrips;
@ -65,35 +90,124 @@ struct __thermal_zone {
int num_tbps;
struct __thermal_bind_params *tbps;
struct list_head list;
/* sensor interface */
void *sensor_data;
const struct thermal_zone_of_device_ops *ops;
struct __sensor_param *senps;
};
/**
* struct virtual_sensor - internal representation of a virtual thermal zone
* @num_sensors - number of sensors this virtual sensor will reference to
* estimate temperature
* @tz - Array of thermal zones of the sensors this virtual sensor will use
* to estimate temperature
* @virt_tz - Virtual thermal zone pointer
* @logic - aggregation logic to be used to estimate the temperature
* @last_reading - last estimated temperature
* @coefficients - array of coefficients to be used for weighted aggregation
* logic
* @avg_offset - offset value to be used for the weighted aggregation logic
* @avg_denominator - denominator value to be used for the weighted aggregation
* logic
*/
struct virtual_sensor {
int num_sensors;
struct thermal_zone_device *tz[THERMAL_MAX_VIRT_SENSORS];
struct thermal_zone_device *virt_tz;
enum aggregation_logic logic;
int last_reading;
int coefficients[THERMAL_MAX_VIRT_SENSORS];
int avg_offset;
int avg_denominator;
};
static int of_thermal_aggregate_trip_types(struct thermal_zone_device *tz,
unsigned int trip_type_mask, int *low, int *high);
/*** DT thermal zone device callbacks ***/
static int virt_sensor_read_temp(void *data, int *val)
{
struct virtual_sensor *sens = data;
int idx, temp = 0, ret = 0;
for (idx = 0; idx < sens->num_sensors; idx++) {
int sens_temp = 0;
ret = thermal_zone_get_temp(sens->tz[idx], &sens_temp);
if (ret) {
pr_err("virt zone: sensor[%s] read error:%d\n",
sens->tz[idx]->type, ret);
return ret;
}
switch (sens->logic) {
case VIRT_WEIGHTED_AVG:
temp += sens_temp * sens->coefficients[idx];
if (idx == (sens->num_sensors - 1))
temp = (temp + sens->avg_offset)
/ sens->avg_denominator;
break;
case VIRT_MAXIMUM:
if (idx == 0)
temp = INT_MIN;
if (sens_temp > temp)
temp = sens_temp;
break;
case VIRT_MINIMUM:
if (idx == 0)
temp = INT_MAX;
if (sens_temp < temp)
temp = sens_temp;
break;
default:
break;
}
trace_virtual_temperature(sens->virt_tz, sens->tz[idx],
sens_temp, temp);
}
sens->last_reading = *val = temp;
return 0;
}
static int of_thermal_get_temp(struct thermal_zone_device *tz,
int *temp)
{
struct __thermal_zone *data = tz->devdata;
if (!data->ops->get_temp)
if (!data->senps || !data->senps->ops->get_temp)
return -EINVAL;
if (data->mode == THERMAL_DEVICE_DISABLED) {
*temp = tz->tzp->tracks_low ?
THERMAL_TEMP_INVALID_LOW :
THERMAL_TEMP_INVALID;
return 0;
}
return data->ops->get_temp(data->sensor_data, temp);
return data->senps->ops->get_temp(data->senps->sensor_data, temp);
}
static int of_thermal_set_trips(struct thermal_zone_device *tz,
int low, int high)
int inp_low, int inp_high)
{
struct __thermal_zone *data = tz->devdata;
int high = INT_MAX, low = INT_MIN, ret = 0;
if (!data->ops || !data->ops->set_trips)
if (!data->senps || !data->senps->ops->set_trips)
return -EINVAL;
return data->ops->set_trips(data->sensor_data, low, high);
}
mutex_lock(&data->senps->lock);
of_thermal_aggregate_trip_types(tz, GENMASK(THERMAL_TRIP_CRITICAL, 0),
&low, &high);
data->senps->trip_low = low;
data->senps->trip_high = high;
ret = data->senps->ops->set_trips(data->senps->sensor_data,
low, high);
mutex_unlock(&data->senps->lock);
return ret;
}
/**
* of_thermal_get_ntrips - function to export number of available trip
* points.
@ -174,7 +288,10 @@ static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
{
struct __thermal_zone *data = tz->devdata;
return data->ops->set_emul_temp(data->sensor_data, temp);
if (!data->senps || !data->senps->ops->set_emul_temp)
return -EINVAL;
return data->senps->ops->set_emul_temp(data->senps->sensor_data, temp);
}
static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
@ -182,10 +299,11 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
{
struct __thermal_zone *data = tz->devdata;
if (!data->ops->get_trend)
if (!data->senps || !data->senps->ops->get_trend)
return -EINVAL;
return data->ops->get_trend(data->sensor_data, trip, trend);
return data->senps->ops->get_trend(data->senps->sensor_data,
trip, trend);
}
static int of_thermal_bind(struct thermal_zone_device *thermal,
@ -297,7 +415,16 @@ static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
if (trip >= data->ntrips || trip < 0)
return -EDOM;
*temp = data->trips[trip].temperature;
if (data->senps && data->senps->ops->get_trip_temp) {
int ret;
ret = data->senps->ops->get_trip_temp(data->senps->sensor_data,
trip, temp);
if (ret)
return ret;
} else {
*temp = data->trips[trip].temperature;
}
return 0;
}
@ -310,10 +437,11 @@ static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
if (trip >= data->ntrips || trip < 0)
return -EDOM;
if (data->ops->set_trip_temp) {
if (data->senps && data->senps->ops->set_trip_temp) {
int ret;
ret = data->ops->set_trip_temp(data->sensor_data, trip, temp);
ret = data->senps->ops->set_trip_temp(data->senps->sensor_data,
trip, temp);
if (ret)
return ret;
}
@ -366,6 +494,93 @@ static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
return -EINVAL;
}
static int of_thermal_aggregate_trip_types(struct thermal_zone_device *tz,
unsigned int trip_type_mask, int *low, int *high)
{
int min = INT_MIN;
int max = INT_MAX;
int tt, th, trip;
int temp = tz->temperature;
struct thermal_zone_device *zone = NULL;
struct __thermal_zone *data = tz->devdata;
struct list_head *head;
enum thermal_trip_type type = 0;
head = &data->senps->first_tz;
list_for_each_entry(data, head, list) {
zone = data->tzd;
if (data->mode == THERMAL_DEVICE_DISABLED)
continue;
for (trip = 0; trip < data->ntrips; trip++) {
of_thermal_get_trip_type(zone, trip, &type);
if (!(BIT(type) & trip_type_mask))
continue;
if (!zone->tzp->tracks_low) {
tt = data->trips[trip].temperature;
if (tt > temp && tt < max)
max = tt;
th = tt - data->trips[trip].hysteresis;
if (th < temp && th > min)
min = th;
} else {
tt = data->trips[trip].temperature;
if (tt < temp && tt > min)
min = tt;
th = tt + data->trips[trip].hysteresis;
if (th > temp && th < max)
max = th;
}
}
}
*high = max;
*low = min;
return 0;
}
/*
* of_thermal_aggregate_trip - aggregate trip temperatures across sibling
* thermal zones.
* @tz: pointer to the primary thermal zone.
* @type: the thermal trip type to be aggregated upon
* @low: the low trip threshold which the most lesser than the @temp
* @high: the high trip threshold which is the least greater than the @temp
*/
int of_thermal_aggregate_trip(struct thermal_zone_device *tz,
enum thermal_trip_type type,
int *low, int *high)
{
if (type <= THERMAL_TRIP_CRITICAL)
return of_thermal_aggregate_trip_types(tz, BIT(type), low,
high);
return -EINVAL;
}
EXPORT_SYMBOL(of_thermal_aggregate_trip);
/*
* of_thermal_handle_trip - Handle thermal trip from sensors
*
* @tz: pointer to the primary thermal zone.
*/
void of_thermal_handle_trip(struct thermal_zone_device *tz)
{
struct thermal_zone_device *zone;
struct __thermal_zone *data = tz->devdata;
struct list_head *head;
head = &data->senps->first_tz;
list_for_each_entry(data, head, list) {
zone = data->tzd;
if (data->mode == THERMAL_DEVICE_DISABLED)
continue;
thermal_zone_device_update(zone, THERMAL_EVENT_UNSPECIFIED);
}
}
EXPORT_SYMBOL(of_thermal_handle_trip);
static struct thermal_zone_device_ops of_thermal_ops = {
.get_mode = of_thermal_get_mode,
.set_mode = of_thermal_set_mode,
@ -381,12 +596,16 @@ static struct thermal_zone_device_ops of_thermal_ops = {
.unbind = of_thermal_unbind,
};
static struct thermal_zone_of_device_ops of_virt_ops = {
.get_temp = virt_sensor_read_temp,
};
/*** sensor API ***/
static struct thermal_zone_device *
thermal_zone_of_add_sensor(struct device_node *zone,
struct device_node *sensor, void *data,
const struct thermal_zone_of_device_ops *ops)
struct device_node *sensor,
struct __sensor_param *sens_param)
{
struct thermal_zone_device *tzd;
struct __thermal_zone *tz;
@ -397,12 +616,11 @@ thermal_zone_of_add_sensor(struct device_node *zone,
tz = tzd->devdata;
if (!ops)
if (!sens_param->ops)
return ERR_PTR(-EINVAL);
mutex_lock(&tzd->lock);
tz->ops = ops;
tz->sensor_data = data;
tz->senps = sens_param;
tzd->ops->get_temp = of_thermal_get_temp;
tzd->ops->get_trend = of_thermal_get_trend;
@ -411,12 +629,13 @@ thermal_zone_of_add_sensor(struct device_node *zone,
* The thermal zone core will calculate the window if they have set the
* optional set_trips pointer.
*/
if (ops->set_trips)
if (sens_param->ops->set_trips)
tzd->ops->set_trips = of_thermal_set_trips;
if (ops->set_emul_temp)
if (sens_param->ops->set_emul_temp)
tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
list_add_tail(&tz->list, &sens_param->first_tz);
mutex_unlock(&tzd->lock);
return tzd;
@ -451,7 +670,9 @@ thermal_zone_of_add_sensor(struct device_node *zone,
* that refer to it.
*
* Return: On success returns a valid struct thermal_zone_device,
* otherwise, it returns a corresponding ERR_PTR(). Caller must
* otherwise, it returns a corresponding ERR_PTR(). Incase there are multiple
* thermal zones referencing the same sensor, the return value will be
* thermal_zone_device pointer of the first thermal zone. Caller must
* check the return value with help of IS_ERR() helper.
*/
struct thermal_zone_device *
@ -460,6 +681,8 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
{
struct device_node *np, *child, *sensor_np;
struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
struct thermal_zone_device *first_tzd = NULL;
struct __sensor_param *sens_param = NULL;
np = of_find_node_by_name(NULL, "thermal-zones");
if (!np)
@ -470,11 +693,23 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
return ERR_PTR(-EINVAL);
}
sens_param = kzalloc(sizeof(*sens_param), GFP_KERNEL);
if (!sens_param) {
of_node_put(np);
return ERR_PTR(-ENOMEM);
}
sens_param->sensor_data = data;
sens_param->ops = ops;
INIT_LIST_HEAD(&sens_param->first_tz);
sens_param->trip_high = INT_MAX;
sens_param->trip_low = INT_MIN;
mutex_init(&sens_param->lock);
sensor_np = of_node_get(dev->of_node);
for_each_available_child_of_node(np, child) {
struct of_phandle_args sensor_specs;
int ret, id;
struct __thermal_zone *tz;
/* For now, thermal framework supports only 1 sensor per zone */
ret = of_parse_phandle_with_args(child, "thermal-sensors",
@ -494,21 +729,26 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
if (sensor_specs.np == sensor_np && id == sensor_id) {
tzd = thermal_zone_of_add_sensor(child, sensor_np,
data, ops);
if (!IS_ERR(tzd))
tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
of_node_put(sensor_specs.np);
of_node_put(child);
goto exit;
sens_param);
if (!IS_ERR(tzd)) {
if (!first_tzd)
first_tzd = tzd;
tz = tzd->devdata;
if (!tz->default_disable)
tzd->ops->set_mode(tzd,
THERMAL_DEVICE_ENABLED);
}
}
of_node_put(sensor_specs.np);
}
exit:
of_node_put(sensor_np);
of_node_put(np);
return tzd;
if (!first_tzd) {
first_tzd = ERR_PTR(-ENODEV);
kfree(sens_param);
}
return first_tzd;
}
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
@ -530,7 +770,9 @@ EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
void thermal_zone_of_sensor_unregister(struct device *dev,
struct thermal_zone_device *tzd)
{
struct __thermal_zone *tz;
struct __thermal_zone *tz, *next;
struct thermal_zone_device *pos;
struct list_head *head;
if (!dev || !tzd || !tzd->devdata)
return;
@ -541,14 +783,20 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
if (!tz)
return;
mutex_lock(&tzd->lock);
tzd->ops->get_temp = NULL;
tzd->ops->get_trend = NULL;
tzd->ops->set_emul_temp = NULL;
head = &tz->senps->first_tz;
list_for_each_entry_safe(tz, next, head, list) {
pos = tz->tzd;
mutex_lock(&pos->lock);
pos->ops->get_temp = NULL;
pos->ops->get_trend = NULL;
pos->ops->set_emul_temp = NULL;
tz->ops = NULL;
tz->sensor_data = NULL;
mutex_unlock(&tzd->lock);
list_del(&tz->list);
if (list_empty(&tz->senps->first_tz))
kfree(tz->senps);
tz->senps = NULL;
mutex_unlock(&pos->lock);
}
}
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
@ -569,6 +817,136 @@ static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
return *r == data;
}
/**
* devm_thermal_of_virtual_sensor_register - Register a virtual sensor.
* Three types of virtual sensors are supported.
* 1. Weighted aggregation type:
* Virtual sensor of this type calculates the weighted aggregation
* of sensor temperatures using the below formula,
* temp = (sensor_1_temp * coeff_1 + ... + sensor_n_temp * coeff_n)
* + avg_offset / avg_denominator
* So the sensor drivers has to specify n+2 coefficients.
* 2. Maximum type:
* Virtual sensors of this type will report the maximum of all
* sensor temperatures.
* 3. Minimum type:
* Virtual sensors of this type will report the minimum of all
* sensor temperatures.
*
* @input arguments:
* @dev: Virtual sensor driver device pointer.
* @sensor_data: Virtual sensor data supported for the device.
*
* @return: Returns a virtual thermal zone pointer. Returns error if thermal
* zone is not created. Returns -EAGAIN, if the sensor that is required for
* this virtual sensor temperature estimation is not registered yet. The
* sensor driver can try again later.
*/
struct thermal_zone_device *devm_thermal_of_virtual_sensor_register(
struct device *dev,
const struct virtual_sensor_data *sensor_data)
{
int sens_idx = 0;
struct virtual_sensor *sens;
struct __thermal_zone *tz;
struct thermal_zone_device **ptr;
struct thermal_zone_device *tzd;
struct __sensor_param *sens_param = NULL;
enum thermal_device_mode mode;
if (!dev || !sensor_data)
return ERR_PTR(-EINVAL);
tzd = thermal_zone_get_zone_by_name(
sensor_data->virt_zone_name);
if (IS_ERR(tzd)) {
dev_dbg(dev, "sens:%s not available err: %ld\n",
sensor_data->virt_zone_name,
PTR_ERR(tzd));
return tzd;
}
mutex_lock(&tzd->lock);
/*
* Check if the virtual zone is registered and enabled.
* If so return the registered thermal zone.
*/
tzd->ops->get_mode(tzd, &mode);
mutex_unlock(&tzd->lock);
if (mode == THERMAL_DEVICE_ENABLED)
return tzd;
sens = devm_kzalloc(dev, sizeof(*sens), GFP_KERNEL);
if (!sens)
return ERR_PTR(-ENOMEM);
sens->virt_tz = tzd;
sens->logic = sensor_data->logic;
sens->num_sensors = sensor_data->num_sensors;
if (sens->logic == VIRT_WEIGHTED_AVG) {
int coeff_ct = sensor_data->coefficient_ct;
/*
* For weighted aggregation, sensor drivers has to specify
* n+2 coefficients.
*/
if (coeff_ct != sens->num_sensors) {
dev_err(dev, "sens:%s Invalid coefficient\n",
sensor_data->virt_zone_name);
return ERR_PTR(-EINVAL);
}
memcpy(sens->coefficients, sensor_data->coefficients,
coeff_ct * sizeof(*sens->coefficients));
sens->avg_offset = sensor_data->avg_offset;
sens->avg_denominator = sensor_data->avg_denominator;
}
for (sens_idx = 0; sens_idx < sens->num_sensors; sens_idx++) {
sens->tz[sens_idx] = thermal_zone_get_zone_by_name(
sensor_data->sensor_names[sens_idx]);
if (IS_ERR(sens->tz[sens_idx])) {
dev_err(dev, "sens:%s sensor[%s] fetch err:%ld\n",
sensor_data->virt_zone_name,
sensor_data->sensor_names[sens_idx],
PTR_ERR(sens->tz[sens_idx]));
break;
}
}
if (sens->num_sensors != sens_idx)
return ERR_PTR(-EAGAIN);
sens_param = kzalloc(sizeof(*sens_param), GFP_KERNEL);
if (!sens_param)
return ERR_PTR(-ENOMEM);
sens_param->sensor_data = sens;
sens_param->ops = &of_virt_ops;
INIT_LIST_HEAD(&sens_param->first_tz);
sens_param->trip_high = INT_MAX;
sens_param->trip_low = INT_MIN;
mutex_init(&sens_param->lock);
mutex_lock(&tzd->lock);
tz = tzd->devdata;
tz->senps = sens_param;
tzd->ops->get_temp = of_thermal_get_temp;
list_add_tail(&tz->list, &sens_param->first_tz);
mutex_unlock(&tzd->lock);
ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
*ptr = tzd;
devres_add(dev, ptr);
if (!tz->default_disable)
tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
return tzd;
}
EXPORT_SYMBOL(devm_thermal_of_virtual_sensor_register);
/**
* devm_thermal_zone_of_sensor_register - Resource managed version of
* thermal_zone_of_sensor_register()
@ -817,6 +1195,7 @@ __init *thermal_of_build_thermal_zone(struct device_node *np)
if (!tz)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&tz->list);
ret = of_property_read_u32(np, "polling-delay-passive", &prop);
if (ret < 0) {
pr_err("missing polling-delay-passive property\n");
@ -831,6 +1210,8 @@ __init *thermal_of_build_thermal_zone(struct device_node *np)
}
tz->polling_delay = prop;
tz->default_disable = of_property_read_bool(np,
"disable-thermal-zone");
/*
* REVIST: for now, the thermal framework supports only
* one sensor per thermal zone. Thus, we are considering
@ -960,6 +1341,7 @@ int __init of_parse_thermal_zones(void)
struct thermal_zone_params *tzp;
int i, mask = 0;
u32 prop;
const char *governor_name;
tz = thermal_of_build_thermal_zone(child);
if (IS_ERR(tz)) {
@ -982,6 +1364,11 @@ int __init of_parse_thermal_zones(void)
/* No hwmon because there might be hwmon drivers registering */
tzp->no_hwmon = true;
if (!of_property_read_string(child, "thermal-governor",
&governor_name))
strlcpy(tzp->governor_name, governor_name,
THERMAL_NAME_LENGTH);
if (!of_property_read_u32(child, "sustainable-power", &prop))
tzp->sustainable_power = prop;
@ -992,6 +1379,9 @@ int __init of_parse_thermal_zones(void)
tzp->slope = tz->slope;
tzp->offset = tz->offset;
if (of_property_read_bool(child, "tracks-low"))
tzp->tracks_low = true;
zone = thermal_zone_device_register(child->name, tz->ntrips,
mask, tz,
ops, tzp,
@ -1004,7 +1394,9 @@ int __init of_parse_thermal_zones(void)
kfree(ops);
of_thermal_free_zone(tz);
/* attempting to build remaining zones still */
continue;
}
tz->tzd = zone;
}
of_node_put(np);

View file

@ -127,7 +127,7 @@ static void update_passive_instance(struct thermal_zone_device *tz,
static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
{
int trip_temp;
int trip_temp, hyst_temp;
enum thermal_trip_type trip_type;
enum thermal_trend trend;
struct thermal_instance *instance;
@ -135,20 +135,21 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
int old_target;
if (trip == THERMAL_TRIPS_NONE) {
trip_temp = tz->forced_passive;
hyst_temp = trip_temp = tz->forced_passive;
trip_type = THERMAL_TRIPS_NONE;
} else {
tz->ops->get_trip_temp(tz, trip, &trip_temp);
if (tz->ops->get_trip_hyst) {
tz->ops->get_trip_hyst(tz, trip, &hyst_temp);
hyst_temp = trip_temp - hyst_temp;
} else {
hyst_temp = trip_temp;
}
tz->ops->get_trip_type(tz, trip, &trip_type);
}
trend = get_tz_trend(tz, trip);
if (tz->temperature >= trip_temp) {
throttle = true;
trace_thermal_zone_trip(tz, trip, trip_type);
}
dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
trip, trip_type, trip_temp, trend, throttle);
@ -159,6 +160,20 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
continue;
old_target = instance->target;
/*
* Step wise has to lower the mitigation only if the
* temperature goes below the hysteresis temperature.
* Atleast, it has to hold on to mitigation device lower
* limit if the temperature is above the hysteresis
* temperature.
*/
if (tz->temperature >= trip_temp ||
(tz->temperature > hyst_temp &&
old_target != THERMAL_NO_TARGET))
throttle = true;
else
throttle = false;
instance->target = get_target_state(instance, trend, throttle);
dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
old_target, (int)instance->target);
@ -166,14 +181,27 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
if (instance->initialized && old_target == instance->target)
continue;
/* Activate a passive thermal instance */
if (old_target == THERMAL_NO_TARGET &&
instance->target != THERMAL_NO_TARGET)
update_passive_instance(tz, trip_type, 1);
/* Deactivate a passive thermal instance */
else if (old_target != THERMAL_NO_TARGET &&
instance->target == THERMAL_NO_TARGET)
update_passive_instance(tz, trip_type, -1);
if (!instance->initialized) {
if (instance->target != THERMAL_NO_TARGET) {
trace_thermal_zone_trip(tz, trip, trip_type,
true);
update_passive_instance(tz, trip_type, 1);
}
} else {
/* Activate a passive thermal instance */
if (old_target == THERMAL_NO_TARGET &&
instance->target != THERMAL_NO_TARGET) {
trace_thermal_zone_trip(tz, trip, trip_type,
true);
update_passive_instance(tz, trip_type, 1);
/* Deactivate a passive thermal instance */
} else if (old_target != THERMAL_NO_TARGET &&
instance->target == THERMAL_NO_TARGET) {
trace_thermal_zone_trip(tz, trip, trip_type,
false);
update_passive_instance(tz, trip_type, -1);
}
}
instance->initialized = true;
mutex_lock(&instance->cdev->lock);

View file

@ -33,6 +33,8 @@ MODULE_AUTHOR("Zhang Rui");
MODULE_DESCRIPTION("Generic thermal management sysfs support");
MODULE_LICENSE("GPL v2");
#define THERMAL_MAX_ACTIVE 16
static DEFINE_IDA(thermal_tz_ida);
static DEFINE_IDA(thermal_cdev_ida);
@ -49,6 +51,8 @@ static bool power_off_triggered;
static struct thermal_governor *def_governor;
static struct workqueue_struct *thermal_passive_wq;
/*
* Governor section: set of functions to handle thermal governors
*
@ -263,6 +267,10 @@ static int __init thermal_register_governors(void)
if (result)
return result;
result = thermal_gov_low_limits_register();
if (result)
return result;
return thermal_gov_power_allocator_register();
}
@ -272,6 +280,7 @@ static void thermal_unregister_governors(void)
thermal_gov_fair_share_unregister();
thermal_gov_bang_bang_unregister();
thermal_gov_user_space_unregister();
thermal_gov_low_limits_unregister();
thermal_gov_power_allocator_unregister();
}
@ -286,14 +295,15 @@ static void thermal_unregister_governors(void)
* - Hot trips will produce a notification to userspace;
* - Critical trip point will cause a system shutdown.
*/
static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
static void thermal_zone_device_set_polling(struct workqueue_struct *queue,
struct thermal_zone_device *tz,
int delay)
{
if (delay > 1000)
mod_delayed_work(system_freezable_wq, &tz->poll_queue,
mod_delayed_work(queue, &tz->poll_queue,
round_jiffies(msecs_to_jiffies(delay)));
else if (delay)
mod_delayed_work(system_freezable_wq, &tz->poll_queue,
mod_delayed_work(queue, &tz->poll_queue,
msecs_to_jiffies(delay));
else
cancel_delayed_work(&tz->poll_queue);
@ -304,11 +314,14 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz)
mutex_lock(&tz->lock);
if (tz->passive)
thermal_zone_device_set_polling(tz, tz->passive_delay);
thermal_zone_device_set_polling(thermal_passive_wq,
tz, tz->passive_delay);
else if (tz->polling_delay)
thermal_zone_device_set_polling(tz, tz->polling_delay);
thermal_zone_device_set_polling(
system_freezable_power_efficient_wq,
tz, tz->polling_delay);
else
thermal_zone_device_set_polling(tz, 0);
thermal_zone_device_set_polling(NULL, tz, 0);
mutex_unlock(&tz->lock);
}
@ -380,7 +393,7 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
if (trip_temp <= 0 || tz->temperature < trip_temp)
return;
trace_thermal_zone_trip(tz, trip, trip_type);
trace_thermal_zone_trip(tz, trip, trip_type, true);
if (tz->ops->notify)
tz->ops->notify(tz, trip, trip_type);
@ -422,6 +435,7 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
* So, start monitoring again.
*/
monitor_thermal_zone(tz);
trace_thermal_handle_trip(tz, trip);
}
static void update_temperature(struct thermal_zone_device *tz)
@ -443,7 +457,8 @@ static void update_temperature(struct thermal_zone_device *tz)
mutex_unlock(&tz->lock);
trace_thermal_temperature(tz);
if (tz->last_temperature == THERMAL_TEMP_INVALID)
if (tz->last_temperature == THERMAL_TEMP_INVALID ||
tz->last_temperature == THERMAL_TEMP_INVALID_LOW)
dev_dbg(&tz->device, "last_temperature N/A, current_temperature=%d\n",
tz->temperature);
else
@ -472,6 +487,7 @@ void thermal_zone_device_update(struct thermal_zone_device *tz,
if (!tz->ops->get_temp)
return;
trace_thermal_device_update(tz, event);
update_temperature(tz);
thermal_zone_set_trips(tz);
@ -700,9 +716,26 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
if (ret)
return ret;
/* lower default 0, upper default max_state */
lower = lower == THERMAL_NO_LIMIT ? 0 : lower;
upper = upper == THERMAL_NO_LIMIT ? max_state : upper;
/*
* If upper or lower has a MACRO to define the mitigation state,
* based on the MACRO determine the default state to use or the
* offset from the max_state.
*/
if (upper >= (THERMAL_MAX_LIMIT - max_state)) {
/* upper default max_state */
if (upper == THERMAL_NO_LIMIT)
upper = max_state;
else
upper = max_state - (THERMAL_MAX_LIMIT - upper);
}
if (lower >= (THERMAL_MAX_LIMIT - max_state)) {
/* lower default 0 */
if (lower == THERMAL_NO_LIMIT)
lower = 0;
else
lower = max_state - (THERMAL_MAX_LIMIT - lower);
}
if (lower > upper || upper > max_state)
return -EINVAL;
@ -738,6 +771,28 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
if (result)
goto remove_symbol_link;
snprintf(dev->upper_attr_name, THERMAL_NAME_LENGTH,
"cdev%d_upper_limit", dev->id);
sysfs_attr_init(&dev->upper_attr.attr);
dev->upper_attr.attr.name = dev->upper_attr_name;
dev->upper_attr.attr.mode = 0644;
dev->upper_attr.show = upper_limit_show;
dev->upper_attr.store = upper_limit_store;
result = device_create_file(&tz->device, &dev->upper_attr);
if (result)
goto remove_trip_file;
snprintf(dev->lower_attr_name, THERMAL_NAME_LENGTH,
"cdev%d_lower_limit", dev->id);
sysfs_attr_init(&dev->lower_attr.attr);
dev->lower_attr.attr.name = dev->lower_attr_name;
dev->lower_attr.attr.mode = 0644;
dev->lower_attr.show = lower_limit_show;
dev->lower_attr.store = lower_limit_store;
result = device_create_file(&tz->device, &dev->lower_attr);
if (result)
goto remove_upper_file;
sprintf(dev->weight_attr_name, "cdev%d_weight", dev->id);
sysfs_attr_init(&dev->weight_attr.attr);
dev->weight_attr.attr.name = dev->weight_attr_name;
@ -746,7 +801,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
dev->weight_attr.store = weight_store;
result = device_create_file(&tz->device, &dev->weight_attr);
if (result)
goto remove_trip_file;
goto remove_lower_file;
mutex_lock(&tz->lock);
mutex_lock(&cdev->lock);
@ -767,6 +822,10 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
return 0;
device_remove_file(&tz->device, &dev->weight_attr);
remove_lower_file:
device_remove_file(&tz->device, &dev->lower_attr);
remove_upper_file:
device_remove_file(&tz->device, &dev->upper_attr);
remove_trip_file:
device_remove_file(&tz->device, &dev->attr);
remove_symbol_link:
@ -816,6 +875,8 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
return -ENODEV;
unbind:
device_remove_file(&tz->device, &pos->lower_attr);
device_remove_file(&tz->device, &pos->upper_attr);
device_remove_file(&tz->device, &pos->weight_attr);
device_remove_file(&tz->device, &pos->attr);
sysfs_remove_link(&tz->device.kobj, pos->name);
@ -970,6 +1031,8 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->updated = false;
cdev->device.class = &thermal_class;
cdev->devdata = devdata;
cdev->sysfs_cur_state_req = 0;
cdev->sysfs_min_state_req = ULONG_MAX;
thermal_cooling_device_setup_sysfs(cdev);
dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
result = device_register(&cdev->device);
@ -1274,7 +1337,7 @@ thermal_zone_device_register(const char *type, int trips, int mask,
/* Bind cooling devices for this zone */
bind_tz(tz);
INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
INIT_DEFERRABLE_WORK(&(tz->poll_queue), thermal_zone_device_check);
thermal_zone_device_reset(tz);
/* Update the new thermal zone and mark it as already updated. */
@ -1345,7 +1408,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
mutex_unlock(&thermal_list_lock);
thermal_zone_device_set_polling(tz, 0);
thermal_zone_device_set_polling(NULL, tz, 0);
thermal_set_governor(tz, NULL);
@ -1409,6 +1472,8 @@ static struct genl_family thermal_event_genl_family __ro_after_init = {
.n_mcgrps = ARRAY_SIZE(thermal_event_mcgrps),
};
static int allow_netlink_events;
int thermal_generate_netlink_event(struct thermal_zone_device *tz,
enum events event)
{
@ -1423,6 +1488,9 @@ int thermal_generate_netlink_event(struct thermal_zone_device *tz,
if (!tz)
return -EINVAL;
if (!allow_netlink_events)
return -ENODEV;
/* allocate memory */
size = nla_total_size(sizeof(struct thermal_genl_event)) +
nla_total_size(0);
@ -1484,6 +1552,8 @@ static void genetlink_exit(void)
#else /* !CONFIG_NET */
static inline int genetlink_init(void) { return 0; }
static inline void genetlink_exit(void) {}
static inline int thermal_generate_netlink_event(struct thermal_zone_device *tz,
enum events event) { return -ENODEV; }
#endif /* !CONFIG_NET */
static int thermal_pm_notify(struct notifier_block *nb,
@ -1522,21 +1592,26 @@ static int __init thermal_init(void)
int result;
mutex_init(&poweroff_lock);
thermal_passive_wq = alloc_workqueue("thermal_passive_wq",
WQ_HIGHPRI | WQ_UNBOUND
| WQ_FREEZABLE,
THERMAL_MAX_ACTIVE);
if (!thermal_passive_wq) {
result = -ENOMEM;
goto error;
}
result = thermal_register_governors();
if (result)
goto error;
goto destroy_wq;
result = class_register(&thermal_class);
if (result)
goto unregister_governors;
result = genetlink_init();
if (result)
goto unregister_class;
result = of_parse_thermal_zones();
if (result)
goto exit_netlink;
goto exit_zone_parse;
result = register_pm_notifier(&thermal_pm_nb);
if (result)
@ -1545,12 +1620,12 @@ static int __init thermal_init(void)
return 0;
exit_netlink:
genetlink_exit();
unregister_class:
exit_zone_parse:
class_unregister(&thermal_class);
unregister_governors:
thermal_unregister_governors();
destroy_wq:
destroy_workqueue(thermal_passive_wq);
error:
ida_destroy(&thermal_tz_ida);
ida_destroy(&thermal_cdev_ida);
@ -1560,10 +1635,11 @@ error:
return result;
}
static void __exit thermal_exit(void)
static void thermal_exit(void)
{
unregister_pm_notifier(&thermal_pm_nb);
of_thermal_destroy_zones();
destroy_workqueue(thermal_passive_wq);
genetlink_exit();
class_unregister(&thermal_class);
thermal_unregister_governors();
@ -1573,5 +1649,19 @@ static void __exit thermal_exit(void)
mutex_destroy(&thermal_governor_lock);
}
fs_initcall(thermal_init);
static int __init thermal_netlink_init(void)
{
int ret = 0;
ret = genetlink_init();
if (!ret)
goto exit_netlink;
thermal_exit();
exit_netlink:
return ret;
}
subsys_initcall(thermal_init);
fs_initcall(thermal_netlink_init);
module_exit(thermal_exit);

View file

@ -34,6 +34,10 @@ struct thermal_instance {
struct device_attribute attr;
char weight_attr_name[THERMAL_NAME_LENGTH];
struct device_attribute weight_attr;
char upper_attr_name[THERMAL_NAME_LENGTH];
struct device_attribute upper_attr;
char lower_attr_name[THERMAL_NAME_LENGTH];
struct device_attribute lower_attr;
struct list_head tz_node; /* node in tz->thermal_instances */
struct list_head cdev_node; /* node in cdev->thermal_instances */
unsigned int weight; /* The weight of the cooling device */
@ -62,8 +66,16 @@ void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev);
/* used only at binding time */
ssize_t trip_point_show(struct device *, struct device_attribute *, char *);
ssize_t weight_show(struct device *, struct device_attribute *, char *);
ssize_t lower_limit_show(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t upper_limit_show(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t weight_store(struct device *, struct device_attribute *, const char *,
size_t);
ssize_t lower_limit_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
ssize_t upper_limit_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
#ifdef CONFIG_THERMAL_STATISTICS
void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
@ -114,6 +126,14 @@ static inline int thermal_gov_power_allocator_register(void) { return 0; }
static inline void thermal_gov_power_allocator_unregister(void) {}
#endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */
#ifdef CONFIG_THERMAL_GOV_LOW_LIMITS
int thermal_gov_low_limits_register(void);
void thermal_gov_low_limits_unregister(void);
#else
static inline int thermal_gov_low_limits_register(void) { return 0; }
static inline void thermal_gov_low_limits_unregister(void) {}
#endif /* CONFIG_THERMAL_GOV_LOW_LIMITS */
/* device tree support */
#ifdef CONFIG_THERMAL_OF
int of_parse_thermal_zones(void);
@ -122,6 +142,10 @@ int of_thermal_get_ntrips(struct thermal_zone_device *);
bool of_thermal_is_trip_valid(struct thermal_zone_device *, int);
const struct thermal_trip *
of_thermal_get_trip_points(struct thermal_zone_device *);
int of_thermal_aggregate_trip(struct thermal_zone_device *tz,
enum thermal_trip_type type,
int *low, int *high);
void of_thermal_handle_trip(struct thermal_zone_device *tz);
#else
static inline int of_parse_thermal_zones(void) { return 0; }
static inline void of_thermal_destroy_zones(void) { }
@ -139,6 +163,15 @@ of_thermal_get_trip_points(struct thermal_zone_device *tz)
{
return NULL;
}
static inline int of_thermal_aggregate_trip(struct thermal_zone_device *tz,
enum thermal_trip_type type,
int *low, int *high)
{
return -ENODEV;
}
static inline
void of_thermal_handle_trip(struct thermal_zone_device *tz)
{ }
#endif
#endif /* __THERMAL_CORE_H__ */

View file

@ -106,7 +106,7 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
if (!ret && *temp < crit_temp)
*temp = tz->emul_temperature;
}
trace_thermal_query_temp(tz, *temp);
mutex_unlock(&tz->lock);
exit:
return ret;
@ -140,10 +140,6 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz)
high = trip_temp;
}
/* No need to change trip points */
if (tz->prev_low_trip == low && tz->prev_high_trip == high)
goto exit;
tz->prev_low_trip = low;
tz->prev_high_trip = high;
@ -157,6 +153,7 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz)
ret = tz->ops->set_trips(tz, low, high);
if (ret)
dev_err(&tz->device, "Failed to set trips: %d\n", ret);
trace_thermal_set_trip(tz);
exit:
mutex_unlock(&tz->lock);
@ -166,7 +163,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_set_trips);
void thermal_cdev_update(struct thermal_cooling_device *cdev)
{
struct thermal_instance *instance;
unsigned long target = 0;
unsigned long current_target = 0, min_target = ULONG_MAX;
mutex_lock(&cdev->lock);
/* cooling device is updated*/
@ -176,22 +173,33 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)
}
/* Make sure cdev enters the deepest cooling state */
current_target = cdev->sysfs_cur_state_req;
min_target = cdev->sysfs_min_state_req;
list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) {
dev_dbg(&cdev->device, "zone%d->target=%lu\n",
instance->tz->id, instance->target);
if (instance->target == THERMAL_NO_TARGET)
continue;
if (instance->target > target)
target = instance->target;
if (instance->tz->governor->min_state_throttle) {
if (instance->target < min_target)
min_target = instance->target;
} else {
if (instance->target > current_target)
current_target = instance->target;
}
}
if (!cdev->ops->set_cur_state(cdev, target))
thermal_cooling_device_stats_update(cdev, target);
trace_cdev_update_start(cdev);
if (!cdev->ops->set_cur_state(cdev, current_target))
thermal_cooling_device_stats_update(cdev, current_target);
if (cdev->ops->set_min_state)
cdev->ops->set_min_state(cdev, min_target);
cdev->updated = true;
mutex_unlock(&cdev->lock);
trace_cdev_update(cdev, target);
dev_dbg(&cdev->device, "set to state %lu\n", target);
trace_cdev_update(cdev, current_target, min_target);
dev_dbg(&cdev->device, "set to state %lu min state %lu\n",
current_target, min_target);
}
EXPORT_SYMBOL(thermal_cdev_update);

View file

@ -63,6 +63,28 @@ mode_show(struct device *dev, struct device_attribute *attr, char *buf)
: "disabled");
}
static int thermal_zone_device_clear(struct thermal_zone_device *tz)
{
struct thermal_instance *pos;
int ret = 0;
ret = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
mutex_lock(&tz->lock);
tz->temperature = THERMAL_TEMP_INVALID;
tz->passive = 0;
list_for_each_entry(pos, &tz->thermal_instances, tz_node) {
pos->initialized = false;
pos->target = THERMAL_NO_TARGET;
mutex_lock(&pos->cdev->lock);
pos->cdev->updated = false; /* cdev needs update */
mutex_unlock(&pos->cdev->lock);
thermal_cdev_update(pos->cdev);
}
mutex_unlock(&tz->lock);
return ret;
}
static ssize_t
mode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
@ -76,7 +98,7 @@ mode_store(struct device *dev, struct device_attribute *attr,
if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
result = thermal_zone_device_clear(tz);
else
result = -EINVAL;
@ -348,6 +370,60 @@ sustainable_power_store(struct device *dev, struct device_attribute *devattr,
return count;
}
static ssize_t
polling_delay_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", tz->polling_delay);
}
static ssize_t
polling_delay_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
u32 delay;
if (kstrtou32(buf, 10, &delay))
return -EINVAL;
mutex_lock(&tz->lock);
tz->polling_delay = delay;
mutex_unlock(&tz->lock);
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
return count;
}
static ssize_t
passive_delay_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", tz->passive_delay);
}
static ssize_t
passive_delay_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
u32 delay;
if (kstrtou32(buf, 10, &delay))
return -EINVAL;
mutex_lock(&tz->lock);
tz->passive_delay = delay;
mutex_unlock(&tz->lock);
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
return count;
}
#define create_s32_tzp_attr(name) \
static ssize_t \
name##_show(struct device *dev, struct device_attribute *devattr, \
@ -399,6 +475,8 @@ static DEVICE_ATTR_RO(temp);
static DEVICE_ATTR_RW(policy);
static DEVICE_ATTR_RO(available_policies);
static DEVICE_ATTR_RW(sustainable_power);
static DEVICE_ATTR_RW(passive_delay);
static DEVICE_ATTR_RW(polling_delay);
/* These thermal zone device attributes are created based on conditions */
static DEVICE_ATTR_RW(mode);
@ -414,6 +492,8 @@ static struct attribute *thermal_zone_dev_attrs[] = {
&dev_attr_policy.attr,
&dev_attr_available_policies.attr,
&dev_attr_sustainable_power.attr,
&dev_attr_passive_delay.attr,
&dev_attr_polling_delay.attr,
&dev_attr_k_po.attr,
&dev_attr_k_pu.attr,
&dev_attr_k_i.attr,
@ -698,13 +778,30 @@ static ssize_t cur_state_show(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%ld\n", state);
}
static ssize_t min_state_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
unsigned long state;
int ret;
if (cdev->ops->get_min_state)
ret = cdev->ops->get_min_state(cdev, &state);
else
ret = -EPERM;
if (ret)
return ret;
return snprintf(buf, PAGE_SIZE, "%lu\n", state);
}
static ssize_t
cur_state_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
unsigned long state;
int result;
if (sscanf(buf, "%ld\n", &state) != 1)
return -EINVAL;
@ -712,10 +809,38 @@ cur_state_store(struct device *dev, struct device_attribute *attr,
if ((long)state < 0)
return -EINVAL;
result = cdev->ops->set_cur_state(cdev, state);
if (result)
return result;
thermal_cooling_device_stats_update(cdev, state);
mutex_lock(&cdev->lock);
cdev->sysfs_cur_state_req = state;
cdev->updated = false;
mutex_unlock(&cdev->lock);
thermal_cdev_update(cdev);
return count;
}
static ssize_t
min_state_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
long state;
int ret = 0;
ret = sscanf(buf, "%ld\n", &state);
if (ret <= 0)
return (ret < 0) ? ret : -EINVAL;
if ((long)state < 0)
return -EINVAL;
mutex_lock(&cdev->lock);
cdev->sysfs_min_state_req = state;
cdev->updated = false;
mutex_unlock(&cdev->lock);
thermal_cdev_update(cdev);
return count;
}
@ -723,11 +848,13 @@ static struct device_attribute
dev_attr_cdev_type = __ATTR(type, 0444, cdev_type_show, NULL);
static DEVICE_ATTR_RO(max_state);
static DEVICE_ATTR_RW(cur_state);
static DEVICE_ATTR_RW(min_state);
static struct attribute *cooling_device_attrs[] = {
&dev_attr_cdev_type.attr,
&dev_attr_max_state.attr,
&dev_attr_cur_state.attr,
&dev_attr_min_state.attr,
NULL,
};
@ -960,6 +1087,29 @@ void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev)
}
/* these helper will be used only at the time of bindig */
ssize_t
lower_limit_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_instance *instance;
instance =
container_of(attr, struct thermal_instance, lower_attr);
return snprintf(buf, PAGE_SIZE, "%lu\n", instance->lower);
}
ssize_t
upper_limit_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_instance *instance;
instance =
container_of(attr, struct thermal_instance, upper_attr);
return snprintf(buf, PAGE_SIZE, "%lu\n", instance->upper);
}
ssize_t
trip_point_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@ -999,3 +1149,49 @@ ssize_t weight_store(struct device *dev, struct device_attribute *attr,
return count;
}
ssize_t
upper_limit_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
struct thermal_instance *instance;
int ret, upper_limit;
instance =
container_of(attr, struct thermal_instance, upper_attr);
ret = kstrtoint(buf, 0, &upper_limit);
if (ret)
return ret;
if (upper_limit < instance->lower)
return -EINVAL;
instance->upper = upper_limit;
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
return count;
}
ssize_t
lower_limit_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
struct thermal_instance *instance;
int ret, lower_limit;
instance =
container_of(attr, struct thermal_instance, lower_attr);
ret = kstrtoint(buf, 0, &lower_limit);
if (ret)
return ret;
if (lower_limit > instance->upper)
return -EINVAL;
instance->lower = lower_limit;
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
return count;
}

View file

@ -26,12 +26,24 @@
/* No upper/lower limit requirement */
#define THERMAL_NO_LIMIT ((u32)~0)
/* upper limit requirement */
#define THERMAL_MAX_LIMIT (THERMAL_NO_LIMIT - 1)
/* Default weight of a bound cooling device */
#define THERMAL_WEIGHT_DEFAULT 0
/* Max sensors that can be used for a single virtual thermalzone */
#define THERMAL_MAX_VIRT_SENSORS 10
/* use value, which < 0K, to indicate an invalid/uninitialized temperature */
#define THERMAL_TEMP_INVALID -274000
/*
* use a high value for low temp tracking zone,
* to indicate an invalid/uninitialized temperature
*/
#define THERMAL_TEMP_INVALID_LOW 274000
/* Unit conversion macros */
#define DECI_KELVIN_TO_CELSIUS(t) ({ \
long _t = (t); \
@ -119,6 +131,10 @@ struct thermal_cooling_device_ops {
int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
int (*set_min_state)(struct thermal_cooling_device *cdev,
unsigned long target);
int (*get_min_state)(struct thermal_cooling_device *cdev,
unsigned long *target);
int (*get_requested_power)(struct thermal_cooling_device *,
struct thermal_zone_device *, u32 *);
int (*state2power)(struct thermal_cooling_device *,
@ -139,6 +155,8 @@ struct thermal_cooling_device {
struct mutex lock; /* protect thermal_instances list */
struct list_head thermal_instances;
struct list_head node;
unsigned long sysfs_cur_state_req;
unsigned long sysfs_min_state_req;
};
struct thermal_attr {
@ -239,6 +257,7 @@ struct thermal_governor {
void (*unbind_from_tz)(struct thermal_zone_device *tz);
int (*throttle)(struct thermal_zone_device *tz, int trip);
struct list_head governor_list;
int min_state_throttle;
};
/* Structure that holds binding parameters for a zone */
@ -327,6 +346,12 @@ struct thermal_zone_params {
* Used by thermal zone drivers (default 0).
*/
int offset;
/*
* @tracks_low: Indicates that the thermal zone params are for
* temperatures falling below the thresholds.
*/
bool tracks_low;
};
struct thermal_genl_event {
@ -349,13 +374,16 @@ struct thermal_genl_event {
* temperature.
* @set_trip_temp: a pointer to a function that sets the trip temperature on
* hardware.
* @get_trip_temp: a pointer to a function that gets the trip temperature on
* hardware.
*/
struct thermal_zone_of_device_ops {
int (*get_temp)(void *, int *);
int (*get_trend)(void *, int, enum thermal_trend *);
int (*set_trips)(void *, int, int);
int (*set_emul_temp)(void *, int);
int (*set_trip_temp)(void *, int, int);
int (*set_trip_temp)(void *data, int trip, int temp);
int (*get_trip_temp)(void *data, int trip, int *temp);
};
/**
@ -373,6 +401,39 @@ struct thermal_trip {
enum thermal_trip_type type;
};
/* Different aggregation logic supported for virtual sensors */
enum aggregation_logic {
VIRT_WEIGHTED_AVG,
VIRT_MAXIMUM,
VIRT_MINIMUM,
VIRT_AGGREGATION_NR,
};
/*
* struct virtual_sensor_data - Data structure used to provide
* information about the virtual zone.
* @virt_zone_name - Virtual thermal zone name
* @num_sensors - Number of sensors this virtual zone uses to compute
* temperature
* @sensor_names - Array of sensor names
* @logic - Temperature aggregation logic to be used
* @coefficients - Coefficients to be used for weighted average logic
* @coefficient_ct - number of coefficients provided as input
* @avg_offset - offset value to be used for the weighted aggregation logic
* @avg_denominator - denominator value to be used for the weighted aggregation
* logic
*/
struct virtual_sensor_data {
int num_sensors;
char virt_zone_name[THERMAL_NAME_LENGTH];
char *sensor_names[THERMAL_MAX_VIRT_SENSORS];
enum aggregation_logic logic;
int coefficients[THERMAL_MAX_VIRT_SENSORS];
int coefficient_ct;
int avg_offset;
int avg_denominator;
};
/* Function declarations */
#ifdef CONFIG_THERMAL_OF
struct thermal_zone_device *
@ -385,6 +446,9 @@ struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
const struct thermal_zone_of_device_ops *ops);
void devm_thermal_zone_of_sensor_unregister(struct device *dev,
struct thermal_zone_device *tz);
struct thermal_zone_device *devm_thermal_of_virtual_sensor_register(
struct device *dev,
const struct virtual_sensor_data *sensor_data);
#else
static inline struct thermal_zone_device *
thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
@ -412,6 +476,14 @@ void devm_thermal_zone_of_sensor_unregister(struct device *dev,
{
}
static inline
struct thermal_zone_device *devm_thermal_of_virtual_sensor_register(
struct device *dev,
const struct virtual_sensor_data *sensor_data)
{
return ERR_PTR(-ENODEV);
}
#endif
#if IS_ENABLED(CONFIG_THERMAL)

View file

@ -21,6 +21,29 @@ TRACE_DEFINE_ENUM(THERMAL_TRIP_ACTIVE);
{ THERMAL_TRIP_PASSIVE, "PASSIVE"}, \
{ THERMAL_TRIP_ACTIVE, "ACTIVE"})
TRACE_EVENT(thermal_query_temp,
TP_PROTO(struct thermal_zone_device *tz, int temp),
TP_ARGS(tz, temp),
TP_STRUCT__entry(
__string(thermal_zone, tz->type)
__field(int, id)
__field(int, temp)
),
TP_fast_assign(
__assign_str(thermal_zone, tz->type);
__entry->id = tz->id;
__entry->temp = temp;
),
TP_printk("thermal_zone=%s id=%d temp=%d",
__get_str(thermal_zone), __entry->id,
__entry->temp)
);
TRACE_EVENT(thermal_temperature,
TP_PROTO(struct thermal_zone_device *tz),
@ -46,37 +69,59 @@ TRACE_EVENT(thermal_temperature,
__entry->temp)
);
TRACE_EVENT(cdev_update_start,
TP_PROTO(struct thermal_cooling_device *cdev),
TP_ARGS(cdev),
TP_STRUCT__entry(
__string(type, cdev->type)
),
TP_fast_assign(
__assign_str(type, cdev->type);
),
TP_printk("type=%s update start", __get_str(type))
);
TRACE_EVENT(cdev_update,
TP_PROTO(struct thermal_cooling_device *cdev, unsigned long target),
TP_PROTO(struct thermal_cooling_device *cdev, unsigned long target,
unsigned long min_target),
TP_ARGS(cdev, target),
TP_ARGS(cdev, target, min_target),
TP_STRUCT__entry(
__string(type, cdev->type)
__field(unsigned long, target)
__field(unsigned long, min_target)
),
TP_fast_assign(
__assign_str(type, cdev->type);
__entry->target = target;
__entry->min_target = min_target;
),
TP_printk("type=%s target=%lu", __get_str(type), __entry->target)
TP_printk("type=%s target=%lu min_target=%lu", __get_str(type),
__entry->target, __entry->min_target)
);
TRACE_EVENT(thermal_zone_trip,
TP_PROTO(struct thermal_zone_device *tz, int trip,
enum thermal_trip_type trip_type),
enum thermal_trip_type trip_type, bool is_trip),
TP_ARGS(tz, trip, trip_type),
TP_ARGS(tz, trip, trip_type, is_trip),
TP_STRUCT__entry(
__string(thermal_zone, tz->type)
__field(int, id)
__field(int, trip)
__field(enum thermal_trip_type, trip_type)
__field(bool, is_trip)
),
TP_fast_assign(
@ -84,13 +129,85 @@ TRACE_EVENT(thermal_zone_trip,
__entry->id = tz->id;
__entry->trip = trip;
__entry->trip_type = trip_type;
__entry->is_trip = is_trip;
),
TP_printk("thermal_zone=%s id=%d trip=%d trip_type=%s",
__get_str(thermal_zone), __entry->id, __entry->trip,
TP_printk("thermal_zone=%s id=%d %s=%d trip_type=%s",
__get_str(thermal_zone), __entry->id,
(__entry->is_trip) ? "trip" : "hyst",
__entry->trip,
show_tzt_type(__entry->trip_type))
);
TRACE_EVENT(thermal_handle_trip,
TP_PROTO(struct thermal_zone_device *tz, int trip),
TP_ARGS(tz, trip),
TP_STRUCT__entry(
__string(thermal_zone, tz->type)
__field(int, id)
__field(int, trip)
),
TP_fast_assign(
__assign_str(thermal_zone, tz->type);
__entry->id = tz->id;
__entry->trip = trip;
),
TP_printk("thermal_zone=%s id=%d handle trip=%d",
__get_str(thermal_zone), __entry->id, __entry->trip)
);
TRACE_EVENT(thermal_device_update,
TP_PROTO(struct thermal_zone_device *tz, int event),
TP_ARGS(tz, event),
TP_STRUCT__entry(
__string(thermal_zone, tz->type)
__field(int, id)
__field(int, event)
),
TP_fast_assign(
__assign_str(thermal_zone, tz->type);
__entry->id = tz->id;
__entry->event = event;
),
TP_printk("thermal_zone=%s id=%d received event:%d",
__get_str(thermal_zone), __entry->id, __entry->event)
);
TRACE_EVENT(thermal_set_trip,
TP_PROTO(struct thermal_zone_device *tz),
TP_ARGS(tz),
TP_STRUCT__entry(
__string(thermal_zone, tz->type)
__field(int, id)
__field(int, low)
__field(int, high)
),
TP_fast_assign(
__assign_str(thermal_zone, tz->type);
__entry->id = tz->id;
__entry->low = tz->prev_low_trip;
__entry->high = tz->prev_high_trip;
),
TP_printk("thermal_zone=%s id=%d low trip=%d high trip=%d",
__get_str(thermal_zone), __entry->id, __entry->low,
__entry->high)
);
#ifdef CONFIG_CPU_THERMAL
TRACE_EVENT(thermal_power_cpu_get_power,
TP_PROTO(const struct cpumask *cpus, unsigned long freq, u32 *load,

View file

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM thermal_virtual
#if !defined(_TRACE_VIRTUAL_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_VIRTUAL_H
#include <linux/thermal.h>
#include <linux/tracepoint.h>
TRACE_EVENT(virtual_temperature,
TP_PROTO(struct thermal_zone_device *virt_tz,
struct thermal_zone_device *tz, int sens_temp,
int est_temp),
TP_ARGS(virt_tz, tz, sens_temp, est_temp),
TP_STRUCT__entry(
__string(virt_zone, virt_tz->type)
__string(therm_zone, tz->type)
__field(int, sens_temp)
__field(int, est_temp)
),
TP_fast_assign(
__assign_str(virt_zone, virt_tz->type);
__assign_str(therm_zone, tz->type);
__entry->sens_temp = sens_temp;
__entry->est_temp = est_temp;
),
TP_printk("virt_zone=%s zone=%s temp=%d virtual zone estimated temp=%d",
__get_str(virt_zone), __get_str(therm_zone),
__entry->sens_temp,
__entry->est_temp)
);
#endif /* _TRACE_VIRTUAL_H */
/* This part must be outside protection */
#include <trace/define_trace.h>