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