aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEduardo Valentin <eduardo.valentin@ti.com>2013-07-03 15:35:39 -0400
committerEduardo Valentin <eduardo.valentin@ti.com>2013-12-04 08:31:34 -0500
commit4e5e4705bf69ea450f58fc709ac5888f321a9299 (patch)
tree523e7c5a3f5459acdd5b81d81b0deeb68de79dc0
parent81bd4e1cebed5efb85bd94a15342ee4d6965a416 (diff)
thermal: introduce device tree parser
This patch introduces a device tree bindings for describing the hardware thermal behavior and limits. Also a parser to read and interpret the data and feed it in the thermal framework is presented. This patch introduces a thermal data parser for device tree. The parsed data is used to build thermal zones and thermal binding parameters. The output data can then be used to deploy thermal policies. This patch adds also documentation regarding this API and how to define tree nodes to use this infrastructure. Note that, in order to be able to have control on the sensor registration on the DT thermal zone, it was required to allow changing the thermal zone .get_temp callback. For this reason, this patch also removes the 'const' modifier from the .ops field of thermal zone devices. Cc: Zhang Rui <rui.zhang@intel.com> Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Acked-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Eduardo Valentin <eduardo.valentin@ti.com>
-rw-r--r--Documentation/devicetree/bindings/thermal/thermal.txt595
-rw-r--r--drivers/thermal/Kconfig13
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/of-thermal.c849
-rw-r--r--drivers/thermal/thermal_core.c9
-rw-r--r--drivers/thermal/thermal_core.h9
-rw-r--r--include/dt-bindings/thermal/thermal.h17
-rw-r--r--include/linux/thermal.h28
8 files changed, 1518 insertions, 3 deletions
diff --git a/Documentation/devicetree/bindings/thermal/thermal.txt b/Documentation/devicetree/bindings/thermal/thermal.txt
new file mode 100644
index 000000000000..f5db6b72a36f
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/thermal.txt
@@ -0,0 +1,595 @@
1* Thermal Framework Device Tree descriptor
2
3This file describes a generic binding to provide a way of
4defining hardware thermal structure using device tree.
5A thermal structure includes thermal zones and their components,
6such as trip points, polling intervals, sensors and cooling devices
7binding descriptors.
8
9The target of device tree thermal descriptors is to describe only
10the hardware thermal aspects. The thermal device tree bindings are
11not about how the system must control or which algorithm or policy
12must be taken in place.
13
14There are five types of nodes involved to describe thermal bindings:
15- thermal sensors: devices which may be used to take temperature
16 measurements.
17- cooling devices: devices which may be used to dissipate heat.
18- trip points: describe key temperatures at which cooling is recommended. The
19 set of points should be chosen based on hardware limits.
20- cooling maps: used to describe links between trip points and cooling devices;
21- thermal zones: used to describe thermal data within the hardware;
22
23The following is a description of each of these node types.
24
25* Thermal sensor devices
26
27Thermal sensor devices are nodes providing temperature sensing capabilities on
28thermal zones. Typical devices are I2C ADC converters and bandgaps. These are
29nodes providing temperature data to thermal zones. Thermal sensor devices may
30control one or more internal sensors.
31
32Required property:
33- #thermal-sensor-cells: Used to provide sensor device specific information
34 Type: unsigned while referring to it. Typically 0 on thermal sensor
35 Size: one cell nodes with only one sensor, and at least 1 on nodes
36 with several internal sensors, in order
37 to identify uniquely the sensor instances within
38 the IC. See thermal zone binding for more details
39 on how consumers refer to sensor devices.
40
41* Cooling device nodes
42
43Cooling devices are nodes providing control on power dissipation. There
44are essentially two ways to provide control on power dissipation. First
45is by means of regulating device performance, which is known as passive
46cooling. A typical passive cooling is a CPU that has dynamic voltage and
47frequency scaling (DVFS), and uses lower frequencies as cooling states.
48Second is by means of activating devices in order to remove
49the dissipated heat, which is known as active cooling, e.g. regulating
50fan speeds. In both cases, cooling devices shall have a way to determine
51the state of cooling in which the device is.
52
53Any cooling device has a range of cooling states (i.e. different levels
54of heat dissipation). For example a fan's cooling states correspond to
55the different fan speeds possible. Cooling states are referred to by
56single unsigned integers, where larger numbers mean greater heat
57dissipation. The precise set of cooling states associated with a device
58(as referred to be the cooling-min-state and cooling-max-state
59properties) should be defined in a particular device's binding.
60For more examples of cooling devices, refer to the example sections below.
61
62Required properties:
63- cooling-min-state: An integer indicating the smallest
64 Type: unsigned cooling state accepted. Typically 0.
65 Size: one cell
66
67- cooling-max-state: An integer indicating the largest
68 Type: unsigned cooling state accepted.
69 Size: one cell
70
71- #cooling-cells: Used to provide cooling device specific information
72 Type: unsigned while referring to it. Must be at least 2, in order
73 Size: one cell to specify minimum and maximum cooling state used
74 in the reference. The first cell is the minimum
75 cooling state requested and the second cell is
76 the maximum cooling state requested in the reference.
77 See Cooling device maps section below for more details
78 on how consumers refer to cooling devices.
79
80* Trip points
81
82The trip node is a node to describe a point in the temperature domain
83in which the system takes an action. This node describes just the point,
84not the action.
85
86Required properties:
87- temperature: An integer indicating the trip temperature level,
88 Type: signed in millicelsius.
89 Size: one cell
90
91- hysteresis: A low hysteresis value on temperature property (above).
92 Type: unsigned This is a relative value, in millicelsius.
93 Size: one cell
94
95- type: a string containing the trip type. Expected values are:
96 "active": A trip point to enable active cooling
97 "passive": A trip point to enable passive cooling
98 "hot": A trip point to notify emergency
99 "critical": Hardware not reliable.
100 Type: string
101
102* Cooling device maps
103
104The cooling device maps node is a node to describe how cooling devices
105get assigned to trip points of the zone. The cooling devices are expected
106to be loaded in the target system.
107
108Required properties:
109- cooling-device: A phandle of a cooling device with its specifier,
110 Type: phandle + referring to which cooling device is used in this
111 cooling specifier binding. In the cooling specifier, the first cell
112 is the minimum cooling state and the second cell
113 is the maximum cooling state used in this map.
114- trip: A phandle of a trip point node within the same thermal
115 Type: phandle of zone.
116 trip point node
117
118Optional property:
119- contribution: The cooling contribution to the thermal zone of the
120 Type: unsigned referred cooling device at the referred trip point.
121 Size: one cell The contribution is a ratio of the sum
122 of all cooling contributions within a thermal zone.
123
124Note: Using the THERMAL_NO_LIMIT (-1UL) constant in the cooling-device phandle
125limit specifier means:
126(i) - minimum state allowed for minimum cooling state used in the reference.
127(ii) - maximum state allowed for maximum cooling state used in the reference.
128Refer to include/dt-bindings/thermal/thermal.h for definition of this constant.
129
130* Thermal zone nodes
131
132The thermal zone node is the node containing all the required info
133for describing a thermal zone, including its cooling device bindings. The
134thermal zone node must contain, apart from its own properties, one sub-node
135containing trip nodes and one sub-node containing all the zone cooling maps.
136
137Required properties:
138- polling-delay: The maximum number of milliseconds to wait between polls
139 Type: unsigned when checking this thermal zone.
140 Size: one cell
141
142- polling-delay-passive: The maximum number of milliseconds to wait
143 Type: unsigned between polls when performing passive cooling.
144 Size: one cell
145
146- thermal-sensors: A list of thermal sensor phandles and sensor specifier
147 Type: list of used while monitoring the thermal zone.
148 phandles + sensor
149 specifier
150
151- trips: A sub-node which is a container of only trip point nodes
152 Type: sub-node required to describe the thermal zone.
153
154- cooling-maps: A sub-node which is a container of only cooling device
155 Type: sub-node map nodes, used to describe the relation between trips
156 and cooling devices.
157
158Optional property:
159- coefficients: An array of integers (one signed cell) containing
160 Type: array coefficients to compose a linear relation between
161 Elem size: one cell the sensors listed in the thermal-sensors property.
162 Elem type: signed Coefficients defaults to 1, in case this property
163 is not specified. A simple linear polynomial is used:
164 Z = c0 * x0 + c1 + x1 + ... + c(n-1) * x(n-1) + cn.
165
166 The coefficients are ordered and they match with sensors
167 by means of sensor ID. Additional coefficients are
168 interpreted as constant offset.
169
170Note: The delay properties are bound to the maximum dT/dt (temperature
171derivative over time) in two situations for a thermal zone:
172(i) - when passive cooling is activated (polling-delay-passive); and
173(ii) - when the zone just needs to be monitored (polling-delay) or
174when active cooling is activated.
175
176The maximum dT/dt is highly bound to hardware power consumption and dissipation
177capability. The delays should be chosen to account for said max dT/dt,
178such that a device does not cross several trip boundaries unexpectedly
179between polls. Choosing the right polling delays shall avoid having the
180device in temperature ranges that may damage the silicon structures and
181reduce silicon lifetime.
182
183* The thermal-zones node
184
185The "thermal-zones" node is a container for all thermal zone nodes. It shall
186contain only sub-nodes describing thermal zones as in the section
187"Thermal zone nodes". The "thermal-zones" node appears under "/".
188
189* Examples
190
191Below are several examples on how to use thermal data descriptors
192using device tree bindings:
193
194(a) - CPU thermal zone
195
196The CPU thermal zone example below describes how to setup one thermal zone
197using one single sensor as temperature source and many cooling devices and
198power dissipation control sources.
199
200#include <dt-bindings/thermal/thermal.h>
201
202cpus {
203 /*
204 * Here is an example of describing a cooling device for a DVFS
205 * capable CPU. The CPU node describes its four OPPs.
206 * The cooling states possible are 0..3, and they are
207 * used as OPP indexes. The minimum cooling state is 0, which means
208 * all four OPPs can be available to the system. The maximum
209 * cooling state is 3, which means only the lowest OPPs (198MHz@0.85V)
210 * can be available in the system.
211 */
212 cpu0: cpu@0 {
213 ...
214 operating-points = <
215 /* kHz uV */
216 970000 1200000
217 792000 1100000
218 396000 950000
219 198000 850000
220 >;
221 cooling-min-state = <0>;
222 cooling-max-state = <3>;
223 #cooling-cells = <2>; /* min followed by max */
224 };
225 ...
226};
227
228&i2c1 {
229 ...
230 /*
231 * A simple fan controller which supports 10 speeds of operation
232 * (represented as 0-9).
233 */
234 fan0: fan@0x48 {
235 ...
236 cooling-min-state = <0>;
237 cooling-max-state = <9>;
238 #cooling-cells = <2>; /* min followed by max */
239 };
240};
241
242ocp {
243 ...
244 /*
245 * A simple IC with a single bandgap temperature sensor.
246 */
247 bandgap0: bandgap@0x0000ED00 {
248 ...
249 #thermal-sensor-cells = <0>;
250 };
251};
252
253thermal-zones {
254 cpu-thermal: cpu-thermal {
255 polling-delay-passive = <250>; /* milliseconds */
256 polling-delay = <1000>; /* milliseconds */
257
258 thermal-sensors = <&bandgap0>;
259
260 trips {
261 cpu-alert0: cpu-alert {
262 temperature = <90000>; /* millicelsius */
263 hysteresis = <2000>; /* millicelsius */
264 type = "active";
265 };
266 cpu-alert1: cpu-alert {
267 temperature = <100000>; /* millicelsius */
268 hysteresis = <2000>; /* millicelsius */
269 type = "passive";
270 };
271 cpu-crit: cpu-crit {
272 temperature = <125000>; /* millicelsius */
273 hysteresis = <2000>; /* millicelsius */
274 type = "critical";
275 };
276 };
277
278 cooling-maps {
279 map0 {
280 trip = <&cpu-alert0>;
281 cooling-device = <&fan0 THERMAL_NO_LIMITS 4>;
282 };
283 map1 {
284 trip = <&cpu-alert1>;
285 cooling-device = <&fan0 5 THERMAL_NO_LIMITS>;
286 };
287 map2 {
288 trip = <&cpu-alert1>;
289 cooling-device =
290 <&cpu0 THERMAL_NO_LIMITS THERMAL_NO_LIMITS>;
291 };
292 };
293 };
294};
295
296In the example above, the ADC sensor (bandgap0) at address 0x0000ED00 is
297used to monitor the zone 'cpu-thermal' using its sole sensor. A fan
298device (fan0) is controlled via I2C bus 1, at address 0x48, and has ten
299different cooling states 0-9. It is used to remove the heat out of
300the thermal zone 'cpu-thermal' using its cooling states
301from its minimum to 4, when it reaches trip point 'cpu-alert0'
302at 90C, as an example of active cooling. The same cooling device is used at
303'cpu-alert1', but from 5 to its maximum state. The cpu@0 device is also
304linked to the same thermal zone, 'cpu-thermal', as a passive cooling device,
305using all its cooling states at trip point 'cpu-alert1',
306which is a trip point at 100C. On the thermal zone 'cpu-thermal', at the
307temperature of 125C, represented by the trip point 'cpu-crit', the silicon
308is not reliable anymore.
309
310(b) - IC with several internal sensors
311
312The example below describes how to deploy several thermal zones based off a
313single sensor IC, assuming it has several internal sensors. This is a common
314case on SoC designs with several internal IPs that may need different thermal
315requirements, and thus may have their own sensor to monitor or detect internal
316hotspots in their silicon.
317
318#include <dt-bindings/thermal/thermal.h>
319
320ocp {
321 ...
322 /*
323 * A simple IC with several bandgap temperature sensors.
324 */
325 bandgap0: bandgap@0x0000ED00 {
326 ...
327 #thermal-sensor-cells = <1>;
328 };
329};
330
331thermal-zones {
332 cpu-thermal: cpu-thermal {
333 polling-delay-passive = <250>; /* milliseconds */
334 polling-delay = <1000>; /* milliseconds */
335
336 /* sensor ID */
337 thermal-sensors = <&bandgap0 0>;
338
339 trips {
340 /* each zone within the SoC may have its own trips */
341 cpu-alert: cpu-alert {
342 temperature = <100000>; /* millicelsius */
343 hysteresis = <2000>; /* millicelsius */
344 type = "passive";
345 };
346 cpu-crit: cpu-crit {
347 temperature = <125000>; /* millicelsius */
348 hysteresis = <2000>; /* millicelsius */
349 type = "critical";
350 };
351 };
352
353 cooling-maps {
354 /* each zone within the SoC may have its own cooling */
355 ...
356 };
357 };
358
359 gpu-thermal: gpu-thermal {
360 polling-delay-passive = <120>; /* milliseconds */
361 polling-delay = <1000>; /* milliseconds */
362
363 /* sensor ID */
364 thermal-sensors = <&bandgap0 1>;
365
366 trips {
367 /* each zone within the SoC may have its own trips */
368 gpu-alert: gpu-alert {
369 temperature = <90000>; /* millicelsius */
370 hysteresis = <2000>; /* millicelsius */
371 type = "passive";
372 };
373 gpu-crit: gpu-crit {
374 temperature = <105000>; /* millicelsius */
375 hysteresis = <2000>; /* millicelsius */
376 type = "critical";
377 };
378 };
379
380 cooling-maps {
381 /* each zone within the SoC may have its own cooling */
382 ...
383 };
384 };
385
386 dsp-thermal: dsp-thermal {
387 polling-delay-passive = <50>; /* milliseconds */
388 polling-delay = <1000>; /* milliseconds */
389
390 /* sensor ID */
391 thermal-sensors = <&bandgap0 2>;
392
393 trips {
394 /* each zone within the SoC may have its own trips */
395 dsp-alert: gpu-alert {
396 temperature = <90000>; /* millicelsius */
397 hysteresis = <2000>; /* millicelsius */
398 type = "passive";
399 };
400 dsp-crit: gpu-crit {
401 temperature = <135000>; /* millicelsius */
402 hysteresis = <2000>; /* millicelsius */
403 type = "critical";
404 };
405 };
406
407 cooling-maps {
408 /* each zone within the SoC may have its own cooling */
409 ...
410 };
411 };
412};
413
414In the example above, there is one bandgap IC which has the capability to
415monitor three sensors. The hardware has been designed so that sensors are
416placed on different places in the DIE to monitor different temperature
417hotspots: one for CPU thermal zone, one for GPU thermal zone and the
418other to monitor a DSP thermal zone.
419
420Thus, there is a need to assign each sensor provided by the bandgap IC
421to different thermal zones. This is achieved by means of using the
422#thermal-sensor-cells property and using the first cell of the sensor
423specifier as sensor ID. In the example, then, <bandgap 0> is used to
424monitor CPU thermal zone, <bandgap 1> is used to monitor GPU thermal
425zone and <bandgap 2> is used to monitor DSP thermal zone. Each zone
426may be uncorrelated, having its own dT/dt requirements, trips
427and cooling maps.
428
429
430(c) - Several sensors within one single thermal zone
431
432The example below illustrates how to use more than one sensor within
433one thermal zone.
434
435#include <dt-bindings/thermal/thermal.h>
436
437&i2c1 {
438 ...
439 /*
440 * A simple IC with a single temperature sensor.
441 */
442 adc: sensor@0x49 {
443 ...
444 #thermal-sensor-cells = <0>;
445 };
446};
447
448ocp {
449 ...
450 /*
451 * A simple IC with a single bandgap temperature sensor.
452 */
453 bandgap0: bandgap@0x0000ED00 {
454 ...
455 #thermal-sensor-cells = <0>;
456 };
457};
458
459thermal-zones {
460 cpu-thermal: cpu-thermal {
461 polling-delay-passive = <250>; /* milliseconds */
462 polling-delay = <1000>; /* milliseconds */
463
464 thermal-sensors = <&bandgap0>, /* cpu */
465 <&adc>; /* pcb north */
466
467 /* hotspot = 100 * bandgap - 120 * adc + 484 */
468 coefficients = <100 -120 484>;
469
470 trips {
471 ...
472 };
473
474 cooling-maps {
475 ...
476 };
477 };
478};
479
480In some cases, there is a need to use more than one sensor to extrapolate
481a thermal hotspot in the silicon. The above example illustrates this situation.
482For instance, it may be the case that a sensor external to CPU IP may be placed
483close to CPU hotspot and together with internal CPU sensor, it is used
484to determine the hotspot. Assuming this is the case for the above example,
485the hypothetical extrapolation rule would be:
486 hotspot = 100 * bandgap - 120 * adc + 484
487
488In other context, the same idea can be used to add fixed offset. For instance,
489consider the hotspot extrapolation rule below:
490 hotspot = 1 * adc + 6000
491
492In the above equation, the hotspot is always 6C higher than what is read
493from the ADC sensor. The binding would be then:
494 thermal-sensors = <&adc>;
495
496 /* hotspot = 1 * adc + 6000 */
497 coefficients = <1 6000>;
498
499(d) - Board thermal
500
501The board thermal example below illustrates how to setup one thermal zone
502with many sensors and many cooling devices.
503
504#include <dt-bindings/thermal/thermal.h>
505
506&i2c1 {
507 ...
508 /*
509 * An IC with several temperature sensor.
510 */
511 adc-dummy: sensor@0x50 {
512 ...
513 #thermal-sensor-cells = <1>; /* sensor internal ID */
514 };
515};
516
517thermal-zones {
518 batt-thermal {
519 polling-delay-passive = <500>; /* milliseconds */
520 polling-delay = <2500>; /* milliseconds */
521
522 /* sensor ID */
523 thermal-sensors = <&adc-dummy 4>;
524
525 trips {
526 ...
527 };
528
529 cooling-maps {
530 ...
531 };
532 };
533
534 board-thermal: board-thermal {
535 polling-delay-passive = <1000>; /* milliseconds */
536 polling-delay = <2500>; /* milliseconds */
537
538 /* sensor ID */
539 thermal-sensors = <&adc-dummy 0>, /* pcb top edge */
540 <&adc-dummy 1>, /* lcd */
541 <&adc-dymmy 2>; /* back cover */
542 /*
543 * An array of coefficients describing the sensor
544 * linear relation. E.g.:
545 * z = c1*x1 + c2*x2 + c3*x3
546 */
547 coefficients = <1200 -345 890>;
548
549 trips {
550 /* Trips are based on resulting linear equation */
551 cpu-trip: cpu-trip {
552 temperature = <60000>; /* millicelsius */
553 hysteresis = <2000>; /* millicelsius */
554 type = "passive";
555 };
556 gpu-trip: gpu-trip {
557 temperature = <55000>; /* millicelsius */
558 hysteresis = <2000>; /* millicelsius */
559 type = "passive";
560 }
561 lcd-trip: lcp-trip {
562 temperature = <53000>; /* millicelsius */
563 hysteresis = <2000>; /* millicelsius */
564 type = "passive";
565 };
566 crit-trip: crit-trip {
567 temperature = <68000>; /* millicelsius */
568 hysteresis = <2000>; /* millicelsius */
569 type = "critical";
570 };
571 };
572
573 cooling-maps {
574 map0 {
575 trip = <&cpu-trip>;
576 cooling-device = <&cpu0 0 2>;
577 contribution = <55>;
578 };
579 map1 {
580 trip = <&gpu-trip>;
581 cooling-device = <&gpu0 0 2>;
582 contribution = <20>;
583 };
584 map2 {
585 trip = <&lcd-trip>;
586 cooling-device = <&lcd0 5 10>;
587 contribution = <15>;
588 };
589 };
590 };
591};
592
593The above example is a mix of previous examples, a sensor IP with several internal
594sensors used to monitor different zones, one of them is composed by several sensors and
595with different cooling devices.
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index f35a1f75b15b..a150f8d53322 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -29,6 +29,19 @@ config THERMAL_HWMON
29 Say 'Y' here if you want all thermal sensors to 29 Say 'Y' here if you want all thermal sensors to
30 have hwmon sysfs interface too. 30 have hwmon sysfs interface too.
31 31
32config THERMAL_OF
33 bool
34 prompt "APIs to parse thermal data out of device tree"
35 depends on OF
36 default y
37 help
38 This options provides helpers to add the support to
39 read and parse thermal data definitions out of the
40 device tree blob.
41
42 Say 'Y' here if you need to build thermal infrastructure
43 based on device tree.
44
32choice 45choice
33 prompt "Default Thermal governor" 46 prompt "Default Thermal governor"
34 default THERMAL_DEFAULT_GOV_STEP_WISE 47 default THERMAL_DEFAULT_GOV_STEP_WISE
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 584b36319d51..4b03956b929c 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -7,6 +7,7 @@ thermal_sys-y += thermal_core.o
7 7
8# interface to/from other layers providing sensors 8# interface to/from other layers providing sensors
9thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o 9thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o
10thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o
10 11
11# governors 12# governors
12thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o 13thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
new file mode 100644
index 000000000000..04b1be7fa018
--- /dev/null
+++ b/drivers/thermal/of-thermal.c
@@ -0,0 +1,849 @@
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>
33
34#include "thermal_core.h"
35
36/*** Private data structures to represent thermal device tree data ***/
37
38/**
39 * struct __thermal_trip - representation of a point in temperature domain
40 * @np: pointer to struct device_node that this trip point was created from
41 * @temperature: temperature value in miliCelsius
42 * @hysteresis: relative hysteresis in miliCelsius
43 * @type: trip point type
44 */
45
46struct __thermal_trip {
47 struct device_node *np;
48 unsigned long int temperature;
49 unsigned long int hysteresis;
50 enum thermal_trip_type type;
51};
52
53/**
54 * struct __thermal_bind_param - a match between trip and cooling device
55 * @cooling_device: a pointer to identify the referred cooling device
56 * @trip_id: the trip point index
57 * @usage: the percentage (from 0 to 100) of cooling contribution
58 * @min: minimum cooling state used at this trip point
59 * @max: maximum cooling state used at this trip point
60 */
61
62struct __thermal_bind_params {
63 struct device_node *cooling_device;
64 unsigned int trip_id;
65 unsigned int usage;
66 unsigned long min;
67 unsigned long max;
68};
69
70/**
71 * struct __thermal_zone - internal representation of a thermal zone
72 * @mode: current thermal zone device mode (enabled/disabled)
73 * @passive_delay: polling interval while passive cooling is activated
74 * @polling_delay: zone polling interval
75 * @ntrips: number of trip points
76 * @trips: an array of trip points (0..ntrips - 1)
77 * @num_tbps: number of thermal bind params
78 * @tbps: an array of thermal bind params (0..num_tbps - 1)
79 * @sensor_data: sensor private data used while reading temperature and trend
80 * @get_temp: sensor callback to read temperature
81 * @get_trend: sensor callback to read temperature trend
82 */
83
84struct __thermal_zone {
85 enum thermal_device_mode mode;
86 int passive_delay;
87 int polling_delay;
88
89 /* trip data */
90 int ntrips;
91 struct __thermal_trip *trips;
92
93 /* cooling binding data */
94 int num_tbps;
95 struct __thermal_bind_params *tbps;
96
97 /* sensor interface */
98 void *sensor_data;
99 int (*get_temp)(void *, long *);
100 int (*get_trend)(void *, long *);
101};
102
103/*** DT thermal zone device callbacks ***/
104
105static int of_thermal_get_temp(struct thermal_zone_device *tz,
106 unsigned long *temp)
107{
108 struct __thermal_zone *data = tz->devdata;
109
110 if (!data->get_temp)
111 return -EINVAL;
112
113 return data->get_temp(data->sensor_data, temp);
114}
115
116static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
117 enum thermal_trend *trend)
118{
119 struct __thermal_zone *data = tz->devdata;
120 long dev_trend;
121 int r;
122
123 if (!data->get_trend)
124 return -EINVAL;
125
126 r = data->get_trend(data->sensor_data, &dev_trend);
127 if (r)
128 return r;
129
130 /* TODO: These intervals might have some thresholds, but in core code */
131 if (dev_trend > 0)
132 *trend = THERMAL_TREND_RAISING;
133 else if (dev_trend < 0)
134 *trend = THERMAL_TREND_DROPPING;
135 else
136 *trend = THERMAL_TREND_STABLE;
137
138 return 0;
139}
140
141static int of_thermal_bind(struct thermal_zone_device *thermal,
142 struct thermal_cooling_device *cdev)
143{
144 struct __thermal_zone *data = thermal->devdata;
145 int i;
146
147 if (!data || IS_ERR(data))
148 return -ENODEV;
149
150 /* find where to bind */
151 for (i = 0; i < data->num_tbps; i++) {
152 struct __thermal_bind_params *tbp = data->tbps + i;
153
154 if (tbp->cooling_device == cdev->np) {
155 int ret;
156
157 ret = thermal_zone_bind_cooling_device(thermal,
158 tbp->trip_id, cdev,
159 tbp->min,
160 tbp->max);
161 if (ret)
162 return ret;
163 }
164 }
165
166 return 0;
167}
168
169static int of_thermal_unbind(struct thermal_zone_device *thermal,
170 struct thermal_cooling_device *cdev)
171{
172 struct __thermal_zone *data = thermal->devdata;
173 int i;
174
175 if (!data || IS_ERR(data))
176 return -ENODEV;
177
178 /* find where to unbind */
179 for (i = 0; i < data->num_tbps; i++) {
180 struct __thermal_bind_params *tbp = data->tbps + i;
181
182 if (tbp->cooling_device == cdev->np) {
183 int ret;
184
185 ret = thermal_zone_unbind_cooling_device(thermal,
186 tbp->trip_id, cdev);
187 if (ret)
188 return ret;
189 }
190 }
191
192 return 0;
193}
194
195static int of_thermal_get_mode(struct thermal_zone_device *tz,
196 enum thermal_device_mode *mode)
197{
198 struct __thermal_zone *data = tz->devdata;
199
200 *mode = data->mode;
201
202 return 0;
203}
204
205static int of_thermal_set_mode(struct thermal_zone_device *tz,
206 enum thermal_device_mode mode)
207{
208 struct __thermal_zone *data = tz->devdata;
209
210 mutex_lock(&tz->lock);
211
212 if (mode == THERMAL_DEVICE_ENABLED)
213 tz->polling_delay = data->polling_delay;
214 else
215 tz->polling_delay = 0;
216
217 mutex_unlock(&tz->lock);
218
219 data->mode = mode;
220 thermal_zone_device_update(tz);
221
222 return 0;
223}
224
225static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
226 enum thermal_trip_type *type)
227{
228 struct __thermal_zone *data = tz->devdata;
229
230 if (trip >= data->ntrips || trip < 0)
231 return -EDOM;
232
233 *type = data->trips[trip].type;
234
235 return 0;
236}
237
238static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
239 unsigned long *temp)
240{
241 struct __thermal_zone *data = tz->devdata;
242
243 if (trip >= data->ntrips || trip < 0)
244 return -EDOM;
245
246 *temp = data->trips[trip].temperature;
247
248 return 0;
249}
250
251static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
252 unsigned long temp)
253{
254 struct __thermal_zone *data = tz->devdata;
255
256 if (trip >= data->ntrips || trip < 0)
257 return -EDOM;
258
259 /* thermal framework should take care of data->mask & (1 << trip) */
260 data->trips[trip].temperature = temp;
261
262 return 0;
263}
264
265static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
266 unsigned long *hyst)
267{
268 struct __thermal_zone *data = tz->devdata;
269
270 if (trip >= data->ntrips || trip < 0)
271 return -EDOM;
272
273 *hyst = data->trips[trip].hysteresis;
274
275 return 0;
276}
277
278static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
279 unsigned long hyst)
280{
281 struct __thermal_zone *data = tz->devdata;
282
283 if (trip >= data->ntrips || trip < 0)
284 return -EDOM;
285
286 /* thermal framework should take care of data->mask & (1 << trip) */
287 data->trips[trip].hysteresis = hyst;
288
289 return 0;
290}
291
292static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
293 unsigned long *temp)
294{
295 struct __thermal_zone *data = tz->devdata;
296 int i;
297
298 for (i = 0; i < data->ntrips; i++)
299 if (data->trips[i].type == THERMAL_TRIP_CRITICAL) {
300 *temp = data->trips[i].temperature;
301 return 0;
302 }
303
304 return -EINVAL;
305}
306
307static struct thermal_zone_device_ops of_thermal_ops = {
308 .get_mode = of_thermal_get_mode,
309 .set_mode = of_thermal_set_mode,
310
311 .get_trip_type = of_thermal_get_trip_type,
312 .get_trip_temp = of_thermal_get_trip_temp,
313 .set_trip_temp = of_thermal_set_trip_temp,
314 .get_trip_hyst = of_thermal_get_trip_hyst,
315 .set_trip_hyst = of_thermal_set_trip_hyst,
316 .get_crit_temp = of_thermal_get_crit_temp,
317
318 .bind = of_thermal_bind,
319 .unbind = of_thermal_unbind,
320};
321
322/*** sensor API ***/
323
324static struct thermal_zone_device *
325thermal_zone_of_add_sensor(struct device_node *zone,
326 struct device_node *sensor, void *data,
327 int (*get_temp)(void *, long *),
328 int (*get_trend)(void *, long *))
329{
330 struct thermal_zone_device *tzd;
331 struct __thermal_zone *tz;
332
333 tzd = thermal_zone_get_zone_by_name(zone->name);
334 if (IS_ERR(tzd))
335 return ERR_PTR(-EPROBE_DEFER);
336
337 tz = tzd->devdata;
338
339 mutex_lock(&tzd->lock);
340 tz->get_temp = get_temp;
341 tz->get_trend = get_trend;
342 tz->sensor_data = data;
343
344 tzd->ops->get_temp = of_thermal_get_temp;
345 tzd->ops->get_trend = of_thermal_get_trend;
346 mutex_unlock(&tzd->lock);
347
348 return tzd;
349}
350
351/**
352 * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
353 * @dev: a valid struct device pointer of a sensor device. Must contain
354 * a valid .of_node, for the sensor node.
355 * @sensor_id: a sensor identifier, in case the sensor IP has more
356 * than one sensors
357 * @data: a private pointer (owned by the caller) that will be passed
358 * back, when a temperature reading is needed.
359 * @get_temp: a pointer to a function that reads the sensor temperature.
360 * @get_trend: a pointer to a function that reads the sensor temperature trend.
361 *
362 * This function will search the list of thermal zones described in device
363 * tree and look for the zone that refer to the sensor device pointed by
364 * @dev->of_node as temperature providers. For the zone pointing to the
365 * sensor node, the sensor will be added to the DT thermal zone device.
366 *
367 * The thermal zone temperature is provided by the @get_temp function
368 * pointer. When called, it will have the private pointer @data back.
369 *
370 * The thermal zone temperature trend is provided by the @get_trend function
371 * pointer. When called, it will have the private pointer @data back.
372 *
373 * TODO:
374 * 01 - This function must enqueue the new sensor instead of using
375 * it as the only source of temperature values.
376 *
377 * 02 - There must be a way to match the sensor with all thermal zones
378 * that refer to it.
379 *
380 * Return: On success returns a valid struct thermal_zone_device,
381 * otherwise, it returns a corresponding ERR_PTR(). Caller must
382 * check the return value with help of IS_ERR() helper.
383 */
384struct thermal_zone_device *
385thermal_zone_of_sensor_register(struct device *dev, int sensor_id,
386 void *data, int (*get_temp)(void *, long *),
387 int (*get_trend)(void *, long *))
388{
389 struct device_node *np, *child, *sensor_np;
390
391 np = of_find_node_by_name(NULL, "thermal-zones");
392 if (!np)
393 return ERR_PTR(-ENODEV);
394
395 if (!dev || !dev->of_node)
396 return ERR_PTR(-EINVAL);
397
398 sensor_np = dev->of_node;
399
400 for_each_child_of_node(np, child) {
401 struct of_phandle_args sensor_specs;
402 int ret, id;
403
404 /* For now, thermal framework supports only 1 sensor per zone */
405 ret = of_parse_phandle_with_args(child, "thermal-sensors",
406 "#thermal-sensor-cells",
407 0, &sensor_specs);
408 if (ret)
409 continue;
410
411 if (sensor_specs.args_count >= 1) {
412 id = sensor_specs.args[0];
413 WARN(sensor_specs.args_count > 1,
414 "%s: too many cells in sensor specifier %d\n",
415 sensor_specs.np->name, sensor_specs.args_count);
416 } else {
417 id = 0;
418 }
419
420 if (sensor_specs.np == sensor_np && id == sensor_id) {
421 of_node_put(np);
422 return thermal_zone_of_add_sensor(child, sensor_np,
423 data,
424 get_temp,
425 get_trend);
426 }
427 }
428 of_node_put(np);
429
430 return ERR_PTR(-ENODEV);
431}
432EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
433
434/**
435 * thermal_zone_of_sensor_unregister - unregisters a sensor from a DT thermal zone
436 * @dev: a valid struct device pointer of a sensor device. Must contain
437 * a valid .of_node, for the sensor node.
438 * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
439 *
440 * This function removes the sensor callbacks and private data from the
441 * thermal zone device registered with thermal_zone_of_sensor_register()
442 * API. It will also silent the zone by remove the .get_temp() and .get_trend()
443 * thermal zone device callbacks.
444 *
445 * TODO: When the support to several sensors per zone is added, this
446 * function must search the sensor list based on @dev parameter.
447 *
448 */
449void thermal_zone_of_sensor_unregister(struct device *dev,
450 struct thermal_zone_device *tzd)
451{
452 struct __thermal_zone *tz;
453
454 if (!dev || !tzd || !tzd->devdata)
455 return;
456
457 tz = tzd->devdata;
458
459 /* no __thermal_zone, nothing to be done */
460 if (!tz)
461 return;
462
463 mutex_lock(&tzd->lock);
464 tzd->ops->get_temp = NULL;
465 tzd->ops->get_trend = NULL;
466
467 tz->get_temp = NULL;
468 tz->get_trend = NULL;
469 tz->sensor_data = NULL;
470 mutex_unlock(&tzd->lock);
471}
472EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
473
474/*** functions parsing device tree nodes ***/
475
476/**
477 * thermal_of_populate_bind_params - parse and fill cooling map data
478 * @np: DT node containing a cooling-map node
479 * @__tbp: data structure to be filled with cooling map info
480 * @trips: array of thermal zone trip points
481 * @ntrips: number of trip points inside trips.
482 *
483 * This function parses a cooling-map type of node represented by
484 * @np parameter and fills the read data into @__tbp data structure.
485 * It needs the already parsed array of trip points of the thermal zone
486 * in consideration.
487 *
488 * Return: 0 on success, proper error code otherwise
489 */
490static int thermal_of_populate_bind_params(struct device_node *np,
491 struct __thermal_bind_params *__tbp,
492 struct __thermal_trip *trips,
493 int ntrips)
494{
495 struct of_phandle_args cooling_spec;
496 struct device_node *trip;
497 int ret, i;
498 u32 prop;
499
500 /* Default weight. Usage is optional */
501 __tbp->usage = 0;
502 ret = of_property_read_u32(np, "contribution", &prop);
503 if (ret == 0)
504 __tbp->usage = prop;
505
506 trip = of_parse_phandle(np, "trip", 0);
507 if (!trip) {
508 pr_err("missing trip property\n");
509 return -ENODEV;
510 }
511
512 /* match using device_node */
513 for (i = 0; i < ntrips; i++)
514 if (trip == trips[i].np) {
515 __tbp->trip_id = i;
516 break;
517 }
518
519 if (i == ntrips) {
520 ret = -ENODEV;
521 goto end;
522 }
523
524 ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
525 0, &cooling_spec);
526 if (ret < 0) {
527 pr_err("missing cooling_device property\n");
528 goto end;
529 }
530 __tbp->cooling_device = cooling_spec.np;
531 if (cooling_spec.args_count >= 2) { /* at least min and max */
532 __tbp->min = cooling_spec.args[0];
533 __tbp->max = cooling_spec.args[1];
534 } else {
535 pr_err("wrong reference to cooling device, missing limits\n");
536 }
537
538end:
539 of_node_put(trip);
540
541 return ret;
542}
543
544/**
545 * It maps 'enum thermal_trip_type' found in include/linux/thermal.h
546 * into the device tree binding of 'trip', property type.
547 */
548static const char * const trip_types[] = {
549 [THERMAL_TRIP_ACTIVE] = "active",
550 [THERMAL_TRIP_PASSIVE] = "passive",
551 [THERMAL_TRIP_HOT] = "hot",
552 [THERMAL_TRIP_CRITICAL] = "critical",
553};
554
555/**
556 * thermal_of_get_trip_type - Get phy mode for given device_node
557 * @np: Pointer to the given device_node
558 * @type: Pointer to resulting trip type
559 *
560 * The function gets trip type string from property 'type',
561 * and store its index in trip_types table in @type,
562 *
563 * Return: 0 on success, or errno in error case.
564 */
565static int thermal_of_get_trip_type(struct device_node *np,
566 enum thermal_trip_type *type)
567{
568 const char *t;
569 int err, i;
570
571 err = of_property_read_string(np, "type", &t);
572 if (err < 0)
573 return err;
574
575 for (i = 0; i < ARRAY_SIZE(trip_types); i++)
576 if (!strcasecmp(t, trip_types[i])) {
577 *type = i;
578 return 0;
579 }
580
581 return -ENODEV;
582}
583
584/**
585 * thermal_of_populate_trip - parse and fill one trip point data
586 * @np: DT node containing a trip point node
587 * @trip: trip point data structure to be filled up
588 *
589 * This function parses a trip point type of node represented by
590 * @np parameter and fills the read data into @trip data structure.
591 *
592 * Return: 0 on success, proper error code otherwise
593 */
594static int thermal_of_populate_trip(struct device_node *np,
595 struct __thermal_trip *trip)
596{
597 int prop;
598 int ret;
599
600 ret = of_property_read_u32(np, "temperature", &prop);
601 if (ret < 0) {
602 pr_err("missing temperature property\n");
603 return ret;
604 }
605 trip->temperature = prop;
606
607 ret = of_property_read_u32(np, "hysteresis", &prop);
608 if (ret < 0) {
609 pr_err("missing hysteresis property\n");
610 return ret;
611 }
612 trip->hysteresis = prop;
613
614 ret = thermal_of_get_trip_type(np, &trip->type);
615 if (ret < 0) {
616 pr_err("wrong trip type property\n");
617 return ret;
618 }
619
620 /* Required for cooling map matching */
621 trip->np = np;
622
623 return 0;
624}
625
626/**
627 * thermal_of_build_thermal_zone - parse and fill one thermal zone data
628 * @np: DT node containing a thermal zone node
629 *
630 * This function parses a thermal zone type of node represented by
631 * @np parameter and fills the read data into a __thermal_zone data structure
632 * and return this pointer.
633 *
634 * TODO: Missing properties to parse: thermal-sensor-names and coefficients
635 *
636 * Return: On success returns a valid struct __thermal_zone,
637 * otherwise, it returns a corresponding ERR_PTR(). Caller must
638 * check the return value with help of IS_ERR() helper.
639 */
640static struct __thermal_zone *
641thermal_of_build_thermal_zone(struct device_node *np)
642{
643 struct device_node *child = NULL, *gchild;
644 struct __thermal_zone *tz;
645 int ret, i;
646 u32 prop;
647
648 if (!np) {
649 pr_err("no thermal zone np\n");
650 return ERR_PTR(-EINVAL);
651 }
652
653 tz = kzalloc(sizeof(*tz), GFP_KERNEL);
654 if (!tz)
655 return ERR_PTR(-ENOMEM);
656
657 ret = of_property_read_u32(np, "polling-delay-passive", &prop);
658 if (ret < 0) {
659 pr_err("missing polling-delay-passive property\n");
660 goto free_tz;
661 }
662 tz->passive_delay = prop;
663
664 ret = of_property_read_u32(np, "polling-delay", &prop);
665 if (ret < 0) {
666 pr_err("missing polling-delay property\n");
667 goto free_tz;
668 }
669 tz->polling_delay = prop;
670
671 /* trips */
672 child = of_get_child_by_name(np, "trips");
673
674 /* No trips provided */
675 if (!child)
676 goto finish;
677
678 tz->ntrips = of_get_child_count(child);
679 if (tz->ntrips == 0) /* must have at least one child */
680 goto finish;
681
682 tz->trips = kzalloc(tz->ntrips * sizeof(*tz->trips), GFP_KERNEL);
683 if (!tz->trips) {
684 ret = -ENOMEM;
685 goto free_tz;
686 }
687
688 i = 0;
689 for_each_child_of_node(child, gchild) {
690 ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
691 if (ret)
692 goto free_trips;
693 }
694
695 of_node_put(child);
696
697 /* cooling-maps */
698 child = of_get_child_by_name(np, "cooling-maps");
699
700 /* cooling-maps not provided */
701 if (!child)
702 goto finish;
703
704 tz->num_tbps = of_get_child_count(child);
705 if (tz->num_tbps == 0)
706 goto finish;
707
708 tz->tbps = kzalloc(tz->num_tbps * sizeof(*tz->tbps), GFP_KERNEL);
709 if (!tz->tbps) {
710 ret = -ENOMEM;
711 goto free_trips;
712 }
713
714 i = 0;
715 for_each_child_of_node(child, gchild)
716 ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
717 tz->trips, tz->ntrips);
718 if (ret)
719 goto free_tbps;
720
721finish:
722 of_node_put(child);
723 tz->mode = THERMAL_DEVICE_DISABLED;
724
725 return tz;
726
727free_tbps:
728 kfree(tz->tbps);
729free_trips:
730 kfree(tz->trips);
731free_tz:
732 kfree(tz);
733 of_node_put(child);
734
735 return ERR_PTR(ret);
736}
737
738static inline void of_thermal_free_zone(struct __thermal_zone *tz)
739{
740 kfree(tz->tbps);
741 kfree(tz->trips);
742 kfree(tz);
743}
744
745/**
746 * of_parse_thermal_zones - parse device tree thermal data
747 *
748 * Initialization function that can be called by machine initialization
749 * code to parse thermal data and populate the thermal framework
750 * with hardware thermal zones info. This function only parses thermal zones.
751 * Cooling devices and sensor devices nodes are supposed to be parsed
752 * by their respective drivers.
753 *
754 * Return: 0 on success, proper error code otherwise
755 *
756 */
757int __init of_parse_thermal_zones(void)
758{
759 struct device_node *np, *child;
760 struct __thermal_zone *tz;
761 struct thermal_zone_device_ops *ops;
762
763 np = of_find_node_by_name(NULL, "thermal-zones");
764 if (!np) {
765 pr_debug("unable to find thermal zones\n");
766 return 0; /* Run successfully on systems without thermal DT */
767 }
768
769 for_each_child_of_node(np, child) {
770 struct thermal_zone_device *zone;
771 struct thermal_zone_params *tzp;
772
773 tz = thermal_of_build_thermal_zone(child);
774 if (IS_ERR(tz)) {
775 pr_err("failed to build thermal zone %s: %ld\n",
776 child->name,
777 PTR_ERR(tz));
778 continue;
779 }
780
781 ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
782 if (!ops)
783 goto exit_free;
784
785 tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
786 if (!tzp) {
787 kfree(ops);
788 goto exit_free;
789 }
790
791 /* No hwmon because there might be hwmon drivers registering */
792 tzp->no_hwmon = true;
793
794 zone = thermal_zone_device_register(child->name, tz->ntrips,
795 0, tz,
796 ops, tzp,
797 tz->passive_delay,
798 tz->polling_delay);
799 if (IS_ERR(zone)) {
800 pr_err("Failed to build %s zone %ld\n", child->name,
801 PTR_ERR(zone));
802 kfree(tzp);
803 kfree(ops);
804 of_thermal_free_zone(tz);
805 /* attempting to build remaining zones still */
806 }
807 }
808
809 return 0;
810
811exit_free:
812 of_thermal_free_zone(tz);
813
814 /* no memory available, so free what we have built */
815 of_thermal_destroy_zones();
816
817 return -ENOMEM;
818}
819
820/**
821 * of_thermal_destroy_zones - remove all zones parsed and allocated resources
822 *
823 * Finds all zones parsed and added to the thermal framework and remove them
824 * from the system, together with their resources.
825 *
826 */
827void of_thermal_destroy_zones(void)
828{
829 struct device_node *np, *child;
830
831 np = of_find_node_by_name(NULL, "thermal-zones");
832 if (!np) {
833 pr_err("unable to find thermal zones\n");
834 return;
835 }
836
837 for_each_child_of_node(np, child) {
838 struct thermal_zone_device *zone;
839
840 zone = thermal_zone_get_zone_by_name(child->name);
841 if (IS_ERR(zone))
842 continue;
843
844 thermal_zone_device_unregister(zone);
845 kfree(zone->tzp);
846 kfree(zone->ops);
847 of_thermal_free_zone(zone->devdata);
848 }
849}
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 0e43dc208fc6..3392fcb92796 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -1373,7 +1373,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz)
1373 */ 1373 */
1374struct thermal_zone_device *thermal_zone_device_register(const char *type, 1374struct thermal_zone_device *thermal_zone_device_register(const char *type,
1375 int trips, int mask, void *devdata, 1375 int trips, int mask, void *devdata,
1376 const struct thermal_zone_device_ops *ops, 1376 struct thermal_zone_device_ops *ops,
1377 const struct thermal_zone_params *tzp, 1377 const struct thermal_zone_params *tzp,
1378 int passive_delay, int polling_delay) 1378 int passive_delay, int polling_delay)
1379{ 1379{
@@ -1746,8 +1746,14 @@ static int __init thermal_init(void)
1746 if (result) 1746 if (result)
1747 goto unregister_class; 1747 goto unregister_class;
1748 1748
1749 result = of_parse_thermal_zones();
1750 if (result)
1751 goto exit_netlink;
1752
1749 return 0; 1753 return 0;
1750 1754
1755exit_netlink:
1756 genetlink_exit();
1751unregister_governors: 1757unregister_governors:
1752 thermal_unregister_governors(); 1758 thermal_unregister_governors();
1753unregister_class: 1759unregister_class:
@@ -1763,6 +1769,7 @@ error:
1763 1769
1764static void __exit thermal_exit(void) 1770static void __exit thermal_exit(void)
1765{ 1771{
1772 of_thermal_destroy_zones();
1766 genetlink_exit(); 1773 genetlink_exit();
1767 class_unregister(&thermal_class); 1774 class_unregister(&thermal_class);
1768 thermal_unregister_governors(); 1775 thermal_unregister_governors();
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index 7cf2f6626251..3db339fb636f 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -77,4 +77,13 @@ static inline int thermal_gov_user_space_register(void) { return 0; }
77static inline void thermal_gov_user_space_unregister(void) {} 77static inline void thermal_gov_user_space_unregister(void) {}
78#endif /* CONFIG_THERMAL_GOV_USER_SPACE */ 78#endif /* CONFIG_THERMAL_GOV_USER_SPACE */
79 79
80/* device tree support */
81#ifdef CONFIG_THERMAL_OF
82int of_parse_thermal_zones(void);
83void of_thermal_destroy_zones(void);
84#else
85static inline int of_parse_thermal_zones(void) { return 0; }
86static inline void of_thermal_destroy_zones(void) { }
87#endif
88
80#endif /* __THERMAL_CORE_H__ */ 89#endif /* __THERMAL_CORE_H__ */
diff --git a/include/dt-bindings/thermal/thermal.h b/include/dt-bindings/thermal/thermal.h
new file mode 100644
index 000000000000..59822a995858
--- /dev/null
+++ b/include/dt-bindings/thermal/thermal.h
@@ -0,0 +1,17 @@
1/*
2 * This header provides constants for most thermal bindings.
3 *
4 * Copyright (C) 2013 Texas Instruments
5 * Eduardo Valentin <eduardo.valentin@ti.com>
6 *
7 * GPLv2 only
8 */
9
10#ifndef _DT_BINDINGS_THERMAL_THERMAL_H
11#define _DT_BINDINGS_THERMAL_THERMAL_H
12
13/* On cooling devices upper and lower limits */
14#define THERMAL_NO_LIMIT (-1UL)
15
16#endif
17
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index b268d3cf7ae3..b780c5b27122 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -143,6 +143,7 @@ struct thermal_cooling_device {
143 int id; 143 int id;
144 char type[THERMAL_NAME_LENGTH]; 144 char type[THERMAL_NAME_LENGTH];
145 struct device device; 145 struct device device;
146 struct device_node *np;
146 void *devdata; 147 void *devdata;
147 const struct thermal_cooling_device_ops *ops; 148 const struct thermal_cooling_device_ops *ops;
148 bool updated; /* true if the cooling device does not need update */ 149 bool updated; /* true if the cooling device does not need update */
@@ -172,7 +173,7 @@ struct thermal_zone_device {
172 int emul_temperature; 173 int emul_temperature;
173 int passive; 174 int passive;
174 unsigned int forced_passive; 175 unsigned int forced_passive;
175 const struct thermal_zone_device_ops *ops; 176 struct thermal_zone_device_ops *ops;
176 const struct thermal_zone_params *tzp; 177 const struct thermal_zone_params *tzp;
177 struct thermal_governor *governor; 178 struct thermal_governor *governor;
178 struct list_head thermal_instances; 179 struct list_head thermal_instances;
@@ -242,8 +243,31 @@ struct thermal_genl_event {
242}; 243};
243 244
244/* Function declarations */ 245/* Function declarations */
246#ifdef CONFIG_THERMAL_OF
247struct thermal_zone_device *
248thermal_zone_of_sensor_register(struct device *dev, int id,
249 void *data, int (*get_temp)(void *, long *),
250 int (*get_trend)(void *, long *));
251void thermal_zone_of_sensor_unregister(struct device *dev,
252 struct thermal_zone_device *tz);
253#else
254static inline struct thermal_zone_device *
255thermal_zone_of_sensor_register(struct device *dev, int id,
256 void *data, int (*get_temp)(void *, long *),
257 int (*get_trend)(void *, long *))
258{
259 return NULL;
260}
261
262static inline
263void thermal_zone_of_sensor_unregister(struct device *dev,
264 struct thermal_zone_device *tz)
265{
266}
267
268#endif
245struct thermal_zone_device *thermal_zone_device_register(const char *, int, int, 269struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
246 void *, const struct thermal_zone_device_ops *, 270 void *, struct thermal_zone_device_ops *,
247 const struct thermal_zone_params *, int, int); 271 const struct thermal_zone_params *, int, int);
248void thermal_zone_device_unregister(struct thermal_zone_device *); 272void thermal_zone_device_unregister(struct thermal_zone_device *);
249 273