xref: /openbmc/linux/tools/thermal/tmon/sysfs.c (revision 94f69966faf8e70bd655ea25f9dd5b9400567b75)
1*94f69966SJacob Pan /*
2*94f69966SJacob Pan  * sysfs.c sysfs ABI access functions for TMON program
3*94f69966SJacob Pan  *
4*94f69966SJacob Pan  * Copyright (C) 2013 Intel Corporation. All rights reserved.
5*94f69966SJacob Pan  *
6*94f69966SJacob Pan  * This program is free software; you can redistribute it and/or
7*94f69966SJacob Pan  * modify it under the terms of the GNU General Public License version
8*94f69966SJacob Pan  * 2 or later as published by the Free Software Foundation.
9*94f69966SJacob Pan  *
10*94f69966SJacob Pan  * This program is distributed in the hope that it will be useful,
11*94f69966SJacob Pan  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*94f69966SJacob Pan  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*94f69966SJacob Pan  * GNU General Public License for more details.
14*94f69966SJacob Pan  *
15*94f69966SJacob Pan  * Author: Jacob Pan <jacob.jun.pan@linux.intel.com>
16*94f69966SJacob Pan  *
17*94f69966SJacob Pan  */
18*94f69966SJacob Pan #include <unistd.h>
19*94f69966SJacob Pan #include <stdio.h>
20*94f69966SJacob Pan #include <stdlib.h>
21*94f69966SJacob Pan #include <string.h>
22*94f69966SJacob Pan #include <stdint.h>
23*94f69966SJacob Pan #include <dirent.h>
24*94f69966SJacob Pan #include <libintl.h>
25*94f69966SJacob Pan #include <ctype.h>
26*94f69966SJacob Pan #include <time.h>
27*94f69966SJacob Pan #include <syslog.h>
28*94f69966SJacob Pan #include <sys/time.h>
29*94f69966SJacob Pan #include <errno.h>
30*94f69966SJacob Pan 
31*94f69966SJacob Pan #include "tmon.h"
32*94f69966SJacob Pan 
33*94f69966SJacob Pan struct tmon_platform_data ptdata;
34*94f69966SJacob Pan const char *trip_type_name[] = {
35*94f69966SJacob Pan 	"critical",
36*94f69966SJacob Pan 	"hot",
37*94f69966SJacob Pan 	"passive",
38*94f69966SJacob Pan 	"active",
39*94f69966SJacob Pan };
40*94f69966SJacob Pan 
41*94f69966SJacob Pan int sysfs_set_ulong(char *path, char *filename, unsigned long val)
42*94f69966SJacob Pan {
43*94f69966SJacob Pan 	FILE *fd;
44*94f69966SJacob Pan 	int ret = -1;
45*94f69966SJacob Pan 	char filepath[256];
46*94f69966SJacob Pan 
47*94f69966SJacob Pan 	snprintf(filepath, 256, "%s/%s", path, filename);
48*94f69966SJacob Pan 
49*94f69966SJacob Pan 	fd = fopen(filepath, "w");
50*94f69966SJacob Pan 	if (!fd) {
51*94f69966SJacob Pan 		syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
52*94f69966SJacob Pan 		return ret;
53*94f69966SJacob Pan 	}
54*94f69966SJacob Pan 	ret = fprintf(fd, "%lu", val);
55*94f69966SJacob Pan 	fclose(fd);
56*94f69966SJacob Pan 
57*94f69966SJacob Pan 	return 0;
58*94f69966SJacob Pan }
59*94f69966SJacob Pan 
60*94f69966SJacob Pan /* history of thermal data, used for control algo */
61*94f69966SJacob Pan #define NR_THERMAL_RECORDS 3
62*94f69966SJacob Pan struct thermal_data_record trec[NR_THERMAL_RECORDS];
63*94f69966SJacob Pan int cur_thermal_record; /* index to the trec array */
64*94f69966SJacob Pan 
65*94f69966SJacob Pan static int sysfs_get_ulong(char *path, char *filename, unsigned long *p_ulong)
66*94f69966SJacob Pan {
67*94f69966SJacob Pan 	FILE *fd;
68*94f69966SJacob Pan 	int ret = -1;
69*94f69966SJacob Pan 	char filepath[256];
70*94f69966SJacob Pan 
71*94f69966SJacob Pan 	snprintf(filepath, 256, "%s/%s", path, filename);
72*94f69966SJacob Pan 
73*94f69966SJacob Pan 	fd = fopen(filepath, "r");
74*94f69966SJacob Pan 	if (!fd) {
75*94f69966SJacob Pan 		syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
76*94f69966SJacob Pan 		return ret;
77*94f69966SJacob Pan 	}
78*94f69966SJacob Pan 	ret = fscanf(fd, "%lu", p_ulong);
79*94f69966SJacob Pan 	fclose(fd);
80*94f69966SJacob Pan 
81*94f69966SJacob Pan 	return 0;
82*94f69966SJacob Pan }
83*94f69966SJacob Pan 
84*94f69966SJacob Pan static int sysfs_get_string(char *path, char *filename, char *str)
85*94f69966SJacob Pan {
86*94f69966SJacob Pan 	FILE *fd;
87*94f69966SJacob Pan 	int ret = -1;
88*94f69966SJacob Pan 	char filepath[256];
89*94f69966SJacob Pan 
90*94f69966SJacob Pan 	snprintf(filepath, 256, "%s/%s", path, filename);
91*94f69966SJacob Pan 
92*94f69966SJacob Pan 	fd = fopen(filepath, "r");
93*94f69966SJacob Pan 	if (!fd) {
94*94f69966SJacob Pan 		syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
95*94f69966SJacob Pan 		return ret;
96*94f69966SJacob Pan 	}
97*94f69966SJacob Pan 	ret = fscanf(fd, "%256s", str);
98*94f69966SJacob Pan 	fclose(fd);
99*94f69966SJacob Pan 
100*94f69966SJacob Pan 	return ret;
101*94f69966SJacob Pan }
102*94f69966SJacob Pan 
103*94f69966SJacob Pan /* get states of the cooling device instance */
104*94f69966SJacob Pan static int probe_cdev(struct cdev_info *cdi, char *path)
105*94f69966SJacob Pan {
106*94f69966SJacob Pan 	sysfs_get_string(path, "type", cdi->type);
107*94f69966SJacob Pan 	sysfs_get_ulong(path, "max_state",  &cdi->max_state);
108*94f69966SJacob Pan 	sysfs_get_ulong(path, "cur_state", &cdi->cur_state);
109*94f69966SJacob Pan 
110*94f69966SJacob Pan 	syslog(LOG_INFO, "%s: %s: type %s, max %lu, curr %lu inst %d\n",
111*94f69966SJacob Pan 		__func__, path,
112*94f69966SJacob Pan 		cdi->type, cdi->max_state, cdi->cur_state, cdi->instance);
113*94f69966SJacob Pan 
114*94f69966SJacob Pan 	return 0;
115*94f69966SJacob Pan }
116*94f69966SJacob Pan 
117*94f69966SJacob Pan static int str_to_trip_type(char *name)
118*94f69966SJacob Pan {
119*94f69966SJacob Pan 	int i;
120*94f69966SJacob Pan 
121*94f69966SJacob Pan 	for (i = 0; i < NR_THERMAL_TRIP_TYPE; i++) {
122*94f69966SJacob Pan 		if (!strcmp(name, trip_type_name[i]))
123*94f69966SJacob Pan 			return i;
124*94f69966SJacob Pan 	}
125*94f69966SJacob Pan 
126*94f69966SJacob Pan 	return -ENOENT;
127*94f69966SJacob Pan }
128*94f69966SJacob Pan 
129*94f69966SJacob Pan /* scan and fill in trip point info for a thermal zone and trip point id */
130*94f69966SJacob Pan static int get_trip_point_data(char *tz_path, int tzid, int tpid)
131*94f69966SJacob Pan {
132*94f69966SJacob Pan 	char filename[256];
133*94f69966SJacob Pan 	char temp_str[256];
134*94f69966SJacob Pan 	int trip_type;
135*94f69966SJacob Pan 
136*94f69966SJacob Pan 	if (tpid >= MAX_NR_TRIP)
137*94f69966SJacob Pan 		return -EINVAL;
138*94f69966SJacob Pan 	/* check trip point type */
139*94f69966SJacob Pan 	snprintf(filename, sizeof(filename), "trip_point_%d_type", tpid);
140*94f69966SJacob Pan 	sysfs_get_string(tz_path, filename, temp_str);
141*94f69966SJacob Pan 	trip_type = str_to_trip_type(temp_str);
142*94f69966SJacob Pan 	if (trip_type < 0) {
143*94f69966SJacob Pan 		syslog(LOG_ERR, "%s:%s no matching type\n", __func__, temp_str);
144*94f69966SJacob Pan 		return -ENOENT;
145*94f69966SJacob Pan 	}
146*94f69966SJacob Pan 	ptdata.tzi[tzid].tp[tpid].type = trip_type;
147*94f69966SJacob Pan 	syslog(LOG_INFO, "%s:tz:%d tp:%d:type:%s type id %d\n", __func__, tzid,
148*94f69966SJacob Pan 		tpid, temp_str, trip_type);
149*94f69966SJacob Pan 
150*94f69966SJacob Pan 	/* TODO: check attribute */
151*94f69966SJacob Pan 
152*94f69966SJacob Pan 	return 0;
153*94f69966SJacob Pan }
154*94f69966SJacob Pan 
155*94f69966SJacob Pan /* return instance id for file format such as trip_point_4_temp */
156*94f69966SJacob Pan static int get_instance_id(char *name, int pos, int skip)
157*94f69966SJacob Pan {
158*94f69966SJacob Pan 	char *ch;
159*94f69966SJacob Pan 	int i = 0;
160*94f69966SJacob Pan 
161*94f69966SJacob Pan 	ch = strtok(name, "_");
162*94f69966SJacob Pan 	while (ch != NULL) {
163*94f69966SJacob Pan 		++i;
164*94f69966SJacob Pan 		syslog(LOG_INFO, "%s:%s:%s:%d", __func__, name, ch, i);
165*94f69966SJacob Pan 		ch = strtok(NULL, "_");
166*94f69966SJacob Pan 		if (pos == i)
167*94f69966SJacob Pan 			return atol(ch + skip);
168*94f69966SJacob Pan 	}
169*94f69966SJacob Pan 
170*94f69966SJacob Pan 	return -1;
171*94f69966SJacob Pan }
172*94f69966SJacob Pan 
173*94f69966SJacob Pan /* Find trip point info of a thermal zone */
174*94f69966SJacob Pan static int find_tzone_tp(char *tz_name, char *d_name, struct tz_info *tzi,
175*94f69966SJacob Pan 			int tz_id)
176*94f69966SJacob Pan {
177*94f69966SJacob Pan 	int tp_id;
178*94f69966SJacob Pan 	unsigned long temp_ulong;
179*94f69966SJacob Pan 
180*94f69966SJacob Pan 	if (strstr(d_name, "trip_point") &&
181*94f69966SJacob Pan 		strstr(d_name, "temp")) {
182*94f69966SJacob Pan 		/* check if trip point temp is non-zero
183*94f69966SJacob Pan 		 * ignore 0/invalid trip points
184*94f69966SJacob Pan 		 */
185*94f69966SJacob Pan 		sysfs_get_ulong(tz_name, d_name, &temp_ulong);
186*94f69966SJacob Pan 		if (temp_ulong < MAX_TEMP_KC) {
187*94f69966SJacob Pan 			tzi->nr_trip_pts++;
188*94f69966SJacob Pan 			/* found a valid trip point */
189*94f69966SJacob Pan 			tp_id = get_instance_id(d_name, 2, 0);
190*94f69966SJacob Pan 			syslog(LOG_DEBUG, "tzone %s trip %d temp %lu tpnode %s",
191*94f69966SJacob Pan 				tz_name, tp_id, temp_ulong, d_name);
192*94f69966SJacob Pan 			if (tp_id < 0 || tp_id >= MAX_NR_TRIP) {
193*94f69966SJacob Pan 				syslog(LOG_ERR, "Failed to find TP inst %s\n",
194*94f69966SJacob Pan 					d_name);
195*94f69966SJacob Pan 				return -1;
196*94f69966SJacob Pan 			}
197*94f69966SJacob Pan 			get_trip_point_data(tz_name, tz_id, tp_id);
198*94f69966SJacob Pan 			tzi->tp[tp_id].temp = temp_ulong;
199*94f69966SJacob Pan 		}
200*94f69966SJacob Pan 	}
201*94f69966SJacob Pan 
202*94f69966SJacob Pan 	return 0;
203*94f69966SJacob Pan }
204*94f69966SJacob Pan 
205*94f69966SJacob Pan /* check cooling devices for binding info. */
206*94f69966SJacob Pan static int find_tzone_cdev(struct dirent *nl, char *tz_name,
207*94f69966SJacob Pan 			struct tz_info *tzi, int tz_id, int cid)
208*94f69966SJacob Pan {
209*94f69966SJacob Pan 	unsigned long trip_instance = 0;
210*94f69966SJacob Pan 	char cdev_name_linked[256];
211*94f69966SJacob Pan 	char cdev_name[256];
212*94f69966SJacob Pan 	char cdev_trip_name[256];
213*94f69966SJacob Pan 	int cdev_id;
214*94f69966SJacob Pan 
215*94f69966SJacob Pan 	if (nl->d_type == DT_LNK) {
216*94f69966SJacob Pan 		syslog(LOG_DEBUG, "TZ%d: cdev: %s cid %d\n", tz_id, nl->d_name,
217*94f69966SJacob Pan 			cid);
218*94f69966SJacob Pan 		tzi->nr_cdev++;
219*94f69966SJacob Pan 		if (tzi->nr_cdev > ptdata.nr_cooling_dev) {
220*94f69966SJacob Pan 			syslog(LOG_ERR, "Err: Too many cdev? %d\n",
221*94f69966SJacob Pan 				tzi->nr_cdev);
222*94f69966SJacob Pan 			return -EINVAL;
223*94f69966SJacob Pan 		}
224*94f69966SJacob Pan 		/* find the link to real cooling device record binding */
225*94f69966SJacob Pan 		snprintf(cdev_name, 256, "%s/%s", tz_name, nl->d_name);
226*94f69966SJacob Pan 		memset(cdev_name_linked, 0, sizeof(cdev_name_linked));
227*94f69966SJacob Pan 		if (readlink(cdev_name, cdev_name_linked,
228*94f69966SJacob Pan 				sizeof(cdev_name_linked) - 1) != -1) {
229*94f69966SJacob Pan 			cdev_id = get_instance_id(cdev_name_linked, 1,
230*94f69966SJacob Pan 						sizeof("device") - 1);
231*94f69966SJacob Pan 			syslog(LOG_DEBUG, "cdev %s linked to %s : %d\n",
232*94f69966SJacob Pan 				cdev_name, cdev_name_linked, cdev_id);
233*94f69966SJacob Pan 			tzi->cdev_binding |= (1 << cdev_id);
234*94f69966SJacob Pan 
235*94f69966SJacob Pan 			/* find the trip point in which the cdev is binded to
236*94f69966SJacob Pan 			 * in this tzone
237*94f69966SJacob Pan 			 */
238*94f69966SJacob Pan 			snprintf(cdev_trip_name, 256, "%s%s", nl->d_name,
239*94f69966SJacob Pan 				"_trip_point");
240*94f69966SJacob Pan 			sysfs_get_ulong(tz_name, cdev_trip_name,
241*94f69966SJacob Pan 					&trip_instance);
242*94f69966SJacob Pan 			/* validate trip point range, e.g. trip could return -1
243*94f69966SJacob Pan 			 * when passive is enabled
244*94f69966SJacob Pan 			 */
245*94f69966SJacob Pan 			if (trip_instance > MAX_NR_TRIP)
246*94f69966SJacob Pan 				trip_instance = 0;
247*94f69966SJacob Pan 			tzi->trip_binding[cdev_id] |= 1 << trip_instance;
248*94f69966SJacob Pan 			syslog(LOG_DEBUG, "cdev %s -> trip:%lu: 0x%lx %d\n",
249*94f69966SJacob Pan 				cdev_name, trip_instance,
250*94f69966SJacob Pan 				tzi->trip_binding[cdev_id],
251*94f69966SJacob Pan 				cdev_id);
252*94f69966SJacob Pan 
253*94f69966SJacob Pan 
254*94f69966SJacob Pan 		}
255*94f69966SJacob Pan 		return 0;
256*94f69966SJacob Pan 	}
257*94f69966SJacob Pan 
258*94f69966SJacob Pan 	return -ENODEV;
259*94f69966SJacob Pan }
260*94f69966SJacob Pan 
261*94f69966SJacob Pan 
262*94f69966SJacob Pan 
263*94f69966SJacob Pan /*****************************************************************************
264*94f69966SJacob Pan  * Before calling scan_tzones, thermal sysfs must be probed to determine
265*94f69966SJacob Pan  * the number of thermal zones and cooling devices.
266*94f69966SJacob Pan  * We loop through each thermal zone and fill in tz_info struct, i.e.
267*94f69966SJacob Pan  * ptdata.tzi[]
268*94f69966SJacob Pan root@jacob-chiefriver:~# tree -d /sys/class/thermal/thermal_zone0
269*94f69966SJacob Pan /sys/class/thermal/thermal_zone0
270*94f69966SJacob Pan |-- cdev0 -> ../cooling_device4
271*94f69966SJacob Pan |-- cdev1 -> ../cooling_device3
272*94f69966SJacob Pan |-- cdev10 -> ../cooling_device7
273*94f69966SJacob Pan |-- cdev11 -> ../cooling_device6
274*94f69966SJacob Pan |-- cdev12 -> ../cooling_device5
275*94f69966SJacob Pan |-- cdev2 -> ../cooling_device2
276*94f69966SJacob Pan |-- cdev3 -> ../cooling_device1
277*94f69966SJacob Pan |-- cdev4 -> ../cooling_device0
278*94f69966SJacob Pan |-- cdev5 -> ../cooling_device12
279*94f69966SJacob Pan |-- cdev6 -> ../cooling_device11
280*94f69966SJacob Pan |-- cdev7 -> ../cooling_device10
281*94f69966SJacob Pan |-- cdev8 -> ../cooling_device9
282*94f69966SJacob Pan |-- cdev9 -> ../cooling_device8
283*94f69966SJacob Pan |-- device -> ../../../LNXSYSTM:00/device:62/LNXTHERM:00
284*94f69966SJacob Pan |-- power
285*94f69966SJacob Pan `-- subsystem -> ../../../../class/thermal
286*94f69966SJacob Pan *****************************************************************************/
287*94f69966SJacob Pan static int scan_tzones(void)
288*94f69966SJacob Pan {
289*94f69966SJacob Pan 	DIR *dir;
290*94f69966SJacob Pan 	struct dirent **namelist;
291*94f69966SJacob Pan 	char tz_name[256];
292*94f69966SJacob Pan 	int i, j, n, k = 0;
293*94f69966SJacob Pan 
294*94f69966SJacob Pan 	if (!ptdata.nr_tz_sensor)
295*94f69966SJacob Pan 		return -1;
296*94f69966SJacob Pan 
297*94f69966SJacob Pan 	for (i = 0; i <= ptdata.max_tz_instance; i++) {
298*94f69966SJacob Pan 		memset(tz_name, 0, sizeof(tz_name));
299*94f69966SJacob Pan 		snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE, i);
300*94f69966SJacob Pan 
301*94f69966SJacob Pan 		dir = opendir(tz_name);
302*94f69966SJacob Pan 		if (!dir) {
303*94f69966SJacob Pan 			syslog(LOG_INFO, "Thermal zone %s skipped\n", tz_name);
304*94f69966SJacob Pan 			continue;
305*94f69966SJacob Pan 		}
306*94f69966SJacob Pan 		/* keep track of valid tzones */
307*94f69966SJacob Pan 		n = scandir(tz_name, &namelist, 0, alphasort);
308*94f69966SJacob Pan 		if (n < 0)
309*94f69966SJacob Pan 			syslog(LOG_ERR, "scandir failed in %s",  tz_name);
310*94f69966SJacob Pan 		else {
311*94f69966SJacob Pan 			sysfs_get_string(tz_name, "type", ptdata.tzi[k].type);
312*94f69966SJacob Pan 			ptdata.tzi[k].instance = i;
313*94f69966SJacob Pan 			/* detect trip points and cdev attached to this tzone */
314*94f69966SJacob Pan 			j = 0; /* index for cdev */
315*94f69966SJacob Pan 			ptdata.tzi[k].nr_cdev = 0;
316*94f69966SJacob Pan 			ptdata.tzi[k].nr_trip_pts = 0;
317*94f69966SJacob Pan 			while (n--) {
318*94f69966SJacob Pan 				char *temp_str;
319*94f69966SJacob Pan 
320*94f69966SJacob Pan 				if (find_tzone_tp(tz_name, namelist[n]->d_name,
321*94f69966SJacob Pan 							&ptdata.tzi[k], k))
322*94f69966SJacob Pan 					break;
323*94f69966SJacob Pan 				temp_str = strstr(namelist[n]->d_name, "cdev");
324*94f69966SJacob Pan 				if (!temp_str) {
325*94f69966SJacob Pan 					free(namelist[n]);
326*94f69966SJacob Pan 					continue;
327*94f69966SJacob Pan 				}
328*94f69966SJacob Pan 				if (!find_tzone_cdev(namelist[n], tz_name,
329*94f69966SJacob Pan 							&ptdata.tzi[k], i, j))
330*94f69966SJacob Pan 					j++; /* increment cdev index */
331*94f69966SJacob Pan 				free(namelist[n]);
332*94f69966SJacob Pan 			}
333*94f69966SJacob Pan 			free(namelist);
334*94f69966SJacob Pan 		}
335*94f69966SJacob Pan 		/*TODO: reverse trip points */
336*94f69966SJacob Pan 		closedir(dir);
337*94f69966SJacob Pan 		syslog(LOG_INFO, "TZ %d has %d cdev\n",	i,
338*94f69966SJacob Pan 			ptdata.tzi[k].nr_cdev);
339*94f69966SJacob Pan 		k++;
340*94f69966SJacob Pan 	}
341*94f69966SJacob Pan 
342*94f69966SJacob Pan 	return 0;
343*94f69966SJacob Pan }
344*94f69966SJacob Pan 
345*94f69966SJacob Pan static int scan_cdevs(void)
346*94f69966SJacob Pan {
347*94f69966SJacob Pan 	DIR *dir;
348*94f69966SJacob Pan 	struct dirent **namelist;
349*94f69966SJacob Pan 	char cdev_name[256];
350*94f69966SJacob Pan 	int i, n, k = 0;
351*94f69966SJacob Pan 
352*94f69966SJacob Pan 	if (!ptdata.nr_cooling_dev) {
353*94f69966SJacob Pan 		fprintf(stderr, "No cooling devices found\n");
354*94f69966SJacob Pan 		return 0;
355*94f69966SJacob Pan 	}
356*94f69966SJacob Pan 	for (i = 0; i <= ptdata.max_cdev_instance; i++) {
357*94f69966SJacob Pan 		memset(cdev_name, 0, sizeof(cdev_name));
358*94f69966SJacob Pan 		snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV, i);
359*94f69966SJacob Pan 
360*94f69966SJacob Pan 		dir = opendir(cdev_name);
361*94f69966SJacob Pan 		if (!dir) {
362*94f69966SJacob Pan 			syslog(LOG_INFO, "Cooling dev %s skipped\n", cdev_name);
363*94f69966SJacob Pan 			/* there is a gap in cooling device id, check again
364*94f69966SJacob Pan 			 * for the same index.
365*94f69966SJacob Pan 			 */
366*94f69966SJacob Pan 			continue;
367*94f69966SJacob Pan 		}
368*94f69966SJacob Pan 
369*94f69966SJacob Pan 		n = scandir(cdev_name, &namelist, 0, alphasort);
370*94f69966SJacob Pan 		if (n < 0)
371*94f69966SJacob Pan 			syslog(LOG_ERR, "scandir failed in %s",  cdev_name);
372*94f69966SJacob Pan 		else {
373*94f69966SJacob Pan 			sysfs_get_string(cdev_name, "type", ptdata.cdi[k].type);
374*94f69966SJacob Pan 			ptdata.cdi[k].instance = i;
375*94f69966SJacob Pan 			if (strstr(ptdata.cdi[k].type, ctrl_cdev)) {
376*94f69966SJacob Pan 				ptdata.cdi[k].flag |= CDEV_FLAG_IN_CONTROL;
377*94f69966SJacob Pan 				syslog(LOG_DEBUG, "control cdev id %d\n", i);
378*94f69966SJacob Pan 			}
379*94f69966SJacob Pan 			while (n--)
380*94f69966SJacob Pan 				free(namelist[n]);
381*94f69966SJacob Pan 			free(namelist);
382*94f69966SJacob Pan 		}
383*94f69966SJacob Pan 		closedir(dir);
384*94f69966SJacob Pan 		k++;
385*94f69966SJacob Pan 	}
386*94f69966SJacob Pan 	return 0;
387*94f69966SJacob Pan }
388*94f69966SJacob Pan 
389*94f69966SJacob Pan 
390*94f69966SJacob Pan int probe_thermal_sysfs(void)
391*94f69966SJacob Pan {
392*94f69966SJacob Pan 	DIR *dir;
393*94f69966SJacob Pan 	struct dirent **namelist;
394*94f69966SJacob Pan 	int n;
395*94f69966SJacob Pan 
396*94f69966SJacob Pan 	dir = opendir(THERMAL_SYSFS);
397*94f69966SJacob Pan 	if (!dir) {
398*94f69966SJacob Pan 		fprintf(stderr, "\nNo thermal sysfs, exit\n");
399*94f69966SJacob Pan 		return -1;
400*94f69966SJacob Pan 	}
401*94f69966SJacob Pan 	n = scandir(THERMAL_SYSFS, &namelist, 0, alphasort);
402*94f69966SJacob Pan 	if (n < 0)
403*94f69966SJacob Pan 		syslog(LOG_ERR, "scandir failed in thermal sysfs");
404*94f69966SJacob Pan 	else {
405*94f69966SJacob Pan 		/* detect number of thermal zones and cooling devices */
406*94f69966SJacob Pan 		while (n--) {
407*94f69966SJacob Pan 			int inst;
408*94f69966SJacob Pan 
409*94f69966SJacob Pan 			if (strstr(namelist[n]->d_name, CDEV)) {
410*94f69966SJacob Pan 				inst = get_instance_id(namelist[n]->d_name, 1,
411*94f69966SJacob Pan 						sizeof("device") - 1);
412*94f69966SJacob Pan 				/* keep track of the max cooling device since
413*94f69966SJacob Pan 				 * there may be gaps.
414*94f69966SJacob Pan 				 */
415*94f69966SJacob Pan 				if (inst > ptdata.max_cdev_instance)
416*94f69966SJacob Pan 					ptdata.max_cdev_instance = inst;
417*94f69966SJacob Pan 
418*94f69966SJacob Pan 				syslog(LOG_DEBUG, "found cdev: %s %d %d\n",
419*94f69966SJacob Pan 					namelist[n]->d_name,
420*94f69966SJacob Pan 					ptdata.nr_cooling_dev,
421*94f69966SJacob Pan 					ptdata.max_cdev_instance);
422*94f69966SJacob Pan 				ptdata.nr_cooling_dev++;
423*94f69966SJacob Pan 			} else if (strstr(namelist[n]->d_name, TZONE)) {
424*94f69966SJacob Pan 				inst = get_instance_id(namelist[n]->d_name, 1,
425*94f69966SJacob Pan 						sizeof("zone") - 1);
426*94f69966SJacob Pan 				if (inst > ptdata.max_tz_instance)
427*94f69966SJacob Pan 					ptdata.max_tz_instance = inst;
428*94f69966SJacob Pan 
429*94f69966SJacob Pan 				syslog(LOG_DEBUG, "found tzone: %s %d %d\n",
430*94f69966SJacob Pan 					namelist[n]->d_name,
431*94f69966SJacob Pan 					ptdata.nr_tz_sensor,
432*94f69966SJacob Pan 					ptdata.max_tz_instance);
433*94f69966SJacob Pan 				ptdata.nr_tz_sensor++;
434*94f69966SJacob Pan 			}
435*94f69966SJacob Pan 			free(namelist[n]);
436*94f69966SJacob Pan 		}
437*94f69966SJacob Pan 		free(namelist);
438*94f69966SJacob Pan 	}
439*94f69966SJacob Pan 	syslog(LOG_INFO, "found %d tzone(s), %d cdev(s), target zone %d\n",
440*94f69966SJacob Pan 		ptdata.nr_tz_sensor, ptdata.nr_cooling_dev,
441*94f69966SJacob Pan 		target_thermal_zone);
442*94f69966SJacob Pan 	closedir(dir);
443*94f69966SJacob Pan 
444*94f69966SJacob Pan 	if (!ptdata.nr_tz_sensor) {
445*94f69966SJacob Pan 		fprintf(stderr, "\nNo thermal zones found, exit\n\n");
446*94f69966SJacob Pan 		return -1;
447*94f69966SJacob Pan 	}
448*94f69966SJacob Pan 
449*94f69966SJacob Pan 	ptdata.tzi = calloc(sizeof(struct tz_info), ptdata.max_tz_instance+1);
450*94f69966SJacob Pan 	if (!ptdata.tzi) {
451*94f69966SJacob Pan 		fprintf(stderr, "Err: allocate tz_info\n");
452*94f69966SJacob Pan 		return -1;
453*94f69966SJacob Pan 	}
454*94f69966SJacob Pan 
455*94f69966SJacob Pan 	/* we still show thermal zone information if there is no cdev */
456*94f69966SJacob Pan 	if (ptdata.nr_cooling_dev) {
457*94f69966SJacob Pan 		ptdata.cdi = calloc(sizeof(struct cdev_info),
458*94f69966SJacob Pan 				ptdata.max_cdev_instance + 1);
459*94f69966SJacob Pan 		if (!ptdata.cdi) {
460*94f69966SJacob Pan 			free(ptdata.tzi);
461*94f69966SJacob Pan 			fprintf(stderr, "Err: allocate cdev_info\n");
462*94f69966SJacob Pan 			return -1;
463*94f69966SJacob Pan 		}
464*94f69966SJacob Pan 	}
465*94f69966SJacob Pan 
466*94f69966SJacob Pan 	/* now probe tzones */
467*94f69966SJacob Pan 	if (scan_tzones())
468*94f69966SJacob Pan 		return -1;
469*94f69966SJacob Pan 	if (scan_cdevs())
470*94f69966SJacob Pan 		return -1;
471*94f69966SJacob Pan 	return 0;
472*94f69966SJacob Pan }
473*94f69966SJacob Pan 
474*94f69966SJacob Pan /* convert sysfs zone instance to zone array index */
475*94f69966SJacob Pan int zone_instance_to_index(int zone_inst)
476*94f69966SJacob Pan {
477*94f69966SJacob Pan 	int i;
478*94f69966SJacob Pan 
479*94f69966SJacob Pan 	for (i = 0; i < ptdata.nr_tz_sensor; i++)
480*94f69966SJacob Pan 		if (ptdata.tzi[i].instance == zone_inst)
481*94f69966SJacob Pan 			return i;
482*94f69966SJacob Pan 	return -ENOENT;
483*94f69966SJacob Pan }
484*94f69966SJacob Pan 
485*94f69966SJacob Pan /* read temperature of all thermal zones */
486*94f69966SJacob Pan int update_thermal_data()
487*94f69966SJacob Pan {
488*94f69966SJacob Pan 	int i;
489*94f69966SJacob Pan 	char tz_name[256];
490*94f69966SJacob Pan 	static unsigned long samples;
491*94f69966SJacob Pan 
492*94f69966SJacob Pan 	if (!ptdata.nr_tz_sensor) {
493*94f69966SJacob Pan 		syslog(LOG_ERR, "No thermal zones found!\n");
494*94f69966SJacob Pan 		return -1;
495*94f69966SJacob Pan 	}
496*94f69966SJacob Pan 
497*94f69966SJacob Pan 	/* circular buffer for keeping historic data */
498*94f69966SJacob Pan 	if (cur_thermal_record >= NR_THERMAL_RECORDS)
499*94f69966SJacob Pan 		cur_thermal_record = 0;
500*94f69966SJacob Pan 	gettimeofday(&trec[cur_thermal_record].tv, NULL);
501*94f69966SJacob Pan 	if (tmon_log) {
502*94f69966SJacob Pan 		fprintf(tmon_log, "%lu ", ++samples);
503*94f69966SJacob Pan 		fprintf(tmon_log, "%3.1f ", p_param.t_target);
504*94f69966SJacob Pan 	}
505*94f69966SJacob Pan 	for (i = 0; i < ptdata.nr_tz_sensor; i++) {
506*94f69966SJacob Pan 		memset(tz_name, 0, sizeof(tz_name));
507*94f69966SJacob Pan 		snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE,
508*94f69966SJacob Pan 			ptdata.tzi[i].instance);
509*94f69966SJacob Pan 		sysfs_get_ulong(tz_name, "temp",
510*94f69966SJacob Pan 				&trec[cur_thermal_record].temp[i]);
511*94f69966SJacob Pan 		if (tmon_log)
512*94f69966SJacob Pan 			fprintf(tmon_log, "%lu ",
513*94f69966SJacob Pan 				trec[cur_thermal_record].temp[i]/1000);
514*94f69966SJacob Pan 	}
515*94f69966SJacob Pan 	for (i = 0; i < ptdata.nr_cooling_dev; i++) {
516*94f69966SJacob Pan 		char cdev_name[256];
517*94f69966SJacob Pan 		unsigned long val;
518*94f69966SJacob Pan 
519*94f69966SJacob Pan 		snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV,
520*94f69966SJacob Pan 			ptdata.cdi[i].instance);
521*94f69966SJacob Pan 		probe_cdev(&ptdata.cdi[i], cdev_name);
522*94f69966SJacob Pan 		val = ptdata.cdi[i].cur_state;
523*94f69966SJacob Pan 		if (val > 1000000)
524*94f69966SJacob Pan 			val = 0;
525*94f69966SJacob Pan 		if (tmon_log)
526*94f69966SJacob Pan 			fprintf(tmon_log, "%lu ", val);
527*94f69966SJacob Pan 	}
528*94f69966SJacob Pan 
529*94f69966SJacob Pan 	if (tmon_log) {
530*94f69966SJacob Pan 		fprintf(tmon_log, "\n");
531*94f69966SJacob Pan 		fflush(tmon_log);
532*94f69966SJacob Pan 	}
533*94f69966SJacob Pan 
534*94f69966SJacob Pan 	return 0;
535*94f69966SJacob Pan }
536*94f69966SJacob Pan 
537*94f69966SJacob Pan void set_ctrl_state(unsigned long state)
538*94f69966SJacob Pan {
539*94f69966SJacob Pan 	char ctrl_cdev_path[256];
540*94f69966SJacob Pan 	int i;
541*94f69966SJacob Pan 	unsigned long cdev_state;
542*94f69966SJacob Pan 
543*94f69966SJacob Pan 	if (no_control)
544*94f69966SJacob Pan 		return;
545*94f69966SJacob Pan 	/* set all ctrl cdev to the same state */
546*94f69966SJacob Pan 	for (i = 0; i < ptdata.nr_cooling_dev; i++) {
547*94f69966SJacob Pan 		if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) {
548*94f69966SJacob Pan 			if (ptdata.cdi[i].max_state < 10) {
549*94f69966SJacob Pan 				strcpy(ctrl_cdev, "None.");
550*94f69966SJacob Pan 				return;
551*94f69966SJacob Pan 			}
552*94f69966SJacob Pan 			/* scale to percentage of max_state */
553*94f69966SJacob Pan 			cdev_state = state * ptdata.cdi[i].max_state/100;
554*94f69966SJacob Pan 			syslog(LOG_DEBUG,
555*94f69966SJacob Pan 				"ctrl cdev %d set state %lu scaled to %lu\n",
556*94f69966SJacob Pan 				ptdata.cdi[i].instance, state, cdev_state);
557*94f69966SJacob Pan 			snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS,
558*94f69966SJacob Pan 				CDEV, ptdata.cdi[i].instance);
559*94f69966SJacob Pan 			syslog(LOG_DEBUG, "ctrl cdev path %s", ctrl_cdev_path);
560*94f69966SJacob Pan 			sysfs_set_ulong(ctrl_cdev_path, "cur_state",
561*94f69966SJacob Pan 					cdev_state);
562*94f69966SJacob Pan 		}
563*94f69966SJacob Pan 	}
564*94f69966SJacob Pan }
565*94f69966SJacob Pan 
566*94f69966SJacob Pan void get_ctrl_state(unsigned long *state)
567*94f69966SJacob Pan {
568*94f69966SJacob Pan 	char ctrl_cdev_path[256];
569*94f69966SJacob Pan 	int ctrl_cdev_id = -1;
570*94f69966SJacob Pan 	int i;
571*94f69966SJacob Pan 
572*94f69966SJacob Pan 	/* TODO: take average of all ctrl types. also consider change based on
573*94f69966SJacob Pan 	 * uevent. Take the first reading for now.
574*94f69966SJacob Pan 	 */
575*94f69966SJacob Pan 	for (i = 0; i < ptdata.nr_cooling_dev; i++) {
576*94f69966SJacob Pan 		if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) {
577*94f69966SJacob Pan 			ctrl_cdev_id = ptdata.cdi[i].instance;
578*94f69966SJacob Pan 			syslog(LOG_INFO, "ctrl cdev %d get state\n",
579*94f69966SJacob Pan 				ptdata.cdi[i].instance);
580*94f69966SJacob Pan 			break;
581*94f69966SJacob Pan 		}
582*94f69966SJacob Pan 	}
583*94f69966SJacob Pan 	if (ctrl_cdev_id == -1) {
584*94f69966SJacob Pan 		*state = 0;
585*94f69966SJacob Pan 		return;
586*94f69966SJacob Pan 	}
587*94f69966SJacob Pan 	snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS,
588*94f69966SJacob Pan 		CDEV, ctrl_cdev_id);
589*94f69966SJacob Pan 	sysfs_get_ulong(ctrl_cdev_path, "cur_state", state);
590*94f69966SJacob Pan }
591*94f69966SJacob Pan 
592*94f69966SJacob Pan void free_thermal_data(void)
593*94f69966SJacob Pan {
594*94f69966SJacob Pan 	free(ptdata.tzi);
595*94f69966SJacob Pan 	free(ptdata.cdi);
596*94f69966SJacob Pan }
597