drivers/thermal: support for multiple TZ for sensor
Subsystems (2)
cover drivers/thermal: support for multiple TZ for sensor
This is an attempt at introducing multiple thermal zones for each thermal sensor. The usecase behind this is that, a sensor may involved in monitoring a region that could have different mitigative actions. For example, a sensor placed between audio and camera IP, will involve different mitigative actions on these blocks. Sometimes it is also desirable to employ different governors and different mitigative strategies for the same sensor. For example, using step-wise governor for temperatures > 0 and a userspace governor for temperatures < 0 degC.
The idea presented in this patchset involves linking thermal zones together that share the same underlying sensor. The sensor still interacts with one thermal zone that we deem as the primary thermal sensor. The low thresholds set on a sensor is the max of min thresholds for the high trip threshold while the low trip thresholds for the sensor is the min of max thresholds from the different thermal zones of the sensor around the current sensor temperature. When the sensor temperature crosses the threshold, the thermal zone is notified and the new thresholds is evaluated again.
These patches continue to return a struct thermal_zone * back to the sensor when the sensor registers. While this prevents breaking the existing interaction, it is not very clean and optimal. This is something to think about as we review the patches. The breakdown of the patches in this set is -
- Patch #1 refactors the current data structures to help with the new support
- Patch #2 allows probing and set up of multiple thermal zones when a sensor is registered.
- Patch #3 aggregates across multiple thermal zones to figure out the mitigation and set new thresholds on the sensor. Sensor low and high thresholds are set based on thresholds from any thermal zone of the sensor.
- Patch #4 notifies the framework and the related thermal zones when the sensor is tripped
Please consider reviewing these patches. We have seen benefits of this idea and is implemented and used in mobile products.
Lina Iyer (4):
drivers: of-thermal: abstract sensor related information
drivers: of-thermal: allow multiple thermal zones for a sensor
drivers: of-thermal: aggregate sensor trips across thermal zones
drivers: of-thermal: notify framework when sensor is tripped
drivers/thermal/of-thermal.c | 205 ++++++++++++++++++++++++++---- drivers/thermal/thermal_helpers.c | 37 +++--- include/linux/thermal.h | 10 ++ 3 files changed, 209 insertions(+), 43 deletions(-)
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
RFC PATCH 1/4 drivers: of-thermal: abstract sensor related information
Sensor related information like the sensor callback ops and sensor data live in the thermal zone data structure along with other parameters. In preparation to use the same sensor across multiple thermal zones separate out the sensor specific information into its own data structure.
--- drivers/thermal/of-thermal.c | 72 ++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 23 deletions(-)
drivers/thermal/of-thermal.c +49 -23
@@ -35,6 +35,16 @@ struct __thermal_bind_params {unsigned long max; }; +/** +* struct thermal_sensor - Holds individual sensor data +* @sensor_data: sensor driver private data passed as input argument +* @ops: sensor driver ops +*/ +struct thermal_sensor { +void *sensor_data; +const struct thermal_zone_of_device_ops *ops; +}; +/** * struct __thermal_zone - internal representation of a thermal zone * @mode: current thermal zone device mode (enabled/disabled)
@@ -46,8 +56,7 @@ struct __thermal_bind_params {* @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 +* @sensor: sensor related parameters */ struct __thermal_zone {
@@ -66,8 +75,7 @@ struct __thermal_zone {struct __thermal_bind_params *tbps;/* sensor interface */ -void *sensor_data; -const struct thermal_zone_of_device_ops *ops; +struct thermal_sensor *sensor; };/*** DT thermal zone device callbacks ***/
@@ -76,22 +84,24 @@ static int of_thermal_get_temp(struct thermal_zone_device *tz, int *temp) {struct __thermal_zone *data = tz->devdata; +struct thermal_sensor *sensor = data->sensor; -if (!data->ops->get_temp) +if (!sensor || !sensor->ops->get_temp)return -EINVAL ; -return data->ops->get_temp (data->sensor_data, temp); +return sensor->ops->get_temp (sensor->sensor_data, temp); }static int of_thermal_set_trips (struct thermal_zone_device *tz,int low,int high) {struct __thermal_zone *data = tz->devdata; +struct thermal_sensor *sensor = data->sensor; -if (!data->ops || !data->ops->set_trips) +if (!sensor || !sensor->ops->set_trips)return -EINVAL ; -return data->ops->set_trips (data->sensor_data, low, high); +return sensor->ops->set_trips (sensor->sensor_data, low, high); }/**
@@ -173,19 +183,24 @@ static int of_thermal_set_emul_temp(struct thermal_zone_device *tz, int temp) {struct __thermal_zone *data = tz->devdata; +struct thermal_sensor *sensor = data->sensor; + +if (!sensor || !sensor->ops->set_emul_temp) +return -EINVAL ; -return data->ops->set_emul_temp (data->sensor_data, temp); +return sensor->ops->set_emul_temp (sensor->sensor_data, temp); }static int of_thermal_get_trend (struct thermal_zone_device *tz,int trip,enum thermal_trend *trend) {struct __thermal_zone *data = tz->devdata; +struct thermal_sensor *sensor = data->sensor; -if (!data->ops->get_trend) +if (!sensor || !sensor->ops->get_trend)return -EINVAL ; -return data->ops->get_trend (data->sensor_data, trip, trend); +return sensor->ops->get_trend (sensor->sensor_data, trip, trend); } static int of_thermal_bind(struct thermal_zone_device *thermal,
@@ -303,14 +318,16 @@ static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip, int temp) {struct __thermal_zone *data = tz->devdata; +struct thermal_sensor *sensor = data->sensor;if (trip >= data->ntrips || trip <0 )return -EDOM ; -if (data->ops->set_trip_temp) { +if (sensor && sensor->ops->set_trip_temp) {int ret; - ret = data->ops->set_trip_temp (data->sensor_data, trip, temp); + ret = sensor->ops->set_trip_temp (sensor->sensor_data, + trip, temp);if (ret)return ret; }
@@ -382,8 +399,8 @@ static struct thermal_zone_device_ops of_thermal_ops = {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_np, +struct thermal_sensor *sensor) {struct thermal_zone_device *tzd;struct __thermal_zone *tz;
@@ -394,12 +411,11 @@ thermal_zone_of_add_sensor(struct device_node *zone, tz = tzd->devdata; -if (!ops) +if (!sensor->ops)return ERR_PTR (-EINVAL );mutex_lock (&tzd->lock); - tz->ops = ops; - tz->sensor_data = data; + tz->sensor = sensor; tzd->ops->get_temp = of_thermal_get_temp; tzd->ops->get_trend = of_thermal_get_trend;
@@ -408,10 +424,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 (sensor->ops->set_trips) tzd->ops->set_trips = of_thermal_set_trips; -if (ops->set_emul_temp) +if (sensor->ops->set_emul_temp) tzd->ops->set_emul_temp = of_thermal_set_emul_temp;mutex_unlock (&tzd->lock);
@@ -457,6 +473,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 thermal_sensor *sensor =NULL ; np =of_find_node_by_name (NULL ,"thermal-zones" );if (!np)
@@ -467,6 +484,13 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,return ERR_PTR (-EINVAL ); } + sensor =kzalloc (sizeof (*sensor),GFP_KERNEL ); +if (!sensor) { +of_node_put (np); +return ERR_PTR (-ENOMEM ); + } + sensor->sensor_data = data; + sensor->ops = ops; sensor_np =of_node_get (dev->of_node);for_each_available_child_of_node (np, child) {
@@ -491,7 +515,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); + sensor);if (!IS_ERR (tzd)) tzd->ops->set_mode (tzd,THERMAL_DEVICE_ENABLED );
@@ -505,6 +529,8 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,of_node_put (sensor_np);of_node_put (np); +if (tzd ==ERR_PTR (-ENODEV )) +kfree (sensor);return tzd; }EXPORT_SYMBOL_GPL (thermal_zone_of_sensor_register);
@@ -543,8 +569,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->sensor); + tz->sensor =NULL ;mutex_unlock (&tzd->lock); }EXPORT_SYMBOL_GPL (thermal_zone_of_sensor_unregister);
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
RFC PATCH 2/4 drivers: of-thermal: allow multiple thermal zones for a sensor
In reply to Lina IyerTo allow different mitigative actions based on a sensor temperature, it is desirable to have multiple thermal zones share the same source. The thermal zones could have different thresholds, mitigative actions and even different governors.
--- drivers/thermal/of-thermal.c | 32 ++++++++++++++++++++++++++------ include/linux/thermal.h | 2 ++ 2 files changed, 28 insertions(+), 6 deletions(-)
drivers/thermal/of-thermal.c +26 -6
@@ -39,10 +39,14 @@ struct __thermal_bind_params {* struct thermal_sensor - Holds individual sensor data * @sensor_data: sensor driver private data passed as input argument * @ops: sensor driver ops +* @tz_list: list of thermal zones for this sensor +* @lock: lock sensor during operations */ struct thermal_sensor {void *sensor_data;const struct thermal_zone_of_device_ops *ops; +struct list_head tz_list; +struct mutex lock; };/**
@@ -430,11 +434,24 @@ thermal_zone_of_add_sensor(struct device_node *zone,if (sensor->ops->set_emul_temp) tzd->ops->set_emul_temp = of_thermal_set_emul_temp; +mutex_lock (&sensor->lock); +list_add_tail (&tzd->sensor_tzd, &sensor->tz_list); +mutex_unlock (&sensor->lock);mutex_unlock (&tzd->lock);return tzd; } +static struct thermal_zone_device * +thermal_zone_of_get_sensor_tzd (struct thermal_sensor *sensor) +{ +if (list_empty (&sensor->tz_list)) +return ERR_PTR (-ENODEV ); + + return list_first_entry(&sensor->tz_list, struct thermal_zone_device, + sensor_tzd); +} +/** * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone * @dev: a valid struct device pointer of a sensor device. Must contain
@@ -460,12 +477,11 @@ thermal_zone_of_add_sensor(struct device_node *zone,* 01 - This function must enqueue the new sensor instead of using * it as the only source of temperature values. * -* 02 - There must be a way to match the sensor with all thermal zones -* that refer to it. -* * Return: On success returns a valid struct thermal_zone_device, * otherwise, it returns a corresponding ERR_PTR(). Caller must * check the return value with help of IS_ERR() helper. +* The returned thermal_zone_device is one of the thermal zones that is +* using this sensor as the temeprature source. */ struct thermal_zone_device * thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
@@ -489,6 +505,8 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,of_node_put (np);return ERR_PTR (-ENOMEM ); } +INIT_LIST_HEAD (&sensor->tz_list); +mutex_init (&sensor->lock); sensor->sensor_data = data; sensor->ops = ops; sensor_np =of_node_get (dev->of_node);
@@ -521,14 +539,14 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,of_node_put (sensor_specs.np);of_node_put (child); - goto exit; }of_node_put (sensor_specs.np); } -exit: +of_node_put (sensor_np);of_node_put (np); + tzd =thermal_zone_of_get_sensor_tzd (sensor);if (tzd ==ERR_PTR (-ENODEV ))kfree (sensor);return tzd;
@@ -569,7 +587,9 @@ void thermal_zone_of_sensor_unregister(struct device *dev, tzd->ops->get_trend =NULL ; tzd->ops->set_emul_temp =NULL ; -kfree (tz->sensor); +list_del (&tzd->sensor_tzd); +if (list_empty (&tz->sensor->tz_list)) +kfree (tz->sensor); tz->sensor =NULL ;mutex_unlock (&tzd->lock); }
include/linux/thermal.h +2 -0
@@ -188,6 +188,7 @@ struct thermal_attr {* @node: node in thermal_tz_list (in thermal_core.c) * @poll_queue: delayed work for polling * @notify_event: Last notification event +* @sensor_tzd: node in sensor's list of thermal zone devices */ struct thermal_zone_device {int id;
@@ -220,6 +221,7 @@ struct thermal_zone_device {struct list_head node;struct delayed_work poll_queue;enum thermal_notify_event notify_event; +struct list_head sensor_tzd; };/**
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
RFC PATCH 3/4 drivers: of-thermal: aggregate sensor trips across thermal zones
In reply to Lina IyerAggregate thermal trip based on the trip points of the thermal zones that use this sensor as the source.
--- drivers/thermal/of-thermal.c | 28 ++++++++++++++++++++++- drivers/thermal/thermal_helpers.c | 37 ++++++++++++++++++------------- include/linux/thermal.h | 4 ++++ 3 files changed, 53 insertions(+), 16 deletions(-)
drivers/thermal/of-thermal.c +27 -1
@@ -96,16 +96,42 @@ static int of_thermal_get_temp(struct thermal_zone_device *tz,return sensor->ops->get_temp (sensor->sensor_data, temp); } +static void __of_thermal_agg_trip (struct thermal_sensor *sensor, +int *floor,int *ceil) +{ +int low, high; +int max_lo =INT_MIN ; +int min_hi =INT_MAX ; +struct thermal_zone_device *tz; + +list_for_each_entry (tz, &sensor->tz_list, sensor_tzd) { +thermal_zone_get_trip (tz, &low, &high); +if (low > max_lo) + max_lo = low; +if (high < min_hi) + min_hi = high; + } + + *floor = max_lo; + *ceil = min_hi; +} +static int of_thermal_set_trips (struct thermal_zone_device *tz,int low,int high) {struct __thermal_zone *data = tz->devdata;struct thermal_sensor *sensor = data->sensor; +int ret;if (!sensor || !sensor->ops->set_trips)return -EINVAL ; -return sensor->ops->set_trips (sensor->sensor_data, low, high); +mutex_lock (&sensor->lock); +__of_thermal_agg_trip (sensor, &low, &high); + ret = sensor->ops->set_trips (sensor->sensor_data, low, high); +mutex_unlock (&sensor->lock); + +return ret; }/**
drivers/thermal/thermal_helpers.c +22 -15
@@ -113,32 +113,39 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) }EXPORT_SYMBOL_GPL (thermal_zone_get_temp); -void thermal_zone_set_trips (struct thermal_zone_device *tz) + +void thermal_zone_get_trip (struct thermal_zone_device *tz,int *low,int *high) { -int low = -INT_MAX ; -int high =INT_MAX ; -int trip_temp, hysteresis; -int i, ret; +int i, trip_low, trip_temp, hysteresis; -mutex_lock (&tz->lock); - -if (!tz->ops->set_trips || !tz->ops->get_trip_hyst) - goto exit; + *low = -INT_MAX ; + *high =INT_MAX ;for (i =0 ; i < tz->trips; i++) { -int trip_low; - tz->ops->get_trip_temp (tz, i, &trip_temp); tz->ops->get_trip_hyst (tz, i, &hysteresis); trip_low = trip_temp - hysteresis; -if (trip_low < tz->temperature && trip_low > low) - low = trip_low; +if (trip_low < tz->temperature && trip_low > *low) + *low = trip_low; -if (trip_temp > tz->temperature && trip_temp < high) - high = trip_temp; +if (trip_temp > tz->temperature && trip_temp < *high) + *high = trip_temp; } +} + +void thermal_zone_set_trips (struct thermal_zone_device *tz) +{ +int low, high; +int ret; + +mutex_lock (&tz->lock); + +if (!tz->ops->set_trips || !tz->ops->get_trip_hyst) + goto exit; + +thermal_zone_get_trip (tz, &low, &high);/* No need to change trip points */ if (tz->prev_low_trip == low && tz->prev_high_trip == high)
include/linux/thermal.h +4 -0
@@ -443,6 +443,7 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int, struct thermal_cooling_device *);void thermal_zone_device_update (struct thermal_zone_device *,enum thermal_notify_event ); +void thermal_zone_get_trip (struct thermal_zone_device *tz,int *low,int *high);void thermal_zone_set_trips (struct thermal_zone_device *); struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
@@ -496,6 +497,9 @@ static inline int thermal_zone_unbind_cooling_device(static inline void thermal_zone_device_update (struct thermal_zone_device *tz,enum thermal_notify_event event) { } +static inline void thermal_zone_get_trip (struct thermal_zone_device *tz, +int *low,int *high) +{ }static inline void thermal_zone_set_trips (struct thermal_zone_device *tz) { } static inline struct thermal_cooling_device *
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
RFC PATCH 4/4 drivers: of-thermal: notify framework when sensor is tripped
In reply to Lina IyerNotify all related thermal zones when the sensor reports a thermal trip.
--- drivers/thermal/of-thermal.c | 83 ++++++++++++++++++++++++++++++++++-- include/linux/thermal.h | 4 ++ 2 files changed, 84 insertions(+), 3 deletions(-)
drivers/thermal/of-thermal.c +80 -3
@@ -41,12 +41,18 @@ struct __thermal_bind_params {* @ops: sensor driver ops * @tz_list: list of thermal zones for this sensor * @lock: lock sensor during operations +* @low_trip: sensor's low trip temp +* @high_trip: sensor's high trip temp +* @low_tz: the thermal zone whose low trip is used as @low_trip +* @high_tz: the thermal zone whose high trip is used as @high_trip */ struct thermal_sensor {void *sensor_data;const struct thermal_zone_of_device_ops *ops;struct list_head tz_list;struct mutex lock; +int low_trip, high_trip; +struct thermal_zone_device *low_tz, *high_tz; };/**
@@ -102,18 +108,26 @@ static void __of_thermal_agg_trip(struct thermal_sensor *sensor,int low, high;int max_lo =INT_MIN ;int min_hi =INT_MAX ; -struct thermal_zone_device *tz; +struct thermal_zone_device *tz, *lo_tz =NULL , *hi_tz =NULL ;list_for_each_entry (tz, &sensor->tz_list, sensor_tzd) {thermal_zone_get_trip (tz, &low, &high); -if (low > max_lo) +if (low > max_lo) { max_lo = low; -if (high < min_hi) + lo_tz = tz; + } +if (high < min_hi) { min_hi = high; + hi_tz = tz; + } } *floor = max_lo; *ceil = min_hi; + sensor->low_trip = max_lo; + sensor->high_trip = min_hi; + sensor->low_tz = lo_tz; + sensor->high_tz = hi_tz; } static int of_thermal_set_trips(struct thermal_zone_device *tz,
@@ -427,6 +441,69 @@ static struct thermal_zone_device_ops of_thermal_ops = {/*** sensor API ***/ +static void thermal_zone_of_get_trip (struct thermal_zone_device *tz, +int temp,int *low_trip,int *hi_trip) +{ +struct __thermal_zone *data = tz->devdata; +int low =INT_MIN ; +int hi =INT_MAX ; +int i; + +for (i =0 ; i < data->ntrips; i++) { +int trip_temp = data->trips[i].temperature; + +if (trip_temp < temp && trip_temp > low) + *low_trip = i; +if (trip_temp > temp && trip_temp < hi) + *hi_trip = i; + } +} + +/** +* thermal_zone_of_sensor_notify - notify framework of a trip +* @tzd: the thermal zone device +* +* Sensor drivers may use this API to notify the thermal framework that the +* temperature has crossed the trip threshold. This function is akin to +* thermal_notify_framework() call, but is expected to be called by a sensor +* that registered itself with the framework using the +* thermal_zone_of_add_sensor() function. +*/ +void thermal_zone_of_sensor_notify (struct thermal_zone_device *tzd) +{ +struct __thermal_zone *tz = tzd->devdata; +struct thermal_sensor *sensor = tz->sensor; +int temp, low_trip, hi_trip, *trip; +struct thermal_zone_device *trip_tz =NULL ; + +if (!tz->sensor) +return ; + +if (of_thermal_get_temp (tzd, &temp)) +return ; + +mutex_lock (&sensor->lock); +if (tzd->temperature < sensor->low_trip && sensor->low_tz) { + trip_tz = sensor->low_tz; + trip = &low_trip; + } +if (tzd->temperature > sensor->high_trip && sensor->high_tz) { + trip_tz = sensor->high_tz; + trip = &hi_trip; + } + +if (trip_tz) +thermal_zone_of_get_trip (trip_tz, temp, &low_trip, &hi_trip); + +mutex_unlock (&sensor->lock); + +if (!trip_tz) +return ; + +thermal_notify_framework (trip_tz, *trip); +} +EXPORT_SYMBOL (thermal_zone_of_sensor_notify); +static struct thermal_zone_device * thermal_zone_of_add_sensor(struct device_node *zone, struct device_node *sensor_np,
include/linux/thermal.h +4 -0
@@ -388,6 +388,7 @@ 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); +void thermal_zone_of_sensor_notify (struct thermal_zone_device *tzd);#else static inline struct thermal_zone_device * thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
@@ -415,6 +416,9 @@ void devm_thermal_zone_of_sensor_unregister(struct device *dev, { } +static inline +void thermal_zone_of_sensor_notify (struct thermal_zone_device *tzd) +{ }#endif #if IS_ENABLED (CONFIG_THERMAL )
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project