blob: 65d8fd7a62261a5c19da42ec0e2742d3e9e5d9d6 [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;
196
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700197 return data->senps->ops->get_temp(data->senps->sensor_data, temp);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400198}
199
Sascha Hauer826386e2016-06-22 16:42:02 +0800200static int of_thermal_set_trips(struct thermal_zone_device *tz,
Lina Iyer01ffea22016-07-27 13:46:32 -0600201 int inp_low, int inp_high)
Sascha Hauer826386e2016-06-22 16:42:02 +0800202{
203 struct __thermal_zone *data = tz->devdata;
Lina Iyer01ffea22016-07-27 13:46:32 -0600204 int high = INT_MAX, low = INT_MIN, ret = 0;
Sascha Hauer826386e2016-06-22 16:42:02 +0800205
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700206 if (!data->senps || !data->senps->ops->set_trips)
Sascha Hauer826386e2016-06-22 16:42:02 +0800207 return -EINVAL;
208
Lina Iyer01ffea22016-07-27 13:46:32 -0600209 mutex_lock(&data->senps->lock);
210 of_thermal_aggregate_trip_types(tz, GENMASK(THERMAL_TRIP_CRITICAL, 0),
211 &low, &high);
Lina Iyer01ffea22016-07-27 13:46:32 -0600212 data->senps->trip_low = low;
213 data->senps->trip_high = high;
214 ret = data->senps->ops->set_trips(data->senps->sensor_data,
215 low, high);
216
Lina Iyer01ffea22016-07-27 13:46:32 -0600217 mutex_unlock(&data->senps->lock);
218 return ret;
Sascha Hauer826386e2016-06-22 16:42:02 +0800219}
220
Lukasz Majewski08dab662014-12-08 18:04:17 +0100221/**
222 * of_thermal_get_ntrips - function to export number of available trip
223 * points.
224 * @tz: pointer to a thermal zone
225 *
226 * This function is a globally visible wrapper to get number of trip points
227 * stored in the local struct __thermal_zone
228 *
229 * Return: number of available trip points, -ENODEV when data not available
230 */
231int of_thermal_get_ntrips(struct thermal_zone_device *tz)
232{
233 struct __thermal_zone *data = tz->devdata;
234
235 if (!data || IS_ERR(data))
236 return -ENODEV;
237
238 return data->ntrips;
239}
240EXPORT_SYMBOL_GPL(of_thermal_get_ntrips);
241
Lukasz Majewskia9bf2cc2014-12-08 18:04:18 +0100242/**
243 * of_thermal_is_trip_valid - function to check if trip point is valid
244 *
245 * @tz: pointer to a thermal zone
246 * @trip: trip point to evaluate
247 *
248 * This function is responsible for checking if passed trip point is valid
249 *
250 * Return: true if trip point is valid, false otherwise
251 */
252bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip)
253{
254 struct __thermal_zone *data = tz->devdata;
255
256 if (!data || trip >= data->ntrips || trip < 0)
257 return false;
258
259 return true;
260}
261EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid);
262
Lukasz Majewskice8be772014-12-08 18:04:20 +0100263/**
264 * of_thermal_get_trip_points - function to get access to a globally exported
265 * trip points
266 *
267 * @tz: pointer to a thermal zone
268 *
269 * This function provides a pointer to trip points table
270 *
271 * Return: pointer to trip points table, NULL otherwise
272 */
Geert Uytterhoevenebc31932015-01-03 22:56:56 +0100273const struct thermal_trip *
Lukasz Majewskice8be772014-12-08 18:04:20 +0100274of_thermal_get_trip_points(struct thermal_zone_device *tz)
275{
276 struct __thermal_zone *data = tz->devdata;
277
278 if (!data)
279 return NULL;
280
281 return data->trips;
282}
283EXPORT_SYMBOL_GPL(of_thermal_get_trip_points);
284
Lukasz Majewski184a4bf2014-12-08 18:04:21 +0100285/**
286 * of_thermal_set_emul_temp - function to set emulated temperature
287 *
288 * @tz: pointer to a thermal zone
289 * @temp: temperature to set
290 *
291 * This function gives the ability to set emulated value of temperature,
292 * which is handy for debugging
293 *
294 * Return: zero on success, error code otherwise
295 */
296static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
Sascha Hauer17e83512015-07-24 08:12:54 +0200297 int temp)
Lukasz Majewski184a4bf2014-12-08 18:04:21 +0100298{
299 struct __thermal_zone *data = tz->devdata;
300
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700301 if (!data->senps || !data->senps->ops->set_emul_temp)
302 return -EINVAL;
303
304 return data->senps->ops->set_emul_temp(data->senps->sensor_data, temp);
Lukasz Majewski184a4bf2014-12-08 18:04:21 +0100305}
306
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400307static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
308 enum thermal_trend *trend)
309{
310 struct __thermal_zone *data = tz->devdata;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400311
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700312 if (!data->senps || !data->senps->ops->get_trend)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400313 return -EINVAL;
314
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700315 return data->senps->ops->get_trend(data->senps->sensor_data,
316 trip, trend);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400317}
318
319static int of_thermal_bind(struct thermal_zone_device *thermal,
320 struct thermal_cooling_device *cdev)
321{
322 struct __thermal_zone *data = thermal->devdata;
323 int i;
324
325 if (!data || IS_ERR(data))
326 return -ENODEV;
327
328 /* find where to bind */
329 for (i = 0; i < data->num_tbps; i++) {
330 struct __thermal_bind_params *tbp = data->tbps + i;
331
332 if (tbp->cooling_device == cdev->np) {
333 int ret;
334
335 ret = thermal_zone_bind_cooling_device(thermal,
336 tbp->trip_id, cdev,
Punit Agrawaldd354b82014-06-03 10:59:58 +0100337 tbp->max,
Kapileshwar Singh6cd9e9f2015-02-18 16:04:21 +0000338 tbp->min,
339 tbp->usage);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400340 if (ret)
341 return ret;
342 }
343 }
344
345 return 0;
346}
347
348static int of_thermal_unbind(struct thermal_zone_device *thermal,
349 struct thermal_cooling_device *cdev)
350{
351 struct __thermal_zone *data = thermal->devdata;
352 int i;
353
354 if (!data || IS_ERR(data))
355 return -ENODEV;
356
357 /* find where to unbind */
358 for (i = 0; i < data->num_tbps; i++) {
359 struct __thermal_bind_params *tbp = data->tbps + i;
360
361 if (tbp->cooling_device == cdev->np) {
362 int ret;
363
364 ret = thermal_zone_unbind_cooling_device(thermal,
365 tbp->trip_id, cdev);
366 if (ret)
367 return ret;
368 }
369 }
370
371 return 0;
372}
373
374static int of_thermal_get_mode(struct thermal_zone_device *tz,
375 enum thermal_device_mode *mode)
376{
377 struct __thermal_zone *data = tz->devdata;
378
379 *mode = data->mode;
380
381 return 0;
382}
383
384static int of_thermal_set_mode(struct thermal_zone_device *tz,
385 enum thermal_device_mode mode)
386{
387 struct __thermal_zone *data = tz->devdata;
388
389 mutex_lock(&tz->lock);
390
391 if (mode == THERMAL_DEVICE_ENABLED)
392 tz->polling_delay = data->polling_delay;
393 else
394 tz->polling_delay = 0;
395
396 mutex_unlock(&tz->lock);
397
398 data->mode = mode;
Srinivas Pandruvada0e70f462016-08-26 16:21:16 -0700399 thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400400
401 return 0;
402}
403
404static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
405 enum thermal_trip_type *type)
406{
407 struct __thermal_zone *data = tz->devdata;
408
409 if (trip >= data->ntrips || trip < 0)
410 return -EDOM;
411
412 *type = data->trips[trip].type;
413
414 return 0;
415}
416
417static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
Sascha Hauer17e83512015-07-24 08:12:54 +0200418 int *temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400419{
420 struct __thermal_zone *data = tz->devdata;
421
422 if (trip >= data->ntrips || trip < 0)
423 return -EDOM;
424
425 *temp = data->trips[trip].temperature;
426
427 return 0;
428}
429
430static int of_thermal_set_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 Chandrasekare34ced62017-03-03 11:22:50 -0700438 if (data->senps && data->senps->ops->set_trip_temp) {
Wei Nic3509522016-03-29 18:29:17 +0800439 int ret;
440
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700441 ret = data->senps->ops->set_trip_temp(data->senps->sensor_data,
442 trip, temp);
Wei Nic3509522016-03-29 18:29:17 +0800443 if (ret)
444 return ret;
445 }
446
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400447 /* thermal framework should take care of data->mask & (1 << trip) */
448 data->trips[trip].temperature = temp;
449
450 return 0;
451}
452
453static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
Sascha Hauer17e83512015-07-24 08:12:54 +0200454 int *hyst)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400455{
456 struct __thermal_zone *data = tz->devdata;
457
458 if (trip >= data->ntrips || trip < 0)
459 return -EDOM;
460
461 *hyst = data->trips[trip].hysteresis;
462
463 return 0;
464}
465
466static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
Sascha Hauer17e83512015-07-24 08:12:54 +0200467 int hyst)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400468{
469 struct __thermal_zone *data = tz->devdata;
470
471 if (trip >= data->ntrips || trip < 0)
472 return -EDOM;
473
474 /* thermal framework should take care of data->mask & (1 << trip) */
475 data->trips[trip].hysteresis = hyst;
476
477 return 0;
478}
479
480static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
Sascha Hauer17e83512015-07-24 08:12:54 +0200481 int *temp)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400482{
483 struct __thermal_zone *data = tz->devdata;
484 int i;
485
486 for (i = 0; i < data->ntrips; i++)
487 if (data->trips[i].type == THERMAL_TRIP_CRITICAL) {
488 *temp = data->trips[i].temperature;
489 return 0;
490 }
491
492 return -EINVAL;
493}
494
Lina Iyer01ffea22016-07-27 13:46:32 -0600495static int of_thermal_aggregate_trip_types(struct thermal_zone_device *tz,
496 unsigned int trip_type_mask, int *low, int *high)
497{
498 int min = INT_MIN;
499 int max = INT_MAX;
500 int tt, th, trip;
501 int temp = tz->temperature;
502 struct thermal_zone_device *zone = NULL;
503 struct __thermal_zone *data = tz->devdata;
504 struct list_head *head;
505 enum thermal_trip_type type = 0;
506
507 head = &data->senps->first_tz;
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600508 list_for_each_entry(data, head, list) {
Lina Iyer01ffea22016-07-27 13:46:32 -0600509 zone = data->tzd;
510 for (trip = 0; trip < data->ntrips; trip++) {
511 of_thermal_get_trip_type(zone, trip, &type);
512 if (!(BIT(type) & trip_type_mask))
513 continue;
514
515 if (!zone->tzp->tracks_low) {
516 tt = data->trips[trip].temperature;
517 if (tt > temp && tt < max)
518 max = tt;
519 th = tt - data->trips[trip].hysteresis;
520 if (th < temp && th > min)
521 min = th;
522 } else {
523 tt = data->trips[trip].temperature;
524 if (tt < temp && tt > min)
525 min = tt;
526 th = tt + data->trips[trip].hysteresis;
527 if (th > temp && th < max)
528 max = th;
529 }
530 }
531 }
532
533 *high = max;
534 *low = min;
535
536 return 0;
537}
538
539/*
540 * of_thermal_aggregate_trip - aggregate trip temperatures across sibling
541 * thermal zones.
542 * @tz: pointer to the primary thermal zone.
543 * @type: the thermal trip type to be aggregated upon
544 * @low: the low trip threshold which the most lesser than the @temp
545 * @high: the high trip threshold which is the least greater than the @temp
546 */
547int of_thermal_aggregate_trip(struct thermal_zone_device *tz,
548 enum thermal_trip_type type,
549 int *low, int *high)
550{
551 if (type <= THERMAL_TRIP_CRITICAL)
552 return of_thermal_aggregate_trip_types(tz, BIT(type), low,
553 high);
554
555 return -EINVAL;
556}
557EXPORT_SYMBOL(of_thermal_aggregate_trip);
558
Ram Chandrasekarcf543602017-03-13 22:59:01 -0600559/*
560 * of_thermal_handle_trip - Handle thermal trip from sensors
561 *
562 * @tz: pointer to the primary thermal zone.
563 */
564void of_thermal_handle_trip(struct thermal_zone_device *tz)
565{
566 struct thermal_zone_device *zone;
567 struct __thermal_zone *data = tz->devdata;
568 struct list_head *head;
569
570 head = &data->senps->first_tz;
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600571 list_for_each_entry(data, head, list) {
Ram Chandrasekarcf543602017-03-13 22:59:01 -0600572 zone = data->tzd;
573 thermal_zone_device_update(zone, THERMAL_EVENT_UNSPECIFIED);
574 }
575}
576EXPORT_SYMBOL(of_thermal_handle_trip);
577
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400578static struct thermal_zone_device_ops of_thermal_ops = {
579 .get_mode = of_thermal_get_mode,
580 .set_mode = of_thermal_set_mode,
581
582 .get_trip_type = of_thermal_get_trip_type,
583 .get_trip_temp = of_thermal_get_trip_temp,
584 .set_trip_temp = of_thermal_set_trip_temp,
585 .get_trip_hyst = of_thermal_get_trip_hyst,
586 .set_trip_hyst = of_thermal_set_trip_hyst,
587 .get_crit_temp = of_thermal_get_crit_temp,
588
589 .bind = of_thermal_bind,
590 .unbind = of_thermal_unbind,
591};
592
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700593static struct thermal_zone_of_device_ops of_virt_ops = {
594 .get_temp = virt_sensor_read_temp,
595};
596
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400597/*** sensor API ***/
598
599static struct thermal_zone_device *
600thermal_zone_of_add_sensor(struct device_node *zone,
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700601 struct device_node *sensor,
602 struct __sensor_param *sens_param)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400603{
604 struct thermal_zone_device *tzd;
605 struct __thermal_zone *tz;
606
607 tzd = thermal_zone_get_zone_by_name(zone->name);
608 if (IS_ERR(tzd))
609 return ERR_PTR(-EPROBE_DEFER);
610
611 tz = tzd->devdata;
612
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700613 if (!sens_param->ops)
Eduardo Valentin2251aef2014-11-07 21:24:39 -0400614 return ERR_PTR(-EINVAL);
615
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400616 mutex_lock(&tzd->lock);
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700617 tz->senps = sens_param;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400618
619 tzd->ops->get_temp = of_thermal_get_temp;
620 tzd->ops->get_trend = of_thermal_get_trend;
Sascha Hauer826386e2016-06-22 16:42:02 +0800621
622 /*
623 * The thermal zone core will calculate the window if they have set the
624 * optional set_trips pointer.
625 */
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700626 if (sens_param->ops->set_trips)
Sascha Hauer826386e2016-06-22 16:42:02 +0800627 tzd->ops->set_trips = of_thermal_set_trips;
628
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700629 if (sens_param->ops->set_emul_temp)
Keerthye2fa7482016-06-02 14:24:50 +0530630 tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
631
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700632 list_add_tail(&tz->list, &sens_param->first_tz);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400633 mutex_unlock(&tzd->lock);
634
635 return tzd;
636}
637
638/**
639 * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
640 * @dev: a valid struct device pointer of a sensor device. Must contain
641 * a valid .of_node, for the sensor node.
642 * @sensor_id: a sensor identifier, in case the sensor IP has more
643 * than one sensors
644 * @data: a private pointer (owned by the caller) that will be passed
645 * back, when a temperature reading is needed.
Eduardo Valentin2251aef2014-11-07 21:24:39 -0400646 * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400647 *
648 * This function will search the list of thermal zones described in device
649 * tree and look for the zone that refer to the sensor device pointed by
650 * @dev->of_node as temperature providers. For the zone pointing to the
651 * sensor node, the sensor will be added to the DT thermal zone device.
652 *
653 * The thermal zone temperature is provided by the @get_temp function
654 * pointer. When called, it will have the private pointer @data back.
655 *
656 * The thermal zone temperature trend is provided by the @get_trend function
657 * pointer. When called, it will have the private pointer @data back.
658 *
659 * TODO:
660 * 01 - This function must enqueue the new sensor instead of using
661 * it as the only source of temperature values.
662 *
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400663 * Return: On success returns a valid struct thermal_zone_device,
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700664 * otherwise, it returns a corresponding ERR_PTR(). Incase there are multiple
665 * thermal zones referencing the same sensor, the return value will be
666 * thermal_zone_device pointer of the first thermal zone. Caller must
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400667 * check the return value with help of IS_ERR() helper.
668 */
669struct thermal_zone_device *
Eduardo Valentin2251aef2014-11-07 21:24:39 -0400670thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
671 const struct thermal_zone_of_device_ops *ops)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400672{
673 struct device_node *np, *child, *sensor_np;
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300674 struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700675 struct thermal_zone_device *first_tzd = NULL;
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700676 struct __sensor_param *sens_param = NULL;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400677
678 np = of_find_node_by_name(NULL, "thermal-zones");
679 if (!np)
680 return ERR_PTR(-ENODEV);
681
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300682 if (!dev || !dev->of_node) {
683 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400684 return ERR_PTR(-EINVAL);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300685 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400686
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700687 sens_param = kzalloc(sizeof(*sens_param), GFP_KERNEL);
688 if (!sens_param) {
689 of_node_put(np);
690 return ERR_PTR(-ENOMEM);
691 }
692 sens_param->sensor_data = data;
693 sens_param->ops = ops;
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700694 INIT_LIST_HEAD(&sens_param->first_tz);
Lina Iyer01ffea22016-07-27 13:46:32 -0600695 sens_param->trip_high = INT_MAX;
696 sens_param->trip_low = INT_MIN;
697 mutex_init(&sens_param->lock);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300698 sensor_np = of_node_get(dev->of_node);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400699
Laxman Dewangan42bbe402016-02-08 18:58:34 +0530700 for_each_available_child_of_node(np, child) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400701 struct of_phandle_args sensor_specs;
702 int ret, id;
703
704 /* For now, thermal framework supports only 1 sensor per zone */
705 ret = of_parse_phandle_with_args(child, "thermal-sensors",
706 "#thermal-sensor-cells",
707 0, &sensor_specs);
708 if (ret)
709 continue;
710
711 if (sensor_specs.args_count >= 1) {
712 id = sensor_specs.args[0];
713 WARN(sensor_specs.args_count > 1,
714 "%s: too many cells in sensor specifier %d\n",
715 sensor_specs.np->name, sensor_specs.args_count);
716 } else {
717 id = 0;
718 }
719
720 if (sensor_specs.np == sensor_np && id == sensor_id) {
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300721 tzd = thermal_zone_of_add_sensor(child, sensor_np,
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700722 sens_param);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700723 if (!IS_ERR(tzd)) {
724 if (!first_tzd)
725 first_tzd = tzd;
Lukasz Majewski528012c12015-01-19 12:44:04 +0100726 tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700727 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400728 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300729 of_node_put(sensor_specs.np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400730 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +0300731 of_node_put(sensor_np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400732 of_node_put(np);
733
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700734 if (!first_tzd) {
735 first_tzd = ERR_PTR(-ENODEV);
Ram Chandrasekare34ced62017-03-03 11:22:50 -0700736 kfree(sens_param);
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700737 }
738 return first_tzd;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400739}
740EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
741
742/**
743 * thermal_zone_of_sensor_unregister - unregisters a sensor from a DT thermal zone
744 * @dev: a valid struct device pointer of a sensor device. Must contain
745 * a valid .of_node, for the sensor node.
746 * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
747 *
748 * This function removes the sensor callbacks and private data from the
749 * thermal zone device registered with thermal_zone_of_sensor_register()
750 * API. It will also silent the zone by remove the .get_temp() and .get_trend()
751 * thermal zone device callbacks.
752 *
753 * TODO: When the support to several sensors per zone is added, this
754 * function must search the sensor list based on @dev parameter.
755 *
756 */
757void thermal_zone_of_sensor_unregister(struct device *dev,
758 struct thermal_zone_device *tzd)
759{
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600760 struct __thermal_zone *tz, *next;
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700761 struct thermal_zone_device *pos;
762 struct list_head *head;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400763
764 if (!dev || !tzd || !tzd->devdata)
765 return;
766
767 tz = tzd->devdata;
768
769 /* no __thermal_zone, nothing to be done */
770 if (!tz)
771 return;
772
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700773 head = &tz->senps->first_tz;
Ram Chandrasekar9a1da902017-05-09 16:58:55 -0600774 list_for_each_entry_safe(tz, next, head, list) {
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700775 pos = tz->tzd;
776 mutex_lock(&pos->lock);
777 pos->ops->get_temp = NULL;
778 pos->ops->get_trend = NULL;
779 pos->ops->set_emul_temp = NULL;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400780
Ram Chandrasekard29230b2017-02-27 11:26:51 -0700781 list_del(&tz->list);
782 if (list_empty(&tz->senps->first_tz))
783 kfree(tz->senps);
784 tz->senps = NULL;
785 mutex_unlock(&pos->lock);
786 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -0400787}
788EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
789
Laxman Dewangane498b492016-03-09 18:40:06 +0530790static void devm_thermal_zone_of_sensor_release(struct device *dev, void *res)
791{
792 thermal_zone_of_sensor_unregister(dev,
793 *(struct thermal_zone_device **)res);
794}
795
796static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
797 void *data)
798{
799 struct thermal_zone_device **r = res;
800
801 if (WARN_ON(!r || !*r))
802 return 0;
803
804 return *r == data;
805}
806
807/**
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700808 * devm_thermal_of_virtual_sensor_register - Register a virtual sensor.
809 * Three types of virtual sensors are supported.
810 * 1. Weighted aggregation type:
811 * Virtual sensor of this type calculates the weighted aggregation
812 * of sensor temperatures using the below formula,
813 * temp = (sensor_1_temp * coeff_1 + ... + sensor_n_temp * coeff_n)
814 * + avg_offset / avg_denominator
815 * So the sensor drivers has to specify n+2 coefficients.
816 * 2. Maximum type:
817 * Virtual sensors of this type will report the maximum of all
818 * sensor temperatures.
819 * 3. Minimum type:
820 * Virtual sensors of this type will report the minimum of all
821 * sensor temperatures.
822 *
823 * @input arguments:
824 * @dev: Virtual sensor driver device pointer.
825 * @sensor_data: Virtual sensor data supported for the device.
826 *
827 * @return: Returns a virtual thermal zone pointer. Returns error if thermal
828 * zone is not created. Returns -EAGAIN, if the sensor that is required for
829 * this virtual sensor temperature estimation is not registered yet. The
830 * sensor driver can try again later.
831 */
832struct thermal_zone_device *devm_thermal_of_virtual_sensor_register(
833 struct device *dev,
834 const struct virtual_sensor_data *sensor_data)
835{
836 int sens_idx = 0;
837 struct virtual_sensor *sens;
838 struct __thermal_zone *tz;
839 struct thermal_zone_device **ptr;
840 struct thermal_zone_device *tzd;
841 struct __sensor_param *sens_param = NULL;
842 enum thermal_device_mode mode;
843
844 if (!dev || !sensor_data)
845 return ERR_PTR(-EINVAL);
846
847 tzd = thermal_zone_get_zone_by_name(
848 sensor_data->virt_zone_name);
849 if (IS_ERR(tzd)) {
850 dev_err(dev, "sens:%s not available err: %ld\n",
851 sensor_data->virt_zone_name,
852 PTR_ERR(tzd));
853 return tzd;
854 }
855
856 mutex_lock(&tzd->lock);
857 /*
858 * Check if the virtual zone is registered and enabled.
859 * If so return the registered thermal zone.
860 */
861 tzd->ops->get_mode(tzd, &mode);
862 mutex_unlock(&tzd->lock);
863 if (mode == THERMAL_DEVICE_ENABLED)
864 return tzd;
865
866 sens = devm_kzalloc(dev, sizeof(*sens), GFP_KERNEL);
867 if (!sens)
868 return ERR_PTR(-ENOMEM);
869
Ram Chandrasekar02592fa2017-05-16 17:03:02 -0600870 sens->virt_tz = tzd;
Ram Chandrasekar2f2b7462017-03-11 19:35:11 -0700871 sens->logic = sensor_data->logic;
872 sens->num_sensors = sensor_data->num_sensors;
873 if (sens->logic == VIRT_WEIGHTED_AVG) {
874 int coeff_ct = sensor_data->coefficient_ct;
875
876 /*
877 * For weighted aggregation, sensor drivers has to specify
878 * n+2 coefficients.
879 */
880 if (coeff_ct != sens->num_sensors) {
881 dev_err(dev, "sens:%s Invalid coefficient\n",
882 sensor_data->virt_zone_name);
883 return ERR_PTR(-EINVAL);
884 }
885 memcpy(sens->coefficients, sensor_data->coefficients,
886 coeff_ct * sizeof(*sens->coefficients));
887 sens->avg_offset = sensor_data->avg_offset;
888 sens->avg_denominator = sensor_data->avg_denominator;
889 }
890
891 for (sens_idx = 0; sens_idx < sens->num_sensors; sens_idx++) {
892 sens->tz[sens_idx] = thermal_zone_get_zone_by_name(
893 sensor_data->sensor_names[sens_idx]);
894 if (IS_ERR(sens->tz[sens_idx])) {
895 dev_err(dev, "sens:%s sensor[%s] fetch err:%ld\n",
896 sensor_data->virt_zone_name,
897 sensor_data->sensor_names[sens_idx],
898 PTR_ERR(sens->tz[sens_idx]));
899 break;
900 }
901 }
902 if (sens->num_sensors != sens_idx)
903 return ERR_PTR(-EAGAIN);
904
905 sens_param = kzalloc(sizeof(*sens_param), GFP_KERNEL);
906 if (!sens_param)
907 return ERR_PTR(-ENOMEM);
908 sens_param->sensor_data = sens;
909 sens_param->ops = &of_virt_ops;
910 INIT_LIST_HEAD(&sens_param->first_tz);
911 sens_param->trip_high = INT_MAX;
912 sens_param->trip_low = INT_MIN;
913 mutex_init(&sens_param->lock);
914
915 mutex_lock(&tzd->lock);
916 tz = tzd->devdata;
917 tz->senps = sens_param;
918 tzd->ops->get_temp = of_thermal_get_temp;
919 list_add_tail(&tz->list, &sens_param->first_tz);
920 mutex_unlock(&tzd->lock);
921
922 ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
923 GFP_KERNEL);
924 if (!ptr)
925 return ERR_PTR(-ENOMEM);
926
927 *ptr = tzd;
928 devres_add(dev, ptr);
929
930 tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
931
932 return tzd;
933}
934EXPORT_SYMBOL(devm_thermal_of_virtual_sensor_register);
935
936/**
Laxman Dewangane498b492016-03-09 18:40:06 +0530937 * devm_thermal_zone_of_sensor_register - Resource managed version of
938 * thermal_zone_of_sensor_register()
939 * @dev: a valid struct device pointer of a sensor device. Must contain
940 * a valid .of_node, for the sensor node.
941 * @sensor_id: a sensor identifier, in case the sensor IP has more
942 * than one sensors
943 * @data: a private pointer (owned by the caller) that will be passed
944 * back, when a temperature reading is needed.
945 * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
946 *
947 * Refer thermal_zone_of_sensor_register() for more details.
948 *
949 * Return: On success returns a valid struct thermal_zone_device,
950 * otherwise, it returns a corresponding ERR_PTR(). Caller must
951 * check the return value with help of IS_ERR() helper.
Zhang Rui7b5c4a02016-08-22 15:48:11 +0800952 * Registered thermal_zone_device device will automatically be
Laxman Dewangane498b492016-03-09 18:40:06 +0530953 * released when device is unbounded.
954 */
955struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
956 struct device *dev, int sensor_id,
957 void *data, const struct thermal_zone_of_device_ops *ops)
958{
959 struct thermal_zone_device **ptr, *tzd;
960
961 ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
962 GFP_KERNEL);
963 if (!ptr)
964 return ERR_PTR(-ENOMEM);
965
966 tzd = thermal_zone_of_sensor_register(dev, sensor_id, data, ops);
967 if (IS_ERR(tzd)) {
968 devres_free(ptr);
969 return tzd;
970 }
971
972 *ptr = tzd;
973 devres_add(dev, ptr);
974
975 return tzd;
976}
977EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_register);
978
979/**
980 * devm_thermal_zone_of_sensor_unregister - Resource managed version of
981 * thermal_zone_of_sensor_unregister().
982 * @dev: Device for which which resource was allocated.
983 * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
984 *
985 * This function removes the sensor callbacks and private data from the
986 * thermal zone device registered with devm_thermal_zone_of_sensor_register()
987 * API. It will also silent the zone by remove the .get_temp() and .get_trend()
988 * thermal zone device callbacks.
989 * Normally this function will not need to be called and the resource
990 * management code will ensure that the resource is freed.
991 */
992void devm_thermal_zone_of_sensor_unregister(struct device *dev,
993 struct thermal_zone_device *tzd)
994{
995 WARN_ON(devres_release(dev, devm_thermal_zone_of_sensor_release,
996 devm_thermal_zone_of_sensor_match, tzd));
997}
998EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_unregister);
999
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001000/*** functions parsing device tree nodes ***/
1001
1002/**
1003 * thermal_of_populate_bind_params - parse and fill cooling map data
1004 * @np: DT node containing a cooling-map node
1005 * @__tbp: data structure to be filled with cooling map info
1006 * @trips: array of thermal zone trip points
1007 * @ntrips: number of trip points inside trips.
1008 *
1009 * This function parses a cooling-map type of node represented by
1010 * @np parameter and fills the read data into @__tbp data structure.
1011 * It needs the already parsed array of trip points of the thermal zone
1012 * in consideration.
1013 *
1014 * Return: 0 on success, proper error code otherwise
1015 */
1016static int thermal_of_populate_bind_params(struct device_node *np,
1017 struct __thermal_bind_params *__tbp,
Lukasz Majewskiad9914a2014-12-08 18:04:19 +01001018 struct thermal_trip *trips,
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001019 int ntrips)
1020{
1021 struct of_phandle_args cooling_spec;
1022 struct device_node *trip;
1023 int ret, i;
1024 u32 prop;
1025
1026 /* Default weight. Usage is optional */
Kapileshwar Singh6cd9e9f2015-02-18 16:04:21 +00001027 __tbp->usage = THERMAL_WEIGHT_DEFAULT;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001028 ret = of_property_read_u32(np, "contribution", &prop);
1029 if (ret == 0)
1030 __tbp->usage = prop;
1031
1032 trip = of_parse_phandle(np, "trip", 0);
1033 if (!trip) {
1034 pr_err("missing trip property\n");
1035 return -ENODEV;
1036 }
1037
1038 /* match using device_node */
1039 for (i = 0; i < ntrips; i++)
1040 if (trip == trips[i].np) {
1041 __tbp->trip_id = i;
1042 break;
1043 }
1044
1045 if (i == ntrips) {
1046 ret = -ENODEV;
1047 goto end;
1048 }
1049
1050 ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
1051 0, &cooling_spec);
1052 if (ret < 0) {
1053 pr_err("missing cooling_device property\n");
1054 goto end;
1055 }
1056 __tbp->cooling_device = cooling_spec.np;
1057 if (cooling_spec.args_count >= 2) { /* at least min and max */
1058 __tbp->min = cooling_spec.args[0];
1059 __tbp->max = cooling_spec.args[1];
1060 } else {
1061 pr_err("wrong reference to cooling device, missing limits\n");
1062 }
1063
1064end:
1065 of_node_put(trip);
1066
1067 return ret;
1068}
1069
1070/**
1071 * It maps 'enum thermal_trip_type' found in include/linux/thermal.h
1072 * into the device tree binding of 'trip', property type.
1073 */
1074static const char * const trip_types[] = {
1075 [THERMAL_TRIP_ACTIVE] = "active",
1076 [THERMAL_TRIP_PASSIVE] = "passive",
1077 [THERMAL_TRIP_HOT] = "hot",
1078 [THERMAL_TRIP_CRITICAL] = "critical",
1079};
1080
1081/**
1082 * thermal_of_get_trip_type - Get phy mode for given device_node
1083 * @np: Pointer to the given device_node
1084 * @type: Pointer to resulting trip type
1085 *
1086 * The function gets trip type string from property 'type',
1087 * and store its index in trip_types table in @type,
1088 *
1089 * Return: 0 on success, or errno in error case.
1090 */
1091static int thermal_of_get_trip_type(struct device_node *np,
1092 enum thermal_trip_type *type)
1093{
1094 const char *t;
1095 int err, i;
1096
1097 err = of_property_read_string(np, "type", &t);
1098 if (err < 0)
1099 return err;
1100
1101 for (i = 0; i < ARRAY_SIZE(trip_types); i++)
1102 if (!strcasecmp(t, trip_types[i])) {
1103 *type = i;
1104 return 0;
1105 }
1106
1107 return -ENODEV;
1108}
1109
1110/**
1111 * thermal_of_populate_trip - parse and fill one trip point data
1112 * @np: DT node containing a trip point node
1113 * @trip: trip point data structure to be filled up
1114 *
1115 * This function parses a trip point type of node represented by
1116 * @np parameter and fills the read data into @trip data structure.
1117 *
1118 * Return: 0 on success, proper error code otherwise
1119 */
1120static int thermal_of_populate_trip(struct device_node *np,
Lukasz Majewskiad9914a2014-12-08 18:04:19 +01001121 struct thermal_trip *trip)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001122{
1123 int prop;
1124 int ret;
1125
1126 ret = of_property_read_u32(np, "temperature", &prop);
1127 if (ret < 0) {
1128 pr_err("missing temperature property\n");
1129 return ret;
1130 }
1131 trip->temperature = prop;
1132
1133 ret = of_property_read_u32(np, "hysteresis", &prop);
1134 if (ret < 0) {
1135 pr_err("missing hysteresis property\n");
1136 return ret;
1137 }
1138 trip->hysteresis = prop;
1139
1140 ret = thermal_of_get_trip_type(np, &trip->type);
1141 if (ret < 0) {
1142 pr_err("wrong trip type property\n");
1143 return ret;
1144 }
1145
1146 /* Required for cooling map matching */
1147 trip->np = np;
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001148 of_node_get(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001149
1150 return 0;
1151}
1152
1153/**
1154 * thermal_of_build_thermal_zone - parse and fill one thermal zone data
1155 * @np: DT node containing a thermal zone node
1156 *
1157 * This function parses a thermal zone type of node represented by
1158 * @np parameter and fills the read data into a __thermal_zone data structure
1159 * and return this pointer.
1160 *
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001161 * TODO: Missing properties to parse: thermal-sensor-names
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001162 *
1163 * Return: On success returns a valid struct __thermal_zone,
1164 * otherwise, it returns a corresponding ERR_PTR(). Caller must
1165 * check the return value with help of IS_ERR() helper.
1166 */
Julia Lawallc0ff8aa2016-04-19 14:33:32 +02001167static struct __thermal_zone
1168__init *thermal_of_build_thermal_zone(struct device_node *np)
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001169{
1170 struct device_node *child = NULL, *gchild;
1171 struct __thermal_zone *tz;
1172 int ret, i;
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001173 u32 prop, coef[2];
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001174
1175 if (!np) {
1176 pr_err("no thermal zone np\n");
1177 return ERR_PTR(-EINVAL);
1178 }
1179
1180 tz = kzalloc(sizeof(*tz), GFP_KERNEL);
1181 if (!tz)
1182 return ERR_PTR(-ENOMEM);
1183
Ram Chandrasekard29230b2017-02-27 11:26:51 -07001184 INIT_LIST_HEAD(&tz->list);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001185 ret = of_property_read_u32(np, "polling-delay-passive", &prop);
1186 if (ret < 0) {
1187 pr_err("missing polling-delay-passive property\n");
1188 goto free_tz;
1189 }
1190 tz->passive_delay = prop;
1191
1192 ret = of_property_read_u32(np, "polling-delay", &prop);
1193 if (ret < 0) {
1194 pr_err("missing polling-delay property\n");
1195 goto free_tz;
1196 }
1197 tz->polling_delay = prop;
1198
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001199 /*
1200 * REVIST: for now, the thermal framework supports only
1201 * one sensor per thermal zone. Thus, we are considering
1202 * only the first two values as slope and offset.
1203 */
1204 ret = of_property_read_u32_array(np, "coefficients", coef, 2);
1205 if (ret == 0) {
1206 tz->slope = coef[0];
1207 tz->offset = coef[1];
1208 } else {
1209 tz->slope = 1;
1210 tz->offset = 0;
1211 }
1212
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001213 /* trips */
1214 child = of_get_child_by_name(np, "trips");
1215
1216 /* No trips provided */
1217 if (!child)
1218 goto finish;
1219
1220 tz->ntrips = of_get_child_count(child);
1221 if (tz->ntrips == 0) /* must have at least one child */
1222 goto finish;
1223
1224 tz->trips = kzalloc(tz->ntrips * sizeof(*tz->trips), GFP_KERNEL);
1225 if (!tz->trips) {
1226 ret = -ENOMEM;
1227 goto free_tz;
1228 }
1229
1230 i = 0;
1231 for_each_child_of_node(child, gchild) {
1232 ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
1233 if (ret)
1234 goto free_trips;
1235 }
1236
1237 of_node_put(child);
1238
1239 /* cooling-maps */
1240 child = of_get_child_by_name(np, "cooling-maps");
1241
1242 /* cooling-maps not provided */
1243 if (!child)
1244 goto finish;
1245
1246 tz->num_tbps = of_get_child_count(child);
1247 if (tz->num_tbps == 0)
1248 goto finish;
1249
1250 tz->tbps = kzalloc(tz->num_tbps * sizeof(*tz->tbps), GFP_KERNEL);
1251 if (!tz->tbps) {
1252 ret = -ENOMEM;
1253 goto free_trips;
1254 }
1255
1256 i = 0;
Stephen Boydca9521b2014-06-18 16:32:08 -07001257 for_each_child_of_node(child, gchild) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001258 ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
1259 tz->trips, tz->ntrips);
1260 if (ret)
1261 goto free_tbps;
Stephen Boydca9521b2014-06-18 16:32:08 -07001262 }
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001263
1264finish:
1265 of_node_put(child);
1266 tz->mode = THERMAL_DEVICE_DISABLED;
1267
1268 return tz;
1269
1270free_tbps:
Ulises Brindis1cd91c12016-03-25 12:55:41 -07001271 for (i = i - 1; i >= 0; i--)
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001272 of_node_put(tz->tbps[i].cooling_device);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001273 kfree(tz->tbps);
1274free_trips:
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001275 for (i = 0; i < tz->ntrips; i++)
1276 of_node_put(tz->trips[i].np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001277 kfree(tz->trips);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001278 of_node_put(gchild);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001279free_tz:
1280 kfree(tz);
1281 of_node_put(child);
1282
1283 return ERR_PTR(ret);
1284}
1285
1286static inline void of_thermal_free_zone(struct __thermal_zone *tz)
1287{
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001288 int i;
1289
1290 for (i = 0; i < tz->num_tbps; i++)
1291 of_node_put(tz->tbps[i].cooling_device);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001292 kfree(tz->tbps);
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001293 for (i = 0; i < tz->ntrips; i++)
1294 of_node_put(tz->trips[i].np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001295 kfree(tz->trips);
1296 kfree(tz);
1297}
1298
1299/**
1300 * of_parse_thermal_zones - parse device tree thermal data
1301 *
1302 * Initialization function that can be called by machine initialization
1303 * code to parse thermal data and populate the thermal framework
1304 * with hardware thermal zones info. This function only parses thermal zones.
1305 * Cooling devices and sensor devices nodes are supposed to be parsed
1306 * by their respective drivers.
1307 *
1308 * Return: 0 on success, proper error code otherwise
1309 *
1310 */
1311int __init of_parse_thermal_zones(void)
1312{
1313 struct device_node *np, *child;
1314 struct __thermal_zone *tz;
1315 struct thermal_zone_device_ops *ops;
1316
1317 np = of_find_node_by_name(NULL, "thermal-zones");
1318 if (!np) {
1319 pr_debug("unable to find thermal zones\n");
1320 return 0; /* Run successfully on systems without thermal DT */
1321 }
1322
Laxman Dewangan42bbe402016-02-08 18:58:34 +05301323 for_each_available_child_of_node(np, child) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001324 struct thermal_zone_device *zone;
1325 struct thermal_zone_params *tzp;
Punit Agrawal76af5492015-03-03 10:43:04 +00001326 int i, mask = 0;
Punit Agrawal647f9922015-02-26 19:00:32 +00001327 u32 prop;
Ram Chandrasekarcaafe202016-07-19 11:25:46 -06001328 const char *governor_name;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001329
1330 tz = thermal_of_build_thermal_zone(child);
1331 if (IS_ERR(tz)) {
1332 pr_err("failed to build thermal zone %s: %ld\n",
1333 child->name,
1334 PTR_ERR(tz));
1335 continue;
1336 }
1337
1338 ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
1339 if (!ops)
1340 goto exit_free;
1341
1342 tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
1343 if (!tzp) {
1344 kfree(ops);
1345 goto exit_free;
1346 }
1347
1348 /* No hwmon because there might be hwmon drivers registering */
1349 tzp->no_hwmon = true;
1350
Ram Chandrasekarcaafe202016-07-19 11:25:46 -06001351 if (!of_property_read_string(child, "thermal-governor",
1352 &governor_name))
1353 strlcpy(tzp->governor_name, governor_name,
1354 THERMAL_NAME_LENGTH);
1355
Punit Agrawal647f9922015-02-26 19:00:32 +00001356 if (!of_property_read_u32(child, "sustainable-power", &prop))
1357 tzp->sustainable_power = prop;
1358
Punit Agrawal76af5492015-03-03 10:43:04 +00001359 for (i = 0; i < tz->ntrips; i++)
1360 mask |= 1 << i;
1361
Eduardo Valentina46dbae2015-05-11 19:48:09 -07001362 /* these two are left for temperature drivers to use */
1363 tzp->slope = tz->slope;
1364 tzp->offset = tz->offset;
1365
Lina Iyer159f67d2016-07-27 11:34:46 -06001366 if (of_property_read_bool(child, "tracks-low"))
1367 tzp->tracks_low = true;
1368
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001369 zone = thermal_zone_device_register(child->name, tz->ntrips,
Punit Agrawal76af5492015-03-03 10:43:04 +00001370 mask, tz,
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001371 ops, tzp,
1372 tz->passive_delay,
1373 tz->polling_delay);
1374 if (IS_ERR(zone)) {
1375 pr_err("Failed to build %s zone %ld\n", child->name,
1376 PTR_ERR(zone));
1377 kfree(tzp);
1378 kfree(ops);
1379 of_thermal_free_zone(tz);
1380 /* attempting to build remaining zones still */
Ram Chandrasekard29230b2017-02-27 11:26:51 -07001381 continue;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001382 }
Ram Chandrasekard29230b2017-02-27 11:26:51 -07001383 tz->tzd = zone;
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001384 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001385 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001386
1387 return 0;
1388
1389exit_free:
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001390 of_node_put(child);
1391 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001392 of_thermal_free_zone(tz);
1393
1394 /* no memory available, so free what we have built */
1395 of_thermal_destroy_zones();
1396
1397 return -ENOMEM;
1398}
1399
1400/**
1401 * of_thermal_destroy_zones - remove all zones parsed and allocated resources
1402 *
1403 * Finds all zones parsed and added to the thermal framework and remove them
1404 * from the system, together with their resources.
1405 *
1406 */
1407void of_thermal_destroy_zones(void)
1408{
1409 struct device_node *np, *child;
1410
1411 np = of_find_node_by_name(NULL, "thermal-zones");
1412 if (!np) {
Jiada Wang28524982015-11-16 17:10:05 +09001413 pr_debug("unable to find thermal zones\n");
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001414 return;
1415 }
1416
Laxman Dewangan42bbe402016-02-08 18:58:34 +05301417 for_each_available_child_of_node(np, child) {
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001418 struct thermal_zone_device *zone;
1419
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001420 zone = thermal_zone_get_zone_by_name(child->name);
1421 if (IS_ERR(zone))
1422 continue;
1423
1424 thermal_zone_device_unregister(zone);
1425 kfree(zone->tzp);
1426 kfree(zone->ops);
1427 of_thermal_free_zone(zone->devdata);
1428 }
Vladimir Zapolskiyc2aad93c2014-09-29 02:47:46 +03001429 of_node_put(np);
Eduardo Valentin4e5e4702013-07-03 15:35:39 -04001430}