162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> 462306a36Sopenharmony_ci * (C) 2011 Thomas Renninger <trenn@novell.com> Novell Inc. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <stdio.h> 862306a36Sopenharmony_ci#include <errno.h> 962306a36Sopenharmony_ci#include <stdlib.h> 1062306a36Sopenharmony_ci#include <string.h> 1162306a36Sopenharmony_ci#include <sys/types.h> 1262306a36Sopenharmony_ci#include <sys/stat.h> 1362306a36Sopenharmony_ci#include <fcntl.h> 1462306a36Sopenharmony_ci#include <unistd.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "cpuidle.h" 1762306a36Sopenharmony_ci#include "cpupower_intern.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir 2162306a36Sopenharmony_ci * exists. 2262306a36Sopenharmony_ci * For example the functionality to disable c-states was introduced in later 2362306a36Sopenharmony_ci * kernel versions, this function can be used to explicitly check for this 2462306a36Sopenharmony_ci * feature. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * returns 1 if the file exists, 0 otherwise. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistatic 2962306a36Sopenharmony_ciunsigned int cpuidle_state_file_exists(unsigned int cpu, 3062306a36Sopenharmony_ci unsigned int idlestate, 3162306a36Sopenharmony_ci const char *fname) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci char path[SYSFS_PATH_MAX]; 3462306a36Sopenharmony_ci struct stat statbuf; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 3862306a36Sopenharmony_ci cpu, idlestate, fname); 3962306a36Sopenharmony_ci if (stat(path, &statbuf) != 0) 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci return 1; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * helper function to read file from /sys into given buffer 4662306a36Sopenharmony_ci * fname is a relative path under "cpuX/cpuidle/stateX/" dir 4762306a36Sopenharmony_ci * cstates starting with 0, C0 is not counted as cstate. 4862306a36Sopenharmony_ci * This means if you want C1 info, pass 0 as idlestate param 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistatic 5162306a36Sopenharmony_ciunsigned int cpuidle_state_read_file(unsigned int cpu, 5262306a36Sopenharmony_ci unsigned int idlestate, 5362306a36Sopenharmony_ci const char *fname, char *buf, 5462306a36Sopenharmony_ci size_t buflen) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci char path[SYSFS_PATH_MAX]; 5762306a36Sopenharmony_ci int fd; 5862306a36Sopenharmony_ci ssize_t numread; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 6162306a36Sopenharmony_ci cpu, idlestate, fname); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci fd = open(path, O_RDONLY); 6462306a36Sopenharmony_ci if (fd == -1) 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci numread = read(fd, buf, buflen - 1); 6862306a36Sopenharmony_ci if (numread < 1) { 6962306a36Sopenharmony_ci close(fd); 7062306a36Sopenharmony_ci return 0; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci buf[numread] = '\0'; 7462306a36Sopenharmony_ci close(fd); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return (unsigned int) numread; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * helper function to write a new value to a /sys file 8162306a36Sopenharmony_ci * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * Returns the number of bytes written or 0 on error 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistatic 8662306a36Sopenharmony_ciunsigned int cpuidle_state_write_file(unsigned int cpu, 8762306a36Sopenharmony_ci unsigned int idlestate, 8862306a36Sopenharmony_ci const char *fname, 8962306a36Sopenharmony_ci const char *value, size_t len) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci char path[SYSFS_PATH_MAX]; 9262306a36Sopenharmony_ci int fd; 9362306a36Sopenharmony_ci ssize_t numwrite; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 9662306a36Sopenharmony_ci cpu, idlestate, fname); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci fd = open(path, O_WRONLY); 9962306a36Sopenharmony_ci if (fd == -1) 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci numwrite = write(fd, value, len); 10362306a36Sopenharmony_ci if (numwrite < 1) { 10462306a36Sopenharmony_ci close(fd); 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci close(fd); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return (unsigned int) numwrite; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* read access to files which contain one numeric value */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cienum idlestate_value { 11662306a36Sopenharmony_ci IDLESTATE_USAGE, 11762306a36Sopenharmony_ci IDLESTATE_POWER, 11862306a36Sopenharmony_ci IDLESTATE_LATENCY, 11962306a36Sopenharmony_ci IDLESTATE_TIME, 12062306a36Sopenharmony_ci IDLESTATE_DISABLE, 12162306a36Sopenharmony_ci MAX_IDLESTATE_VALUE_FILES 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = { 12562306a36Sopenharmony_ci [IDLESTATE_USAGE] = "usage", 12662306a36Sopenharmony_ci [IDLESTATE_POWER] = "power", 12762306a36Sopenharmony_ci [IDLESTATE_LATENCY] = "latency", 12862306a36Sopenharmony_ci [IDLESTATE_TIME] = "time", 12962306a36Sopenharmony_ci [IDLESTATE_DISABLE] = "disable", 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic 13362306a36Sopenharmony_ciunsigned long long cpuidle_state_get_one_value(unsigned int cpu, 13462306a36Sopenharmony_ci unsigned int idlestate, 13562306a36Sopenharmony_ci enum idlestate_value which) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci unsigned long long value; 13862306a36Sopenharmony_ci unsigned int len; 13962306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 14062306a36Sopenharmony_ci char *endp; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (which >= MAX_IDLESTATE_VALUE_FILES) 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci len = cpuidle_state_read_file(cpu, idlestate, 14662306a36Sopenharmony_ci idlestate_value_files[which], 14762306a36Sopenharmony_ci linebuf, sizeof(linebuf)); 14862306a36Sopenharmony_ci if (len == 0) 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci value = strtoull(linebuf, &endp, 0); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (endp == linebuf || errno == ERANGE) 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return value; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* read access to files which contain one string */ 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cienum idlestate_string { 16262306a36Sopenharmony_ci IDLESTATE_DESC, 16362306a36Sopenharmony_ci IDLESTATE_NAME, 16462306a36Sopenharmony_ci MAX_IDLESTATE_STRING_FILES 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { 16862306a36Sopenharmony_ci [IDLESTATE_DESC] = "desc", 16962306a36Sopenharmony_ci [IDLESTATE_NAME] = "name", 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic char *cpuidle_state_get_one_string(unsigned int cpu, 17462306a36Sopenharmony_ci unsigned int idlestate, 17562306a36Sopenharmony_ci enum idlestate_string which) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 17862306a36Sopenharmony_ci char *result; 17962306a36Sopenharmony_ci unsigned int len; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (which >= MAX_IDLESTATE_STRING_FILES) 18262306a36Sopenharmony_ci return NULL; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci len = cpuidle_state_read_file(cpu, idlestate, 18562306a36Sopenharmony_ci idlestate_string_files[which], 18662306a36Sopenharmony_ci linebuf, sizeof(linebuf)); 18762306a36Sopenharmony_ci if (len == 0) 18862306a36Sopenharmony_ci return NULL; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci result = strdup(linebuf); 19162306a36Sopenharmony_ci if (result == NULL) 19262306a36Sopenharmony_ci return NULL; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (result[strlen(result) - 1] == '\n') 19562306a36Sopenharmony_ci result[strlen(result) - 1] = '\0'; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return result; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* 20162306a36Sopenharmony_ci * Returns: 20262306a36Sopenharmony_ci * 1 if disabled 20362306a36Sopenharmony_ci * 0 if enabled 20462306a36Sopenharmony_ci * -1 if idlestate is not available 20562306a36Sopenharmony_ci * -2 if disabling is not supported by the kernel 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ciint cpuidle_is_state_disabled(unsigned int cpu, 20862306a36Sopenharmony_ci unsigned int idlestate) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci if (cpuidle_state_count(cpu) <= idlestate) 21162306a36Sopenharmony_ci return -1; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (!cpuidle_state_file_exists(cpu, idlestate, 21462306a36Sopenharmony_ci idlestate_value_files[IDLESTATE_DISABLE])) 21562306a36Sopenharmony_ci return -2; 21662306a36Sopenharmony_ci return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* 22062306a36Sopenharmony_ci * Pass 1 as last argument to disable or 0 to enable the state 22162306a36Sopenharmony_ci * Returns: 22262306a36Sopenharmony_ci * 0 on success 22362306a36Sopenharmony_ci * negative values on error, for example: 22462306a36Sopenharmony_ci * -1 if idlestate is not available 22562306a36Sopenharmony_ci * -2 if disabling is not supported by the kernel 22662306a36Sopenharmony_ci * -3 No write access to disable/enable C-states 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ciint cpuidle_state_disable(unsigned int cpu, 22962306a36Sopenharmony_ci unsigned int idlestate, 23062306a36Sopenharmony_ci unsigned int disable) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci char value[SYSFS_PATH_MAX]; 23362306a36Sopenharmony_ci int bytes_written; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (cpuidle_state_count(cpu) <= idlestate) 23662306a36Sopenharmony_ci return -1; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (!cpuidle_state_file_exists(cpu, idlestate, 23962306a36Sopenharmony_ci idlestate_value_files[IDLESTATE_DISABLE])) 24062306a36Sopenharmony_ci return -2; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci snprintf(value, SYSFS_PATH_MAX, "%u", disable); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable", 24562306a36Sopenharmony_ci value, sizeof(disable)); 24662306a36Sopenharmony_ci if (bytes_written) 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci return -3; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ciunsigned long cpuidle_state_latency(unsigned int cpu, 25262306a36Sopenharmony_ci unsigned int idlestate) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ciunsigned long cpuidle_state_usage(unsigned int cpu, 25862306a36Sopenharmony_ci unsigned int idlestate) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ciunsigned long long cpuidle_state_time(unsigned int cpu, 26462306a36Sopenharmony_ci unsigned int idlestate) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cichar *cpuidle_state_name(unsigned int cpu, unsigned int idlestate) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cichar *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* 28062306a36Sopenharmony_ci * Returns number of supported C-states of CPU core cpu 28162306a36Sopenharmony_ci * Negativ in error case 28262306a36Sopenharmony_ci * Zero if cpuidle does not export any C-states 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ciunsigned int cpuidle_state_count(unsigned int cpu) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci char file[SYSFS_PATH_MAX]; 28762306a36Sopenharmony_ci struct stat statbuf; 28862306a36Sopenharmony_ci int idlestates = 1; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle"); 29262306a36Sopenharmony_ci if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu); 29662306a36Sopenharmony_ci if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { 30062306a36Sopenharmony_ci snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU 30162306a36Sopenharmony_ci "cpu%u/cpuidle/state%d", cpu, idlestates); 30262306a36Sopenharmony_ci idlestates++; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci idlestates--; 30562306a36Sopenharmony_ci return idlestates; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/ 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* 31162306a36Sopenharmony_ci * helper function to read file from /sys into given buffer 31262306a36Sopenharmony_ci * fname is a relative path under "cpu/cpuidle/" dir 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_cistatic unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf, 31562306a36Sopenharmony_ci size_t buflen) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci char path[SYSFS_PATH_MAX]; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return cpupower_read_sysfs(path, buf, buflen); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/* read access to files which contain one string */ 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cienum cpuidle_string { 32962306a36Sopenharmony_ci CPUIDLE_GOVERNOR, 33062306a36Sopenharmony_ci CPUIDLE_GOVERNOR_RO, 33162306a36Sopenharmony_ci CPUIDLE_DRIVER, 33262306a36Sopenharmony_ci MAX_CPUIDLE_STRING_FILES 33362306a36Sopenharmony_ci}; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = { 33662306a36Sopenharmony_ci [CPUIDLE_GOVERNOR] = "current_governor", 33762306a36Sopenharmony_ci [CPUIDLE_GOVERNOR_RO] = "current_governor_ro", 33862306a36Sopenharmony_ci [CPUIDLE_DRIVER] = "current_driver", 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic char *sysfs_cpuidle_get_one_string(enum cpuidle_string which) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 34562306a36Sopenharmony_ci char *result; 34662306a36Sopenharmony_ci unsigned int len; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (which >= MAX_CPUIDLE_STRING_FILES) 34962306a36Sopenharmony_ci return NULL; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci len = sysfs_cpuidle_read_file(cpuidle_string_files[which], 35262306a36Sopenharmony_ci linebuf, sizeof(linebuf)); 35362306a36Sopenharmony_ci if (len == 0) 35462306a36Sopenharmony_ci return NULL; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci result = strdup(linebuf); 35762306a36Sopenharmony_ci if (result == NULL) 35862306a36Sopenharmony_ci return NULL; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (result[strlen(result) - 1] == '\n') 36162306a36Sopenharmony_ci result[strlen(result) - 1] = '\0'; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return result; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cichar *cpuidle_get_governor(void) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO); 36962306a36Sopenharmony_ci if (!tmp) 37062306a36Sopenharmony_ci return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR); 37162306a36Sopenharmony_ci else 37262306a36Sopenharmony_ci return tmp; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cichar *cpuidle_get_driver(void) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ 380