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