Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * of-thermal.c - Generic Thermal Management device tree support.
4 : : *
5 : : * Copyright (C) 2013 Texas Instruments
6 : : * Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com>
7 : : */
8 : :
9 : : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 : :
11 : : #include <linux/thermal.h>
12 : : #include <linux/slab.h>
13 : : #include <linux/types.h>
14 : : #include <linux/of_device.h>
15 : : #include <linux/of_platform.h>
16 : : #include <linux/err.h>
17 : : #include <linux/export.h>
18 : : #include <linux/string.h>
19 : :
20 : : #include "thermal_core.h"
21 : :
22 : : /*** Private data structures to represent thermal device tree data ***/
23 : :
24 : : /**
25 : : * struct __thermal_cooling_bind_param - a cooling device for a trip point
26 : : * @cooling_device: a pointer to identify the referred cooling device
27 : : * @min: minimum cooling state used at this trip point
28 : : * @max: maximum cooling state used at this trip point
29 : : */
30 : :
31 : : struct __thermal_cooling_bind_param {
32 : : struct device_node *cooling_device;
33 : : unsigned long min;
34 : : unsigned long max;
35 : : };
36 : :
37 : : /**
38 : : * struct __thermal_bind_param - a match between trip and cooling device
39 : : * @tcbp: a pointer to an array of cooling devices
40 : : * @count: number of elements in array
41 : : * @trip_id: the trip point index
42 : : * @usage: the percentage (from 0 to 100) of cooling contribution
43 : : */
44 : :
45 : : struct __thermal_bind_params {
46 : : struct __thermal_cooling_bind_param *tcbp;
47 : : unsigned int count;
48 : : unsigned int trip_id;
49 : : unsigned int usage;
50 : : };
51 : :
52 : : /**
53 : : * struct __thermal_zone - internal representation of a thermal zone
54 : : * @mode: current thermal zone device mode (enabled/disabled)
55 : : * @passive_delay: polling interval while passive cooling is activated
56 : : * @polling_delay: zone polling interval
57 : : * @slope: slope of the temperature adjustment curve
58 : : * @offset: offset of the temperature adjustment curve
59 : : * @ntrips: number of trip points
60 : : * @trips: an array of trip points (0..ntrips - 1)
61 : : * @num_tbps: number of thermal bind params
62 : : * @tbps: an array of thermal bind params (0..num_tbps - 1)
63 : : * @sensor_data: sensor private data used while reading temperature and trend
64 : : * @ops: set of callbacks to handle the thermal zone based on DT
65 : : */
66 : :
67 : : struct __thermal_zone {
68 : : enum thermal_device_mode mode;
69 : : int passive_delay;
70 : : int polling_delay;
71 : : int slope;
72 : : int offset;
73 : :
74 : : /* trip data */
75 : : int ntrips;
76 : : struct thermal_trip *trips;
77 : :
78 : : /* cooling binding data */
79 : : int num_tbps;
80 : : struct __thermal_bind_params *tbps;
81 : :
82 : : /* sensor interface */
83 : : void *sensor_data;
84 : : const struct thermal_zone_of_device_ops *ops;
85 : : };
86 : :
87 : : /*** DT thermal zone device callbacks ***/
88 : :
89 : 3 : static int of_thermal_get_temp(struct thermal_zone_device *tz,
90 : : int *temp)
91 : : {
92 : 3 : struct __thermal_zone *data = tz->devdata;
93 : :
94 : 3 : if (!data->ops->get_temp)
95 : : return -EINVAL;
96 : :
97 : 3 : return data->ops->get_temp(data->sensor_data, temp);
98 : : }
99 : :
100 : 0 : static int of_thermal_set_trips(struct thermal_zone_device *tz,
101 : : int low, int high)
102 : : {
103 : 0 : struct __thermal_zone *data = tz->devdata;
104 : :
105 : 0 : if (!data->ops || !data->ops->set_trips)
106 : : return -EINVAL;
107 : :
108 : 0 : return data->ops->set_trips(data->sensor_data, low, high);
109 : : }
110 : :
111 : : /**
112 : : * of_thermal_get_ntrips - function to export number of available trip
113 : : * points.
114 : : * @tz: pointer to a thermal zone
115 : : *
116 : : * This function is a globally visible wrapper to get number of trip points
117 : : * stored in the local struct __thermal_zone
118 : : *
119 : : * Return: number of available trip points, -ENODEV when data not available
120 : : */
121 : 0 : int of_thermal_get_ntrips(struct thermal_zone_device *tz)
122 : : {
123 : 0 : struct __thermal_zone *data = tz->devdata;
124 : :
125 : 0 : if (!data || IS_ERR(data))
126 : : return -ENODEV;
127 : :
128 : 0 : return data->ntrips;
129 : : }
130 : : EXPORT_SYMBOL_GPL(of_thermal_get_ntrips);
131 : :
132 : : /**
133 : : * of_thermal_is_trip_valid - function to check if trip point is valid
134 : : *
135 : : * @tz: pointer to a thermal zone
136 : : * @trip: trip point to evaluate
137 : : *
138 : : * This function is responsible for checking if passed trip point is valid
139 : : *
140 : : * Return: true if trip point is valid, false otherwise
141 : : */
142 : 0 : bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip)
143 : : {
144 : 0 : struct __thermal_zone *data = tz->devdata;
145 : :
146 : 0 : if (!data || trip >= data->ntrips || trip < 0)
147 : : return false;
148 : :
149 : 0 : return true;
150 : : }
151 : : EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid);
152 : :
153 : : /**
154 : : * of_thermal_get_trip_points - function to get access to a globally exported
155 : : * trip points
156 : : *
157 : : * @tz: pointer to a thermal zone
158 : : *
159 : : * This function provides a pointer to trip points table
160 : : *
161 : : * Return: pointer to trip points table, NULL otherwise
162 : : */
163 : : const struct thermal_trip *
164 : 0 : of_thermal_get_trip_points(struct thermal_zone_device *tz)
165 : : {
166 : 0 : struct __thermal_zone *data = tz->devdata;
167 : :
168 : 0 : if (!data)
169 : : return NULL;
170 : :
171 : 0 : return data->trips;
172 : : }
173 : : EXPORT_SYMBOL_GPL(of_thermal_get_trip_points);
174 : :
175 : : /**
176 : : * of_thermal_set_emul_temp - function to set emulated temperature
177 : : *
178 : : * @tz: pointer to a thermal zone
179 : : * @temp: temperature to set
180 : : *
181 : : * This function gives the ability to set emulated value of temperature,
182 : : * which is handy for debugging
183 : : *
184 : : * Return: zero on success, error code otherwise
185 : : */
186 : 0 : static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
187 : : int temp)
188 : : {
189 : 0 : struct __thermal_zone *data = tz->devdata;
190 : :
191 : 0 : return data->ops->set_emul_temp(data->sensor_data, temp);
192 : : }
193 : :
194 : 0 : static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
195 : : enum thermal_trend *trend)
196 : : {
197 : 0 : struct __thermal_zone *data = tz->devdata;
198 : :
199 : 0 : if (!data->ops->get_trend)
200 : : return -EINVAL;
201 : :
202 : 0 : return data->ops->get_trend(data->sensor_data, trip, trend);
203 : : }
204 : :
205 : 0 : static int of_thermal_bind(struct thermal_zone_device *thermal,
206 : : struct thermal_cooling_device *cdev)
207 : : {
208 : 0 : struct __thermal_zone *data = thermal->devdata;
209 : : struct __thermal_bind_params *tbp;
210 : : struct __thermal_cooling_bind_param *tcbp;
211 : : int i, j;
212 : :
213 : 0 : if (!data || IS_ERR(data))
214 : : return -ENODEV;
215 : :
216 : : /* find where to bind */
217 : 0 : for (i = 0; i < data->num_tbps; i++) {
218 : 0 : tbp = data->tbps + i;
219 : :
220 : 0 : for (j = 0; j < tbp->count; j++) {
221 : 0 : tcbp = tbp->tcbp + j;
222 : :
223 : 0 : if (tcbp->cooling_device == cdev->np) {
224 : : int ret;
225 : :
226 : 0 : ret = thermal_zone_bind_cooling_device(thermal,
227 : 0 : tbp->trip_id, cdev,
228 : : tcbp->max,
229 : : tcbp->min,
230 : : tbp->usage);
231 : 0 : if (ret)
232 : 0 : return ret;
233 : : }
234 : : }
235 : : }
236 : :
237 : : return 0;
238 : : }
239 : :
240 : 0 : static int of_thermal_unbind(struct thermal_zone_device *thermal,
241 : : struct thermal_cooling_device *cdev)
242 : : {
243 : 0 : struct __thermal_zone *data = thermal->devdata;
244 : : struct __thermal_bind_params *tbp;
245 : : struct __thermal_cooling_bind_param *tcbp;
246 : : int i, j;
247 : :
248 : 0 : if (!data || IS_ERR(data))
249 : : return -ENODEV;
250 : :
251 : : /* find where to unbind */
252 : 0 : for (i = 0; i < data->num_tbps; i++) {
253 : 0 : tbp = data->tbps + i;
254 : :
255 : 0 : for (j = 0; j < tbp->count; j++) {
256 : 0 : tcbp = tbp->tcbp + j;
257 : :
258 : 0 : if (tcbp->cooling_device == cdev->np) {
259 : : int ret;
260 : :
261 : 0 : ret = thermal_zone_unbind_cooling_device(thermal,
262 : 0 : tbp->trip_id, cdev);
263 : 0 : if (ret)
264 : 0 : return ret;
265 : : }
266 : : }
267 : : }
268 : :
269 : : return 0;
270 : : }
271 : :
272 : 0 : static int of_thermal_get_mode(struct thermal_zone_device *tz,
273 : : enum thermal_device_mode *mode)
274 : : {
275 : 0 : struct __thermal_zone *data = tz->devdata;
276 : :
277 : 0 : *mode = data->mode;
278 : :
279 : 0 : return 0;
280 : : }
281 : :
282 : 3 : static int of_thermal_set_mode(struct thermal_zone_device *tz,
283 : : enum thermal_device_mode mode)
284 : : {
285 : 3 : struct __thermal_zone *data = tz->devdata;
286 : :
287 : 3 : mutex_lock(&tz->lock);
288 : :
289 : 3 : if (mode == THERMAL_DEVICE_ENABLED) {
290 : 3 : tz->polling_delay = data->polling_delay;
291 : 3 : tz->passive_delay = data->passive_delay;
292 : : } else {
293 : 0 : tz->polling_delay = 0;
294 : 0 : tz->passive_delay = 0;
295 : : }
296 : :
297 : 3 : mutex_unlock(&tz->lock);
298 : :
299 : 3 : data->mode = mode;
300 : 3 : thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
301 : :
302 : 3 : return 0;
303 : : }
304 : :
305 : 0 : static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
306 : : enum thermal_trip_type *type)
307 : : {
308 : 0 : struct __thermal_zone *data = tz->devdata;
309 : :
310 : 0 : if (trip >= data->ntrips || trip < 0)
311 : : return -EDOM;
312 : :
313 : 0 : *type = data->trips[trip].type;
314 : :
315 : 0 : return 0;
316 : : }
317 : :
318 : 1 : static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
319 : : int *temp)
320 : : {
321 : 1 : struct __thermal_zone *data = tz->devdata;
322 : :
323 : 1 : if (trip >= data->ntrips || trip < 0)
324 : : return -EDOM;
325 : :
326 : 0 : *temp = data->trips[trip].temperature;
327 : :
328 : 0 : return 0;
329 : : }
330 : :
331 : 0 : static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
332 : : int temp)
333 : : {
334 : 0 : struct __thermal_zone *data = tz->devdata;
335 : :
336 : 0 : if (trip >= data->ntrips || trip < 0)
337 : : return -EDOM;
338 : :
339 : 0 : if (data->ops->set_trip_temp) {
340 : : int ret;
341 : :
342 : 0 : ret = data->ops->set_trip_temp(data->sensor_data, trip, temp);
343 : 0 : if (ret)
344 : : return ret;
345 : : }
346 : :
347 : : /* thermal framework should take care of data->mask & (1 << trip) */
348 : 0 : data->trips[trip].temperature = temp;
349 : :
350 : 0 : return 0;
351 : : }
352 : :
353 : 0 : static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
354 : : int *hyst)
355 : : {
356 : 0 : struct __thermal_zone *data = tz->devdata;
357 : :
358 : 0 : if (trip >= data->ntrips || trip < 0)
359 : : return -EDOM;
360 : :
361 : 0 : *hyst = data->trips[trip].hysteresis;
362 : :
363 : 0 : return 0;
364 : : }
365 : :
366 : 0 : static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
367 : : int hyst)
368 : : {
369 : 0 : struct __thermal_zone *data = tz->devdata;
370 : :
371 : 0 : if (trip >= data->ntrips || trip < 0)
372 : : return -EDOM;
373 : :
374 : : /* thermal framework should take care of data->mask & (1 << trip) */
375 : 0 : data->trips[trip].hysteresis = hyst;
376 : :
377 : 0 : return 0;
378 : : }
379 : :
380 : 2 : static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
381 : : int *temp)
382 : : {
383 : 2 : struct __thermal_zone *data = tz->devdata;
384 : : int i;
385 : :
386 : 2 : for (i = 0; i < data->ntrips; i++)
387 : 0 : if (data->trips[i].type == THERMAL_TRIP_CRITICAL) {
388 : 0 : *temp = data->trips[i].temperature;
389 : 0 : return 0;
390 : : }
391 : :
392 : : return -EINVAL;
393 : : }
394 : :
395 : : static struct thermal_zone_device_ops of_thermal_ops = {
396 : : .get_mode = of_thermal_get_mode,
397 : : .set_mode = of_thermal_set_mode,
398 : :
399 : : .get_trip_type = of_thermal_get_trip_type,
400 : : .get_trip_temp = of_thermal_get_trip_temp,
401 : : .set_trip_temp = of_thermal_set_trip_temp,
402 : : .get_trip_hyst = of_thermal_get_trip_hyst,
403 : : .set_trip_hyst = of_thermal_set_trip_hyst,
404 : : .get_crit_temp = of_thermal_get_crit_temp,
405 : :
406 : : .bind = of_thermal_bind,
407 : : .unbind = of_thermal_unbind,
408 : : };
409 : :
410 : : /*** sensor API ***/
411 : :
412 : : static struct thermal_zone_device *
413 : 3 : thermal_zone_of_add_sensor(struct device_node *zone,
414 : : struct device_node *sensor, void *data,
415 : : const struct thermal_zone_of_device_ops *ops)
416 : : {
417 : : struct thermal_zone_device *tzd;
418 : : struct __thermal_zone *tz;
419 : :
420 : 3 : tzd = thermal_zone_get_zone_by_name(zone->name);
421 : 3 : if (IS_ERR(tzd))
422 : : return ERR_PTR(-EPROBE_DEFER);
423 : :
424 : 3 : tz = tzd->devdata;
425 : :
426 : 3 : if (!ops)
427 : : return ERR_PTR(-EINVAL);
428 : :
429 : 3 : mutex_lock(&tzd->lock);
430 : 3 : tz->ops = ops;
431 : 3 : tz->sensor_data = data;
432 : :
433 : 3 : tzd->ops->get_temp = of_thermal_get_temp;
434 : 3 : tzd->ops->get_trend = of_thermal_get_trend;
435 : :
436 : : /*
437 : : * The thermal zone core will calculate the window if they have set the
438 : : * optional set_trips pointer.
439 : : */
440 : 3 : if (ops->set_trips)
441 : 0 : tzd->ops->set_trips = of_thermal_set_trips;
442 : :
443 : 3 : if (ops->set_emul_temp)
444 : 0 : tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
445 : :
446 : 3 : mutex_unlock(&tzd->lock);
447 : :
448 : 3 : return tzd;
449 : : }
450 : :
451 : : /**
452 : : * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
453 : : * @dev: a valid struct device pointer of a sensor device. Must contain
454 : : * a valid .of_node, for the sensor node.
455 : : * @sensor_id: a sensor identifier, in case the sensor IP has more
456 : : * than one sensors
457 : : * @data: a private pointer (owned by the caller) that will be passed
458 : : * back, when a temperature reading is needed.
459 : : * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
460 : : *
461 : : * This function will search the list of thermal zones described in device
462 : : * tree and look for the zone that refer to the sensor device pointed by
463 : : * @dev->of_node as temperature providers. For the zone pointing to the
464 : : * sensor node, the sensor will be added to the DT thermal zone device.
465 : : *
466 : : * The thermal zone temperature is provided by the @get_temp function
467 : : * pointer. When called, it will have the private pointer @data back.
468 : : *
469 : : * The thermal zone temperature trend is provided by the @get_trend function
470 : : * pointer. When called, it will have the private pointer @data back.
471 : : *
472 : : * TODO:
473 : : * 01 - This function must enqueue the new sensor instead of using
474 : : * it as the only source of temperature values.
475 : : *
476 : : * 02 - There must be a way to match the sensor with all thermal zones
477 : : * that refer to it.
478 : : *
479 : : * Return: On success returns a valid struct thermal_zone_device,
480 : : * otherwise, it returns a corresponding ERR_PTR(). Caller must
481 : : * check the return value with help of IS_ERR() helper.
482 : : */
483 : : struct thermal_zone_device *
484 : 3 : thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
485 : : const struct thermal_zone_of_device_ops *ops)
486 : : {
487 : : struct device_node *np, *child, *sensor_np;
488 : : struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
489 : :
490 : 3 : np = of_find_node_by_name(NULL, "thermal-zones");
491 : 3 : if (!np)
492 : : return ERR_PTR(-ENODEV);
493 : :
494 : 3 : if (!dev || !dev->of_node) {
495 : 0 : of_node_put(np);
496 : 0 : return ERR_PTR(-EINVAL);
497 : : }
498 : :
499 : 3 : sensor_np = of_node_get(dev->of_node);
500 : :
501 : 3 : for_each_available_child_of_node(np, child) {
502 : : struct of_phandle_args sensor_specs;
503 : : int ret, id;
504 : :
505 : : /* For now, thermal framework supports only 1 sensor per zone */
506 : 3 : ret = of_parse_phandle_with_args(child, "thermal-sensors",
507 : : "#thermal-sensor-cells",
508 : : 0, &sensor_specs);
509 : 3 : if (ret)
510 : 0 : continue;
511 : :
512 : 3 : if (sensor_specs.args_count >= 1) {
513 : 0 : id = sensor_specs.args[0];
514 : 0 : WARN(sensor_specs.args_count > 1,
515 : : "%pOFn: too many cells in sensor specifier %d\n",
516 : : sensor_specs.np, sensor_specs.args_count);
517 : : } else {
518 : : id = 0;
519 : : }
520 : :
521 : 3 : if (sensor_specs.np == sensor_np && id == sensor_id) {
522 : 3 : tzd = thermal_zone_of_add_sensor(child, sensor_np,
523 : : data, ops);
524 : 3 : if (!IS_ERR(tzd))
525 : 3 : tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
526 : :
527 : 3 : of_node_put(sensor_specs.np);
528 : 3 : of_node_put(child);
529 : 3 : goto exit;
530 : : }
531 : 0 : of_node_put(sensor_specs.np);
532 : : }
533 : : exit:
534 : 3 : of_node_put(sensor_np);
535 : 3 : of_node_put(np);
536 : :
537 : 3 : return tzd;
538 : : }
539 : : EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
540 : :
541 : : /**
542 : : * thermal_zone_of_sensor_unregister - unregisters a sensor from a DT thermal zone
543 : : * @dev: a valid struct device pointer of a sensor device. Must contain
544 : : * a valid .of_node, for the sensor node.
545 : : * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
546 : : *
547 : : * This function removes the sensor callbacks and private data from the
548 : : * thermal zone device registered with thermal_zone_of_sensor_register()
549 : : * API. It will also silent the zone by remove the .get_temp() and .get_trend()
550 : : * thermal zone device callbacks.
551 : : *
552 : : * TODO: When the support to several sensors per zone is added, this
553 : : * function must search the sensor list based on @dev parameter.
554 : : *
555 : : */
556 : 1 : void thermal_zone_of_sensor_unregister(struct device *dev,
557 : : struct thermal_zone_device *tzd)
558 : : {
559 : : struct __thermal_zone *tz;
560 : :
561 : 1 : if (!dev || !tzd || !tzd->devdata)
562 : : return;
563 : :
564 : : tz = tzd->devdata;
565 : :
566 : : /* no __thermal_zone, nothing to be done */
567 : 1 : if (!tz)
568 : : return;
569 : :
570 : 1 : mutex_lock(&tzd->lock);
571 : 1 : tzd->ops->get_temp = NULL;
572 : 1 : tzd->ops->get_trend = NULL;
573 : 1 : tzd->ops->set_emul_temp = NULL;
574 : :
575 : 1 : tz->ops = NULL;
576 : 1 : tz->sensor_data = NULL;
577 : 1 : mutex_unlock(&tzd->lock);
578 : : }
579 : : EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
580 : :
581 : 0 : static void devm_thermal_zone_of_sensor_release(struct device *dev, void *res)
582 : : {
583 : 0 : thermal_zone_of_sensor_unregister(dev,
584 : : *(struct thermal_zone_device **)res);
585 : 0 : }
586 : :
587 : 0 : static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
588 : : void *data)
589 : : {
590 : : struct thermal_zone_device **r = res;
591 : :
592 : 0 : if (WARN_ON(!r || !*r))
593 : : return 0;
594 : :
595 : 0 : return *r == data;
596 : : }
597 : :
598 : : /**
599 : : * devm_thermal_zone_of_sensor_register - Resource managed version of
600 : : * thermal_zone_of_sensor_register()
601 : : * @dev: a valid struct device pointer of a sensor device. Must contain
602 : : * a valid .of_node, for the sensor node.
603 : : * @sensor_id: a sensor identifier, in case the sensor IP has more
604 : : * than one sensors
605 : : * @data: a private pointer (owned by the caller) that will be passed
606 : : * back, when a temperature reading is needed.
607 : : * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
608 : : *
609 : : * Refer thermal_zone_of_sensor_register() for more details.
610 : : *
611 : : * Return: On success returns a valid struct thermal_zone_device,
612 : : * otherwise, it returns a corresponding ERR_PTR(). Caller must
613 : : * check the return value with help of IS_ERR() helper.
614 : : * Registered thermal_zone_device device will automatically be
615 : : * released when device is unbounded.
616 : : */
617 : 0 : struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
618 : : struct device *dev, int sensor_id,
619 : : void *data, const struct thermal_zone_of_device_ops *ops)
620 : : {
621 : : struct thermal_zone_device **ptr, *tzd;
622 : :
623 : : ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
624 : : GFP_KERNEL);
625 : 0 : if (!ptr)
626 : : return ERR_PTR(-ENOMEM);
627 : :
628 : 0 : tzd = thermal_zone_of_sensor_register(dev, sensor_id, data, ops);
629 : 0 : if (IS_ERR(tzd)) {
630 : 0 : devres_free(ptr);
631 : 0 : return tzd;
632 : : }
633 : :
634 : 0 : *ptr = tzd;
635 : 0 : devres_add(dev, ptr);
636 : :
637 : 0 : return tzd;
638 : : }
639 : : EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_register);
640 : :
641 : : /**
642 : : * devm_thermal_zone_of_sensor_unregister - Resource managed version of
643 : : * thermal_zone_of_sensor_unregister().
644 : : * @dev: Device for which which resource was allocated.
645 : : * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
646 : : *
647 : : * This function removes the sensor callbacks and private data from the
648 : : * thermal zone device registered with devm_thermal_zone_of_sensor_register()
649 : : * API. It will also silent the zone by remove the .get_temp() and .get_trend()
650 : : * thermal zone device callbacks.
651 : : * Normally this function will not need to be called and the resource
652 : : * management code will ensure that the resource is freed.
653 : : */
654 : 0 : void devm_thermal_zone_of_sensor_unregister(struct device *dev,
655 : : struct thermal_zone_device *tzd)
656 : : {
657 : 0 : WARN_ON(devres_release(dev, devm_thermal_zone_of_sensor_release,
658 : : devm_thermal_zone_of_sensor_match, tzd));
659 : 0 : }
660 : : EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_unregister);
661 : :
662 : : /*** functions parsing device tree nodes ***/
663 : :
664 : : /**
665 : : * thermal_of_populate_bind_params - parse and fill cooling map data
666 : : * @np: DT node containing a cooling-map node
667 : : * @__tbp: data structure to be filled with cooling map info
668 : : * @trips: array of thermal zone trip points
669 : : * @ntrips: number of trip points inside trips.
670 : : *
671 : : * This function parses a cooling-map type of node represented by
672 : : * @np parameter and fills the read data into @__tbp data structure.
673 : : * It needs the already parsed array of trip points of the thermal zone
674 : : * in consideration.
675 : : *
676 : : * Return: 0 on success, proper error code otherwise
677 : : */
678 : 0 : static int thermal_of_populate_bind_params(struct device_node *np,
679 : : struct __thermal_bind_params *__tbp,
680 : : struct thermal_trip *trips,
681 : : int ntrips)
682 : : {
683 : : struct of_phandle_args cooling_spec;
684 : : struct __thermal_cooling_bind_param *__tcbp;
685 : : struct device_node *trip;
686 : : int ret, i, count;
687 : : u32 prop;
688 : :
689 : : /* Default weight. Usage is optional */
690 : 0 : __tbp->usage = THERMAL_WEIGHT_DEFAULT;
691 : : ret = of_property_read_u32(np, "contribution", &prop);
692 : 0 : if (ret == 0)
693 : 0 : __tbp->usage = prop;
694 : :
695 : 0 : trip = of_parse_phandle(np, "trip", 0);
696 : 0 : if (!trip) {
697 : 0 : pr_err("missing trip property\n");
698 : 0 : return -ENODEV;
699 : : }
700 : :
701 : : /* match using device_node */
702 : 0 : for (i = 0; i < ntrips; i++)
703 : 0 : if (trip == trips[i].np) {
704 : 0 : __tbp->trip_id = i;
705 : 0 : break;
706 : : }
707 : :
708 : 0 : if (i == ntrips) {
709 : : ret = -ENODEV;
710 : : goto end;
711 : : }
712 : :
713 : 0 : count = of_count_phandle_with_args(np, "cooling-device",
714 : : "#cooling-cells");
715 : 0 : if (!count) {
716 : 0 : pr_err("Add a cooling_device property with at least one device\n");
717 : 0 : goto end;
718 : : }
719 : :
720 : 0 : __tcbp = kcalloc(count, sizeof(*__tcbp), GFP_KERNEL);
721 : 0 : if (!__tcbp)
722 : : goto end;
723 : :
724 : 0 : for (i = 0; i < count; i++) {
725 : 0 : ret = of_parse_phandle_with_args(np, "cooling-device",
726 : : "#cooling-cells", i, &cooling_spec);
727 : 0 : if (ret < 0) {
728 : 0 : pr_err("Invalid cooling-device entry\n");
729 : : goto free_tcbp;
730 : : }
731 : :
732 : 0 : __tcbp[i].cooling_device = cooling_spec.np;
733 : :
734 : 0 : if (cooling_spec.args_count >= 2) { /* at least min and max */
735 : 0 : __tcbp[i].min = cooling_spec.args[0];
736 : 0 : __tcbp[i].max = cooling_spec.args[1];
737 : : } else {
738 : 0 : pr_err("wrong reference to cooling device, missing limits\n");
739 : : }
740 : : }
741 : :
742 : 0 : __tbp->tcbp = __tcbp;
743 : 0 : __tbp->count = count;
744 : :
745 : 0 : goto end;
746 : :
747 : : free_tcbp:
748 : 0 : for (i = i - 1; i >= 0; i--)
749 : 0 : of_node_put(__tcbp[i].cooling_device);
750 : 0 : kfree(__tcbp);
751 : : end:
752 : 0 : of_node_put(trip);
753 : :
754 : 0 : return ret;
755 : : }
756 : :
757 : : /**
758 : : * It maps 'enum thermal_trip_type' found in include/linux/thermal.h
759 : : * into the device tree binding of 'trip', property type.
760 : : */
761 : : static const char * const trip_types[] = {
762 : : [THERMAL_TRIP_ACTIVE] = "active",
763 : : [THERMAL_TRIP_PASSIVE] = "passive",
764 : : [THERMAL_TRIP_HOT] = "hot",
765 : : [THERMAL_TRIP_CRITICAL] = "critical",
766 : : };
767 : :
768 : : /**
769 : : * thermal_of_get_trip_type - Get phy mode for given device_node
770 : : * @np: Pointer to the given device_node
771 : : * @type: Pointer to resulting trip type
772 : : *
773 : : * The function gets trip type string from property 'type',
774 : : * and store its index in trip_types table in @type,
775 : : *
776 : : * Return: 0 on success, or errno in error case.
777 : : */
778 : 0 : static int thermal_of_get_trip_type(struct device_node *np,
779 : : enum thermal_trip_type *type)
780 : : {
781 : : const char *t;
782 : : int err, i;
783 : :
784 : 0 : err = of_property_read_string(np, "type", &t);
785 : 0 : if (err < 0)
786 : : return err;
787 : :
788 : 0 : for (i = 0; i < ARRAY_SIZE(trip_types); i++)
789 : 0 : if (!strcasecmp(t, trip_types[i])) {
790 : 0 : *type = i;
791 : 0 : return 0;
792 : : }
793 : :
794 : : return -ENODEV;
795 : : }
796 : :
797 : : /**
798 : : * thermal_of_populate_trip - parse and fill one trip point data
799 : : * @np: DT node containing a trip point node
800 : : * @trip: trip point data structure to be filled up
801 : : *
802 : : * This function parses a trip point type of node represented by
803 : : * @np parameter and fills the read data into @trip data structure.
804 : : *
805 : : * Return: 0 on success, proper error code otherwise
806 : : */
807 : 0 : static int thermal_of_populate_trip(struct device_node *np,
808 : : struct thermal_trip *trip)
809 : : {
810 : : int prop;
811 : : int ret;
812 : :
813 : : ret = of_property_read_u32(np, "temperature", &prop);
814 : 0 : if (ret < 0) {
815 : 0 : pr_err("missing temperature property\n");
816 : 0 : return ret;
817 : : }
818 : 0 : trip->temperature = prop;
819 : :
820 : : ret = of_property_read_u32(np, "hysteresis", &prop);
821 : 0 : if (ret < 0) {
822 : 0 : pr_err("missing hysteresis property\n");
823 : 0 : return ret;
824 : : }
825 : 0 : trip->hysteresis = prop;
826 : :
827 : 0 : ret = thermal_of_get_trip_type(np, &trip->type);
828 : 0 : if (ret < 0) {
829 : 0 : pr_err("wrong trip type property\n");
830 : 0 : return ret;
831 : : }
832 : :
833 : : /* Required for cooling map matching */
834 : 0 : trip->np = np;
835 : 0 : of_node_get(np);
836 : :
837 : 0 : return 0;
838 : : }
839 : :
840 : : /**
841 : : * thermal_of_build_thermal_zone - parse and fill one thermal zone data
842 : : * @np: DT node containing a thermal zone node
843 : : *
844 : : * This function parses a thermal zone type of node represented by
845 : : * @np parameter and fills the read data into a __thermal_zone data structure
846 : : * and return this pointer.
847 : : *
848 : : * TODO: Missing properties to parse: thermal-sensor-names
849 : : *
850 : : * Return: On success returns a valid struct __thermal_zone,
851 : : * otherwise, it returns a corresponding ERR_PTR(). Caller must
852 : : * check the return value with help of IS_ERR() helper.
853 : : */
854 : : static struct __thermal_zone
855 : 3 : __init *thermal_of_build_thermal_zone(struct device_node *np)
856 : : {
857 : : struct device_node *child = NULL, *gchild;
858 : : struct __thermal_zone *tz;
859 : : int ret, i;
860 : : u32 prop, coef[2];
861 : :
862 : 3 : if (!np) {
863 : 0 : pr_err("no thermal zone np\n");
864 : 0 : return ERR_PTR(-EINVAL);
865 : : }
866 : :
867 : 3 : tz = kzalloc(sizeof(*tz), GFP_KERNEL);
868 : 3 : if (!tz)
869 : : return ERR_PTR(-ENOMEM);
870 : :
871 : : ret = of_property_read_u32(np, "polling-delay-passive", &prop);
872 : 3 : if (ret < 0) {
873 : 0 : pr_err("%pOFn: missing polling-delay-passive property\n", np);
874 : 0 : goto free_tz;
875 : : }
876 : 3 : tz->passive_delay = prop;
877 : :
878 : : ret = of_property_read_u32(np, "polling-delay", &prop);
879 : 3 : if (ret < 0) {
880 : 0 : pr_err("%pOFn: missing polling-delay property\n", np);
881 : 0 : goto free_tz;
882 : : }
883 : 3 : tz->polling_delay = prop;
884 : :
885 : : /*
886 : : * REVIST: for now, the thermal framework supports only
887 : : * one sensor per thermal zone. Thus, we are considering
888 : : * only the first two values as slope and offset.
889 : : */
890 : : ret = of_property_read_u32_array(np, "coefficients", coef, 2);
891 : 3 : if (ret == 0) {
892 : 3 : tz->slope = coef[0];
893 : 3 : tz->offset = coef[1];
894 : : } else {
895 : 0 : tz->slope = 1;
896 : 0 : tz->offset = 0;
897 : : }
898 : :
899 : : /* trips */
900 : 3 : child = of_get_child_by_name(np, "trips");
901 : :
902 : : /* No trips provided */
903 : 3 : if (!child)
904 : : goto finish;
905 : :
906 : 0 : tz->ntrips = of_get_child_count(child);
907 : 0 : if (tz->ntrips == 0) /* must have at least one child */
908 : : goto finish;
909 : :
910 : 0 : tz->trips = kcalloc(tz->ntrips, sizeof(*tz->trips), GFP_KERNEL);
911 : 0 : if (!tz->trips) {
912 : : ret = -ENOMEM;
913 : : goto free_tz;
914 : : }
915 : :
916 : : i = 0;
917 : 0 : for_each_child_of_node(child, gchild) {
918 : 0 : ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
919 : 0 : if (ret)
920 : : goto free_trips;
921 : : }
922 : :
923 : 0 : of_node_put(child);
924 : :
925 : : /* cooling-maps */
926 : 0 : child = of_get_child_by_name(np, "cooling-maps");
927 : :
928 : : /* cooling-maps not provided */
929 : 0 : if (!child)
930 : : goto finish;
931 : :
932 : 0 : tz->num_tbps = of_get_child_count(child);
933 : 0 : if (tz->num_tbps == 0)
934 : : goto finish;
935 : :
936 : 0 : tz->tbps = kcalloc(tz->num_tbps, sizeof(*tz->tbps), GFP_KERNEL);
937 : 0 : if (!tz->tbps) {
938 : : ret = -ENOMEM;
939 : : goto free_trips;
940 : : }
941 : :
942 : : i = 0;
943 : 0 : for_each_child_of_node(child, gchild) {
944 : 0 : ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
945 : : tz->trips, tz->ntrips);
946 : 0 : if (ret)
947 : : goto free_tbps;
948 : : }
949 : :
950 : : finish:
951 : 3 : of_node_put(child);
952 : 3 : tz->mode = THERMAL_DEVICE_DISABLED;
953 : :
954 : 3 : return tz;
955 : :
956 : : free_tbps:
957 : 0 : for (i = i - 1; i >= 0; i--) {
958 : 0 : struct __thermal_bind_params *tbp = tz->tbps + i;
959 : : int j;
960 : :
961 : 0 : for (j = 0; j < tbp->count; j++)
962 : 0 : of_node_put(tbp->tcbp[j].cooling_device);
963 : :
964 : 0 : kfree(tbp->tcbp);
965 : : }
966 : :
967 : 0 : kfree(tz->tbps);
968 : : free_trips:
969 : 0 : for (i = 0; i < tz->ntrips; i++)
970 : 0 : of_node_put(tz->trips[i].np);
971 : 0 : kfree(tz->trips);
972 : 0 : of_node_put(gchild);
973 : : free_tz:
974 : 0 : kfree(tz);
975 : 0 : of_node_put(child);
976 : :
977 : 0 : return ERR_PTR(ret);
978 : : }
979 : :
980 : 0 : static inline void of_thermal_free_zone(struct __thermal_zone *tz)
981 : : {
982 : : struct __thermal_bind_params *tbp;
983 : : int i, j;
984 : :
985 : 0 : for (i = 0; i < tz->num_tbps; i++) {
986 : 0 : tbp = tz->tbps + i;
987 : :
988 : 0 : for (j = 0; j < tbp->count; j++)
989 : 0 : of_node_put(tbp->tcbp[j].cooling_device);
990 : :
991 : 0 : kfree(tbp->tcbp);
992 : : }
993 : :
994 : 0 : kfree(tz->tbps);
995 : 0 : for (i = 0; i < tz->ntrips; i++)
996 : 0 : of_node_put(tz->trips[i].np);
997 : 0 : kfree(tz->trips);
998 : 0 : kfree(tz);
999 : 0 : }
1000 : :
1001 : : /**
1002 : : * of_parse_thermal_zones - parse device tree thermal data
1003 : : *
1004 : : * Initialization function that can be called by machine initialization
1005 : : * code to parse thermal data and populate the thermal framework
1006 : : * with hardware thermal zones info. This function only parses thermal zones.
1007 : : * Cooling devices and sensor devices nodes are supposed to be parsed
1008 : : * by their respective drivers.
1009 : : *
1010 : : * Return: 0 on success, proper error code otherwise
1011 : : *
1012 : : */
1013 : 3 : int __init of_parse_thermal_zones(void)
1014 : : {
1015 : : struct device_node *np, *child;
1016 : : struct __thermal_zone *tz;
1017 : : struct thermal_zone_device_ops *ops;
1018 : :
1019 : 3 : np = of_find_node_by_name(NULL, "thermal-zones");
1020 : 3 : if (!np) {
1021 : : pr_debug("unable to find thermal zones\n");
1022 : : return 0; /* Run successfully on systems without thermal DT */
1023 : : }
1024 : :
1025 : 3 : for_each_available_child_of_node(np, child) {
1026 : : struct thermal_zone_device *zone;
1027 : : struct thermal_zone_params *tzp;
1028 : : int i, mask = 0;
1029 : : u32 prop;
1030 : :
1031 : 3 : tz = thermal_of_build_thermal_zone(child);
1032 : 3 : if (IS_ERR(tz)) {
1033 : 0 : pr_err("failed to build thermal zone %pOFn: %ld\n",
1034 : : child,
1035 : : PTR_ERR(tz));
1036 : 0 : continue;
1037 : : }
1038 : :
1039 : 3 : ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
1040 : 3 : if (!ops)
1041 : : goto exit_free;
1042 : :
1043 : 3 : tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
1044 : 3 : if (!tzp) {
1045 : 0 : kfree(ops);
1046 : 0 : goto exit_free;
1047 : : }
1048 : :
1049 : : /* No hwmon because there might be hwmon drivers registering */
1050 : 3 : tzp->no_hwmon = true;
1051 : :
1052 : 3 : if (!of_property_read_u32(child, "sustainable-power", &prop))
1053 : 0 : tzp->sustainable_power = prop;
1054 : :
1055 : 3 : for (i = 0; i < tz->ntrips; i++)
1056 : 0 : mask |= 1 << i;
1057 : :
1058 : : /* these two are left for temperature drivers to use */
1059 : 3 : tzp->slope = tz->slope;
1060 : 3 : tzp->offset = tz->offset;
1061 : :
1062 : 3 : zone = thermal_zone_device_register(child->name, tz->ntrips,
1063 : : mask, tz,
1064 : : ops, tzp,
1065 : : tz->passive_delay,
1066 : : tz->polling_delay);
1067 : 3 : if (IS_ERR(zone)) {
1068 : 0 : pr_err("Failed to build %pOFn zone %ld\n", child,
1069 : : PTR_ERR(zone));
1070 : 0 : kfree(tzp);
1071 : 0 : kfree(ops);
1072 : 0 : of_thermal_free_zone(tz);
1073 : : /* attempting to build remaining zones still */
1074 : : }
1075 : : }
1076 : 3 : of_node_put(np);
1077 : :
1078 : 3 : return 0;
1079 : :
1080 : : exit_free:
1081 : 0 : of_node_put(child);
1082 : 0 : of_node_put(np);
1083 : 0 : of_thermal_free_zone(tz);
1084 : :
1085 : : /* no memory available, so free what we have built */
1086 : 0 : of_thermal_destroy_zones();
1087 : :
1088 : 0 : return -ENOMEM;
1089 : : }
1090 : :
1091 : : /**
1092 : : * of_thermal_destroy_zones - remove all zones parsed and allocated resources
1093 : : *
1094 : : * Finds all zones parsed and added to the thermal framework and remove them
1095 : : * from the system, together with their resources.
1096 : : *
1097 : : */
1098 : 0 : void of_thermal_destroy_zones(void)
1099 : : {
1100 : : struct device_node *np, *child;
1101 : :
1102 : 0 : np = of_find_node_by_name(NULL, "thermal-zones");
1103 : 0 : if (!np) {
1104 : : pr_debug("unable to find thermal zones\n");
1105 : 0 : return;
1106 : : }
1107 : :
1108 : 0 : for_each_available_child_of_node(np, child) {
1109 : : struct thermal_zone_device *zone;
1110 : :
1111 : 0 : zone = thermal_zone_get_zone_by_name(child->name);
1112 : 0 : if (IS_ERR(zone))
1113 : 0 : continue;
1114 : :
1115 : 0 : thermal_zone_device_unregister(zone);
1116 : 0 : kfree(zone->tzp);
1117 : 0 : kfree(zone->ops);
1118 : 0 : of_thermal_free_zone(zone->devdata);
1119 : : }
1120 : 0 : of_node_put(np);
1121 : : }
|