ANDROID: GKI: Add devm_thermal_of_virtual_sensor_register API.

This API registers a virtual thermal sensor.

Test: build
Bug: 149945768
Signed-off-by: Ram Chandrasekar <rkumbako@codeaurora.org>
Change-Id: I72b29b4296d9be949d096913471153b596c67e1f
[hridya: commit amended to only pull in the API implementation and code
needed to build it.]
(cherry picked commit from 8a12149c26)
Signed-off-by: Hridya Valsaraju <hridya@google.com>
This commit is contained in:
Ram Chandrasekar
2017-03-03 11:22:50 -07:00
committed by Hridya Valsaraju
parent 892b1f54ca
commit 3b10834869
3 changed files with 371 additions and 23 deletions

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
* @senps: sensor related parameters
* @list: sibling thermal zone
*/
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,22 +90,101 @@ 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;
};
/*** 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_COUNT_THRESHOLD:
if ((sens->coefficients[idx] < 0 &&
sens_temp < -sens->coefficients[idx]) ||
(sens->coefficients[idx] > 0 &&
sens_temp >= sens->coefficients[idx]))
temp += 1;
break;
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;
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,
@@ -88,10 +192,10 @@ static int of_thermal_set_trips(struct thermal_zone_device *tz,
{
struct __thermal_zone *data = tz->devdata;
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);
return data->senps->ops->set_trips(data->senps->sensor_data, low, high);
}
/**
@@ -174,7 +278,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 +289,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,
@@ -310,10 +418,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;
}
@@ -381,12 +490,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 +510,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,10 +523,10 @@ 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;
mutex_unlock(&tzd->lock);
@@ -460,6 +572,7 @@ 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 __sensor_param *sens_param = NULL;
np = of_find_node_by_name(NULL, "thermal-zones");
if (!np)
@@ -470,6 +583,13 @@ 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;
sensor_np = of_node_get(dev->of_node);
for_each_available_child_of_node(np, child) {
@@ -494,7 +614,7 @@ 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);
sens_param);
if (!IS_ERR(tzd))
tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
@@ -508,6 +628,8 @@ exit:
of_node_put(sensor_np);
of_node_put(np);
if (tzd == ERR_PTR(-ENODEV))
kfree(sens_param);
return tzd;
}
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
@@ -546,8 +668,8 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
tzd->ops->get_trend = NULL;
tzd->ops->set_emul_temp = NULL;
tz->ops = NULL;
tz->sensor_data = NULL;
kfree(tz->senps);
tz->senps = NULL;
mutex_unlock(&tzd->lock);
}
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
@@ -633,6 +755,138 @@ void devm_thermal_zone_of_sensor_unregister(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_unregister);
/**
* 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) ||
(sens->logic == VIRT_COUNT_THRESHOLD)) {
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);
/*** functions parsing device tree nodes ***/
/**

View File

@@ -29,6 +29,9 @@
/* 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
@@ -381,6 +384,40 @@ 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_COUNT_THRESHOLD,
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 *
@@ -393,6 +430,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,
@@ -420,6 +460,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

@@ -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>