blob: 9f675cdfaafdf3524fdbd8baf8d48882d6e7039f [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 Chandrasekard29230b2017-02-27 11:26:51 -070083 * @tzd: thermal zone device pointer for this sensor
Eduardo Valentin4e5e4702013-07-03 15:35:39 -040084 * @ntrips: number of trip points
85 * @trips: an array of trip points (0..ntrips - 1)
86 * @num_tbps: number of thermal bind params
87 * @tbps: an array of thermal bind params (0..num_tbps - 1)
Ram Chandrasekard29230b2017-02-27 11:26:51 -070088 * @list: sibling thermal zone pointer
Ram Chandrasekare34ced62017-03-03 11:22:50 -070089 * @senps: sensor related parameters
Eduardo Valentin4e5e4702013-07-03 15:35:39 -040090 */
91
92struct __thermal_zone {
93 enum thermal_device_mode mode;
94 int passive_delay;
95 int polling_delay;
Eduardo Valentina46dbae2015-05-11 19:48:09 -070096 int slope;
97 int offset;
Ram Chandrasekard29230b2017-02-27 11:26:51 -070098 struct thermal_zone_device *tzd;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -040099
100 /* trip data */
101 int ntrips;
Lukasz Majewskiad9914a2014-12-08 18:04:19 +0100102 struct thermal_trip *trips;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400103
104 /* cooling binding data */
105 int num_tbps;
106 struct __thermal_bind_params *tbps;
107
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700108 struct list_head list;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400109 /* sensor interface */
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700110 struct __sensor_param *senps;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400111};
112
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700113/**
114 * struct virtual_sensor - internal representation of a virtual thermal zone
115 * @num_sensors - number of sensors this virtual sensor will reference to
116 * estimate temperature
117 * @tz - Array of thermal zones of the sensors this virtual sensor will use
118 * to estimate temperature
Ram Chandrasekar02592fa2017-05-16 17:03:02 -0600119 * @virt_tz - Virtual thermal zone pointer
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700120 * @logic - aggregation logic to be used to estimate the temperature
121 * @last_reading - last estimated temperature
122 * @coefficients - array of coefficients to be used for weighted aggregation
123 * logic
124 * @avg_offset - offset value to be used for the weighted aggregation logic
125 * @avg_denominator - denominator value to be used for the weighted aggregation
126 * logic
127 */
128struct virtual_sensor {
129 int num_sensors;
130 struct thermal_zone_device *tz[THERMAL_MAX_VIRT_SENSORS];
Ram Chandrasekar02592fa2017-05-16 17:03:02 -0600131 struct thermal_zone_device *virt_tz;
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700132 enum aggregation_logic logic;
133 int last_reading;
134 int coefficients[THERMAL_MAX_VIRT_SENSORS];
135 int avg_offset;
136 int avg_denominator;
137};
138
Lina Iyer01ffea22016-07-27 13:46:32 -0600139static int of_thermal_aggregate_trip_types(struct thermal_zone_device *tz,
140 unsigned int trip_type_mask, int *low, int *high);
141
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400142/*** DT thermal zone device callbacks ***/
143
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700144static int virt_sensor_read_temp(void *data, int *val)
145{
146 struct virtual_sensor *sens = data;
147 int idx, temp = 0, ret = 0;
148
149 for (idx = 0; idx < sens->num_sensors; idx++) {
150 int sens_temp = 0;
151
152 ret = thermal_zone_get_temp(sens->tz[idx], &sens_temp);
153 if (ret) {
154 pr_err("virt zone: sensor[%s] read error:%d\n",
155 sens->tz[idx]->type, ret);
156 return ret;
157 }
158 switch (sens->logic) {
159 case VIRT_WEIGHTED_AVG:
160 temp += sens_temp * sens->coefficients[idx];
161 if (idx == (sens->num_sensors - 1))
162 temp = (temp + sens->avg_offset)
163 / sens->avg_denominator;
164 break;
165 case VIRT_MAXIMUM:
166 if (idx == 0)
167 temp = INT_MIN;
168 if (sens_temp > temp)
169 temp = sens_temp;
170 break;
171 case VIRT_MINIMUM:
172 if (idx == 0)
173 temp = INT_MAX;
174 if (sens_temp < temp)
175 temp = sens_temp;
176 break;
177 default:
178 break;
179 }
Ram Chandrasekar02592fa2017-05-16 17:03:02 -0600180 trace_virtual_temperature(sens->virt_tz, sens->tz[idx],
181 sens_temp, temp);
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700182 }
183
184 sens->last_reading = *val = temp;
185
186 return 0;
187}
188
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400189static int of_thermal_get_temp(struct thermal_zone_device *tz,
Sascha Hauer17e83512015-07-24 08:12:54 +0200190 int *temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400191{
192 struct __thermal_zone *data = tz->devdata;
193
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700194 if (!data->senps || !data->senps->ops->get_temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400195 return -EINVAL;
Ram Chandrasekar37c816d2017-08-25 12:34:02 -0600196 if (data->mode == THERMAL_DEVICE_DISABLED) {
197 *temp = tz->tzp->tracks_low ?
198 THERMAL_TEMP_INVALID_LOW :
199 THERMAL_TEMP_INVALID;
200 return 0;
201 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400202
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700203 return data->senps->ops->get_temp(data->senps->sensor_data, temp);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400204}
205
Sascha Hauer826386e2016-06-22 16:42:02 +0800206static int of_thermal_set_trips(struct thermal_zone_device *tz,
Lina Iyer01ffea22016-07-27 13:46:32 -0600207 int inp_low, int inp_high)
Sascha Hauer826386e2016-06-22 16:42:02 +0800208{
209 struct __thermal_zone *data = tz->devdata;
Lina Iyer01ffea22016-07-27 13:46:32 -0600210 int high = INT_MAX, low = INT_MIN, ret = 0;
Sascha Hauer826386e2016-06-22 16:42:02 +0800211
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700212 if (!data->senps || !data->senps->ops->set_trips)
Sascha Hauer826386e2016-06-22 16:42:02 +0800213 return -EINVAL;
214
Lina Iyer01ffea22016-07-27 13:46:32 -0600215 mutex_lock(&data->senps->lock);
216 of_thermal_aggregate_trip_types(tz, GENMASK(THERMAL_TRIP_CRITICAL, 0),
217 &low, &high);
Lina Iyer01ffea22016-07-27 13:46:32 -0600218 data->senps->trip_low = low;
219 data->senps->trip_high = high;
220 ret = data->senps->ops->set_trips(data->senps->sensor_data,
221 low, high);
222
Lina Iyer01ffea22016-07-27 13:46:32 -0600223 mutex_unlock(&data->senps->lock);
224 return ret;
Sascha Hauer826386e2016-06-22 16:42:02 +0800225}
226
Lukasz Majewski08dab662014-12-08 18:04:17 +0100227/**
228 * of_thermal_get_ntrips - function to export number of available trip
229 * points.
230 * @tz: pointer to a thermal zone
231 *
232 * This function is a globally visible wrapper to get number of trip points
233 * stored in the local struct __thermal_zone
234 *
235 * Return: number of available trip points, -ENODEV when data not available
236 */
237int of_thermal_get_ntrips(struct thermal_zone_device *tz)
238{
239 struct __thermal_zone *data = tz->devdata;
240
241 if (!data || IS_ERR(data))
242 return -ENODEV;
243
244 return data->ntrips;
245}
246EXPORT_SYMBOL_GPL(of_thermal_get_ntrips);
247
Lukasz Majewskia9bf2cc2014-12-08 18:04:18 +0100248/**
249 * of_thermal_is_trip_valid - function to check if trip point is valid
250 *
251 * @tz: pointer to a thermal zone
252 * @trip: trip point to evaluate
253 *
254 * This function is responsible for checking if passed trip point is valid
255 *
256 * Return: true if trip point is valid, false otherwise
257 */
258bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip)
259{
260 struct __thermal_zone *data = tz->devdata;
261
262 if (!data || trip >= data->ntrips || trip < 0)
263 return false;
264
265 return true;
266}
267EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid);
268
Lukasz Majewskice8be772014-12-08 18:04:20 +0100269/**
270 * of_thermal_get_trip_points - function to get access to a globally exported
271 * trip points
272 *
273 * @tz: pointer to a thermal zone
274 *
275 * This function provides a pointer to trip points table
276 *
277 * Return: pointer to trip points table, NULL otherwise
278 */
Geert Uytterhoevenebc31932015-01-03 22:56:56 +0100279const struct thermal_trip *
Lukasz Majewskice8be772014-12-08 18:04:20 +0100280of_thermal_get_trip_points(struct thermal_zone_device *tz)
281{
282 struct __thermal_zone *data = tz->devdata;
283
284 if (!data)
285 return NULL;
286
287 return data->trips;
288}
289EXPORT_SYMBOL_GPL(of_thermal_get_trip_points);
290
Lukasz Majewski184a4bf2014-12-08 18:04:21 +0100291/**
292 * of_thermal_set_emul_temp - function to set emulated temperature
293 *
294 * @tz: pointer to a thermal zone
295 * @temp: temperature to set
296 *
297 * This function gives the ability to set emulated value of temperature,
298 * which is handy for debugging
299 *
300 * Return: zero on success, error code otherwise
301 */
302static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
Sascha Hauer17e83512015-07-24 08:12:54 +0200303 int temp)
Lukasz Majewski184a4bf2014-12-08 18:04:21 +0100304{
305 struct __thermal_zone *data = tz->devdata;
306
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700307 if (!data->senps || !data->senps->ops->set_emul_temp)
308 return -EINVAL;
309
310 return data->senps->ops->set_emul_temp(data->senps->sensor_data, temp);
Lukasz Majewski184a4bf2014-12-08 18:04:21 +0100311}
312
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400313static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
314 enum thermal_trend *trend)
315{
316 struct __thermal_zone *data = tz->devdata;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400317
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700318 if (!data->senps || !data->senps->ops->get_trend)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400319 return -EINVAL;
320
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700321 return data->senps->ops->get_trend(data->senps->sensor_data,
322 trip, trend);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400323}
324
325static int of_thermal_bind(struct thermal_zone_device *thermal,
326 struct thermal_cooling_device *cdev)
327{
328 struct __thermal_zone *data = thermal->devdata;
329 int i;
330
331 if (!data || IS_ERR(data))
332 return -ENODEV;
333
334 /* find where to bind */
335 for (i = 0; i < data->num_tbps; i++) {
336 struct __thermal_bind_params *tbp = data->tbps + i;
337
338 if (tbp->cooling_device == cdev->np) {
339 int ret;
340
341 ret = thermal_zone_bind_cooling_device(thermal,
342 tbp->trip_id, cdev,
Punit Agrawaldd354b82014-06-03 10:59:58 +0100343 tbp->max,
Kapileshwar Singh6cd9e9f2015-02-18 16:04:21 +0000344 tbp->min,
345 tbp->usage);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400346 if (ret)
347 return ret;
348 }
349 }
350
351 return 0;
352}
353
354static int of_thermal_unbind(struct thermal_zone_device *thermal,
355 struct thermal_cooling_device *cdev)
356{
357 struct __thermal_zone *data = thermal->devdata;
358 int i;
359
360 if (!data || IS_ERR(data))
361 return -ENODEV;
362
363 /* find where to unbind */
364 for (i = 0; i < data->num_tbps; i++) {
365 struct __thermal_bind_params *tbp = data->tbps + i;
366
367 if (tbp->cooling_device == cdev->np) {
368 int ret;
369
370 ret = thermal_zone_unbind_cooling_device(thermal,
371 tbp->trip_id, cdev);
372 if (ret)
373 return ret;
374 }
375 }
376
377 return 0;
378}
379
380static int of_thermal_get_mode(struct thermal_zone_device *tz,
381 enum thermal_device_mode *mode)
382{
383 struct __thermal_zone *data = tz->devdata;
384
385 *mode = data->mode;
386
387 return 0;
388}
389
390static int of_thermal_set_mode(struct thermal_zone_device *tz,
391 enum thermal_device_mode mode)
392{
393 struct __thermal_zone *data = tz->devdata;
394
395 mutex_lock(&tz->lock);
396
397 if (mode == THERMAL_DEVICE_ENABLED)
398 tz->polling_delay = data->polling_delay;
399 else
400 tz->polling_delay = 0;
401
402 mutex_unlock(&tz->lock);
403
404 data->mode = mode;
Srinivas Pandruvada0e70f462016-08-26 16:21:16 -0700405 thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400406
407 return 0;
408}
409
410static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
411 enum thermal_trip_type *type)
412{
413 struct __thermal_zone *data = tz->devdata;
414
415 if (trip >= data->ntrips || trip < 0)
416 return -EDOM;
417
418 *type = data->trips[trip].type;
419
420 return 0;
421}
422
423static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
Sascha Hauer17e83512015-07-24 08:12:54 +0200424 int *temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400425{
426 struct __thermal_zone *data = tz->devdata;
427
428 if (trip >= data->ntrips || trip < 0)
429 return -EDOM;
430
431 *temp = data->trips[trip].temperature;
432
433 return 0;
434}
435
436static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
Sascha Hauer17e83512015-07-24 08:12:54 +0200437 int temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400438{
439 struct __thermal_zone *data = tz->devdata;
440
441 if (trip >= data->ntrips || trip < 0)
442 return -EDOM;
443
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700444 if (data->senps && data->senps->ops->set_trip_temp) {
Wei Nic3509522016-03-29 18:29:17 +0800445 int ret;
446
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700447 ret = data->senps->ops->set_trip_temp(data->senps->sensor_data,
448 trip, temp);
Wei Nic3509522016-03-29 18:29:17 +0800449 if (ret)
450 return ret;
451 }
452
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400453 /* thermal framework should take care of data->mask & (1 << trip) */
454 data->trips[trip].temperature = temp;
455
456 return 0;
457}
458
459static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
Sascha Hauer17e83512015-07-24 08:12:54 +0200460 int *hyst)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400461{
462 struct __thermal_zone *data = tz->devdata;
463
464 if (trip >= data->ntrips || trip < 0)
465 return -EDOM;
466
467 *hyst = data->trips[trip].hysteresis;
468
469 return 0;
470}
471
472static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
Sascha Hauer17e83512015-07-24 08:12:54 +0200473 int hyst)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400474{
475 struct __thermal_zone *data = tz->devdata;
476
477 if (trip >= data->ntrips || trip < 0)
478 return -EDOM;
479
480 /* thermal framework should take care of data->mask & (1 << trip) */
481 data->trips[trip].hysteresis = hyst;
482
483 return 0;
484}
485
486static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
Sascha Hauer17e83512015-07-24 08:12:54 +0200487 int *temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400488{
489 struct __thermal_zone *data = tz->devdata;
490 int i;
491
492 for (i = 0; i < data->ntrips; i++)
493 if (data->trips[i].type == THERMAL_TRIP_CRITICAL) {
494 *temp = data->trips[i].temperature;
495 return 0;
496 }
497
498 return -EINVAL;
499}
500
Lina Iyer01ffea22016-07-27 13:46:32 -0600501static int of_thermal_aggregate_trip_types(struct thermal_zone_device *tz,
502 unsigned int trip_type_mask, int *low, int *high)
503{
504 int min = INT_MIN;
505 int max = INT_MAX;
506 int tt, th, trip;
507 int temp = tz->temperature;
508 struct thermal_zone_device *zone = NULL;
509 struct __thermal_zone *data = tz->devdata;
510 struct list_head *head;
511 enum thermal_trip_type type = 0;
512
513 head = &data->senps->first_tz;
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600514 list_for_each_entry(data, head, list) {
Lina Iyer01ffea22016-07-27 13:46:32 -0600515 zone = data->tzd;
Ram Chandrasekar37c816d2017-08-25 12:34:02 -0600516 if (data->mode == THERMAL_DEVICE_DISABLED)
517 continue;
Lina Iyer01ffea22016-07-27 13:46:32 -0600518 for (trip = 0; trip < data->ntrips; trip++) {
519 of_thermal_get_trip_type(zone, trip, &type);
520 if (!(BIT(type) & trip_type_mask))
521 continue;
522
523 if (!zone->tzp->tracks_low) {
524 tt = data->trips[trip].temperature;
525 if (tt > temp && tt < max)
526 max = tt;
527 th = tt - data->trips[trip].hysteresis;
528 if (th < temp && th > min)
529 min = th;
530 } else {
531 tt = data->trips[trip].temperature;
532 if (tt < temp && tt > min)
533 min = tt;
534 th = tt + data->trips[trip].hysteresis;
535 if (th > temp && th < max)
536 max = th;
537 }
538 }
539 }
540
541 *high = max;
542 *low = min;
543
544 return 0;
545}
546
547/*
548 * of_thermal_aggregate_trip - aggregate trip temperatures across sibling
549 * thermal zones.
550 * @tz: pointer to the primary thermal zone.
551 * @type: the thermal trip type to be aggregated upon
552 * @low: the low trip threshold which the most lesser than the @temp
553 * @high: the high trip threshold which is the least greater than the @temp
554 */
555int of_thermal_aggregate_trip(struct thermal_zone_device *tz,
556 enum thermal_trip_type type,
557 int *low, int *high)
558{
559 if (type <= THERMAL_TRIP_CRITICAL)
560 return of_thermal_aggregate_trip_types(tz, BIT(type), low,
561 high);
562
563 return -EINVAL;
564}
565EXPORT_SYMBOL(of_thermal_aggregate_trip);
566
Ram Chandrasekarcf543602017-03-13 22:59:01 -0600567/*
568 * of_thermal_handle_trip - Handle thermal trip from sensors
569 *
570 * @tz: pointer to the primary thermal zone.
571 */
572void of_thermal_handle_trip(struct thermal_zone_device *tz)
573{
574 struct thermal_zone_device *zone;
575 struct __thermal_zone *data = tz->devdata;
576 struct list_head *head;
577
578 head = &data->senps->first_tz;
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600579 list_for_each_entry(data, head, list) {
Ram Chandrasekarcf543602017-03-13 22:59:01 -0600580 zone = data->tzd;
Ram Chandrasekar37c816d2017-08-25 12:34:02 -0600581 if (data->mode == THERMAL_DEVICE_DISABLED)
582 continue;
Ram Chandrasekarcf543602017-03-13 22:59:01 -0600583 thermal_zone_device_update(zone, THERMAL_EVENT_UNSPECIFIED);
584 }
585}
586EXPORT_SYMBOL(of_thermal_handle_trip);
587
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400588static struct thermal_zone_device_ops of_thermal_ops = {
589 .get_mode = of_thermal_get_mode,
590 .set_mode = of_thermal_set_mode,
591
592 .get_trip_type = of_thermal_get_trip_type,
593 .get_trip_temp = of_thermal_get_trip_temp,
594 .set_trip_temp = of_thermal_set_trip_temp,
595 .get_trip_hyst = of_thermal_get_trip_hyst,
596 .set_trip_hyst = of_thermal_set_trip_hyst,
597 .get_crit_temp = of_thermal_get_crit_temp,
598
599 .bind = of_thermal_bind,
600 .unbind = of_thermal_unbind,
601};
602
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700603static struct thermal_zone_of_device_ops of_virt_ops = {
604 .get_temp = virt_sensor_read_temp,
605};
606
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400607/*** sensor API ***/
608
609static struct thermal_zone_device *
610thermal_zone_of_add_sensor(struct device_node *zone,
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700611 struct device_node *sensor,
612 struct __sensor_param *sens_param)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400613{
614 struct thermal_zone_device *tzd;
615 struct __thermal_zone *tz;
616
617 tzd = thermal_zone_get_zone_by_name(zone->name);
618 if (IS_ERR(tzd))
619 return ERR_PTR(-EPROBE_DEFER);
620
621 tz = tzd->devdata;
622
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700623 if (!sens_param->ops)
Eduardo Valentin2251aef2014-11-07 21:24:39 -0400624 return ERR_PTR(-EINVAL);
625
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400626 mutex_lock(&tzd->lock);
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700627 tz->senps = sens_param;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400628
629 tzd->ops->get_temp = of_thermal_get_temp;
630 tzd->ops->get_trend = of_thermal_get_trend;
Sascha Hauer826386e2016-06-22 16:42:02 +0800631
632 /*
633 * The thermal zone core will calculate the window if they have set the
634 * optional set_trips pointer.
635 */
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700636 if (sens_param->ops->set_trips)
Sascha Hauer826386e2016-06-22 16:42:02 +0800637 tzd->ops->set_trips = of_thermal_set_trips;
638
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700639 if (sens_param->ops->set_emul_temp)
Keerthye2fa7482016-06-02 14:24:50 +0530640 tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
641
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700642 list_add_tail(&tz->list, &sens_param->first_tz);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400643 mutex_unlock(&tzd->lock);
644
645 return tzd;
646}
647
648/**
649 * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
650 * @dev: a valid struct device pointer of a sensor device. Must contain
651 * a valid .of_node, for the sensor node.
652 * @sensor_id: a sensor identifier, in case the sensor IP has more
653 * than one sensors
654 * @data: a private pointer (owned by the caller) that will be passed
655 * back, when a temperature reading is needed.
Eduardo Valentin2251aef2014-11-07 21:24:39 -0400656 * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400657 *
658 * This function will search the list of thermal zones described in device
659 * tree and look for the zone that refer to the sensor device pointed by
660 * @dev->of_node as temperature providers. For the zone pointing to the
661 * sensor node, the sensor will be added to the DT thermal zone device.
662 *
663 * The thermal zone temperature is provided by the @get_temp function
664 * pointer. When called, it will have the private pointer @data back.
665 *
666 * The thermal zone temperature trend is provided by the @get_trend function
667 * pointer. When called, it will have the private pointer @data back.
668 *
669 * TODO:
670 * 01 - This function must enqueue the new sensor instead of using
671 * it as the only source of temperature values.
672 *
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400673 * Return: On success returns a valid struct thermal_zone_device,
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700674 * otherwise, it returns a corresponding ERR_PTR(). Incase there are multiple
675 * thermal zones referencing the same sensor, the return value will be
676 * thermal_zone_device pointer of the first thermal zone. Caller must
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400677 * check the return value with help of IS_ERR() helper.
678 */
679struct thermal_zone_device *
Eduardo Valentin2251aef2014-11-07 21:24:39 -0400680thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
681 const struct thermal_zone_of_device_ops *ops)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400682{
683 struct device_node *np, *child, *sensor_np;
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300684 struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700685 struct thermal_zone_device *first_tzd = NULL;
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700686 struct __sensor_param *sens_param = NULL;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400687
688 np = of_find_node_by_name(NULL, "thermal-zones");
689 if (!np)
690 return ERR_PTR(-ENODEV);
691
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300692 if (!dev || !dev->of_node) {
693 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400694 return ERR_PTR(-EINVAL);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300695 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400696
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700697 sens_param = kzalloc(sizeof(*sens_param), GFP_KERNEL);
698 if (!sens_param) {
699 of_node_put(np);
700 return ERR_PTR(-ENOMEM);
701 }
702 sens_param->sensor_data = data;
703 sens_param->ops = ops;
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700704 INIT_LIST_HEAD(&sens_param->first_tz);
Lina Iyer01ffea22016-07-27 13:46:32 -0600705 sens_param->trip_high = INT_MAX;
706 sens_param->trip_low = INT_MIN;
707 mutex_init(&sens_param->lock);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300708 sensor_np = of_node_get(dev->of_node);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400709
Laxman Dewangan42bbe402016-02-08 18:58:34 +0530710 for_each_available_child_of_node(np, child) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400711 struct of_phandle_args sensor_specs;
712 int ret, id;
713
714 /* For now, thermal framework supports only 1 sensor per zone */
715 ret = of_parse_phandle_with_args(child, "thermal-sensors",
716 "#thermal-sensor-cells",
717 0, &sensor_specs);
718 if (ret)
719 continue;
720
721 if (sensor_specs.args_count >= 1) {
722 id = sensor_specs.args[0];
723 WARN(sensor_specs.args_count > 1,
724 "%s: too many cells in sensor specifier %d\n",
725 sensor_specs.np->name, sensor_specs.args_count);
726 } else {
727 id = 0;
728 }
729
730 if (sensor_specs.np == sensor_np && id == sensor_id) {
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300731 tzd = thermal_zone_of_add_sensor(child, sensor_np,
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700732 sens_param);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700733 if (!IS_ERR(tzd)) {
734 if (!first_tzd)
735 first_tzd = tzd;
Lukasz Majewski528012c12015-01-19 12:44:04 +0100736 tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700737 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400738 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300739 of_node_put(sensor_specs.np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400740 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300741 of_node_put(sensor_np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400742 of_node_put(np);
743
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700744 if (!first_tzd) {
745 first_tzd = ERR_PTR(-ENODEV);
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700746 kfree(sens_param);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700747 }
748 return first_tzd;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400749}
750EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
751
752/**
753 * thermal_zone_of_sensor_unregister - unregisters a sensor from a DT thermal zone
754 * @dev: a valid struct device pointer of a sensor device. Must contain
755 * a valid .of_node, for the sensor node.
756 * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
757 *
758 * This function removes the sensor callbacks and private data from the
759 * thermal zone device registered with thermal_zone_of_sensor_register()
760 * API. It will also silent the zone by remove the .get_temp() and .get_trend()
761 * thermal zone device callbacks.
762 *
763 * TODO: When the support to several sensors per zone is added, this
764 * function must search the sensor list based on @dev parameter.
765 *
766 */
767void thermal_zone_of_sensor_unregister(struct device *dev,
768 struct thermal_zone_device *tzd)
769{
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600770 struct __thermal_zone *tz, *next;
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700771 struct thermal_zone_device *pos;
772 struct list_head *head;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400773
774 if (!dev || !tzd || !tzd->devdata)
775 return;
776
777 tz = tzd->devdata;
778
779 /* no __thermal_zone, nothing to be done */
780 if (!tz)
781 return;
782
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700783 head = &tz->senps->first_tz;
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600784 list_for_each_entry_safe(tz, next, head, list) {
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700785 pos = tz->tzd;
786 mutex_lock(&pos->lock);
787 pos->ops->get_temp = NULL;
788 pos->ops->get_trend = NULL;
789 pos->ops->set_emul_temp = NULL;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400790
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700791 list_del(&tz->list);
792 if (list_empty(&tz->senps->first_tz))
793 kfree(tz->senps);
794 tz->senps = NULL;
795 mutex_unlock(&pos->lock);
796 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400797}
798EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
799
Laxman Dewangane498b492016-03-09 18:40:06 +0530800static void devm_thermal_zone_of_sensor_release(struct device *dev, void *res)
801{
802 thermal_zone_of_sensor_unregister(dev,
803 *(struct thermal_zone_device **)res);
804}
805
806static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
807 void *data)
808{
809 struct thermal_zone_device **r = res;
810
811 if (WARN_ON(!r || !*r))
812 return 0;
813
814 return *r == data;
815}
816
817/**
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700818 * devm_thermal_of_virtual_sensor_register - Register a virtual sensor.
819 * Three types of virtual sensors are supported.
820 * 1. Weighted aggregation type:
821 * Virtual sensor of this type calculates the weighted aggregation
822 * of sensor temperatures using the below formula,
823 * temp = (sensor_1_temp * coeff_1 + ... + sensor_n_temp * coeff_n)
824 * + avg_offset / avg_denominator
825 * So the sensor drivers has to specify n+2 coefficients.
826 * 2. Maximum type:
827 * Virtual sensors of this type will report the maximum of all
828 * sensor temperatures.
829 * 3. Minimum type:
830 * Virtual sensors of this type will report the minimum of all
831 * sensor temperatures.
832 *
833 * @input arguments:
834 * @dev: Virtual sensor driver device pointer.
835 * @sensor_data: Virtual sensor data supported for the device.
836 *
837 * @return: Returns a virtual thermal zone pointer. Returns error if thermal
838 * zone is not created. Returns -EAGAIN, if the sensor that is required for
839 * this virtual sensor temperature estimation is not registered yet. The
840 * sensor driver can try again later.
841 */
842struct thermal_zone_device *devm_thermal_of_virtual_sensor_register(
843 struct device *dev,
844 const struct virtual_sensor_data *sensor_data)
845{
846 int sens_idx = 0;
847 struct virtual_sensor *sens;
848 struct __thermal_zone *tz;
849 struct thermal_zone_device **ptr;
850 struct thermal_zone_device *tzd;
851 struct __sensor_param *sens_param = NULL;
852 enum thermal_device_mode mode;
853
854 if (!dev || !sensor_data)
855 return ERR_PTR(-EINVAL);
856
857 tzd = thermal_zone_get_zone_by_name(
858 sensor_data->virt_zone_name);
859 if (IS_ERR(tzd)) {
860 dev_err(dev, "sens:%s not available err: %ld\n",
861 sensor_data->virt_zone_name,
862 PTR_ERR(tzd));
863 return tzd;
864 }
865
866 mutex_lock(&tzd->lock);
867 /*
868 * Check if the virtual zone is registered and enabled.
869 * If so return the registered thermal zone.
870 */
871 tzd->ops->get_mode(tzd, &mode);
872 mutex_unlock(&tzd->lock);
873 if (mode == THERMAL_DEVICE_ENABLED)
874 return tzd;
875
876 sens = devm_kzalloc(dev, sizeof(*sens), GFP_KERNEL);
877 if (!sens)
878 return ERR_PTR(-ENOMEM);
879
Ram Chandrasekar02592fa2017-05-16 17:03:02 -0600880 sens->virt_tz = tzd;
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700881 sens->logic = sensor_data->logic;
882 sens->num_sensors = sensor_data->num_sensors;
883 if (sens->logic == VIRT_WEIGHTED_AVG) {
884 int coeff_ct = sensor_data->coefficient_ct;
885
886 /*
887 * For weighted aggregation, sensor drivers has to specify
888 * n+2 coefficients.
889 */
890 if (coeff_ct != sens->num_sensors) {
891 dev_err(dev, "sens:%s Invalid coefficient\n",
892 sensor_data->virt_zone_name);
893 return ERR_PTR(-EINVAL);
894 }
895 memcpy(sens->coefficients, sensor_data->coefficients,
896 coeff_ct * sizeof(*sens->coefficients));
897 sens->avg_offset = sensor_data->avg_offset;
898 sens->avg_denominator = sensor_data->avg_denominator;
899 }
900
901 for (sens_idx = 0; sens_idx < sens->num_sensors; sens_idx++) {
902 sens->tz[sens_idx] = thermal_zone_get_zone_by_name(
903 sensor_data->sensor_names[sens_idx]);
904 if (IS_ERR(sens->tz[sens_idx])) {
905 dev_err(dev, "sens:%s sensor[%s] fetch err:%ld\n",
906 sensor_data->virt_zone_name,
907 sensor_data->sensor_names[sens_idx],
908 PTR_ERR(sens->tz[sens_idx]));
909 break;
910 }
911 }
912 if (sens->num_sensors != sens_idx)
913 return ERR_PTR(-EAGAIN);
914
915 sens_param = kzalloc(sizeof(*sens_param), GFP_KERNEL);
916 if (!sens_param)
917 return ERR_PTR(-ENOMEM);
918 sens_param->sensor_data = sens;
919 sens_param->ops = &of_virt_ops;
920 INIT_LIST_HEAD(&sens_param->first_tz);
921 sens_param->trip_high = INT_MAX;
922 sens_param->trip_low = INT_MIN;
923 mutex_init(&sens_param->lock);
924
925 mutex_lock(&tzd->lock);
926 tz = tzd->devdata;
927 tz->senps = sens_param;
928 tzd->ops->get_temp = of_thermal_get_temp;
929 list_add_tail(&tz->list, &sens_param->first_tz);
930 mutex_unlock(&tzd->lock);
931
932 ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
933 GFP_KERNEL);
934 if (!ptr)
935 return ERR_PTR(-ENOMEM);
936
937 *ptr = tzd;
938 devres_add(dev, ptr);
939
940 tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
941
942 return tzd;
943}
944EXPORT_SYMBOL(devm_thermal_of_virtual_sensor_register);
945
946/**
Laxman Dewangane498b492016-03-09 18:40:06 +0530947 * devm_thermal_zone_of_sensor_register - Resource managed version of
948 * thermal_zone_of_sensor_register()
949 * @dev: a valid struct device pointer of a sensor device. Must contain
950 * a valid .of_node, for the sensor node.
951 * @sensor_id: a sensor identifier, in case the sensor IP has more
952 * than one sensors
953 * @data: a private pointer (owned by the caller) that will be passed
954 * back, when a temperature reading is needed.
955 * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
956 *
957 * Refer thermal_zone_of_sensor_register() for more details.
958 *
959 * Return: On success returns a valid struct thermal_zone_device,
960 * otherwise, it returns a corresponding ERR_PTR(). Caller must
961 * check the return value with help of IS_ERR() helper.
Zhang Rui7b5c4a02016-08-22 15:48:11 +0800962 * Registered thermal_zone_device device will automatically be
Laxman Dewangane498b492016-03-09 18:40:06 +0530963 * released when device is unbounded.
964 */
965struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
966 struct device *dev, int sensor_id,
967 void *data, const struct thermal_zone_of_device_ops *ops)
968{
969 struct thermal_zone_device **ptr, *tzd;
970
971 ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
972 GFP_KERNEL);
973 if (!ptr)
974 return ERR_PTR(-ENOMEM);
975
976 tzd = thermal_zone_of_sensor_register(dev, sensor_id, data, ops);
977 if (IS_ERR(tzd)) {
978 devres_free(ptr);
979 return tzd;
980 }
981
982 *ptr = tzd;
983 devres_add(dev, ptr);
984
985 return tzd;
986}
987EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_register);
988
989/**
990 * devm_thermal_zone_of_sensor_unregister - Resource managed version of
991 * thermal_zone_of_sensor_unregister().
992 * @dev: Device for which which resource was allocated.
993 * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
994 *
995 * This function removes the sensor callbacks and private data from the
996 * thermal zone device registered with devm_thermal_zone_of_sensor_register()
997 * API. It will also silent the zone by remove the .get_temp() and .get_trend()
998 * thermal zone device callbacks.
999 * Normally this function will not need to be called and the resource
1000 * management code will ensure that the resource is freed.
1001 */
1002void devm_thermal_zone_of_sensor_unregister(struct device *dev,
1003 struct thermal_zone_device *tzd)
1004{
1005 WARN_ON(devres_release(dev, devm_thermal_zone_of_sensor_release,
1006 devm_thermal_zone_of_sensor_match, tzd));
1007}
1008EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_unregister);
1009
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001010/*** functions parsing device tree nodes ***/
1011
1012/**
1013 * thermal_of_populate_bind_params - parse and fill cooling map data
1014 * @np: DT node containing a cooling-map node
1015 * @__tbp: data structure to be filled with cooling map info
1016 * @trips: array of thermal zone trip points
1017 * @ntrips: number of trip points inside trips.
1018 *
1019 * This function parses a cooling-map type of node represented by
1020 * @np parameter and fills the read data into @__tbp data structure.
1021 * It needs the already parsed array of trip points of the thermal zone
1022 * in consideration.
1023 *
1024 * Return: 0 on success, proper error code otherwise
1025 */
1026static int thermal_of_populate_bind_params(struct device_node *np,
1027 struct __thermal_bind_params *__tbp,
Lukasz Majewskiad9914a2014-12-08 18:04:19 +01001028 struct thermal_trip *trips,
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001029 int ntrips)
1030{
1031 struct of_phandle_args cooling_spec;
1032 struct device_node *trip;
1033 int ret, i;
1034 u32 prop;
1035
1036 /* Default weight. Usage is optional */
Kapileshwar Singh6cd9e9f2015-02-18 16:04:21 +00001037 __tbp->usage = THERMAL_WEIGHT_DEFAULT;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001038 ret = of_property_read_u32(np, "contribution", &prop);
1039 if (ret == 0)
1040 __tbp->usage = prop;
1041
1042 trip = of_parse_phandle(np, "trip", 0);
1043 if (!trip) {
1044 pr_err("missing trip property\n");
1045 return -ENODEV;
1046 }
1047
1048 /* match using device_node */
1049 for (i = 0; i < ntrips; i++)
1050 if (trip == trips[i].np) {
1051 __tbp->trip_id = i;
1052 break;
1053 }
1054
1055 if (i == ntrips) {
1056 ret = -ENODEV;
1057 goto end;
1058 }
1059
1060 ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
1061 0, &cooling_spec);
1062 if (ret < 0) {
1063 pr_err("missing cooling_device property\n");
1064 goto end;
1065 }
1066 __tbp->cooling_device = cooling_spec.np;
1067 if (cooling_spec.args_count >= 2) { /* at least min and max */
1068 __tbp->min = cooling_spec.args[0];
1069 __tbp->max = cooling_spec.args[1];
1070 } else {
1071 pr_err("wrong reference to cooling device, missing limits\n");
1072 }
1073
1074end:
1075 of_node_put(trip);
1076
1077 return ret;
1078}
1079
1080/**
1081 * It maps 'enum thermal_trip_type' found in include/linux/thermal.h
1082 * into the device tree binding of 'trip', property type.
1083 */
1084static const char * const trip_types[] = {
1085 [THERMAL_TRIP_ACTIVE] = "active",
1086 [THERMAL_TRIP_PASSIVE] = "passive",
1087 [THERMAL_TRIP_HOT] = "hot",
1088 [THERMAL_TRIP_CRITICAL] = "critical",
1089};
1090
1091/**
1092 * thermal_of_get_trip_type - Get phy mode for given device_node
1093 * @np: Pointer to the given device_node
1094 * @type: Pointer to resulting trip type
1095 *
1096 * The function gets trip type string from property 'type',
1097 * and store its index in trip_types table in @type,
1098 *
1099 * Return: 0 on success, or errno in error case.
1100 */
1101static int thermal_of_get_trip_type(struct device_node *np,
1102 enum thermal_trip_type *type)
1103{
1104 const char *t;
1105 int err, i;
1106
1107 err = of_property_read_string(np, "type", &t);
1108 if (err < 0)
1109 return err;
1110
1111 for (i = 0; i < ARRAY_SIZE(trip_types); i++)
1112 if (!strcasecmp(t, trip_types[i])) {
1113 *type = i;
1114 return 0;
1115 }
1116
1117 return -ENODEV;
1118}
1119
1120/**
1121 * thermal_of_populate_trip - parse and fill one trip point data
1122 * @np: DT node containing a trip point node
1123 * @trip: trip point data structure to be filled up
1124 *
1125 * This function parses a trip point type of node represented by
1126 * @np parameter and fills the read data into @trip data structure.
1127 *
1128 * Return: 0 on success, proper error code otherwise
1129 */
1130static int thermal_of_populate_trip(struct device_node *np,
Lukasz Majewskiad9914a2014-12-08 18:04:19 +01001131 struct thermal_trip *trip)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001132{
1133 int prop;
1134 int ret;
1135
1136 ret = of_property_read_u32(np, "temperature", &prop);
1137 if (ret < 0) {
1138 pr_err("missing temperature property\n");
1139 return ret;
1140 }
1141 trip->temperature = prop;
1142
1143 ret = of_property_read_u32(np, "hysteresis", &prop);
1144 if (ret < 0) {
1145 pr_err("missing hysteresis property\n");
1146 return ret;
1147 }
1148 trip->hysteresis = prop;
1149
1150 ret = thermal_of_get_trip_type(np, &trip->type);
1151 if (ret < 0) {
1152 pr_err("wrong trip type property\n");
1153 return ret;
1154 }
1155
1156 /* Required for cooling map matching */
1157 trip->np = np;
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001158 of_node_get(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001159
1160 return 0;
1161}
1162
1163/**
1164 * thermal_of_build_thermal_zone - parse and fill one thermal zone data
1165 * @np: DT node containing a thermal zone node
1166 *
1167 * This function parses a thermal zone type of node represented by
1168 * @np parameter and fills the read data into a __thermal_zone data structure
1169 * and return this pointer.
1170 *
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001171 * TODO: Missing properties to parse: thermal-sensor-names
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001172 *
1173 * Return: On success returns a valid struct __thermal_zone,
1174 * otherwise, it returns a corresponding ERR_PTR(). Caller must
1175 * check the return value with help of IS_ERR() helper.
1176 */
Julia Lawallc0ff8aa2016-04-19 14:33:32 +02001177static struct __thermal_zone
1178__init *thermal_of_build_thermal_zone(struct device_node *np)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001179{
1180 struct device_node *child = NULL, *gchild;
1181 struct __thermal_zone *tz;
1182 int ret, i;
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001183 u32 prop, coef[2];
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001184
1185 if (!np) {
1186 pr_err("no thermal zone np\n");
1187 return ERR_PTR(-EINVAL);
1188 }
1189
1190 tz = kzalloc(sizeof(*tz), GFP_KERNEL);
1191 if (!tz)
1192 return ERR_PTR(-ENOMEM);
1193
Ram Chandrasekard29230b2017-02-27 11:26:51 -07001194 INIT_LIST_HEAD(&tz->list);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001195 ret = of_property_read_u32(np, "polling-delay-passive", &prop);
1196 if (ret < 0) {
1197 pr_err("missing polling-delay-passive property\n");
1198 goto free_tz;
1199 }
1200 tz->passive_delay = prop;
1201
1202 ret = of_property_read_u32(np, "polling-delay", &prop);
1203 if (ret < 0) {
1204 pr_err("missing polling-delay property\n");
1205 goto free_tz;
1206 }
1207 tz->polling_delay = prop;
1208
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001209 /*
1210 * REVIST: for now, the thermal framework supports only
1211 * one sensor per thermal zone. Thus, we are considering
1212 * only the first two values as slope and offset.
1213 */
1214 ret = of_property_read_u32_array(np, "coefficients", coef, 2);
1215 if (ret == 0) {
1216 tz->slope = coef[0];
1217 tz->offset = coef[1];
1218 } else {
1219 tz->slope = 1;
1220 tz->offset = 0;
1221 }
1222
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001223 /* trips */
1224 child = of_get_child_by_name(np, "trips");
1225
1226 /* No trips provided */
1227 if (!child)
1228 goto finish;
1229
1230 tz->ntrips = of_get_child_count(child);
1231 if (tz->ntrips == 0) /* must have at least one child */
1232 goto finish;
1233
1234 tz->trips = kzalloc(tz->ntrips * sizeof(*tz->trips), GFP_KERNEL);
1235 if (!tz->trips) {
1236 ret = -ENOMEM;
1237 goto free_tz;
1238 }
1239
1240 i = 0;
1241 for_each_child_of_node(child, gchild) {
1242 ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
1243 if (ret)
1244 goto free_trips;
1245 }
1246
1247 of_node_put(child);
1248
1249 /* cooling-maps */
1250 child = of_get_child_by_name(np, "cooling-maps");
1251
1252 /* cooling-maps not provided */
1253 if (!child)
1254 goto finish;
1255
1256 tz->num_tbps = of_get_child_count(child);
1257 if (tz->num_tbps == 0)
1258 goto finish;
1259
1260 tz->tbps = kzalloc(tz->num_tbps * sizeof(*tz->tbps), GFP_KERNEL);
1261 if (!tz->tbps) {
1262 ret = -ENOMEM;
1263 goto free_trips;
1264 }
1265
1266 i = 0;
Stephen Boydca9521b2014-06-18 16:32:08 -07001267 for_each_child_of_node(child, gchild) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001268 ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
1269 tz->trips, tz->ntrips);
1270 if (ret)
1271 goto free_tbps;
Stephen Boydca9521b2014-06-18 16:32:08 -07001272 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001273
1274finish:
1275 of_node_put(child);
1276 tz->mode = THERMAL_DEVICE_DISABLED;
1277
1278 return tz;
1279
1280free_tbps:
Ulises Brindis1cd91c12016-03-25 12:55:41 -07001281 for (i = i - 1; i >= 0; i--)
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001282 of_node_put(tz->tbps[i].cooling_device);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001283 kfree(tz->tbps);
1284free_trips:
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001285 for (i = 0; i < tz->ntrips; i++)
1286 of_node_put(tz->trips[i].np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001287 kfree(tz->trips);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001288 of_node_put(gchild);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001289free_tz:
1290 kfree(tz);
1291 of_node_put(child);
1292
1293 return ERR_PTR(ret);
1294}
1295
1296static inline void of_thermal_free_zone(struct __thermal_zone *tz)
1297{
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001298 int i;
1299
1300 for (i = 0; i < tz->num_tbps; i++)
1301 of_node_put(tz->tbps[i].cooling_device);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001302 kfree(tz->tbps);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001303 for (i = 0; i < tz->ntrips; i++)
1304 of_node_put(tz->trips[i].np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001305 kfree(tz->trips);
1306 kfree(tz);
1307}
1308
1309/**
1310 * of_parse_thermal_zones - parse device tree thermal data
1311 *
1312 * Initialization function that can be called by machine initialization
1313 * code to parse thermal data and populate the thermal framework
1314 * with hardware thermal zones info. This function only parses thermal zones.
1315 * Cooling devices and sensor devices nodes are supposed to be parsed
1316 * by their respective drivers.
1317 *
1318 * Return: 0 on success, proper error code otherwise
1319 *
1320 */
1321int __init of_parse_thermal_zones(void)
1322{
1323 struct device_node *np, *child;
1324 struct __thermal_zone *tz;
1325 struct thermal_zone_device_ops *ops;
1326
1327 np = of_find_node_by_name(NULL, "thermal-zones");
1328 if (!np) {
1329 pr_debug("unable to find thermal zones\n");
1330 return 0; /* Run successfully on systems without thermal DT */
1331 }
1332
Laxman Dewangan42bbe402016-02-08 18:58:34 +05301333 for_each_available_child_of_node(np, child) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001334 struct thermal_zone_device *zone;
1335 struct thermal_zone_params *tzp;
Punit Agrawal76af5492015-03-03 10:43:04 +00001336 int i, mask = 0;
Punit Agrawal647f9922015-02-26 19:00:32 +00001337 u32 prop;
Ram Chandrasekarcaafe202016-07-19 11:25:46 -06001338 const char *governor_name;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001339
1340 tz = thermal_of_build_thermal_zone(child);
1341 if (IS_ERR(tz)) {
1342 pr_err("failed to build thermal zone %s: %ld\n",
1343 child->name,
1344 PTR_ERR(tz));
1345 continue;
1346 }
1347
1348 ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
1349 if (!ops)
1350 goto exit_free;
1351
1352 tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
1353 if (!tzp) {
1354 kfree(ops);
1355 goto exit_free;
1356 }
1357
1358 /* No hwmon because there might be hwmon drivers registering */
1359 tzp->no_hwmon = true;
1360
Ram Chandrasekarcaafe202016-07-19 11:25:46 -06001361 if (!of_property_read_string(child, "thermal-governor",
1362 &governor_name))
1363 strlcpy(tzp->governor_name, governor_name,
1364 THERMAL_NAME_LENGTH);
1365
Punit Agrawal647f9922015-02-26 19:00:32 +00001366 if (!of_property_read_u32(child, "sustainable-power", &prop))
1367 tzp->sustainable_power = prop;
1368
Punit Agrawal76af5492015-03-03 10:43:04 +00001369 for (i = 0; i < tz->ntrips; i++)
1370 mask |= 1 << i;
1371
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001372 /* these two are left for temperature drivers to use */
1373 tzp->slope = tz->slope;
1374 tzp->offset = tz->offset;
1375
Lina Iyer159f67d2016-07-27 11:34:46 -06001376 if (of_property_read_bool(child, "tracks-low"))
1377 tzp->tracks_low = true;
1378
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001379 zone = thermal_zone_device_register(child->name, tz->ntrips,
Punit Agrawal76af5492015-03-03 10:43:04 +00001380 mask, tz,
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001381 ops, tzp,
1382 tz->passive_delay,
1383 tz->polling_delay);
1384 if (IS_ERR(zone)) {
1385 pr_err("Failed to build %s zone %ld\n", child->name,
1386 PTR_ERR(zone));
1387 kfree(tzp);
1388 kfree(ops);
1389 of_thermal_free_zone(tz);
1390 /* attempting to build remaining zones still */
Ram Chandrasekard29230b2017-02-27 11:26:51 -07001391 continue;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001392 }
Ram Chandrasekard29230b2017-02-27 11:26:51 -07001393 tz->tzd = zone;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001394 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001395 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001396
1397 return 0;
1398
1399exit_free:
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001400 of_node_put(child);
1401 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001402 of_thermal_free_zone(tz);
1403
1404 /* no memory available, so free what we have built */
1405 of_thermal_destroy_zones();
1406
1407 return -ENOMEM;
1408}
1409
1410/**
1411 * of_thermal_destroy_zones - remove all zones parsed and allocated resources
1412 *
1413 * Finds all zones parsed and added to the thermal framework and remove them
1414 * from the system, together with their resources.
1415 *
1416 */
1417void of_thermal_destroy_zones(void)
1418{
1419 struct device_node *np, *child;
1420
1421 np = of_find_node_by_name(NULL, "thermal-zones");
1422 if (!np) {
Jiada Wang28524982015-11-16 17:10:05 +09001423 pr_debug("unable to find thermal zones\n");
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001424 return;
1425 }
1426
Laxman Dewangan42bbe402016-02-08 18:58:34 +05301427 for_each_available_child_of_node(np, child) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001428 struct thermal_zone_device *zone;
1429
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001430 zone = thermal_zone_get_zone_by_name(child->name);
1431 if (IS_ERR(zone))
1432 continue;
1433
1434 thermal_zone_device_unregister(zone);
1435 kfree(zone->tzp);
1436 kfree(zone->ops);
1437 of_thermal_free_zone(zone->devdata);
1438 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001439 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001440}