blob: 72c40bdc396ebd22f205eeb3dd13e11ac70bb276 [file] [log] [blame]
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001/*
2 * of-thermal.c - Generic Thermal Management device tree support.
3 *
4 * Copyright (C) 2013 Texas Instruments
5 * Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com>
6 *
7 *
8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22 *
23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 */
25#include <linux/thermal.h>
26#include <linux/slab.h>
27#include <linux/types.h>
28#include <linux/of_device.h>
29#include <linux/of_platform.h>
30#include <linux/err.h>
31#include <linux/export.h>
32#include <linux/string.h>
Eduardo Valentin2251aef2014-11-07 21:24:39 -040033#include <linux/thermal.h>
Ram Chandrasekard29230b2017-02-27 11:26:51 -070034#include <linux/list.h>
Eduardo Valentin4e5e4702013-07-03 15:35:39 -040035
Ram Chandrasekar02592fa2017-05-16 17:03:02 -060036#define CREATE_TRACE_POINTS
37#include <trace/events/thermal_virtual.h>
38
Eduardo Valentin4e5e4702013-07-03 15:35:39 -040039#include "thermal_core.h"
40
Ram Chandrasekard29230b2017-02-27 11:26:51 -070041/*** Private data structures to represent thermal device tree data ***/
Eduardo Valentin4e5e4702013-07-03 15:35:39 -040042/**
Eduardo Valentin4e5e4702013-07-03 15:35:39 -040043 * struct __thermal_bind_param - a match between trip and cooling device
44 * @cooling_device: a pointer to identify the referred cooling device
45 * @trip_id: the trip point index
46 * @usage: the percentage (from 0 to 100) of cooling contribution
47 * @min: minimum cooling state used at this trip point
48 * @max: maximum cooling state used at this trip point
49 */
50
51struct __thermal_bind_params {
52 struct device_node *cooling_device;
53 unsigned int trip_id;
54 unsigned int usage;
55 unsigned long min;
56 unsigned long max;
57};
58
59/**
Ram Chandrasekare34ced62017-03-03 11:22:50 -070060 * struct __sensor_param - Holds individual sensor data
61 * @sensor_data: sensor driver private data passed as input argument
62 * @ops: sensor driver ops
Lina Iyer01ffea22016-07-27 13:46:32 -060063 * @trip_high: last trip high value programmed in the sensor driver
64 * @trip_low: last trip low value programmed in the sensor driver
65 * @lock: mutex lock acquired before updating the trip temperatures
Ram Chandrasekard29230b2017-02-27 11:26:51 -070066 * @first_tz: list head pointing the first thermal zone
Ram Chandrasekare34ced62017-03-03 11:22:50 -070067 */
68struct __sensor_param {
69 void *sensor_data;
70 const struct thermal_zone_of_device_ops *ops;
Lina Iyer01ffea22016-07-27 13:46:32 -060071 int trip_high, trip_low;
72 struct mutex lock;
Ram Chandrasekard29230b2017-02-27 11:26:51 -070073 struct list_head first_tz;
Ram Chandrasekare34ced62017-03-03 11:22:50 -070074};
75
76/**
Eduardo Valentin4e5e4702013-07-03 15:35:39 -040077 * struct __thermal_zone - internal representation of a thermal zone
78 * @mode: current thermal zone device mode (enabled/disabled)
79 * @passive_delay: polling interval while passive cooling is activated
80 * @polling_delay: zone polling interval
Eduardo Valentina46dbae2015-05-11 19:48:09 -070081 * @slope: slope of the temperature adjustment curve
82 * @offset: offset of the temperature adjustment curve
Ram Chandrasekar07479402017-08-25 14:04:42 -060083 * @default_disable: Keep the thermal zone disabled by default
Manaf Meethalavalappu Pallikunhi11175e32019-02-14 18:03:00 +053084 * @is_wakeable: Ignore post suspend thermal zone re-evaluation
Ram Chandrasekard29230b2017-02-27 11:26:51 -070085 * @tzd: thermal zone device pointer for this sensor
Eduardo Valentin4e5e4702013-07-03 15:35:39 -040086 * @ntrips: number of trip points
87 * @trips: an array of trip points (0..ntrips - 1)
88 * @num_tbps: number of thermal bind params
89 * @tbps: an array of thermal bind params (0..num_tbps - 1)
Ram Chandrasekard29230b2017-02-27 11:26:51 -070090 * @list: sibling thermal zone pointer
Ram Chandrasekare34ced62017-03-03 11:22:50 -070091 * @senps: sensor related parameters
Eduardo Valentin4e5e4702013-07-03 15:35:39 -040092 */
93
94struct __thermal_zone {
95 enum thermal_device_mode mode;
96 int passive_delay;
97 int polling_delay;
Eduardo Valentina46dbae2015-05-11 19:48:09 -070098 int slope;
99 int offset;
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700100 struct thermal_zone_device *tzd;
Ram Chandrasekar07479402017-08-25 14:04:42 -0600101 bool default_disable;
Manaf Meethalavalappu Pallikunhi11175e32019-02-14 18:03:00 +0530102 bool is_wakeable;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400103
104 /* trip data */
105 int ntrips;
Lukasz Majewskiad9914a2014-12-08 18:04:19 +0100106 struct thermal_trip *trips;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400107
108 /* cooling binding data */
109 int num_tbps;
110 struct __thermal_bind_params *tbps;
111
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700112 struct list_head list;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400113 /* sensor interface */
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700114 struct __sensor_param *senps;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400115};
116
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700117/**
118 * struct virtual_sensor - internal representation of a virtual thermal zone
119 * @num_sensors - number of sensors this virtual sensor will reference to
120 * estimate temperature
121 * @tz - Array of thermal zones of the sensors this virtual sensor will use
122 * to estimate temperature
Ram Chandrasekar02592fa2017-05-16 17:03:02 -0600123 * @virt_tz - Virtual thermal zone pointer
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700124 * @logic - aggregation logic to be used to estimate the temperature
125 * @last_reading - last estimated temperature
126 * @coefficients - array of coefficients to be used for weighted aggregation
127 * logic
128 * @avg_offset - offset value to be used for the weighted aggregation logic
129 * @avg_denominator - denominator value to be used for the weighted aggregation
130 * logic
131 */
132struct virtual_sensor {
133 int num_sensors;
134 struct thermal_zone_device *tz[THERMAL_MAX_VIRT_SENSORS];
Ram Chandrasekar02592fa2017-05-16 17:03:02 -0600135 struct thermal_zone_device *virt_tz;
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700136 enum aggregation_logic logic;
137 int last_reading;
138 int coefficients[THERMAL_MAX_VIRT_SENSORS];
139 int avg_offset;
140 int avg_denominator;
141};
142
Lina Iyer01ffea22016-07-27 13:46:32 -0600143static int of_thermal_aggregate_trip_types(struct thermal_zone_device *tz,
144 unsigned int trip_type_mask, int *low, int *high);
145
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400146/*** DT thermal zone device callbacks ***/
147
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700148static int virt_sensor_read_temp(void *data, int *val)
149{
150 struct virtual_sensor *sens = data;
151 int idx, temp = 0, ret = 0;
152
153 for (idx = 0; idx < sens->num_sensors; idx++) {
154 int sens_temp = 0;
155
156 ret = thermal_zone_get_temp(sens->tz[idx], &sens_temp);
157 if (ret) {
158 pr_err("virt zone: sensor[%s] read error:%d\n",
159 sens->tz[idx]->type, ret);
160 return ret;
161 }
162 switch (sens->logic) {
163 case VIRT_WEIGHTED_AVG:
164 temp += sens_temp * sens->coefficients[idx];
165 if (idx == (sens->num_sensors - 1))
166 temp = (temp + sens->avg_offset)
167 / sens->avg_denominator;
168 break;
169 case VIRT_MAXIMUM:
170 if (idx == 0)
171 temp = INT_MIN;
172 if (sens_temp > temp)
173 temp = sens_temp;
174 break;
175 case VIRT_MINIMUM:
176 if (idx == 0)
177 temp = INT_MAX;
178 if (sens_temp < temp)
179 temp = sens_temp;
180 break;
181 default:
182 break;
183 }
Ram Chandrasekar02592fa2017-05-16 17:03:02 -0600184 trace_virtual_temperature(sens->virt_tz, sens->tz[idx],
185 sens_temp, temp);
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700186 }
187
188 sens->last_reading = *val = temp;
189
190 return 0;
191}
192
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400193static int of_thermal_get_temp(struct thermal_zone_device *tz,
Sascha Hauer17e83512015-07-24 08:12:54 +0200194 int *temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400195{
196 struct __thermal_zone *data = tz->devdata;
197
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700198 if (!data->senps || !data->senps->ops->get_temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400199 return -EINVAL;
Ram Chandrasekar37c816d2017-08-25 12:34:02 -0600200 if (data->mode == THERMAL_DEVICE_DISABLED) {
201 *temp = tz->tzp->tracks_low ?
202 THERMAL_TEMP_INVALID_LOW :
203 THERMAL_TEMP_INVALID;
204 return 0;
205 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400206
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700207 return data->senps->ops->get_temp(data->senps->sensor_data, temp);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400208}
209
Sascha Hauer826386e2016-06-22 16:42:02 +0800210static int of_thermal_set_trips(struct thermal_zone_device *tz,
Lina Iyer01ffea22016-07-27 13:46:32 -0600211 int inp_low, int inp_high)
Sascha Hauer826386e2016-06-22 16:42:02 +0800212{
213 struct __thermal_zone *data = tz->devdata;
Lina Iyer01ffea22016-07-27 13:46:32 -0600214 int high = INT_MAX, low = INT_MIN, ret = 0;
Sascha Hauer826386e2016-06-22 16:42:02 +0800215
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700216 if (!data->senps || !data->senps->ops->set_trips)
Sascha Hauer826386e2016-06-22 16:42:02 +0800217 return -EINVAL;
218
Lina Iyer01ffea22016-07-27 13:46:32 -0600219 mutex_lock(&data->senps->lock);
220 of_thermal_aggregate_trip_types(tz, GENMASK(THERMAL_TRIP_CRITICAL, 0),
221 &low, &high);
Lina Iyer01ffea22016-07-27 13:46:32 -0600222 data->senps->trip_low = low;
223 data->senps->trip_high = high;
224 ret = data->senps->ops->set_trips(data->senps->sensor_data,
225 low, high);
226
Lina Iyer01ffea22016-07-27 13:46:32 -0600227 mutex_unlock(&data->senps->lock);
228 return ret;
Sascha Hauer826386e2016-06-22 16:42:02 +0800229}
230
Lukasz Majewski08dab662014-12-08 18:04:17 +0100231/**
232 * of_thermal_get_ntrips - function to export number of available trip
233 * points.
234 * @tz: pointer to a thermal zone
235 *
236 * This function is a globally visible wrapper to get number of trip points
237 * stored in the local struct __thermal_zone
238 *
239 * Return: number of available trip points, -ENODEV when data not available
240 */
241int of_thermal_get_ntrips(struct thermal_zone_device *tz)
242{
243 struct __thermal_zone *data = tz->devdata;
244
245 if (!data || IS_ERR(data))
246 return -ENODEV;
247
248 return data->ntrips;
249}
250EXPORT_SYMBOL_GPL(of_thermal_get_ntrips);
251
Lukasz Majewskia9bf2cc2014-12-08 18:04:18 +0100252/**
253 * of_thermal_is_trip_valid - function to check if trip point is valid
254 *
255 * @tz: pointer to a thermal zone
256 * @trip: trip point to evaluate
257 *
258 * This function is responsible for checking if passed trip point is valid
259 *
260 * Return: true if trip point is valid, false otherwise
261 */
262bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip)
263{
264 struct __thermal_zone *data = tz->devdata;
265
266 if (!data || trip >= data->ntrips || trip < 0)
267 return false;
268
269 return true;
270}
271EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid);
272
Lukasz Majewskice8be772014-12-08 18:04:20 +0100273/**
274 * of_thermal_get_trip_points - function to get access to a globally exported
275 * trip points
276 *
277 * @tz: pointer to a thermal zone
278 *
279 * This function provides a pointer to trip points table
280 *
281 * Return: pointer to trip points table, NULL otherwise
282 */
Geert Uytterhoevenebc31932015-01-03 22:56:56 +0100283const struct thermal_trip *
Lukasz Majewskice8be772014-12-08 18:04:20 +0100284of_thermal_get_trip_points(struct thermal_zone_device *tz)
285{
286 struct __thermal_zone *data = tz->devdata;
287
288 if (!data)
289 return NULL;
290
291 return data->trips;
292}
293EXPORT_SYMBOL_GPL(of_thermal_get_trip_points);
294
Lukasz Majewski184a4bf2014-12-08 18:04:21 +0100295/**
296 * of_thermal_set_emul_temp - function to set emulated temperature
297 *
298 * @tz: pointer to a thermal zone
299 * @temp: temperature to set
300 *
301 * This function gives the ability to set emulated value of temperature,
302 * which is handy for debugging
303 *
304 * Return: zero on success, error code otherwise
305 */
306static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
Sascha Hauer17e83512015-07-24 08:12:54 +0200307 int temp)
Lukasz Majewski184a4bf2014-12-08 18:04:21 +0100308{
309 struct __thermal_zone *data = tz->devdata;
310
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700311 if (!data->senps || !data->senps->ops->set_emul_temp)
312 return -EINVAL;
313
314 return data->senps->ops->set_emul_temp(data->senps->sensor_data, temp);
Lukasz Majewski184a4bf2014-12-08 18:04:21 +0100315}
316
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400317static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
318 enum thermal_trend *trend)
319{
320 struct __thermal_zone *data = tz->devdata;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400321
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700322 if (!data->senps || !data->senps->ops->get_trend)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400323 return -EINVAL;
324
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700325 return data->senps->ops->get_trend(data->senps->sensor_data,
326 trip, trend);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400327}
328
329static int of_thermal_bind(struct thermal_zone_device *thermal,
330 struct thermal_cooling_device *cdev)
331{
332 struct __thermal_zone *data = thermal->devdata;
333 int i;
334
335 if (!data || IS_ERR(data))
336 return -ENODEV;
337
338 /* find where to bind */
339 for (i = 0; i < data->num_tbps; i++) {
340 struct __thermal_bind_params *tbp = data->tbps + i;
341
342 if (tbp->cooling_device == cdev->np) {
343 int ret;
344
345 ret = thermal_zone_bind_cooling_device(thermal,
346 tbp->trip_id, cdev,
Punit Agrawaldd354b82014-06-03 10:59:58 +0100347 tbp->max,
Kapileshwar Singh6cd9e9f2015-02-18 16:04:21 +0000348 tbp->min,
349 tbp->usage);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400350 if (ret)
351 return ret;
352 }
353 }
354
355 return 0;
356}
357
358static int of_thermal_unbind(struct thermal_zone_device *thermal,
359 struct thermal_cooling_device *cdev)
360{
361 struct __thermal_zone *data = thermal->devdata;
362 int i;
363
364 if (!data || IS_ERR(data))
365 return -ENODEV;
366
367 /* find where to unbind */
368 for (i = 0; i < data->num_tbps; i++) {
369 struct __thermal_bind_params *tbp = data->tbps + i;
370
371 if (tbp->cooling_device == cdev->np) {
372 int ret;
373
374 ret = thermal_zone_unbind_cooling_device(thermal,
375 tbp->trip_id, cdev);
376 if (ret)
377 return ret;
378 }
379 }
380
381 return 0;
382}
383
384static int of_thermal_get_mode(struct thermal_zone_device *tz,
385 enum thermal_device_mode *mode)
386{
387 struct __thermal_zone *data = tz->devdata;
388
389 *mode = data->mode;
390
391 return 0;
392}
393
394static int of_thermal_set_mode(struct thermal_zone_device *tz,
395 enum thermal_device_mode mode)
396{
397 struct __thermal_zone *data = tz->devdata;
398
399 mutex_lock(&tz->lock);
400
Anson Huang6ad98842018-07-31 00:56:49 +0800401 if (mode == THERMAL_DEVICE_ENABLED) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400402 tz->polling_delay = data->polling_delay;
Anson Huang6ad98842018-07-31 00:56:49 +0800403 tz->passive_delay = data->passive_delay;
404 } else {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400405 tz->polling_delay = 0;
Anson Huang6ad98842018-07-31 00:56:49 +0800406 tz->passive_delay = 0;
407 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400408
409 mutex_unlock(&tz->lock);
410
411 data->mode = mode;
Srinivas Pandruvada0e70f462016-08-26 16:21:16 -0700412 thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400413
414 return 0;
415}
416
417static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
418 enum thermal_trip_type *type)
419{
420 struct __thermal_zone *data = tz->devdata;
421
422 if (trip >= data->ntrips || trip < 0)
423 return -EDOM;
424
425 *type = data->trips[trip].type;
426
427 return 0;
428}
429
430static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
Sascha Hauer17e83512015-07-24 08:12:54 +0200431 int *temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400432{
433 struct __thermal_zone *data = tz->devdata;
434
435 if (trip >= data->ntrips || trip < 0)
436 return -EDOM;
437
Ram Chandrasekar32e8bfd2018-02-06 17:12:04 -0700438 if (data->senps && data->senps->ops->get_trip_temp) {
439 int ret;
440
441 ret = data->senps->ops->get_trip_temp(data->senps->sensor_data,
442 trip, temp);
443 if (ret)
444 return ret;
445 } else {
446 *temp = data->trips[trip].temperature;
447 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400448
449 return 0;
450}
451
452static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
Sascha Hauer17e83512015-07-24 08:12:54 +0200453 int temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400454{
455 struct __thermal_zone *data = tz->devdata;
456
457 if (trip >= data->ntrips || trip < 0)
458 return -EDOM;
459
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700460 if (data->senps && data->senps->ops->set_trip_temp) {
Wei Nic3509522016-03-29 18:29:17 +0800461 int ret;
462
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700463 ret = data->senps->ops->set_trip_temp(data->senps->sensor_data,
464 trip, temp);
Wei Nic3509522016-03-29 18:29:17 +0800465 if (ret)
466 return ret;
467 }
468
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400469 /* thermal framework should take care of data->mask & (1 << trip) */
470 data->trips[trip].temperature = temp;
471
472 return 0;
473}
474
475static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
Sascha Hauer17e83512015-07-24 08:12:54 +0200476 int *hyst)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400477{
478 struct __thermal_zone *data = tz->devdata;
479
480 if (trip >= data->ntrips || trip < 0)
481 return -EDOM;
482
483 *hyst = data->trips[trip].hysteresis;
484
485 return 0;
486}
487
488static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
Sascha Hauer17e83512015-07-24 08:12:54 +0200489 int hyst)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400490{
491 struct __thermal_zone *data = tz->devdata;
492
493 if (trip >= data->ntrips || trip < 0)
494 return -EDOM;
495
496 /* thermal framework should take care of data->mask & (1 << trip) */
497 data->trips[trip].hysteresis = hyst;
498
499 return 0;
500}
501
502static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
Sascha Hauer17e83512015-07-24 08:12:54 +0200503 int *temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400504{
505 struct __thermal_zone *data = tz->devdata;
506 int i;
507
508 for (i = 0; i < data->ntrips; i++)
509 if (data->trips[i].type == THERMAL_TRIP_CRITICAL) {
510 *temp = data->trips[i].temperature;
511 return 0;
512 }
513
514 return -EINVAL;
515}
516
Manaf Meethalavalappu Pallikunhi11175e32019-02-14 18:03:00 +0530517static bool of_thermal_is_wakeable(struct thermal_zone_device *tz)
518{
519 struct __thermal_zone *data = tz->devdata;
520
521 return data->is_wakeable;
522}
523
Lina Iyer01ffea22016-07-27 13:46:32 -0600524static int of_thermal_aggregate_trip_types(struct thermal_zone_device *tz,
525 unsigned int trip_type_mask, int *low, int *high)
526{
527 int min = INT_MIN;
528 int max = INT_MAX;
529 int tt, th, trip;
530 int temp = tz->temperature;
531 struct thermal_zone_device *zone = NULL;
532 struct __thermal_zone *data = tz->devdata;
533 struct list_head *head;
534 enum thermal_trip_type type = 0;
535
536 head = &data->senps->first_tz;
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600537 list_for_each_entry(data, head, list) {
Lina Iyer01ffea22016-07-27 13:46:32 -0600538 zone = data->tzd;
Ram Chandrasekar37c816d2017-08-25 12:34:02 -0600539 if (data->mode == THERMAL_DEVICE_DISABLED)
540 continue;
Lina Iyer01ffea22016-07-27 13:46:32 -0600541 for (trip = 0; trip < data->ntrips; trip++) {
542 of_thermal_get_trip_type(zone, trip, &type);
543 if (!(BIT(type) & trip_type_mask))
544 continue;
545
546 if (!zone->tzp->tracks_low) {
547 tt = data->trips[trip].temperature;
548 if (tt > temp && tt < max)
549 max = tt;
550 th = tt - data->trips[trip].hysteresis;
551 if (th < temp && th > min)
552 min = th;
553 } else {
554 tt = data->trips[trip].temperature;
555 if (tt < temp && tt > min)
556 min = tt;
557 th = tt + data->trips[trip].hysteresis;
558 if (th > temp && th < max)
559 max = th;
560 }
561 }
562 }
563
564 *high = max;
565 *low = min;
566
567 return 0;
568}
569
570/*
571 * of_thermal_aggregate_trip - aggregate trip temperatures across sibling
572 * thermal zones.
573 * @tz: pointer to the primary thermal zone.
574 * @type: the thermal trip type to be aggregated upon
575 * @low: the low trip threshold which the most lesser than the @temp
576 * @high: the high trip threshold which is the least greater than the @temp
577 */
578int of_thermal_aggregate_trip(struct thermal_zone_device *tz,
579 enum thermal_trip_type type,
580 int *low, int *high)
581{
582 if (type <= THERMAL_TRIP_CRITICAL)
583 return of_thermal_aggregate_trip_types(tz, BIT(type), low,
584 high);
585
586 return -EINVAL;
587}
588EXPORT_SYMBOL(of_thermal_aggregate_trip);
589
Ram Chandrasekarbae93a62018-11-02 12:23:40 -0600590static void handle_thermal_trip(struct thermal_zone_device *tz,
591 bool temp_valid, int trip_temp)
Ram Chandrasekarcf543602017-03-13 22:59:01 -0600592{
593 struct thermal_zone_device *zone;
594 struct __thermal_zone *data = tz->devdata;
595 struct list_head *head;
596
597 head = &data->senps->first_tz;
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600598 list_for_each_entry(data, head, list) {
Ram Chandrasekarcf543602017-03-13 22:59:01 -0600599 zone = data->tzd;
Ram Chandrasekar37c816d2017-08-25 12:34:02 -0600600 if (data->mode == THERMAL_DEVICE_DISABLED)
601 continue;
Ram Chandrasekarbae93a62018-11-02 12:23:40 -0600602 if (!temp_valid) {
603 thermal_zone_device_update(zone,
604 THERMAL_EVENT_UNSPECIFIED);
605 } else {
606 thermal_zone_device_update_temp(zone,
607 THERMAL_EVENT_UNSPECIFIED, trip_temp);
608 }
Ram Chandrasekarcf543602017-03-13 22:59:01 -0600609 }
610}
Ram Chandrasekarbae93a62018-11-02 12:23:40 -0600611
612/*
613 * of_thermal_handle_trip_temp - Handle thermal trip from sensors
614 *
615 * @tz: pointer to the primary thermal zone.
616 * @trip_temp: The temperature
617 */
618void of_thermal_handle_trip_temp(struct thermal_zone_device *tz,
619 int trip_temp)
620{
621 return handle_thermal_trip(tz, true, trip_temp);
622}
623EXPORT_SYMBOL(of_thermal_handle_trip_temp);
624
625/*
626 * of_thermal_handle_trip - Handle thermal trip from sensors
627 *
628 * @tz: pointer to the primary thermal zone.
629 */
630void of_thermal_handle_trip(struct thermal_zone_device *tz)
631{
632 return handle_thermal_trip(tz, false, 0);
633}
Ram Chandrasekarcf543602017-03-13 22:59:01 -0600634EXPORT_SYMBOL(of_thermal_handle_trip);
635
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400636static struct thermal_zone_device_ops of_thermal_ops = {
637 .get_mode = of_thermal_get_mode,
638 .set_mode = of_thermal_set_mode,
639
640 .get_trip_type = of_thermal_get_trip_type,
641 .get_trip_temp = of_thermal_get_trip_temp,
642 .set_trip_temp = of_thermal_set_trip_temp,
643 .get_trip_hyst = of_thermal_get_trip_hyst,
644 .set_trip_hyst = of_thermal_set_trip_hyst,
645 .get_crit_temp = of_thermal_get_crit_temp,
646
647 .bind = of_thermal_bind,
648 .unbind = of_thermal_unbind,
Manaf Meethalavalappu Pallikunhi11175e32019-02-14 18:03:00 +0530649
650 .is_wakeable = of_thermal_is_wakeable,
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400651};
652
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700653static struct thermal_zone_of_device_ops of_virt_ops = {
654 .get_temp = virt_sensor_read_temp,
655};
656
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400657/*** sensor API ***/
658
659static struct thermal_zone_device *
660thermal_zone_of_add_sensor(struct device_node *zone,
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700661 struct device_node *sensor,
662 struct __sensor_param *sens_param)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400663{
664 struct thermal_zone_device *tzd;
665 struct __thermal_zone *tz;
666
667 tzd = thermal_zone_get_zone_by_name(zone->name);
668 if (IS_ERR(tzd))
669 return ERR_PTR(-EPROBE_DEFER);
670
671 tz = tzd->devdata;
672
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700673 if (!sens_param->ops)
Eduardo Valentin2251aef2014-11-07 21:24:39 -0400674 return ERR_PTR(-EINVAL);
675
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400676 mutex_lock(&tzd->lock);
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700677 tz->senps = sens_param;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400678
679 tzd->ops->get_temp = of_thermal_get_temp;
680 tzd->ops->get_trend = of_thermal_get_trend;
Sascha Hauer826386e2016-06-22 16:42:02 +0800681
682 /*
683 * The thermal zone core will calculate the window if they have set the
684 * optional set_trips pointer.
685 */
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700686 if (sens_param->ops->set_trips)
Sascha Hauer826386e2016-06-22 16:42:02 +0800687 tzd->ops->set_trips = of_thermal_set_trips;
688
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700689 if (sens_param->ops->set_emul_temp)
Keerthye2fa7482016-06-02 14:24:50 +0530690 tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
691
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700692 list_add_tail(&tz->list, &sens_param->first_tz);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400693 mutex_unlock(&tzd->lock);
694
695 return tzd;
696}
697
698/**
699 * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
700 * @dev: a valid struct device pointer of a sensor device. Must contain
701 * a valid .of_node, for the sensor node.
702 * @sensor_id: a sensor identifier, in case the sensor IP has more
703 * than one sensors
704 * @data: a private pointer (owned by the caller) that will be passed
705 * back, when a temperature reading is needed.
Eduardo Valentin2251aef2014-11-07 21:24:39 -0400706 * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400707 *
708 * This function will search the list of thermal zones described in device
709 * tree and look for the zone that refer to the sensor device pointed by
710 * @dev->of_node as temperature providers. For the zone pointing to the
711 * sensor node, the sensor will be added to the DT thermal zone device.
712 *
713 * The thermal zone temperature is provided by the @get_temp function
714 * pointer. When called, it will have the private pointer @data back.
715 *
716 * The thermal zone temperature trend is provided by the @get_trend function
717 * pointer. When called, it will have the private pointer @data back.
718 *
719 * TODO:
720 * 01 - This function must enqueue the new sensor instead of using
721 * it as the only source of temperature values.
722 *
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400723 * Return: On success returns a valid struct thermal_zone_device,
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700724 * otherwise, it returns a corresponding ERR_PTR(). Incase there are multiple
725 * thermal zones referencing the same sensor, the return value will be
726 * thermal_zone_device pointer of the first thermal zone. Caller must
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400727 * check the return value with help of IS_ERR() helper.
728 */
729struct thermal_zone_device *
Eduardo Valentin2251aef2014-11-07 21:24:39 -0400730thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
731 const struct thermal_zone_of_device_ops *ops)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400732{
733 struct device_node *np, *child, *sensor_np;
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300734 struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700735 struct thermal_zone_device *first_tzd = NULL;
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700736 struct __sensor_param *sens_param = NULL;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400737
738 np = of_find_node_by_name(NULL, "thermal-zones");
739 if (!np)
740 return ERR_PTR(-ENODEV);
741
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300742 if (!dev || !dev->of_node) {
743 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400744 return ERR_PTR(-EINVAL);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300745 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400746
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700747 sens_param = kzalloc(sizeof(*sens_param), GFP_KERNEL);
748 if (!sens_param) {
749 of_node_put(np);
750 return ERR_PTR(-ENOMEM);
751 }
752 sens_param->sensor_data = data;
753 sens_param->ops = ops;
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700754 INIT_LIST_HEAD(&sens_param->first_tz);
Lina Iyer01ffea22016-07-27 13:46:32 -0600755 sens_param->trip_high = INT_MAX;
756 sens_param->trip_low = INT_MIN;
757 mutex_init(&sens_param->lock);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300758 sensor_np = of_node_get(dev->of_node);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400759
Laxman Dewangan42bbe402016-02-08 18:58:34 +0530760 for_each_available_child_of_node(np, child) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400761 struct of_phandle_args sensor_specs;
762 int ret, id;
Ram Chandrasekar07479402017-08-25 14:04:42 -0600763 struct __thermal_zone *tz;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400764
765 /* For now, thermal framework supports only 1 sensor per zone */
766 ret = of_parse_phandle_with_args(child, "thermal-sensors",
767 "#thermal-sensor-cells",
768 0, &sensor_specs);
769 if (ret)
770 continue;
771
772 if (sensor_specs.args_count >= 1) {
773 id = sensor_specs.args[0];
774 WARN(sensor_specs.args_count > 1,
775 "%s: too many cells in sensor specifier %d\n",
776 sensor_specs.np->name, sensor_specs.args_count);
777 } else {
778 id = 0;
779 }
780
781 if (sensor_specs.np == sensor_np && id == sensor_id) {
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300782 tzd = thermal_zone_of_add_sensor(child, sensor_np,
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700783 sens_param);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700784 if (!IS_ERR(tzd)) {
785 if (!first_tzd)
786 first_tzd = tzd;
Ram Chandrasekar07479402017-08-25 14:04:42 -0600787 tz = tzd->devdata;
788 if (!tz->default_disable)
789 tzd->ops->set_mode(tzd,
790 THERMAL_DEVICE_ENABLED);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700791 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400792 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300793 of_node_put(sensor_specs.np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400794 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300795 of_node_put(sensor_np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400796 of_node_put(np);
797
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700798 if (!first_tzd) {
799 first_tzd = ERR_PTR(-ENODEV);
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700800 kfree(sens_param);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700801 }
802 return first_tzd;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400803}
804EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
805
806/**
807 * thermal_zone_of_sensor_unregister - unregisters a sensor from a DT thermal zone
808 * @dev: a valid struct device pointer of a sensor device. Must contain
809 * a valid .of_node, for the sensor node.
810 * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
811 *
812 * This function removes the sensor callbacks and private data from the
813 * thermal zone device registered with thermal_zone_of_sensor_register()
814 * API. It will also silent the zone by remove the .get_temp() and .get_trend()
815 * thermal zone device callbacks.
816 *
817 * TODO: When the support to several sensors per zone is added, this
818 * function must search the sensor list based on @dev parameter.
819 *
820 */
821void thermal_zone_of_sensor_unregister(struct device *dev,
822 struct thermal_zone_device *tzd)
823{
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600824 struct __thermal_zone *tz, *next;
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700825 struct thermal_zone_device *pos;
826 struct list_head *head;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400827
828 if (!dev || !tzd || !tzd->devdata)
829 return;
830
831 tz = tzd->devdata;
832
833 /* no __thermal_zone, nothing to be done */
834 if (!tz)
835 return;
836
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700837 head = &tz->senps->first_tz;
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600838 list_for_each_entry_safe(tz, next, head, list) {
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700839 pos = tz->tzd;
840 mutex_lock(&pos->lock);
841 pos->ops->get_temp = NULL;
842 pos->ops->get_trend = NULL;
843 pos->ops->set_emul_temp = NULL;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400844
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700845 list_del(&tz->list);
846 if (list_empty(&tz->senps->first_tz))
847 kfree(tz->senps);
848 tz->senps = NULL;
849 mutex_unlock(&pos->lock);
850 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400851}
852EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
853
Laxman Dewangane498b492016-03-09 18:40:06 +0530854static void devm_thermal_zone_of_sensor_release(struct device *dev, void *res)
855{
856 thermal_zone_of_sensor_unregister(dev,
857 *(struct thermal_zone_device **)res);
858}
859
860static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
861 void *data)
862{
863 struct thermal_zone_device **r = res;
864
865 if (WARN_ON(!r || !*r))
866 return 0;
867
868 return *r == data;
869}
870
871/**
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700872 * devm_thermal_of_virtual_sensor_register - Register a virtual sensor.
873 * Three types of virtual sensors are supported.
874 * 1. Weighted aggregation type:
875 * Virtual sensor of this type calculates the weighted aggregation
876 * of sensor temperatures using the below formula,
877 * temp = (sensor_1_temp * coeff_1 + ... + sensor_n_temp * coeff_n)
878 * + avg_offset / avg_denominator
879 * So the sensor drivers has to specify n+2 coefficients.
880 * 2. Maximum type:
881 * Virtual sensors of this type will report the maximum of all
882 * sensor temperatures.
883 * 3. Minimum type:
884 * Virtual sensors of this type will report the minimum of all
885 * sensor temperatures.
886 *
887 * @input arguments:
888 * @dev: Virtual sensor driver device pointer.
889 * @sensor_data: Virtual sensor data supported for the device.
890 *
891 * @return: Returns a virtual thermal zone pointer. Returns error if thermal
892 * zone is not created. Returns -EAGAIN, if the sensor that is required for
893 * this virtual sensor temperature estimation is not registered yet. The
894 * sensor driver can try again later.
895 */
896struct thermal_zone_device *devm_thermal_of_virtual_sensor_register(
897 struct device *dev,
898 const struct virtual_sensor_data *sensor_data)
899{
900 int sens_idx = 0;
901 struct virtual_sensor *sens;
902 struct __thermal_zone *tz;
903 struct thermal_zone_device **ptr;
904 struct thermal_zone_device *tzd;
905 struct __sensor_param *sens_param = NULL;
906 enum thermal_device_mode mode;
907
908 if (!dev || !sensor_data)
909 return ERR_PTR(-EINVAL);
910
911 tzd = thermal_zone_get_zone_by_name(
912 sensor_data->virt_zone_name);
913 if (IS_ERR(tzd)) {
Manaf Meethalavalappu Pallikunhi3d62c3f2017-08-23 21:39:35 +0530914 dev_dbg(dev, "sens:%s not available err: %ld\n",
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700915 sensor_data->virt_zone_name,
916 PTR_ERR(tzd));
917 return tzd;
918 }
919
920 mutex_lock(&tzd->lock);
921 /*
922 * Check if the virtual zone is registered and enabled.
923 * If so return the registered thermal zone.
924 */
925 tzd->ops->get_mode(tzd, &mode);
926 mutex_unlock(&tzd->lock);
927 if (mode == THERMAL_DEVICE_ENABLED)
928 return tzd;
929
930 sens = devm_kzalloc(dev, sizeof(*sens), GFP_KERNEL);
931 if (!sens)
932 return ERR_PTR(-ENOMEM);
933
Ram Chandrasekar02592fa2017-05-16 17:03:02 -0600934 sens->virt_tz = tzd;
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700935 sens->logic = sensor_data->logic;
936 sens->num_sensors = sensor_data->num_sensors;
937 if (sens->logic == VIRT_WEIGHTED_AVG) {
938 int coeff_ct = sensor_data->coefficient_ct;
939
940 /*
941 * For weighted aggregation, sensor drivers has to specify
942 * n+2 coefficients.
943 */
944 if (coeff_ct != sens->num_sensors) {
945 dev_err(dev, "sens:%s Invalid coefficient\n",
946 sensor_data->virt_zone_name);
947 return ERR_PTR(-EINVAL);
948 }
949 memcpy(sens->coefficients, sensor_data->coefficients,
950 coeff_ct * sizeof(*sens->coefficients));
951 sens->avg_offset = sensor_data->avg_offset;
952 sens->avg_denominator = sensor_data->avg_denominator;
953 }
954
955 for (sens_idx = 0; sens_idx < sens->num_sensors; sens_idx++) {
956 sens->tz[sens_idx] = thermal_zone_get_zone_by_name(
957 sensor_data->sensor_names[sens_idx]);
958 if (IS_ERR(sens->tz[sens_idx])) {
959 dev_err(dev, "sens:%s sensor[%s] fetch err:%ld\n",
960 sensor_data->virt_zone_name,
961 sensor_data->sensor_names[sens_idx],
962 PTR_ERR(sens->tz[sens_idx]));
963 break;
964 }
965 }
966 if (sens->num_sensors != sens_idx)
967 return ERR_PTR(-EAGAIN);
968
969 sens_param = kzalloc(sizeof(*sens_param), GFP_KERNEL);
970 if (!sens_param)
971 return ERR_PTR(-ENOMEM);
972 sens_param->sensor_data = sens;
973 sens_param->ops = &of_virt_ops;
974 INIT_LIST_HEAD(&sens_param->first_tz);
975 sens_param->trip_high = INT_MAX;
976 sens_param->trip_low = INT_MIN;
977 mutex_init(&sens_param->lock);
978
979 mutex_lock(&tzd->lock);
980 tz = tzd->devdata;
981 tz->senps = sens_param;
982 tzd->ops->get_temp = of_thermal_get_temp;
983 list_add_tail(&tz->list, &sens_param->first_tz);
984 mutex_unlock(&tzd->lock);
985
986 ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
987 GFP_KERNEL);
988 if (!ptr)
989 return ERR_PTR(-ENOMEM);
990
991 *ptr = tzd;
992 devres_add(dev, ptr);
993
Ram Chandrasekar07479402017-08-25 14:04:42 -0600994 if (!tz->default_disable)
995 tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700996
997 return tzd;
998}
999EXPORT_SYMBOL(devm_thermal_of_virtual_sensor_register);
1000
1001/**
Laxman Dewangane498b492016-03-09 18:40:06 +05301002 * devm_thermal_zone_of_sensor_register - Resource managed version of
1003 * thermal_zone_of_sensor_register()
1004 * @dev: a valid struct device pointer of a sensor device. Must contain
1005 * a valid .of_node, for the sensor node.
1006 * @sensor_id: a sensor identifier, in case the sensor IP has more
1007 * than one sensors
1008 * @data: a private pointer (owned by the caller) that will be passed
1009 * back, when a temperature reading is needed.
1010 * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
1011 *
1012 * Refer thermal_zone_of_sensor_register() for more details.
1013 *
1014 * Return: On success returns a valid struct thermal_zone_device,
1015 * otherwise, it returns a corresponding ERR_PTR(). Caller must
1016 * check the return value with help of IS_ERR() helper.
Zhang Rui7b5c4a02016-08-22 15:48:11 +08001017 * Registered thermal_zone_device device will automatically be
Laxman Dewangane498b492016-03-09 18:40:06 +05301018 * released when device is unbounded.
1019 */
1020struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
1021 struct device *dev, int sensor_id,
1022 void *data, const struct thermal_zone_of_device_ops *ops)
1023{
1024 struct thermal_zone_device **ptr, *tzd;
1025
1026 ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
1027 GFP_KERNEL);
1028 if (!ptr)
1029 return ERR_PTR(-ENOMEM);
1030
1031 tzd = thermal_zone_of_sensor_register(dev, sensor_id, data, ops);
1032 if (IS_ERR(tzd)) {
1033 devres_free(ptr);
1034 return tzd;
1035 }
1036
1037 *ptr = tzd;
1038 devres_add(dev, ptr);
1039
1040 return tzd;
1041}
1042EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_register);
1043
1044/**
1045 * devm_thermal_zone_of_sensor_unregister - Resource managed version of
1046 * thermal_zone_of_sensor_unregister().
1047 * @dev: Device for which which resource was allocated.
1048 * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
1049 *
1050 * This function removes the sensor callbacks and private data from the
1051 * thermal zone device registered with devm_thermal_zone_of_sensor_register()
1052 * API. It will also silent the zone by remove the .get_temp() and .get_trend()
1053 * thermal zone device callbacks.
1054 * Normally this function will not need to be called and the resource
1055 * management code will ensure that the resource is freed.
1056 */
1057void devm_thermal_zone_of_sensor_unregister(struct device *dev,
1058 struct thermal_zone_device *tzd)
1059{
1060 WARN_ON(devres_release(dev, devm_thermal_zone_of_sensor_release,
1061 devm_thermal_zone_of_sensor_match, tzd));
1062}
1063EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_unregister);
1064
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001065/*** functions parsing device tree nodes ***/
1066
1067/**
1068 * thermal_of_populate_bind_params - parse and fill cooling map data
1069 * @np: DT node containing a cooling-map node
1070 * @__tbp: data structure to be filled with cooling map info
1071 * @trips: array of thermal zone trip points
1072 * @ntrips: number of trip points inside trips.
1073 *
1074 * This function parses a cooling-map type of node represented by
1075 * @np parameter and fills the read data into @__tbp data structure.
1076 * It needs the already parsed array of trip points of the thermal zone
1077 * in consideration.
1078 *
1079 * Return: 0 on success, proper error code otherwise
1080 */
1081static int thermal_of_populate_bind_params(struct device_node *np,
1082 struct __thermal_bind_params *__tbp,
Lukasz Majewskiad9914a2014-12-08 18:04:19 +01001083 struct thermal_trip *trips,
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001084 int ntrips)
1085{
1086 struct of_phandle_args cooling_spec;
1087 struct device_node *trip;
1088 int ret, i;
1089 u32 prop;
1090
1091 /* Default weight. Usage is optional */
Kapileshwar Singh6cd9e9f2015-02-18 16:04:21 +00001092 __tbp->usage = THERMAL_WEIGHT_DEFAULT;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001093 ret = of_property_read_u32(np, "contribution", &prop);
1094 if (ret == 0)
1095 __tbp->usage = prop;
1096
1097 trip = of_parse_phandle(np, "trip", 0);
1098 if (!trip) {
1099 pr_err("missing trip property\n");
1100 return -ENODEV;
1101 }
1102
1103 /* match using device_node */
1104 for (i = 0; i < ntrips; i++)
1105 if (trip == trips[i].np) {
1106 __tbp->trip_id = i;
1107 break;
1108 }
1109
1110 if (i == ntrips) {
1111 ret = -ENODEV;
1112 goto end;
1113 }
1114
1115 ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
1116 0, &cooling_spec);
1117 if (ret < 0) {
1118 pr_err("missing cooling_device property\n");
1119 goto end;
1120 }
1121 __tbp->cooling_device = cooling_spec.np;
1122 if (cooling_spec.args_count >= 2) { /* at least min and max */
1123 __tbp->min = cooling_spec.args[0];
1124 __tbp->max = cooling_spec.args[1];
1125 } else {
1126 pr_err("wrong reference to cooling device, missing limits\n");
1127 }
1128
1129end:
1130 of_node_put(trip);
1131
1132 return ret;
1133}
1134
1135/**
1136 * It maps 'enum thermal_trip_type' found in include/linux/thermal.h
1137 * into the device tree binding of 'trip', property type.
1138 */
1139static const char * const trip_types[] = {
1140 [THERMAL_TRIP_ACTIVE] = "active",
1141 [THERMAL_TRIP_PASSIVE] = "passive",
1142 [THERMAL_TRIP_HOT] = "hot",
1143 [THERMAL_TRIP_CRITICAL] = "critical",
1144};
1145
1146/**
1147 * thermal_of_get_trip_type - Get phy mode for given device_node
1148 * @np: Pointer to the given device_node
1149 * @type: Pointer to resulting trip type
1150 *
1151 * The function gets trip type string from property 'type',
1152 * and store its index in trip_types table in @type,
1153 *
1154 * Return: 0 on success, or errno in error case.
1155 */
1156static int thermal_of_get_trip_type(struct device_node *np,
1157 enum thermal_trip_type *type)
1158{
1159 const char *t;
1160 int err, i;
1161
1162 err = of_property_read_string(np, "type", &t);
1163 if (err < 0)
1164 return err;
1165
1166 for (i = 0; i < ARRAY_SIZE(trip_types); i++)
1167 if (!strcasecmp(t, trip_types[i])) {
1168 *type = i;
1169 return 0;
1170 }
1171
1172 return -ENODEV;
1173}
1174
1175/**
1176 * thermal_of_populate_trip - parse and fill one trip point data
1177 * @np: DT node containing a trip point node
1178 * @trip: trip point data structure to be filled up
1179 *
1180 * This function parses a trip point type of node represented by
1181 * @np parameter and fills the read data into @trip data structure.
1182 *
1183 * Return: 0 on success, proper error code otherwise
1184 */
1185static int thermal_of_populate_trip(struct device_node *np,
Lukasz Majewskiad9914a2014-12-08 18:04:19 +01001186 struct thermal_trip *trip)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001187{
1188 int prop;
1189 int ret;
1190
1191 ret = of_property_read_u32(np, "temperature", &prop);
1192 if (ret < 0) {
1193 pr_err("missing temperature property\n");
1194 return ret;
1195 }
1196 trip->temperature = prop;
1197
1198 ret = of_property_read_u32(np, "hysteresis", &prop);
1199 if (ret < 0) {
1200 pr_err("missing hysteresis property\n");
1201 return ret;
1202 }
1203 trip->hysteresis = prop;
1204
1205 ret = thermal_of_get_trip_type(np, &trip->type);
1206 if (ret < 0) {
1207 pr_err("wrong trip type property\n");
1208 return ret;
1209 }
1210
1211 /* Required for cooling map matching */
1212 trip->np = np;
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001213 of_node_get(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001214
1215 return 0;
1216}
1217
1218/**
1219 * thermal_of_build_thermal_zone - parse and fill one thermal zone data
1220 * @np: DT node containing a thermal zone node
1221 *
1222 * This function parses a thermal zone type of node represented by
1223 * @np parameter and fills the read data into a __thermal_zone data structure
1224 * and return this pointer.
1225 *
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001226 * TODO: Missing properties to parse: thermal-sensor-names
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001227 *
1228 * Return: On success returns a valid struct __thermal_zone,
1229 * otherwise, it returns a corresponding ERR_PTR(). Caller must
1230 * check the return value with help of IS_ERR() helper.
1231 */
Julia Lawallc0ff8aa2016-04-19 14:33:32 +02001232static struct __thermal_zone
1233__init *thermal_of_build_thermal_zone(struct device_node *np)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001234{
1235 struct device_node *child = NULL, *gchild;
1236 struct __thermal_zone *tz;
1237 int ret, i;
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001238 u32 prop, coef[2];
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001239
1240 if (!np) {
1241 pr_err("no thermal zone np\n");
1242 return ERR_PTR(-EINVAL);
1243 }
1244
1245 tz = kzalloc(sizeof(*tz), GFP_KERNEL);
1246 if (!tz)
1247 return ERR_PTR(-ENOMEM);
1248
Ram Chandrasekard29230b2017-02-27 11:26:51 -07001249 INIT_LIST_HEAD(&tz->list);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001250 ret = of_property_read_u32(np, "polling-delay-passive", &prop);
1251 if (ret < 0) {
1252 pr_err("missing polling-delay-passive property\n");
1253 goto free_tz;
1254 }
1255 tz->passive_delay = prop;
1256
1257 ret = of_property_read_u32(np, "polling-delay", &prop);
1258 if (ret < 0) {
1259 pr_err("missing polling-delay property\n");
1260 goto free_tz;
1261 }
1262 tz->polling_delay = prop;
1263
Ram Chandrasekar07479402017-08-25 14:04:42 -06001264 tz->default_disable = of_property_read_bool(np,
1265 "disable-thermal-zone");
Manaf Meethalavalappu Pallikunhi11175e32019-02-14 18:03:00 +05301266
1267 tz->is_wakeable = of_property_read_bool(np,
1268 "wake-capable-sensor");
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001269 /*
1270 * REVIST: for now, the thermal framework supports only
1271 * one sensor per thermal zone. Thus, we are considering
1272 * only the first two values as slope and offset.
1273 */
1274 ret = of_property_read_u32_array(np, "coefficients", coef, 2);
1275 if (ret == 0) {
1276 tz->slope = coef[0];
1277 tz->offset = coef[1];
1278 } else {
1279 tz->slope = 1;
1280 tz->offset = 0;
1281 }
1282
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001283 /* trips */
1284 child = of_get_child_by_name(np, "trips");
1285
1286 /* No trips provided */
1287 if (!child)
1288 goto finish;
1289
1290 tz->ntrips = of_get_child_count(child);
1291 if (tz->ntrips == 0) /* must have at least one child */
1292 goto finish;
1293
1294 tz->trips = kzalloc(tz->ntrips * sizeof(*tz->trips), GFP_KERNEL);
1295 if (!tz->trips) {
1296 ret = -ENOMEM;
1297 goto free_tz;
1298 }
1299
1300 i = 0;
1301 for_each_child_of_node(child, gchild) {
1302 ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
1303 if (ret)
1304 goto free_trips;
1305 }
1306
1307 of_node_put(child);
1308
1309 /* cooling-maps */
1310 child = of_get_child_by_name(np, "cooling-maps");
1311
1312 /* cooling-maps not provided */
1313 if (!child)
1314 goto finish;
1315
1316 tz->num_tbps = of_get_child_count(child);
1317 if (tz->num_tbps == 0)
1318 goto finish;
1319
1320 tz->tbps = kzalloc(tz->num_tbps * sizeof(*tz->tbps), GFP_KERNEL);
1321 if (!tz->tbps) {
1322 ret = -ENOMEM;
1323 goto free_trips;
1324 }
1325
1326 i = 0;
Stephen Boydca9521b2014-06-18 16:32:08 -07001327 for_each_child_of_node(child, gchild) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001328 ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
1329 tz->trips, tz->ntrips);
1330 if (ret)
1331 goto free_tbps;
Stephen Boydca9521b2014-06-18 16:32:08 -07001332 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001333
1334finish:
1335 of_node_put(child);
1336 tz->mode = THERMAL_DEVICE_DISABLED;
1337
1338 return tz;
1339
1340free_tbps:
Ulises Brindis1cd91c12016-03-25 12:55:41 -07001341 for (i = i - 1; i >= 0; i--)
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001342 of_node_put(tz->tbps[i].cooling_device);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001343 kfree(tz->tbps);
1344free_trips:
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001345 for (i = 0; i < tz->ntrips; i++)
1346 of_node_put(tz->trips[i].np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001347 kfree(tz->trips);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001348 of_node_put(gchild);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001349free_tz:
1350 kfree(tz);
1351 of_node_put(child);
1352
1353 return ERR_PTR(ret);
1354}
1355
1356static inline void of_thermal_free_zone(struct __thermal_zone *tz)
1357{
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001358 int i;
1359
1360 for (i = 0; i < tz->num_tbps; i++)
1361 of_node_put(tz->tbps[i].cooling_device);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001362 kfree(tz->tbps);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001363 for (i = 0; i < tz->ntrips; i++)
1364 of_node_put(tz->trips[i].np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001365 kfree(tz->trips);
1366 kfree(tz);
1367}
1368
1369/**
1370 * of_parse_thermal_zones - parse device tree thermal data
1371 *
1372 * Initialization function that can be called by machine initialization
1373 * code to parse thermal data and populate the thermal framework
1374 * with hardware thermal zones info. This function only parses thermal zones.
1375 * Cooling devices and sensor devices nodes are supposed to be parsed
1376 * by their respective drivers.
1377 *
1378 * Return: 0 on success, proper error code otherwise
1379 *
1380 */
1381int __init of_parse_thermal_zones(void)
1382{
1383 struct device_node *np, *child;
1384 struct __thermal_zone *tz;
1385 struct thermal_zone_device_ops *ops;
1386
1387 np = of_find_node_by_name(NULL, "thermal-zones");
1388 if (!np) {
1389 pr_debug("unable to find thermal zones\n");
1390 return 0; /* Run successfully on systems without thermal DT */
1391 }
1392
Laxman Dewangan42bbe402016-02-08 18:58:34 +05301393 for_each_available_child_of_node(np, child) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001394 struct thermal_zone_device *zone;
1395 struct thermal_zone_params *tzp;
Punit Agrawal76af5492015-03-03 10:43:04 +00001396 int i, mask = 0;
Punit Agrawal647f9922015-02-26 19:00:32 +00001397 u32 prop;
Ram Chandrasekarcaafe202016-07-19 11:25:46 -06001398 const char *governor_name;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001399
1400 tz = thermal_of_build_thermal_zone(child);
1401 if (IS_ERR(tz)) {
1402 pr_err("failed to build thermal zone %s: %ld\n",
1403 child->name,
1404 PTR_ERR(tz));
1405 continue;
1406 }
1407
1408 ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
1409 if (!ops)
1410 goto exit_free;
1411
1412 tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
1413 if (!tzp) {
1414 kfree(ops);
1415 goto exit_free;
1416 }
1417
1418 /* No hwmon because there might be hwmon drivers registering */
1419 tzp->no_hwmon = true;
1420
Ram Chandrasekarcaafe202016-07-19 11:25:46 -06001421 if (!of_property_read_string(child, "thermal-governor",
1422 &governor_name))
1423 strlcpy(tzp->governor_name, governor_name,
1424 THERMAL_NAME_LENGTH);
1425
Punit Agrawal647f9922015-02-26 19:00:32 +00001426 if (!of_property_read_u32(child, "sustainable-power", &prop))
1427 tzp->sustainable_power = prop;
1428
Punit Agrawal76af5492015-03-03 10:43:04 +00001429 for (i = 0; i < tz->ntrips; i++)
1430 mask |= 1 << i;
1431
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001432 /* these two are left for temperature drivers to use */
1433 tzp->slope = tz->slope;
1434 tzp->offset = tz->offset;
1435
Lina Iyer159f67d2016-07-27 11:34:46 -06001436 if (of_property_read_bool(child, "tracks-low"))
1437 tzp->tracks_low = true;
1438
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001439 zone = thermal_zone_device_register(child->name, tz->ntrips,
Punit Agrawal76af5492015-03-03 10:43:04 +00001440 mask, tz,
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001441 ops, tzp,
1442 tz->passive_delay,
1443 tz->polling_delay);
1444 if (IS_ERR(zone)) {
1445 pr_err("Failed to build %s zone %ld\n", child->name,
1446 PTR_ERR(zone));
1447 kfree(tzp);
1448 kfree(ops);
1449 of_thermal_free_zone(tz);
1450 /* attempting to build remaining zones still */
Ram Chandrasekard29230b2017-02-27 11:26:51 -07001451 continue;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001452 }
Ram Chandrasekard29230b2017-02-27 11:26:51 -07001453 tz->tzd = zone;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001454 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001455 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001456
1457 return 0;
1458
1459exit_free:
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001460 of_node_put(child);
1461 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001462 of_thermal_free_zone(tz);
1463
1464 /* no memory available, so free what we have built */
1465 of_thermal_destroy_zones();
1466
1467 return -ENOMEM;
1468}
1469
1470/**
1471 * of_thermal_destroy_zones - remove all zones parsed and allocated resources
1472 *
1473 * Finds all zones parsed and added to the thermal framework and remove them
1474 * from the system, together with their resources.
1475 *
1476 */
1477void of_thermal_destroy_zones(void)
1478{
1479 struct device_node *np, *child;
1480
1481 np = of_find_node_by_name(NULL, "thermal-zones");
1482 if (!np) {
Jiada Wang28524982015-11-16 17:10:05 +09001483 pr_debug("unable to find thermal zones\n");
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001484 return;
1485 }
1486
Laxman Dewangan42bbe402016-02-08 18:58:34 +05301487 for_each_available_child_of_node(np, child) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001488 struct thermal_zone_device *zone;
1489
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001490 zone = thermal_zone_get_zone_by_name(child->name);
1491 if (IS_ERR(zone))
1492 continue;
1493
1494 thermal_zone_device_unregister(zone);
1495 kfree(zone->tzp);
1496 kfree(zone->ops);
1497 of_thermal_free_zone(zone->devdata);
1498 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001499 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001500}