thermal: of: Rename struct __thermal_trip to struct thermal_trip
[deliverable/linux.git] / drivers / thermal / of-thermal.c
CommitLineData
4e5e4705
EV
1/*
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>
2251aef6 33#include <linux/thermal.h>
4e5e4705
EV
34
35#include "thermal_core.h"
36
37/*** Private data structures to represent thermal device tree data ***/
38
4e5e4705
EV
39/**
40 * struct __thermal_bind_param - a match between trip and cooling device
41 * @cooling_device: a pointer to identify the referred cooling device
42 * @trip_id: the trip point index
43 * @usage: the percentage (from 0 to 100) of cooling contribution
44 * @min: minimum cooling state used at this trip point
45 * @max: maximum cooling state used at this trip point
46 */
47
48struct __thermal_bind_params {
49 struct device_node *cooling_device;
50 unsigned int trip_id;
51 unsigned int usage;
52 unsigned long min;
53 unsigned long max;
54};
55
56/**
57 * struct __thermal_zone - internal representation of a thermal zone
58 * @mode: current thermal zone device mode (enabled/disabled)
59 * @passive_delay: polling interval while passive cooling is activated
60 * @polling_delay: zone polling interval
61 * @ntrips: number of trip points
62 * @trips: an array of trip points (0..ntrips - 1)
63 * @num_tbps: number of thermal bind params
64 * @tbps: an array of thermal bind params (0..num_tbps - 1)
65 * @sensor_data: sensor private data used while reading temperature and trend
2251aef6 66 * @ops: set of callbacks to handle the thermal zone based on DT
4e5e4705
EV
67 */
68
69struct __thermal_zone {
70 enum thermal_device_mode mode;
71 int passive_delay;
72 int polling_delay;
73
74 /* trip data */
75 int ntrips;
ad9914ac 76 struct thermal_trip *trips;
4e5e4705
EV
77
78 /* cooling binding data */
79 int num_tbps;
80 struct __thermal_bind_params *tbps;
81
82 /* sensor interface */
83 void *sensor_data;
2251aef6 84 const struct thermal_zone_of_device_ops *ops;
4e5e4705
EV
85};
86
87/*** DT thermal zone device callbacks ***/
88
89static int of_thermal_get_temp(struct thermal_zone_device *tz,
90 unsigned long *temp)
91{
92 struct __thermal_zone *data = tz->devdata;
93
2251aef6 94 if (!data->ops->get_temp)
4e5e4705
EV
95 return -EINVAL;
96
2251aef6 97 return data->ops->get_temp(data->sensor_data, temp);
4e5e4705
EV
98}
99
08dab66e
LM
100/**
101 * of_thermal_get_ntrips - function to export number of available trip
102 * points.
103 * @tz: pointer to a thermal zone
104 *
105 * This function is a globally visible wrapper to get number of trip points
106 * stored in the local struct __thermal_zone
107 *
108 * Return: number of available trip points, -ENODEV when data not available
109 */
110int of_thermal_get_ntrips(struct thermal_zone_device *tz)
111{
112 struct __thermal_zone *data = tz->devdata;
113
114 if (!data || IS_ERR(data))
115 return -ENODEV;
116
117 return data->ntrips;
118}
119EXPORT_SYMBOL_GPL(of_thermal_get_ntrips);
120
a9bf2cc4
LM
121/**
122 * of_thermal_is_trip_valid - function to check if trip point is valid
123 *
124 * @tz: pointer to a thermal zone
125 * @trip: trip point to evaluate
126 *
127 * This function is responsible for checking if passed trip point is valid
128 *
129 * Return: true if trip point is valid, false otherwise
130 */
131bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip)
132{
133 struct __thermal_zone *data = tz->devdata;
134
135 if (!data || trip >= data->ntrips || trip < 0)
136 return false;
137
138 return true;
139}
140EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid);
141
4e5e4705
EV
142static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
143 enum thermal_trend *trend)
144{
145 struct __thermal_zone *data = tz->devdata;
146 long dev_trend;
147 int r;
148
2251aef6 149 if (!data->ops->get_trend)
4e5e4705
EV
150 return -EINVAL;
151
2251aef6 152 r = data->ops->get_trend(data->sensor_data, &dev_trend);
4e5e4705
EV
153 if (r)
154 return r;
155
156 /* TODO: These intervals might have some thresholds, but in core code */
157 if (dev_trend > 0)
158 *trend = THERMAL_TREND_RAISING;
159 else if (dev_trend < 0)
160 *trend = THERMAL_TREND_DROPPING;
161 else
162 *trend = THERMAL_TREND_STABLE;
163
164 return 0;
165}
166
167static int of_thermal_bind(struct thermal_zone_device *thermal,
168 struct thermal_cooling_device *cdev)
169{
170 struct __thermal_zone *data = thermal->devdata;
171 int i;
172
173 if (!data || IS_ERR(data))
174 return -ENODEV;
175
176 /* find where to bind */
177 for (i = 0; i < data->num_tbps; i++) {
178 struct __thermal_bind_params *tbp = data->tbps + i;
179
180 if (tbp->cooling_device == cdev->np) {
181 int ret;
182
183 ret = thermal_zone_bind_cooling_device(thermal,
184 tbp->trip_id, cdev,
dd354b84
PA
185 tbp->max,
186 tbp->min);
4e5e4705
EV
187 if (ret)
188 return ret;
189 }
190 }
191
192 return 0;
193}
194
195static int of_thermal_unbind(struct thermal_zone_device *thermal,
196 struct thermal_cooling_device *cdev)
197{
198 struct __thermal_zone *data = thermal->devdata;
199 int i;
200
201 if (!data || IS_ERR(data))
202 return -ENODEV;
203
204 /* find where to unbind */
205 for (i = 0; i < data->num_tbps; i++) {
206 struct __thermal_bind_params *tbp = data->tbps + i;
207
208 if (tbp->cooling_device == cdev->np) {
209 int ret;
210
211 ret = thermal_zone_unbind_cooling_device(thermal,
212 tbp->trip_id, cdev);
213 if (ret)
214 return ret;
215 }
216 }
217
218 return 0;
219}
220
221static int of_thermal_get_mode(struct thermal_zone_device *tz,
222 enum thermal_device_mode *mode)
223{
224 struct __thermal_zone *data = tz->devdata;
225
226 *mode = data->mode;
227
228 return 0;
229}
230
231static int of_thermal_set_mode(struct thermal_zone_device *tz,
232 enum thermal_device_mode mode)
233{
234 struct __thermal_zone *data = tz->devdata;
235
236 mutex_lock(&tz->lock);
237
238 if (mode == THERMAL_DEVICE_ENABLED)
239 tz->polling_delay = data->polling_delay;
240 else
241 tz->polling_delay = 0;
242
243 mutex_unlock(&tz->lock);
244
245 data->mode = mode;
246 thermal_zone_device_update(tz);
247
248 return 0;
249}
250
251static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
252 enum thermal_trip_type *type)
253{
254 struct __thermal_zone *data = tz->devdata;
255
256 if (trip >= data->ntrips || trip < 0)
257 return -EDOM;
258
259 *type = data->trips[trip].type;
260
261 return 0;
262}
263
264static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
265 unsigned long *temp)
266{
267 struct __thermal_zone *data = tz->devdata;
268
269 if (trip >= data->ntrips || trip < 0)
270 return -EDOM;
271
272 *temp = data->trips[trip].temperature;
273
274 return 0;
275}
276
277static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
278 unsigned long temp)
279{
280 struct __thermal_zone *data = tz->devdata;
281
282 if (trip >= data->ntrips || trip < 0)
283 return -EDOM;
284
285 /* thermal framework should take care of data->mask & (1 << trip) */
286 data->trips[trip].temperature = temp;
287
288 return 0;
289}
290
291static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
292 unsigned long *hyst)
293{
294 struct __thermal_zone *data = tz->devdata;
295
296 if (trip >= data->ntrips || trip < 0)
297 return -EDOM;
298
299 *hyst = data->trips[trip].hysteresis;
300
301 return 0;
302}
303
304static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
305 unsigned long hyst)
306{
307 struct __thermal_zone *data = tz->devdata;
308
309 if (trip >= data->ntrips || trip < 0)
310 return -EDOM;
311
312 /* thermal framework should take care of data->mask & (1 << trip) */
313 data->trips[trip].hysteresis = hyst;
314
315 return 0;
316}
317
318static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
319 unsigned long *temp)
320{
321 struct __thermal_zone *data = tz->devdata;
322 int i;
323
324 for (i = 0; i < data->ntrips; i++)
325 if (data->trips[i].type == THERMAL_TRIP_CRITICAL) {
326 *temp = data->trips[i].temperature;
327 return 0;
328 }
329
330 return -EINVAL;
331}
332
333static struct thermal_zone_device_ops of_thermal_ops = {
334 .get_mode = of_thermal_get_mode,
335 .set_mode = of_thermal_set_mode,
336
337 .get_trip_type = of_thermal_get_trip_type,
338 .get_trip_temp = of_thermal_get_trip_temp,
339 .set_trip_temp = of_thermal_set_trip_temp,
340 .get_trip_hyst = of_thermal_get_trip_hyst,
341 .set_trip_hyst = of_thermal_set_trip_hyst,
342 .get_crit_temp = of_thermal_get_crit_temp,
343
344 .bind = of_thermal_bind,
345 .unbind = of_thermal_unbind,
346};
347
348/*** sensor API ***/
349
350static struct thermal_zone_device *
351thermal_zone_of_add_sensor(struct device_node *zone,
352 struct device_node *sensor, void *data,
2251aef6 353 const struct thermal_zone_of_device_ops *ops)
4e5e4705
EV
354{
355 struct thermal_zone_device *tzd;
356 struct __thermal_zone *tz;
357
358 tzd = thermal_zone_get_zone_by_name(zone->name);
359 if (IS_ERR(tzd))
360 return ERR_PTR(-EPROBE_DEFER);
361
362 tz = tzd->devdata;
363
2251aef6
EV
364 if (!ops)
365 return ERR_PTR(-EINVAL);
366
4e5e4705 367 mutex_lock(&tzd->lock);
2251aef6 368 tz->ops = ops;
4e5e4705
EV
369 tz->sensor_data = data;
370
371 tzd->ops->get_temp = of_thermal_get_temp;
372 tzd->ops->get_trend = of_thermal_get_trend;
373 mutex_unlock(&tzd->lock);
374
375 return tzd;
376}
377
378/**
379 * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
380 * @dev: a valid struct device pointer of a sensor device. Must contain
381 * a valid .of_node, for the sensor node.
382 * @sensor_id: a sensor identifier, in case the sensor IP has more
383 * than one sensors
384 * @data: a private pointer (owned by the caller) that will be passed
385 * back, when a temperature reading is needed.
2251aef6 386 * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
4e5e4705
EV
387 *
388 * This function will search the list of thermal zones described in device
389 * tree and look for the zone that refer to the sensor device pointed by
390 * @dev->of_node as temperature providers. For the zone pointing to the
391 * sensor node, the sensor will be added to the DT thermal zone device.
392 *
393 * The thermal zone temperature is provided by the @get_temp function
394 * pointer. When called, it will have the private pointer @data back.
395 *
396 * The thermal zone temperature trend is provided by the @get_trend function
397 * pointer. When called, it will have the private pointer @data back.
398 *
399 * TODO:
400 * 01 - This function must enqueue the new sensor instead of using
401 * it as the only source of temperature values.
402 *
403 * 02 - There must be a way to match the sensor with all thermal zones
404 * that refer to it.
405 *
406 * Return: On success returns a valid struct thermal_zone_device,
407 * otherwise, it returns a corresponding ERR_PTR(). Caller must
408 * check the return value with help of IS_ERR() helper.
409 */
410struct thermal_zone_device *
2251aef6
EV
411thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
412 const struct thermal_zone_of_device_ops *ops)
4e5e4705
EV
413{
414 struct device_node *np, *child, *sensor_np;
c2aad93c 415 struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
4e5e4705
EV
416
417 np = of_find_node_by_name(NULL, "thermal-zones");
418 if (!np)
419 return ERR_PTR(-ENODEV);
420
c2aad93c
VZ
421 if (!dev || !dev->of_node) {
422 of_node_put(np);
4e5e4705 423 return ERR_PTR(-EINVAL);
c2aad93c 424 }
4e5e4705 425
c2aad93c 426 sensor_np = of_node_get(dev->of_node);
4e5e4705
EV
427
428 for_each_child_of_node(np, child) {
429 struct of_phandle_args sensor_specs;
430 int ret, id;
431
a020279e
LD
432 /* Check whether child is enabled or not */
433 if (!of_device_is_available(child))
434 continue;
435
4e5e4705
EV
436 /* For now, thermal framework supports only 1 sensor per zone */
437 ret = of_parse_phandle_with_args(child, "thermal-sensors",
438 "#thermal-sensor-cells",
439 0, &sensor_specs);
440 if (ret)
441 continue;
442
443 if (sensor_specs.args_count >= 1) {
444 id = sensor_specs.args[0];
445 WARN(sensor_specs.args_count > 1,
446 "%s: too many cells in sensor specifier %d\n",
447 sensor_specs.np->name, sensor_specs.args_count);
448 } else {
449 id = 0;
450 }
451
452 if (sensor_specs.np == sensor_np && id == sensor_id) {
c2aad93c 453 tzd = thermal_zone_of_add_sensor(child, sensor_np,
2251aef6 454 data, ops);
c2aad93c
VZ
455 of_node_put(sensor_specs.np);
456 of_node_put(child);
457 goto exit;
4e5e4705 458 }
c2aad93c 459 of_node_put(sensor_specs.np);
4e5e4705 460 }
c2aad93c
VZ
461exit:
462 of_node_put(sensor_np);
4e5e4705
EV
463 of_node_put(np);
464
c2aad93c 465 return tzd;
4e5e4705
EV
466}
467EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
468
469/**
470 * thermal_zone_of_sensor_unregister - unregisters a sensor from a DT thermal zone
471 * @dev: a valid struct device pointer of a sensor device. Must contain
472 * a valid .of_node, for the sensor node.
473 * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
474 *
475 * This function removes the sensor callbacks and private data from the
476 * thermal zone device registered with thermal_zone_of_sensor_register()
477 * API. It will also silent the zone by remove the .get_temp() and .get_trend()
478 * thermal zone device callbacks.
479 *
480 * TODO: When the support to several sensors per zone is added, this
481 * function must search the sensor list based on @dev parameter.
482 *
483 */
484void thermal_zone_of_sensor_unregister(struct device *dev,
485 struct thermal_zone_device *tzd)
486{
487 struct __thermal_zone *tz;
488
489 if (!dev || !tzd || !tzd->devdata)
490 return;
491
492 tz = tzd->devdata;
493
494 /* no __thermal_zone, nothing to be done */
495 if (!tz)
496 return;
497
498 mutex_lock(&tzd->lock);
499 tzd->ops->get_temp = NULL;
500 tzd->ops->get_trend = NULL;
501
2251aef6 502 tz->ops = NULL;
4e5e4705
EV
503 tz->sensor_data = NULL;
504 mutex_unlock(&tzd->lock);
505}
506EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
507
508/*** functions parsing device tree nodes ***/
509
510/**
511 * thermal_of_populate_bind_params - parse and fill cooling map data
512 * @np: DT node containing a cooling-map node
513 * @__tbp: data structure to be filled with cooling map info
514 * @trips: array of thermal zone trip points
515 * @ntrips: number of trip points inside trips.
516 *
517 * This function parses a cooling-map type of node represented by
518 * @np parameter and fills the read data into @__tbp data structure.
519 * It needs the already parsed array of trip points of the thermal zone
520 * in consideration.
521 *
522 * Return: 0 on success, proper error code otherwise
523 */
524static int thermal_of_populate_bind_params(struct device_node *np,
525 struct __thermal_bind_params *__tbp,
ad9914ac 526 struct thermal_trip *trips,
4e5e4705
EV
527 int ntrips)
528{
529 struct of_phandle_args cooling_spec;
530 struct device_node *trip;
531 int ret, i;
532 u32 prop;
533
534 /* Default weight. Usage is optional */
535 __tbp->usage = 0;
536 ret = of_property_read_u32(np, "contribution", &prop);
537 if (ret == 0)
538 __tbp->usage = prop;
539
540 trip = of_parse_phandle(np, "trip", 0);
541 if (!trip) {
542 pr_err("missing trip property\n");
543 return -ENODEV;
544 }
545
546 /* match using device_node */
547 for (i = 0; i < ntrips; i++)
548 if (trip == trips[i].np) {
549 __tbp->trip_id = i;
550 break;
551 }
552
553 if (i == ntrips) {
554 ret = -ENODEV;
555 goto end;
556 }
557
558 ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
559 0, &cooling_spec);
560 if (ret < 0) {
561 pr_err("missing cooling_device property\n");
562 goto end;
563 }
564 __tbp->cooling_device = cooling_spec.np;
565 if (cooling_spec.args_count >= 2) { /* at least min and max */
566 __tbp->min = cooling_spec.args[0];
567 __tbp->max = cooling_spec.args[1];
568 } else {
569 pr_err("wrong reference to cooling device, missing limits\n");
570 }
571
572end:
573 of_node_put(trip);
574
575 return ret;
576}
577
578/**
579 * It maps 'enum thermal_trip_type' found in include/linux/thermal.h
580 * into the device tree binding of 'trip', property type.
581 */
582static const char * const trip_types[] = {
583 [THERMAL_TRIP_ACTIVE] = "active",
584 [THERMAL_TRIP_PASSIVE] = "passive",
585 [THERMAL_TRIP_HOT] = "hot",
586 [THERMAL_TRIP_CRITICAL] = "critical",
587};
588
589/**
590 * thermal_of_get_trip_type - Get phy mode for given device_node
591 * @np: Pointer to the given device_node
592 * @type: Pointer to resulting trip type
593 *
594 * The function gets trip type string from property 'type',
595 * and store its index in trip_types table in @type,
596 *
597 * Return: 0 on success, or errno in error case.
598 */
599static int thermal_of_get_trip_type(struct device_node *np,
600 enum thermal_trip_type *type)
601{
602 const char *t;
603 int err, i;
604
605 err = of_property_read_string(np, "type", &t);
606 if (err < 0)
607 return err;
608
609 for (i = 0; i < ARRAY_SIZE(trip_types); i++)
610 if (!strcasecmp(t, trip_types[i])) {
611 *type = i;
612 return 0;
613 }
614
615 return -ENODEV;
616}
617
618/**
619 * thermal_of_populate_trip - parse and fill one trip point data
620 * @np: DT node containing a trip point node
621 * @trip: trip point data structure to be filled up
622 *
623 * This function parses a trip point type of node represented by
624 * @np parameter and fills the read data into @trip data structure.
625 *
626 * Return: 0 on success, proper error code otherwise
627 */
628static int thermal_of_populate_trip(struct device_node *np,
ad9914ac 629 struct thermal_trip *trip)
4e5e4705
EV
630{
631 int prop;
632 int ret;
633
634 ret = of_property_read_u32(np, "temperature", &prop);
635 if (ret < 0) {
636 pr_err("missing temperature property\n");
637 return ret;
638 }
639 trip->temperature = prop;
640
641 ret = of_property_read_u32(np, "hysteresis", &prop);
642 if (ret < 0) {
643 pr_err("missing hysteresis property\n");
644 return ret;
645 }
646 trip->hysteresis = prop;
647
648 ret = thermal_of_get_trip_type(np, &trip->type);
649 if (ret < 0) {
650 pr_err("wrong trip type property\n");
651 return ret;
652 }
653
654 /* Required for cooling map matching */
655 trip->np = np;
c2aad93c 656 of_node_get(np);
4e5e4705
EV
657
658 return 0;
659}
660
661/**
662 * thermal_of_build_thermal_zone - parse and fill one thermal zone data
663 * @np: DT node containing a thermal zone node
664 *
665 * This function parses a thermal zone type of node represented by
666 * @np parameter and fills the read data into a __thermal_zone data structure
667 * and return this pointer.
668 *
669 * TODO: Missing properties to parse: thermal-sensor-names and coefficients
670 *
671 * Return: On success returns a valid struct __thermal_zone,
672 * otherwise, it returns a corresponding ERR_PTR(). Caller must
673 * check the return value with help of IS_ERR() helper.
674 */
675static struct __thermal_zone *
676thermal_of_build_thermal_zone(struct device_node *np)
677{
678 struct device_node *child = NULL, *gchild;
679 struct __thermal_zone *tz;
680 int ret, i;
681 u32 prop;
682
683 if (!np) {
684 pr_err("no thermal zone np\n");
685 return ERR_PTR(-EINVAL);
686 }
687
688 tz = kzalloc(sizeof(*tz), GFP_KERNEL);
689 if (!tz)
690 return ERR_PTR(-ENOMEM);
691
692 ret = of_property_read_u32(np, "polling-delay-passive", &prop);
693 if (ret < 0) {
694 pr_err("missing polling-delay-passive property\n");
695 goto free_tz;
696 }
697 tz->passive_delay = prop;
698
699 ret = of_property_read_u32(np, "polling-delay", &prop);
700 if (ret < 0) {
701 pr_err("missing polling-delay property\n");
702 goto free_tz;
703 }
704 tz->polling_delay = prop;
705
706 /* trips */
707 child = of_get_child_by_name(np, "trips");
708
709 /* No trips provided */
710 if (!child)
711 goto finish;
712
713 tz->ntrips = of_get_child_count(child);
714 if (tz->ntrips == 0) /* must have at least one child */
715 goto finish;
716
717 tz->trips = kzalloc(tz->ntrips * sizeof(*tz->trips), GFP_KERNEL);
718 if (!tz->trips) {
719 ret = -ENOMEM;
720 goto free_tz;
721 }
722
723 i = 0;
724 for_each_child_of_node(child, gchild) {
725 ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
726 if (ret)
727 goto free_trips;
728 }
729
730 of_node_put(child);
731
732 /* cooling-maps */
733 child = of_get_child_by_name(np, "cooling-maps");
734
735 /* cooling-maps not provided */
736 if (!child)
737 goto finish;
738
739 tz->num_tbps = of_get_child_count(child);
740 if (tz->num_tbps == 0)
741 goto finish;
742
743 tz->tbps = kzalloc(tz->num_tbps * sizeof(*tz->tbps), GFP_KERNEL);
744 if (!tz->tbps) {
745 ret = -ENOMEM;
746 goto free_trips;
747 }
748
749 i = 0;
ca9521b7 750 for_each_child_of_node(child, gchild) {
4e5e4705
EV
751 ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
752 tz->trips, tz->ntrips);
753 if (ret)
754 goto free_tbps;
ca9521b7 755 }
4e5e4705
EV
756
757finish:
758 of_node_put(child);
759 tz->mode = THERMAL_DEVICE_DISABLED;
760
761 return tz;
762
763free_tbps:
c2aad93c
VZ
764 for (i = 0; i < tz->num_tbps; i++)
765 of_node_put(tz->tbps[i].cooling_device);
4e5e4705
EV
766 kfree(tz->tbps);
767free_trips:
c2aad93c
VZ
768 for (i = 0; i < tz->ntrips; i++)
769 of_node_put(tz->trips[i].np);
4e5e4705 770 kfree(tz->trips);
c2aad93c 771 of_node_put(gchild);
4e5e4705
EV
772free_tz:
773 kfree(tz);
774 of_node_put(child);
775
776 return ERR_PTR(ret);
777}
778
779static inline void of_thermal_free_zone(struct __thermal_zone *tz)
780{
c2aad93c
VZ
781 int i;
782
783 for (i = 0; i < tz->num_tbps; i++)
784 of_node_put(tz->tbps[i].cooling_device);
4e5e4705 785 kfree(tz->tbps);
c2aad93c
VZ
786 for (i = 0; i < tz->ntrips; i++)
787 of_node_put(tz->trips[i].np);
4e5e4705
EV
788 kfree(tz->trips);
789 kfree(tz);
790}
791
792/**
793 * of_parse_thermal_zones - parse device tree thermal data
794 *
795 * Initialization function that can be called by machine initialization
796 * code to parse thermal data and populate the thermal framework
797 * with hardware thermal zones info. This function only parses thermal zones.
798 * Cooling devices and sensor devices nodes are supposed to be parsed
799 * by their respective drivers.
800 *
801 * Return: 0 on success, proper error code otherwise
802 *
803 */
804int __init of_parse_thermal_zones(void)
805{
806 struct device_node *np, *child;
807 struct __thermal_zone *tz;
808 struct thermal_zone_device_ops *ops;
809
810 np = of_find_node_by_name(NULL, "thermal-zones");
811 if (!np) {
812 pr_debug("unable to find thermal zones\n");
813 return 0; /* Run successfully on systems without thermal DT */
814 }
815
816 for_each_child_of_node(np, child) {
817 struct thermal_zone_device *zone;
818 struct thermal_zone_params *tzp;
819
a020279e
LD
820 /* Check whether child is enabled or not */
821 if (!of_device_is_available(child))
822 continue;
823
4e5e4705
EV
824 tz = thermal_of_build_thermal_zone(child);
825 if (IS_ERR(tz)) {
826 pr_err("failed to build thermal zone %s: %ld\n",
827 child->name,
828 PTR_ERR(tz));
829 continue;
830 }
831
832 ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
833 if (!ops)
834 goto exit_free;
835
836 tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
837 if (!tzp) {
838 kfree(ops);
839 goto exit_free;
840 }
841
842 /* No hwmon because there might be hwmon drivers registering */
843 tzp->no_hwmon = true;
844
845 zone = thermal_zone_device_register(child->name, tz->ntrips,
846 0, tz,
847 ops, tzp,
848 tz->passive_delay,
849 tz->polling_delay);
850 if (IS_ERR(zone)) {
851 pr_err("Failed to build %s zone %ld\n", child->name,
852 PTR_ERR(zone));
853 kfree(tzp);
854 kfree(ops);
855 of_thermal_free_zone(tz);
856 /* attempting to build remaining zones still */
857 }
858 }
c2aad93c 859 of_node_put(np);
4e5e4705
EV
860
861 return 0;
862
863exit_free:
c2aad93c
VZ
864 of_node_put(child);
865 of_node_put(np);
4e5e4705
EV
866 of_thermal_free_zone(tz);
867
868 /* no memory available, so free what we have built */
869 of_thermal_destroy_zones();
870
871 return -ENOMEM;
872}
873
874/**
875 * of_thermal_destroy_zones - remove all zones parsed and allocated resources
876 *
877 * Finds all zones parsed and added to the thermal framework and remove them
878 * from the system, together with their resources.
879 *
880 */
881void of_thermal_destroy_zones(void)
882{
883 struct device_node *np, *child;
884
885 np = of_find_node_by_name(NULL, "thermal-zones");
886 if (!np) {
887 pr_err("unable to find thermal zones\n");
888 return;
889 }
890
891 for_each_child_of_node(np, child) {
892 struct thermal_zone_device *zone;
893
a020279e
LD
894 /* Check whether child is enabled or not */
895 if (!of_device_is_available(child))
896 continue;
897
4e5e4705
EV
898 zone = thermal_zone_get_zone_by_name(child->name);
899 if (IS_ERR(zone))
900 continue;
901
902 thermal_zone_device_unregister(zone);
903 kfree(zone->tzp);
904 kfree(zone->ops);
905 of_thermal_free_zone(zone->devdata);
906 }
c2aad93c 907 of_node_put(np);
4e5e4705 908}
This page took 0.110495 seconds and 5 git commands to generate.