18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * sysfs.c sysfs ABI access functions for TMON program
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Intel Corporation. All rights reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Jacob Pan <jacob.jun.pan@linux.intel.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <unistd.h>
108c2ecf20Sopenharmony_ci#include <stdio.h>
118c2ecf20Sopenharmony_ci#include <stdlib.h>
128c2ecf20Sopenharmony_ci#include <string.h>
138c2ecf20Sopenharmony_ci#include <stdint.h>
148c2ecf20Sopenharmony_ci#include <dirent.h>
158c2ecf20Sopenharmony_ci#include <libintl.h>
168c2ecf20Sopenharmony_ci#include <limits.h>
178c2ecf20Sopenharmony_ci#include <ctype.h>
188c2ecf20Sopenharmony_ci#include <time.h>
198c2ecf20Sopenharmony_ci#include <syslog.h>
208c2ecf20Sopenharmony_ci#include <sys/time.h>
218c2ecf20Sopenharmony_ci#include <errno.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "tmon.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct tmon_platform_data ptdata;
268c2ecf20Sopenharmony_ciconst char *trip_type_name[] = {
278c2ecf20Sopenharmony_ci	"critical",
288c2ecf20Sopenharmony_ci	"hot",
298c2ecf20Sopenharmony_ci	"passive",
308c2ecf20Sopenharmony_ci	"active",
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ciint sysfs_set_ulong(char *path, char *filename, unsigned long val)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	FILE *fd;
368c2ecf20Sopenharmony_ci	int ret = -1;
378c2ecf20Sopenharmony_ci	char filepath[PATH_MAX + 2]; /* NUL and '/' */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	snprintf(filepath, sizeof(filepath), "%s/%s", path, filename);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	fd = fopen(filepath, "w");
428c2ecf20Sopenharmony_ci	if (!fd) {
438c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
448c2ecf20Sopenharmony_ci		return ret;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci	ret = fprintf(fd, "%lu", val);
478c2ecf20Sopenharmony_ci	fclose(fd);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	return 0;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* history of thermal data, used for control algo */
538c2ecf20Sopenharmony_ci#define NR_THERMAL_RECORDS 3
548c2ecf20Sopenharmony_cistruct thermal_data_record trec[NR_THERMAL_RECORDS];
558c2ecf20Sopenharmony_ciint cur_thermal_record; /* index to the trec array */
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int sysfs_get_ulong(char *path, char *filename, unsigned long *p_ulong)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	FILE *fd;
608c2ecf20Sopenharmony_ci	int ret = -1;
618c2ecf20Sopenharmony_ci	char filepath[PATH_MAX + 2]; /* NUL and '/' */
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	snprintf(filepath, sizeof(filepath), "%s/%s", path, filename);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	fd = fopen(filepath, "r");
668c2ecf20Sopenharmony_ci	if (!fd) {
678c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
688c2ecf20Sopenharmony_ci		return ret;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	ret = fscanf(fd, "%lu", p_ulong);
718c2ecf20Sopenharmony_ci	fclose(fd);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int sysfs_get_string(char *path, char *filename, char *str)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	FILE *fd;
798c2ecf20Sopenharmony_ci	int ret = -1;
808c2ecf20Sopenharmony_ci	char filepath[PATH_MAX + 2]; /* NUL and '/' */
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	snprintf(filepath, sizeof(filepath), "%s/%s", path, filename);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	fd = fopen(filepath, "r");
858c2ecf20Sopenharmony_ci	if (!fd) {
868c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
878c2ecf20Sopenharmony_ci		return ret;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	ret = fscanf(fd, "%256s", str);
908c2ecf20Sopenharmony_ci	fclose(fd);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return ret;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* get states of the cooling device instance */
968c2ecf20Sopenharmony_cistatic int probe_cdev(struct cdev_info *cdi, char *path)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	sysfs_get_string(path, "type", cdi->type);
998c2ecf20Sopenharmony_ci	sysfs_get_ulong(path, "max_state",  &cdi->max_state);
1008c2ecf20Sopenharmony_ci	sysfs_get_ulong(path, "cur_state", &cdi->cur_state);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	syslog(LOG_INFO, "%s: %s: type %s, max %lu, curr %lu inst %d\n",
1038c2ecf20Sopenharmony_ci		__func__, path,
1048c2ecf20Sopenharmony_ci		cdi->type, cdi->max_state, cdi->cur_state, cdi->instance);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int str_to_trip_type(char *name)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	int i;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	for (i = 0; i < NR_THERMAL_TRIP_TYPE; i++) {
1148c2ecf20Sopenharmony_ci		if (!strcmp(name, trip_type_name[i]))
1158c2ecf20Sopenharmony_ci			return i;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return -ENOENT;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/* scan and fill in trip point info for a thermal zone and trip point id */
1228c2ecf20Sopenharmony_cistatic int get_trip_point_data(char *tz_path, int tzid, int tpid)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	char filename[256];
1258c2ecf20Sopenharmony_ci	char temp_str[256];
1268c2ecf20Sopenharmony_ci	int trip_type;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (tpid >= MAX_NR_TRIP)
1298c2ecf20Sopenharmony_ci		return -EINVAL;
1308c2ecf20Sopenharmony_ci	/* check trip point type */
1318c2ecf20Sopenharmony_ci	snprintf(filename, sizeof(filename), "trip_point_%d_type", tpid);
1328c2ecf20Sopenharmony_ci	sysfs_get_string(tz_path, filename, temp_str);
1338c2ecf20Sopenharmony_ci	trip_type = str_to_trip_type(temp_str);
1348c2ecf20Sopenharmony_ci	if (trip_type < 0) {
1358c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "%s:%s no matching type\n", __func__, temp_str);
1368c2ecf20Sopenharmony_ci		return -ENOENT;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci	ptdata.tzi[tzid].tp[tpid].type = trip_type;
1398c2ecf20Sopenharmony_ci	syslog(LOG_INFO, "%s:tz:%d tp:%d:type:%s type id %d\n", __func__, tzid,
1408c2ecf20Sopenharmony_ci		tpid, temp_str, trip_type);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* TODO: check attribute */
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/* return instance id for file format such as trip_point_4_temp */
1488c2ecf20Sopenharmony_cistatic int get_instance_id(char *name, int pos, int skip)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	char *ch;
1518c2ecf20Sopenharmony_ci	int i = 0;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ch = strtok(name, "_");
1548c2ecf20Sopenharmony_ci	while (ch != NULL) {
1558c2ecf20Sopenharmony_ci		++i;
1568c2ecf20Sopenharmony_ci		syslog(LOG_INFO, "%s:%s:%s:%d", __func__, name, ch, i);
1578c2ecf20Sopenharmony_ci		ch = strtok(NULL, "_");
1588c2ecf20Sopenharmony_ci		if (pos == i)
1598c2ecf20Sopenharmony_ci			return atol(ch + skip);
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return -1;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/* Find trip point info of a thermal zone */
1668c2ecf20Sopenharmony_cistatic int find_tzone_tp(char *tz_name, char *d_name, struct tz_info *tzi,
1678c2ecf20Sopenharmony_ci			int tz_id)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	int tp_id;
1708c2ecf20Sopenharmony_ci	unsigned long temp_ulong;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (strstr(d_name, "trip_point") &&
1738c2ecf20Sopenharmony_ci		strstr(d_name, "temp")) {
1748c2ecf20Sopenharmony_ci		/* check if trip point temp is non-zero
1758c2ecf20Sopenharmony_ci		 * ignore 0/invalid trip points
1768c2ecf20Sopenharmony_ci		 */
1778c2ecf20Sopenharmony_ci		sysfs_get_ulong(tz_name, d_name, &temp_ulong);
1788c2ecf20Sopenharmony_ci		if (temp_ulong < MAX_TEMP_KC) {
1798c2ecf20Sopenharmony_ci			tzi->nr_trip_pts++;
1808c2ecf20Sopenharmony_ci			/* found a valid trip point */
1818c2ecf20Sopenharmony_ci			tp_id = get_instance_id(d_name, 2, 0);
1828c2ecf20Sopenharmony_ci			syslog(LOG_DEBUG, "tzone %s trip %d temp %lu tpnode %s",
1838c2ecf20Sopenharmony_ci				tz_name, tp_id, temp_ulong, d_name);
1848c2ecf20Sopenharmony_ci			if (tp_id < 0 || tp_id >= MAX_NR_TRIP) {
1858c2ecf20Sopenharmony_ci				syslog(LOG_ERR, "Failed to find TP inst %s\n",
1868c2ecf20Sopenharmony_ci					d_name);
1878c2ecf20Sopenharmony_ci				return -1;
1888c2ecf20Sopenharmony_ci			}
1898c2ecf20Sopenharmony_ci			get_trip_point_data(tz_name, tz_id, tp_id);
1908c2ecf20Sopenharmony_ci			tzi->tp[tp_id].temp = temp_ulong;
1918c2ecf20Sopenharmony_ci		}
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/* check cooling devices for binding info. */
1988c2ecf20Sopenharmony_cistatic int find_tzone_cdev(struct dirent *nl, char *tz_name,
1998c2ecf20Sopenharmony_ci			struct tz_info *tzi, int tz_id, int cid)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	unsigned long trip_instance = 0;
2028c2ecf20Sopenharmony_ci	char cdev_name_linked[256];
2038c2ecf20Sopenharmony_ci	char cdev_name[PATH_MAX];
2048c2ecf20Sopenharmony_ci	char cdev_trip_name[PATH_MAX];
2058c2ecf20Sopenharmony_ci	int cdev_id;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (nl->d_type == DT_LNK) {
2088c2ecf20Sopenharmony_ci		syslog(LOG_DEBUG, "TZ%d: cdev: %s cid %d\n", tz_id, nl->d_name,
2098c2ecf20Sopenharmony_ci			cid);
2108c2ecf20Sopenharmony_ci		tzi->nr_cdev++;
2118c2ecf20Sopenharmony_ci		if (tzi->nr_cdev > ptdata.nr_cooling_dev) {
2128c2ecf20Sopenharmony_ci			syslog(LOG_ERR, "Err: Too many cdev? %d\n",
2138c2ecf20Sopenharmony_ci				tzi->nr_cdev);
2148c2ecf20Sopenharmony_ci			return -EINVAL;
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci		/* find the link to real cooling device record binding */
2178c2ecf20Sopenharmony_ci		snprintf(cdev_name, sizeof(cdev_name) - 2, "%s/%s",
2188c2ecf20Sopenharmony_ci			 tz_name, nl->d_name);
2198c2ecf20Sopenharmony_ci		memset(cdev_name_linked, 0, sizeof(cdev_name_linked));
2208c2ecf20Sopenharmony_ci		if (readlink(cdev_name, cdev_name_linked,
2218c2ecf20Sopenharmony_ci				sizeof(cdev_name_linked) - 1) != -1) {
2228c2ecf20Sopenharmony_ci			cdev_id = get_instance_id(cdev_name_linked, 1,
2238c2ecf20Sopenharmony_ci						sizeof("device") - 1);
2248c2ecf20Sopenharmony_ci			syslog(LOG_DEBUG, "cdev %s linked to %s : %d\n",
2258c2ecf20Sopenharmony_ci				cdev_name, cdev_name_linked, cdev_id);
2268c2ecf20Sopenharmony_ci			tzi->cdev_binding |= (1 << cdev_id);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci			/* find the trip point in which the cdev is binded to
2298c2ecf20Sopenharmony_ci			 * in this tzone
2308c2ecf20Sopenharmony_ci			 */
2318c2ecf20Sopenharmony_ci			snprintf(cdev_trip_name, sizeof(cdev_trip_name) - 1,
2328c2ecf20Sopenharmony_ci				"%s%s", nl->d_name, "_trip_point");
2338c2ecf20Sopenharmony_ci			sysfs_get_ulong(tz_name, cdev_trip_name,
2348c2ecf20Sopenharmony_ci					&trip_instance);
2358c2ecf20Sopenharmony_ci			/* validate trip point range, e.g. trip could return -1
2368c2ecf20Sopenharmony_ci			 * when passive is enabled
2378c2ecf20Sopenharmony_ci			 */
2388c2ecf20Sopenharmony_ci			if (trip_instance > MAX_NR_TRIP)
2398c2ecf20Sopenharmony_ci				trip_instance = 0;
2408c2ecf20Sopenharmony_ci			tzi->trip_binding[cdev_id] |= 1 << trip_instance;
2418c2ecf20Sopenharmony_ci			syslog(LOG_DEBUG, "cdev %s -> trip:%lu: 0x%lx %d\n",
2428c2ecf20Sopenharmony_ci				cdev_name, trip_instance,
2438c2ecf20Sopenharmony_ci				tzi->trip_binding[cdev_id],
2448c2ecf20Sopenharmony_ci				cdev_id);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		}
2488c2ecf20Sopenharmony_ci		return 0;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return -ENODEV;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci/*****************************************************************************
2578c2ecf20Sopenharmony_ci * Before calling scan_tzones, thermal sysfs must be probed to determine
2588c2ecf20Sopenharmony_ci * the number of thermal zones and cooling devices.
2598c2ecf20Sopenharmony_ci * We loop through each thermal zone and fill in tz_info struct, i.e.
2608c2ecf20Sopenharmony_ci * ptdata.tzi[]
2618c2ecf20Sopenharmony_ciroot@jacob-chiefriver:~# tree -d /sys/class/thermal/thermal_zone0
2628c2ecf20Sopenharmony_ci/sys/class/thermal/thermal_zone0
2638c2ecf20Sopenharmony_ci|-- cdev0 -> ../cooling_device4
2648c2ecf20Sopenharmony_ci|-- cdev1 -> ../cooling_device3
2658c2ecf20Sopenharmony_ci|-- cdev10 -> ../cooling_device7
2668c2ecf20Sopenharmony_ci|-- cdev11 -> ../cooling_device6
2678c2ecf20Sopenharmony_ci|-- cdev12 -> ../cooling_device5
2688c2ecf20Sopenharmony_ci|-- cdev2 -> ../cooling_device2
2698c2ecf20Sopenharmony_ci|-- cdev3 -> ../cooling_device1
2708c2ecf20Sopenharmony_ci|-- cdev4 -> ../cooling_device0
2718c2ecf20Sopenharmony_ci|-- cdev5 -> ../cooling_device12
2728c2ecf20Sopenharmony_ci|-- cdev6 -> ../cooling_device11
2738c2ecf20Sopenharmony_ci|-- cdev7 -> ../cooling_device10
2748c2ecf20Sopenharmony_ci|-- cdev8 -> ../cooling_device9
2758c2ecf20Sopenharmony_ci|-- cdev9 -> ../cooling_device8
2768c2ecf20Sopenharmony_ci|-- device -> ../../../LNXSYSTM:00/device:62/LNXTHERM:00
2778c2ecf20Sopenharmony_ci|-- power
2788c2ecf20Sopenharmony_ci`-- subsystem -> ../../../../class/thermal
2798c2ecf20Sopenharmony_ci*****************************************************************************/
2808c2ecf20Sopenharmony_cistatic int scan_tzones(void)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	DIR *dir;
2838c2ecf20Sopenharmony_ci	struct dirent **namelist;
2848c2ecf20Sopenharmony_ci	char tz_name[256];
2858c2ecf20Sopenharmony_ci	int i, j, n, k = 0;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (!ptdata.nr_tz_sensor)
2888c2ecf20Sopenharmony_ci		return -1;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	for (i = 0; i <= ptdata.max_tz_instance; i++) {
2918c2ecf20Sopenharmony_ci		memset(tz_name, 0, sizeof(tz_name));
2928c2ecf20Sopenharmony_ci		snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE, i);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		dir = opendir(tz_name);
2958c2ecf20Sopenharmony_ci		if (!dir) {
2968c2ecf20Sopenharmony_ci			syslog(LOG_INFO, "Thermal zone %s skipped\n", tz_name);
2978c2ecf20Sopenharmony_ci			continue;
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci		/* keep track of valid tzones */
3008c2ecf20Sopenharmony_ci		n = scandir(tz_name, &namelist, 0, alphasort);
3018c2ecf20Sopenharmony_ci		if (n < 0)
3028c2ecf20Sopenharmony_ci			syslog(LOG_ERR, "scandir failed in %s",  tz_name);
3038c2ecf20Sopenharmony_ci		else {
3048c2ecf20Sopenharmony_ci			sysfs_get_string(tz_name, "type", ptdata.tzi[k].type);
3058c2ecf20Sopenharmony_ci			ptdata.tzi[k].instance = i;
3068c2ecf20Sopenharmony_ci			/* detect trip points and cdev attached to this tzone */
3078c2ecf20Sopenharmony_ci			j = 0; /* index for cdev */
3088c2ecf20Sopenharmony_ci			ptdata.tzi[k].nr_cdev = 0;
3098c2ecf20Sopenharmony_ci			ptdata.tzi[k].nr_trip_pts = 0;
3108c2ecf20Sopenharmony_ci			while (n--) {
3118c2ecf20Sopenharmony_ci				char *temp_str;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci				if (find_tzone_tp(tz_name, namelist[n]->d_name,
3148c2ecf20Sopenharmony_ci							&ptdata.tzi[k], k))
3158c2ecf20Sopenharmony_ci					break;
3168c2ecf20Sopenharmony_ci				temp_str = strstr(namelist[n]->d_name, "cdev");
3178c2ecf20Sopenharmony_ci				if (!temp_str) {
3188c2ecf20Sopenharmony_ci					free(namelist[n]);
3198c2ecf20Sopenharmony_ci					continue;
3208c2ecf20Sopenharmony_ci				}
3218c2ecf20Sopenharmony_ci				if (!find_tzone_cdev(namelist[n], tz_name,
3228c2ecf20Sopenharmony_ci							&ptdata.tzi[k], i, j))
3238c2ecf20Sopenharmony_ci					j++; /* increment cdev index */
3248c2ecf20Sopenharmony_ci				free(namelist[n]);
3258c2ecf20Sopenharmony_ci			}
3268c2ecf20Sopenharmony_ci			free(namelist);
3278c2ecf20Sopenharmony_ci		}
3288c2ecf20Sopenharmony_ci		/*TODO: reverse trip points */
3298c2ecf20Sopenharmony_ci		closedir(dir);
3308c2ecf20Sopenharmony_ci		syslog(LOG_INFO, "TZ %d has %d cdev\n",	i,
3318c2ecf20Sopenharmony_ci			ptdata.tzi[k].nr_cdev);
3328c2ecf20Sopenharmony_ci		k++;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	return 0;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic int scan_cdevs(void)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	DIR *dir;
3418c2ecf20Sopenharmony_ci	struct dirent **namelist;
3428c2ecf20Sopenharmony_ci	char cdev_name[256];
3438c2ecf20Sopenharmony_ci	int i, n, k = 0;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (!ptdata.nr_cooling_dev) {
3468c2ecf20Sopenharmony_ci		fprintf(stderr, "No cooling devices found\n");
3478c2ecf20Sopenharmony_ci		return 0;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci	for (i = 0; i <= ptdata.max_cdev_instance; i++) {
3508c2ecf20Sopenharmony_ci		memset(cdev_name, 0, sizeof(cdev_name));
3518c2ecf20Sopenharmony_ci		snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV, i);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		dir = opendir(cdev_name);
3548c2ecf20Sopenharmony_ci		if (!dir) {
3558c2ecf20Sopenharmony_ci			syslog(LOG_INFO, "Cooling dev %s skipped\n", cdev_name);
3568c2ecf20Sopenharmony_ci			/* there is a gap in cooling device id, check again
3578c2ecf20Sopenharmony_ci			 * for the same index.
3588c2ecf20Sopenharmony_ci			 */
3598c2ecf20Sopenharmony_ci			continue;
3608c2ecf20Sopenharmony_ci		}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		n = scandir(cdev_name, &namelist, 0, alphasort);
3638c2ecf20Sopenharmony_ci		if (n < 0)
3648c2ecf20Sopenharmony_ci			syslog(LOG_ERR, "scandir failed in %s",  cdev_name);
3658c2ecf20Sopenharmony_ci		else {
3668c2ecf20Sopenharmony_ci			sysfs_get_string(cdev_name, "type", ptdata.cdi[k].type);
3678c2ecf20Sopenharmony_ci			ptdata.cdi[k].instance = i;
3688c2ecf20Sopenharmony_ci			if (strstr(ptdata.cdi[k].type, ctrl_cdev)) {
3698c2ecf20Sopenharmony_ci				ptdata.cdi[k].flag |= CDEV_FLAG_IN_CONTROL;
3708c2ecf20Sopenharmony_ci				syslog(LOG_DEBUG, "control cdev id %d\n", i);
3718c2ecf20Sopenharmony_ci			}
3728c2ecf20Sopenharmony_ci			while (n--)
3738c2ecf20Sopenharmony_ci				free(namelist[n]);
3748c2ecf20Sopenharmony_ci			free(namelist);
3758c2ecf20Sopenharmony_ci		}
3768c2ecf20Sopenharmony_ci		closedir(dir);
3778c2ecf20Sopenharmony_ci		k++;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci	return 0;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ciint probe_thermal_sysfs(void)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	DIR *dir;
3868c2ecf20Sopenharmony_ci	struct dirent **namelist;
3878c2ecf20Sopenharmony_ci	int n;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	dir = opendir(THERMAL_SYSFS);
3908c2ecf20Sopenharmony_ci	if (!dir) {
3918c2ecf20Sopenharmony_ci		fprintf(stderr, "\nNo thermal sysfs, exit\n");
3928c2ecf20Sopenharmony_ci		return -1;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci	n = scandir(THERMAL_SYSFS, &namelist, 0, alphasort);
3958c2ecf20Sopenharmony_ci	if (n < 0)
3968c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "scandir failed in thermal sysfs");
3978c2ecf20Sopenharmony_ci	else {
3988c2ecf20Sopenharmony_ci		/* detect number of thermal zones and cooling devices */
3998c2ecf20Sopenharmony_ci		while (n--) {
4008c2ecf20Sopenharmony_ci			int inst;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci			if (strstr(namelist[n]->d_name, CDEV)) {
4038c2ecf20Sopenharmony_ci				inst = get_instance_id(namelist[n]->d_name, 1,
4048c2ecf20Sopenharmony_ci						sizeof("device") - 1);
4058c2ecf20Sopenharmony_ci				/* keep track of the max cooling device since
4068c2ecf20Sopenharmony_ci				 * there may be gaps.
4078c2ecf20Sopenharmony_ci				 */
4088c2ecf20Sopenharmony_ci				if (inst > ptdata.max_cdev_instance)
4098c2ecf20Sopenharmony_ci					ptdata.max_cdev_instance = inst;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci				syslog(LOG_DEBUG, "found cdev: %s %d %d\n",
4128c2ecf20Sopenharmony_ci					namelist[n]->d_name,
4138c2ecf20Sopenharmony_ci					ptdata.nr_cooling_dev,
4148c2ecf20Sopenharmony_ci					ptdata.max_cdev_instance);
4158c2ecf20Sopenharmony_ci				ptdata.nr_cooling_dev++;
4168c2ecf20Sopenharmony_ci			} else if (strstr(namelist[n]->d_name, TZONE)) {
4178c2ecf20Sopenharmony_ci				inst = get_instance_id(namelist[n]->d_name, 1,
4188c2ecf20Sopenharmony_ci						sizeof("zone") - 1);
4198c2ecf20Sopenharmony_ci				if (inst > ptdata.max_tz_instance)
4208c2ecf20Sopenharmony_ci					ptdata.max_tz_instance = inst;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci				syslog(LOG_DEBUG, "found tzone: %s %d %d\n",
4238c2ecf20Sopenharmony_ci					namelist[n]->d_name,
4248c2ecf20Sopenharmony_ci					ptdata.nr_tz_sensor,
4258c2ecf20Sopenharmony_ci					ptdata.max_tz_instance);
4268c2ecf20Sopenharmony_ci				ptdata.nr_tz_sensor++;
4278c2ecf20Sopenharmony_ci			}
4288c2ecf20Sopenharmony_ci			free(namelist[n]);
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci		free(namelist);
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci	syslog(LOG_INFO, "found %d tzone(s), %d cdev(s), target zone %d\n",
4338c2ecf20Sopenharmony_ci		ptdata.nr_tz_sensor, ptdata.nr_cooling_dev,
4348c2ecf20Sopenharmony_ci		target_thermal_zone);
4358c2ecf20Sopenharmony_ci	closedir(dir);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (!ptdata.nr_tz_sensor) {
4388c2ecf20Sopenharmony_ci		fprintf(stderr, "\nNo thermal zones found, exit\n\n");
4398c2ecf20Sopenharmony_ci		return -1;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	ptdata.tzi = calloc(ptdata.max_tz_instance+1, sizeof(struct tz_info));
4438c2ecf20Sopenharmony_ci	if (!ptdata.tzi) {
4448c2ecf20Sopenharmony_ci		fprintf(stderr, "Err: allocate tz_info\n");
4458c2ecf20Sopenharmony_ci		return -1;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* we still show thermal zone information if there is no cdev */
4498c2ecf20Sopenharmony_ci	if (ptdata.nr_cooling_dev) {
4508c2ecf20Sopenharmony_ci		ptdata.cdi = calloc(ptdata.max_cdev_instance + 1,
4518c2ecf20Sopenharmony_ci				sizeof(struct cdev_info));
4528c2ecf20Sopenharmony_ci		if (!ptdata.cdi) {
4538c2ecf20Sopenharmony_ci			free(ptdata.tzi);
4548c2ecf20Sopenharmony_ci			fprintf(stderr, "Err: allocate cdev_info\n");
4558c2ecf20Sopenharmony_ci			return -1;
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* now probe tzones */
4608c2ecf20Sopenharmony_ci	if (scan_tzones())
4618c2ecf20Sopenharmony_ci		return -1;
4628c2ecf20Sopenharmony_ci	if (scan_cdevs())
4638c2ecf20Sopenharmony_ci		return -1;
4648c2ecf20Sopenharmony_ci	return 0;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci/* convert sysfs zone instance to zone array index */
4688c2ecf20Sopenharmony_ciint zone_instance_to_index(int zone_inst)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	int i;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	for (i = 0; i < ptdata.nr_tz_sensor; i++)
4738c2ecf20Sopenharmony_ci		if (ptdata.tzi[i].instance == zone_inst)
4748c2ecf20Sopenharmony_ci			return i;
4758c2ecf20Sopenharmony_ci	return -ENOENT;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci/* read temperature of all thermal zones */
4798c2ecf20Sopenharmony_ciint update_thermal_data()
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	int i;
4828c2ecf20Sopenharmony_ci	int next_thermal_record = cur_thermal_record + 1;
4838c2ecf20Sopenharmony_ci	char tz_name[256];
4848c2ecf20Sopenharmony_ci	static unsigned long samples;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (!ptdata.nr_tz_sensor) {
4878c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "No thermal zones found!\n");
4888c2ecf20Sopenharmony_ci		return -1;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* circular buffer for keeping historic data */
4928c2ecf20Sopenharmony_ci	if (next_thermal_record >= NR_THERMAL_RECORDS)
4938c2ecf20Sopenharmony_ci		next_thermal_record = 0;
4948c2ecf20Sopenharmony_ci	gettimeofday(&trec[next_thermal_record].tv, NULL);
4958c2ecf20Sopenharmony_ci	if (tmon_log) {
4968c2ecf20Sopenharmony_ci		fprintf(tmon_log, "%lu ", ++samples);
4978c2ecf20Sopenharmony_ci		fprintf(tmon_log, "%3.1f ", p_param.t_target);
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci	for (i = 0; i < ptdata.nr_tz_sensor; i++) {
5008c2ecf20Sopenharmony_ci		memset(tz_name, 0, sizeof(tz_name));
5018c2ecf20Sopenharmony_ci		snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE,
5028c2ecf20Sopenharmony_ci			ptdata.tzi[i].instance);
5038c2ecf20Sopenharmony_ci		sysfs_get_ulong(tz_name, "temp",
5048c2ecf20Sopenharmony_ci				&trec[next_thermal_record].temp[i]);
5058c2ecf20Sopenharmony_ci		if (tmon_log)
5068c2ecf20Sopenharmony_ci			fprintf(tmon_log, "%lu ",
5078c2ecf20Sopenharmony_ci				trec[next_thermal_record].temp[i] / 1000);
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci	cur_thermal_record = next_thermal_record;
5108c2ecf20Sopenharmony_ci	for (i = 0; i < ptdata.nr_cooling_dev; i++) {
5118c2ecf20Sopenharmony_ci		char cdev_name[256];
5128c2ecf20Sopenharmony_ci		unsigned long val;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci		snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV,
5158c2ecf20Sopenharmony_ci			ptdata.cdi[i].instance);
5168c2ecf20Sopenharmony_ci		probe_cdev(&ptdata.cdi[i], cdev_name);
5178c2ecf20Sopenharmony_ci		val = ptdata.cdi[i].cur_state;
5188c2ecf20Sopenharmony_ci		if (val > 1000000)
5198c2ecf20Sopenharmony_ci			val = 0;
5208c2ecf20Sopenharmony_ci		if (tmon_log)
5218c2ecf20Sopenharmony_ci			fprintf(tmon_log, "%lu ", val);
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	if (tmon_log) {
5258c2ecf20Sopenharmony_ci		fprintf(tmon_log, "\n");
5268c2ecf20Sopenharmony_ci		fflush(tmon_log);
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	return 0;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_civoid set_ctrl_state(unsigned long state)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	char ctrl_cdev_path[256];
5358c2ecf20Sopenharmony_ci	int i;
5368c2ecf20Sopenharmony_ci	unsigned long cdev_state;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	if (no_control)
5398c2ecf20Sopenharmony_ci		return;
5408c2ecf20Sopenharmony_ci	/* set all ctrl cdev to the same state */
5418c2ecf20Sopenharmony_ci	for (i = 0; i < ptdata.nr_cooling_dev; i++) {
5428c2ecf20Sopenharmony_ci		if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) {
5438c2ecf20Sopenharmony_ci			if (ptdata.cdi[i].max_state < 10) {
5448c2ecf20Sopenharmony_ci				strcpy(ctrl_cdev, "None.");
5458c2ecf20Sopenharmony_ci				return;
5468c2ecf20Sopenharmony_ci			}
5478c2ecf20Sopenharmony_ci			/* scale to percentage of max_state */
5488c2ecf20Sopenharmony_ci			cdev_state = state * ptdata.cdi[i].max_state/100;
5498c2ecf20Sopenharmony_ci			syslog(LOG_DEBUG,
5508c2ecf20Sopenharmony_ci				"ctrl cdev %d set state %lu scaled to %lu\n",
5518c2ecf20Sopenharmony_ci				ptdata.cdi[i].instance, state, cdev_state);
5528c2ecf20Sopenharmony_ci			snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS,
5538c2ecf20Sopenharmony_ci				CDEV, ptdata.cdi[i].instance);
5548c2ecf20Sopenharmony_ci			syslog(LOG_DEBUG, "ctrl cdev path %s", ctrl_cdev_path);
5558c2ecf20Sopenharmony_ci			sysfs_set_ulong(ctrl_cdev_path, "cur_state",
5568c2ecf20Sopenharmony_ci					cdev_state);
5578c2ecf20Sopenharmony_ci		}
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_civoid get_ctrl_state(unsigned long *state)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	char ctrl_cdev_path[256];
5648c2ecf20Sopenharmony_ci	int ctrl_cdev_id = -1;
5658c2ecf20Sopenharmony_ci	int i;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* TODO: take average of all ctrl types. also consider change based on
5688c2ecf20Sopenharmony_ci	 * uevent. Take the first reading for now.
5698c2ecf20Sopenharmony_ci	 */
5708c2ecf20Sopenharmony_ci	for (i = 0; i < ptdata.nr_cooling_dev; i++) {
5718c2ecf20Sopenharmony_ci		if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) {
5728c2ecf20Sopenharmony_ci			ctrl_cdev_id = ptdata.cdi[i].instance;
5738c2ecf20Sopenharmony_ci			syslog(LOG_INFO, "ctrl cdev %d get state\n",
5748c2ecf20Sopenharmony_ci				ptdata.cdi[i].instance);
5758c2ecf20Sopenharmony_ci			break;
5768c2ecf20Sopenharmony_ci		}
5778c2ecf20Sopenharmony_ci	}
5788c2ecf20Sopenharmony_ci	if (ctrl_cdev_id == -1) {
5798c2ecf20Sopenharmony_ci		*state = 0;
5808c2ecf20Sopenharmony_ci		return;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci	snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS,
5838c2ecf20Sopenharmony_ci		CDEV, ctrl_cdev_id);
5848c2ecf20Sopenharmony_ci	sysfs_get_ulong(ctrl_cdev_path, "cur_state", state);
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_civoid free_thermal_data(void)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	free(ptdata.tzi);
5908c2ecf20Sopenharmony_ci	free(ptdata.cdi);
5918c2ecf20Sopenharmony_ci}
592