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:
parent
aabf400fc4
commit
8a12149c26
12 changed files with 1231 additions and 108 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
130
drivers/thermal/gov_low_limits.c
Normal file
130
drivers/thermal/gov_low_limits.c
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 @@ static int __init thermal_init(void)
|
|||
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);
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
46
include/trace/events/thermal_virtual.h
Normal file
46
include/trace/events/thermal_virtual.h
Normal 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>
|
Loading…
Add table
Reference in a new issue