162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> 462306a36Sopenharmony_ci */ 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 "cpufreq.h" 1762306a36Sopenharmony_ci#include "cpupower_intern.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* CPUFREQ sysfs access **************************************************/ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* helper function to read file from /sys into given buffer */ 2262306a36Sopenharmony_ci/* fname is a relative path under "cpuX/cpufreq" dir */ 2362306a36Sopenharmony_cistatic unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname, 2462306a36Sopenharmony_ci char *buf, size_t buflen) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci char path[SYSFS_PATH_MAX]; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", 2962306a36Sopenharmony_ci cpu, fname); 3062306a36Sopenharmony_ci return cpupower_read_sysfs(path, buf, buflen); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* helper function to write a new value to a /sys file */ 3462306a36Sopenharmony_ci/* fname is a relative path under "cpuX/cpufreq" dir */ 3562306a36Sopenharmony_cistatic unsigned int sysfs_cpufreq_write_file(unsigned int cpu, 3662306a36Sopenharmony_ci const char *fname, 3762306a36Sopenharmony_ci const char *value, size_t len) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci char path[SYSFS_PATH_MAX]; 4062306a36Sopenharmony_ci int fd; 4162306a36Sopenharmony_ci ssize_t numwrite; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", 4462306a36Sopenharmony_ci cpu, fname); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci fd = open(path, O_WRONLY); 4762306a36Sopenharmony_ci if (fd == -1) 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci numwrite = write(fd, value, len); 5162306a36Sopenharmony_ci if (numwrite < 1) { 5262306a36Sopenharmony_ci close(fd); 5362306a36Sopenharmony_ci return 0; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci close(fd); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return (unsigned int) numwrite; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* read access to files which contain one numeric value */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cienum cpufreq_value { 6462306a36Sopenharmony_ci CPUINFO_CUR_FREQ, 6562306a36Sopenharmony_ci CPUINFO_MIN_FREQ, 6662306a36Sopenharmony_ci CPUINFO_MAX_FREQ, 6762306a36Sopenharmony_ci CPUINFO_LATENCY, 6862306a36Sopenharmony_ci SCALING_CUR_FREQ, 6962306a36Sopenharmony_ci SCALING_MIN_FREQ, 7062306a36Sopenharmony_ci SCALING_MAX_FREQ, 7162306a36Sopenharmony_ci STATS_NUM_TRANSITIONS, 7262306a36Sopenharmony_ci MAX_CPUFREQ_VALUE_READ_FILES 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = { 7662306a36Sopenharmony_ci [CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq", 7762306a36Sopenharmony_ci [CPUINFO_MIN_FREQ] = "cpuinfo_min_freq", 7862306a36Sopenharmony_ci [CPUINFO_MAX_FREQ] = "cpuinfo_max_freq", 7962306a36Sopenharmony_ci [CPUINFO_LATENCY] = "cpuinfo_transition_latency", 8062306a36Sopenharmony_ci [SCALING_CUR_FREQ] = "scaling_cur_freq", 8162306a36Sopenharmony_ci [SCALING_MIN_FREQ] = "scaling_min_freq", 8262306a36Sopenharmony_ci [SCALING_MAX_FREQ] = "scaling_max_freq", 8362306a36Sopenharmony_ci [STATS_NUM_TRANSITIONS] = "stats/total_trans" 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ciunsigned long cpufreq_get_sysfs_value_from_table(unsigned int cpu, 8762306a36Sopenharmony_ci const char **table, 8862306a36Sopenharmony_ci unsigned int index, 8962306a36Sopenharmony_ci unsigned int size) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci unsigned long value; 9262306a36Sopenharmony_ci unsigned int len; 9362306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 9462306a36Sopenharmony_ci char *endp; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (!table || index >= size || !table[index]) 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci len = sysfs_cpufreq_read_file(cpu, table[index], linebuf, 10062306a36Sopenharmony_ci sizeof(linebuf)); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (len == 0) 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci value = strtoul(linebuf, &endp, 0); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (endp == linebuf || errno == ERANGE) 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return value; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu, 11462306a36Sopenharmony_ci enum cpufreq_value which) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci return cpufreq_get_sysfs_value_from_table(cpu, cpufreq_value_files, 11762306a36Sopenharmony_ci which, 11862306a36Sopenharmony_ci MAX_CPUFREQ_VALUE_READ_FILES); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* read access to files which contain one string */ 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cienum cpufreq_string { 12462306a36Sopenharmony_ci SCALING_DRIVER, 12562306a36Sopenharmony_ci SCALING_GOVERNOR, 12662306a36Sopenharmony_ci MAX_CPUFREQ_STRING_FILES 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = { 13062306a36Sopenharmony_ci [SCALING_DRIVER] = "scaling_driver", 13162306a36Sopenharmony_ci [SCALING_GOVERNOR] = "scaling_governor", 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic char *sysfs_cpufreq_get_one_string(unsigned int cpu, 13662306a36Sopenharmony_ci enum cpufreq_string which) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 13962306a36Sopenharmony_ci char *result; 14062306a36Sopenharmony_ci unsigned int len; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (which >= MAX_CPUFREQ_STRING_FILES) 14362306a36Sopenharmony_ci return NULL; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which], 14662306a36Sopenharmony_ci linebuf, sizeof(linebuf)); 14762306a36Sopenharmony_ci if (len == 0) 14862306a36Sopenharmony_ci return NULL; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci result = strdup(linebuf); 15162306a36Sopenharmony_ci if (result == NULL) 15262306a36Sopenharmony_ci return NULL; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (result[strlen(result) - 1] == '\n') 15562306a36Sopenharmony_ci result[strlen(result) - 1] = '\0'; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return result; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* write access */ 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cienum cpufreq_write { 16362306a36Sopenharmony_ci WRITE_SCALING_MIN_FREQ, 16462306a36Sopenharmony_ci WRITE_SCALING_MAX_FREQ, 16562306a36Sopenharmony_ci WRITE_SCALING_GOVERNOR, 16662306a36Sopenharmony_ci WRITE_SCALING_SET_SPEED, 16762306a36Sopenharmony_ci MAX_CPUFREQ_WRITE_FILES 16862306a36Sopenharmony_ci}; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = { 17162306a36Sopenharmony_ci [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq", 17262306a36Sopenharmony_ci [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq", 17362306a36Sopenharmony_ci [WRITE_SCALING_GOVERNOR] = "scaling_governor", 17462306a36Sopenharmony_ci [WRITE_SCALING_SET_SPEED] = "scaling_setspeed", 17562306a36Sopenharmony_ci}; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int sysfs_cpufreq_write_one_value(unsigned int cpu, 17862306a36Sopenharmony_ci enum cpufreq_write which, 17962306a36Sopenharmony_ci const char *new_value, size_t len) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci if (which >= MAX_CPUFREQ_WRITE_FILES) 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which], 18562306a36Sopenharmony_ci new_value, len) != len) 18662306a36Sopenharmony_ci return -ENODEV; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ciunsigned long cpufreq_get_freq_kernel(unsigned int cpu) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ciunsigned long cpufreq_get_freq_hardware(unsigned int cpu) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ciunsigned long cpufreq_get_transition_latency(unsigned int cpu) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciint cpufreq_get_hardware_limits(unsigned int cpu, 20762306a36Sopenharmony_ci unsigned long *min, 20862306a36Sopenharmony_ci unsigned long *max) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci if ((!min) || (!max)) 21162306a36Sopenharmony_ci return -EINVAL; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ); 21462306a36Sopenharmony_ci if (!*min) 21562306a36Sopenharmony_ci return -ENODEV; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ); 21862306a36Sopenharmony_ci if (!*max) 21962306a36Sopenharmony_ci return -ENODEV; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cichar *cpufreq_get_driver(unsigned int cpu) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_civoid cpufreq_put_driver(char *ptr) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci if (!ptr) 23262306a36Sopenharmony_ci return; 23362306a36Sopenharmony_ci free(ptr); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistruct cpufreq_policy *cpufreq_get_policy(unsigned int cpu) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct cpufreq_policy *policy; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci policy = malloc(sizeof(struct cpufreq_policy)); 24162306a36Sopenharmony_ci if (!policy) 24262306a36Sopenharmony_ci return NULL; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR); 24562306a36Sopenharmony_ci if (!policy->governor) { 24662306a36Sopenharmony_ci free(policy); 24762306a36Sopenharmony_ci return NULL; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); 25062306a36Sopenharmony_ci policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ); 25162306a36Sopenharmony_ci if ((!policy->min) || (!policy->max)) { 25262306a36Sopenharmony_ci free(policy->governor); 25362306a36Sopenharmony_ci free(policy); 25462306a36Sopenharmony_ci return NULL; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return policy; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_civoid cpufreq_put_policy(struct cpufreq_policy *policy) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci if ((!policy) || (!policy->governor)) 26362306a36Sopenharmony_ci return; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci free(policy->governor); 26662306a36Sopenharmony_ci policy->governor = NULL; 26762306a36Sopenharmony_ci free(policy); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistruct cpufreq_available_governors *cpufreq_get_available_governors(unsigned 27162306a36Sopenharmony_ci int cpu) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct cpufreq_available_governors *first = NULL; 27462306a36Sopenharmony_ci struct cpufreq_available_governors *current = NULL; 27562306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 27662306a36Sopenharmony_ci unsigned int pos, i; 27762306a36Sopenharmony_ci unsigned int len; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors", 28062306a36Sopenharmony_ci linebuf, sizeof(linebuf)); 28162306a36Sopenharmony_ci if (len == 0) 28262306a36Sopenharmony_ci return NULL; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci pos = 0; 28562306a36Sopenharmony_ci for (i = 0; i < len; i++) { 28662306a36Sopenharmony_ci if (linebuf[i] == ' ' || linebuf[i] == '\n') { 28762306a36Sopenharmony_ci if (i - pos < 2) 28862306a36Sopenharmony_ci continue; 28962306a36Sopenharmony_ci if (current) { 29062306a36Sopenharmony_ci current->next = malloc(sizeof(*current)); 29162306a36Sopenharmony_ci if (!current->next) 29262306a36Sopenharmony_ci goto error_out; 29362306a36Sopenharmony_ci current = current->next; 29462306a36Sopenharmony_ci } else { 29562306a36Sopenharmony_ci first = malloc(sizeof(*first)); 29662306a36Sopenharmony_ci if (!first) 29762306a36Sopenharmony_ci return NULL; 29862306a36Sopenharmony_ci current = first; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci current->first = first; 30162306a36Sopenharmony_ci current->next = NULL; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci current->governor = malloc(i - pos + 1); 30462306a36Sopenharmony_ci if (!current->governor) 30562306a36Sopenharmony_ci goto error_out; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci memcpy(current->governor, linebuf + pos, i - pos); 30862306a36Sopenharmony_ci current->governor[i - pos] = '\0'; 30962306a36Sopenharmony_ci pos = i + 1; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return first; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci error_out: 31662306a36Sopenharmony_ci while (first) { 31762306a36Sopenharmony_ci current = first->next; 31862306a36Sopenharmony_ci if (first->governor) 31962306a36Sopenharmony_ci free(first->governor); 32062306a36Sopenharmony_ci free(first); 32162306a36Sopenharmony_ci first = current; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci return NULL; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_civoid cpufreq_put_available_governors(struct cpufreq_available_governors *any) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct cpufreq_available_governors *tmp, *next; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!any) 33162306a36Sopenharmony_ci return; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci tmp = any->first; 33462306a36Sopenharmony_ci while (tmp) { 33562306a36Sopenharmony_ci next = tmp->next; 33662306a36Sopenharmony_ci if (tmp->governor) 33762306a36Sopenharmony_ci free(tmp->governor); 33862306a36Sopenharmony_ci free(tmp); 33962306a36Sopenharmony_ci tmp = next; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistruct cpufreq_available_frequencies 34562306a36Sopenharmony_ci*cpufreq_get_available_frequencies(unsigned int cpu) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct cpufreq_available_frequencies *first = NULL; 34862306a36Sopenharmony_ci struct cpufreq_available_frequencies *current = NULL; 34962306a36Sopenharmony_ci char one_value[SYSFS_PATH_MAX]; 35062306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 35162306a36Sopenharmony_ci unsigned int pos, i; 35262306a36Sopenharmony_ci unsigned int len; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies", 35562306a36Sopenharmony_ci linebuf, sizeof(linebuf)); 35662306a36Sopenharmony_ci if (len == 0) 35762306a36Sopenharmony_ci return NULL; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci pos = 0; 36062306a36Sopenharmony_ci for (i = 0; i < len; i++) { 36162306a36Sopenharmony_ci if (linebuf[i] == ' ' || linebuf[i] == '\n') { 36262306a36Sopenharmony_ci if (i - pos < 2) 36362306a36Sopenharmony_ci continue; 36462306a36Sopenharmony_ci if (i - pos >= SYSFS_PATH_MAX) 36562306a36Sopenharmony_ci goto error_out; 36662306a36Sopenharmony_ci if (current) { 36762306a36Sopenharmony_ci current->next = malloc(sizeof(*current)); 36862306a36Sopenharmony_ci if (!current->next) 36962306a36Sopenharmony_ci goto error_out; 37062306a36Sopenharmony_ci current = current->next; 37162306a36Sopenharmony_ci } else { 37262306a36Sopenharmony_ci first = malloc(sizeof(*first)); 37362306a36Sopenharmony_ci if (!first) 37462306a36Sopenharmony_ci return NULL; 37562306a36Sopenharmony_ci current = first; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci current->first = first; 37862306a36Sopenharmony_ci current->next = NULL; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci memcpy(one_value, linebuf + pos, i - pos); 38162306a36Sopenharmony_ci one_value[i - pos] = '\0'; 38262306a36Sopenharmony_ci if (sscanf(one_value, "%lu", ¤t->frequency) != 1) 38362306a36Sopenharmony_ci goto error_out; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci pos = i + 1; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return first; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci error_out: 39262306a36Sopenharmony_ci while (first) { 39362306a36Sopenharmony_ci current = first->next; 39462306a36Sopenharmony_ci free(first); 39562306a36Sopenharmony_ci first = current; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci return NULL; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistruct cpufreq_available_frequencies 40162306a36Sopenharmony_ci*cpufreq_get_boost_frequencies(unsigned int cpu) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct cpufreq_available_frequencies *first = NULL; 40462306a36Sopenharmony_ci struct cpufreq_available_frequencies *current = NULL; 40562306a36Sopenharmony_ci char one_value[SYSFS_PATH_MAX]; 40662306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 40762306a36Sopenharmony_ci unsigned int pos, i; 40862306a36Sopenharmony_ci unsigned int len; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci len = sysfs_cpufreq_read_file(cpu, "scaling_boost_frequencies", 41162306a36Sopenharmony_ci linebuf, sizeof(linebuf)); 41262306a36Sopenharmony_ci if (len == 0) 41362306a36Sopenharmony_ci return NULL; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci pos = 0; 41662306a36Sopenharmony_ci for (i = 0; i < len; i++) { 41762306a36Sopenharmony_ci if (linebuf[i] == ' ' || linebuf[i] == '\n') { 41862306a36Sopenharmony_ci if (i - pos < 2) 41962306a36Sopenharmony_ci continue; 42062306a36Sopenharmony_ci if (i - pos >= SYSFS_PATH_MAX) 42162306a36Sopenharmony_ci goto error_out; 42262306a36Sopenharmony_ci if (current) { 42362306a36Sopenharmony_ci current->next = malloc(sizeof(*current)); 42462306a36Sopenharmony_ci if (!current->next) 42562306a36Sopenharmony_ci goto error_out; 42662306a36Sopenharmony_ci current = current->next; 42762306a36Sopenharmony_ci } else { 42862306a36Sopenharmony_ci first = malloc(sizeof(*first)); 42962306a36Sopenharmony_ci if (!first) 43062306a36Sopenharmony_ci return NULL; 43162306a36Sopenharmony_ci current = first; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci current->first = first; 43462306a36Sopenharmony_ci current->next = NULL; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci memcpy(one_value, linebuf + pos, i - pos); 43762306a36Sopenharmony_ci one_value[i - pos] = '\0'; 43862306a36Sopenharmony_ci if (sscanf(one_value, "%lu", ¤t->frequency) != 1) 43962306a36Sopenharmony_ci goto error_out; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci pos = i + 1; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return first; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci error_out: 44862306a36Sopenharmony_ci while (first) { 44962306a36Sopenharmony_ci current = first->next; 45062306a36Sopenharmony_ci free(first); 45162306a36Sopenharmony_ci first = current; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci return NULL; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_civoid cpufreq_put_available_frequencies(struct cpufreq_available_frequencies *any) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct cpufreq_available_frequencies *tmp, *next; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (!any) 46162306a36Sopenharmony_ci return; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci tmp = any->first; 46462306a36Sopenharmony_ci while (tmp) { 46562306a36Sopenharmony_ci next = tmp->next; 46662306a36Sopenharmony_ci free(tmp); 46762306a36Sopenharmony_ci tmp = next; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_civoid cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies *any) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci cpufreq_put_available_frequencies(any); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu, 47762306a36Sopenharmony_ci const char *file) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct cpufreq_affected_cpus *first = NULL; 48062306a36Sopenharmony_ci struct cpufreq_affected_cpus *current = NULL; 48162306a36Sopenharmony_ci char one_value[SYSFS_PATH_MAX]; 48262306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 48362306a36Sopenharmony_ci unsigned int pos, i; 48462306a36Sopenharmony_ci unsigned int len; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf)); 48762306a36Sopenharmony_ci if (len == 0) 48862306a36Sopenharmony_ci return NULL; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci pos = 0; 49162306a36Sopenharmony_ci for (i = 0; i < len; i++) { 49262306a36Sopenharmony_ci if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') { 49362306a36Sopenharmony_ci if (i - pos < 1) 49462306a36Sopenharmony_ci continue; 49562306a36Sopenharmony_ci if (i - pos >= SYSFS_PATH_MAX) 49662306a36Sopenharmony_ci goto error_out; 49762306a36Sopenharmony_ci if (current) { 49862306a36Sopenharmony_ci current->next = malloc(sizeof(*current)); 49962306a36Sopenharmony_ci if (!current->next) 50062306a36Sopenharmony_ci goto error_out; 50162306a36Sopenharmony_ci current = current->next; 50262306a36Sopenharmony_ci } else { 50362306a36Sopenharmony_ci first = malloc(sizeof(*first)); 50462306a36Sopenharmony_ci if (!first) 50562306a36Sopenharmony_ci return NULL; 50662306a36Sopenharmony_ci current = first; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci current->first = first; 50962306a36Sopenharmony_ci current->next = NULL; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci memcpy(one_value, linebuf + pos, i - pos); 51262306a36Sopenharmony_ci one_value[i - pos] = '\0'; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (sscanf(one_value, "%u", ¤t->cpu) != 1) 51562306a36Sopenharmony_ci goto error_out; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci pos = i + 1; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return first; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci error_out: 52462306a36Sopenharmony_ci while (first) { 52562306a36Sopenharmony_ci current = first->next; 52662306a36Sopenharmony_ci free(first); 52762306a36Sopenharmony_ci first = current; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci return NULL; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistruct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci return sysfs_get_cpu_list(cpu, "affected_cpus"); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_civoid cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct cpufreq_affected_cpus *tmp, *next; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (!any) 54262306a36Sopenharmony_ci return; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci tmp = any->first; 54562306a36Sopenharmony_ci while (tmp) { 54662306a36Sopenharmony_ci next = tmp->next; 54762306a36Sopenharmony_ci free(tmp); 54862306a36Sopenharmony_ci tmp = next; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistruct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci return sysfs_get_cpu_list(cpu, "related_cpus"); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_civoid cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci cpufreq_put_affected_cpus(any); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int verify_gov(char *new_gov, char *passed_gov) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci unsigned int i, j = 0; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (!passed_gov || (strlen(passed_gov) > 19)) 56862306a36Sopenharmony_ci return -EINVAL; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci strncpy(new_gov, passed_gov, 20); 57162306a36Sopenharmony_ci for (i = 0; i < 20; i++) { 57262306a36Sopenharmony_ci if (j) { 57362306a36Sopenharmony_ci new_gov[i] = '\0'; 57462306a36Sopenharmony_ci continue; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z')) 57762306a36Sopenharmony_ci continue; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z')) 58062306a36Sopenharmony_ci continue; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (new_gov[i] == '-') 58362306a36Sopenharmony_ci continue; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (new_gov[i] == '_') 58662306a36Sopenharmony_ci continue; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (new_gov[i] == '\0') { 58962306a36Sopenharmony_ci j = 1; 59062306a36Sopenharmony_ci continue; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci return -EINVAL; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci new_gov[19] = '\0'; 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ciint cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci char min[SYSFS_PATH_MAX]; 60162306a36Sopenharmony_ci char max[SYSFS_PATH_MAX]; 60262306a36Sopenharmony_ci char gov[SYSFS_PATH_MAX]; 60362306a36Sopenharmony_ci int ret; 60462306a36Sopenharmony_ci unsigned long old_min; 60562306a36Sopenharmony_ci int write_max_first; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (!policy || !(policy->governor)) 60862306a36Sopenharmony_ci return -EINVAL; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (policy->max < policy->min) 61162306a36Sopenharmony_ci return -EINVAL; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (verify_gov(gov, policy->governor)) 61462306a36Sopenharmony_ci return -EINVAL; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min); 61762306a36Sopenharmony_ci snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); 62062306a36Sopenharmony_ci write_max_first = (old_min && (policy->max < old_min) ? 0 : 1); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (write_max_first) { 62362306a36Sopenharmony_ci ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 62462306a36Sopenharmony_ci max, strlen(max)); 62562306a36Sopenharmony_ci if (ret) 62662306a36Sopenharmony_ci return ret; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min, 63062306a36Sopenharmony_ci strlen(min)); 63162306a36Sopenharmony_ci if (ret) 63262306a36Sopenharmony_ci return ret; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (!write_max_first) { 63562306a36Sopenharmony_ci ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 63662306a36Sopenharmony_ci max, strlen(max)); 63762306a36Sopenharmony_ci if (ret) 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, 64262306a36Sopenharmony_ci gov, strlen(gov)); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ciint cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci char value[SYSFS_PATH_MAX]; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, 65362306a36Sopenharmony_ci value, strlen(value)); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ciint cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci char value[SYSFS_PATH_MAX]; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 66462306a36Sopenharmony_ci value, strlen(value)); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ciint cpufreq_modify_policy_governor(unsigned int cpu, char *governor) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci char new_gov[SYSFS_PATH_MAX]; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if ((!governor) || (strlen(governor) > 19)) 67262306a36Sopenharmony_ci return -EINVAL; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (verify_gov(new_gov, governor)) 67562306a36Sopenharmony_ci return -EINVAL; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, 67862306a36Sopenharmony_ci new_gov, strlen(new_gov)); 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ciint cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct cpufreq_policy *pol = cpufreq_get_policy(cpu); 68462306a36Sopenharmony_ci char userspace_gov[] = "userspace"; 68562306a36Sopenharmony_ci char freq[SYSFS_PATH_MAX]; 68662306a36Sopenharmony_ci int ret; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (!pol) 68962306a36Sopenharmony_ci return -ENODEV; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (strncmp(pol->governor, userspace_gov, 9) != 0) { 69262306a36Sopenharmony_ci ret = cpufreq_modify_policy_governor(cpu, userspace_gov); 69362306a36Sopenharmony_ci if (ret) { 69462306a36Sopenharmony_ci cpufreq_put_policy(pol); 69562306a36Sopenharmony_ci return ret; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci cpufreq_put_policy(pol); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED, 70462306a36Sopenharmony_ci freq, strlen(freq)); 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistruct cpufreq_stats *cpufreq_get_stats(unsigned int cpu, 70862306a36Sopenharmony_ci unsigned long long *total_time) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct cpufreq_stats *first = NULL; 71162306a36Sopenharmony_ci struct cpufreq_stats *current = NULL; 71262306a36Sopenharmony_ci char one_value[SYSFS_PATH_MAX]; 71362306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 71462306a36Sopenharmony_ci unsigned int pos, i; 71562306a36Sopenharmony_ci unsigned int len; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state", 71862306a36Sopenharmony_ci linebuf, sizeof(linebuf)); 71962306a36Sopenharmony_ci if (len == 0) 72062306a36Sopenharmony_ci return NULL; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci *total_time = 0; 72362306a36Sopenharmony_ci pos = 0; 72462306a36Sopenharmony_ci for (i = 0; i < len; i++) { 72562306a36Sopenharmony_ci if (i == strlen(linebuf) || linebuf[i] == '\n') { 72662306a36Sopenharmony_ci if (i - pos < 2) 72762306a36Sopenharmony_ci continue; 72862306a36Sopenharmony_ci if ((i - pos) >= SYSFS_PATH_MAX) 72962306a36Sopenharmony_ci goto error_out; 73062306a36Sopenharmony_ci if (current) { 73162306a36Sopenharmony_ci current->next = malloc(sizeof(*current)); 73262306a36Sopenharmony_ci if (!current->next) 73362306a36Sopenharmony_ci goto error_out; 73462306a36Sopenharmony_ci current = current->next; 73562306a36Sopenharmony_ci } else { 73662306a36Sopenharmony_ci first = malloc(sizeof(*first)); 73762306a36Sopenharmony_ci if (!first) 73862306a36Sopenharmony_ci return NULL; 73962306a36Sopenharmony_ci current = first; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci current->first = first; 74262306a36Sopenharmony_ci current->next = NULL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci memcpy(one_value, linebuf + pos, i - pos); 74562306a36Sopenharmony_ci one_value[i - pos] = '\0'; 74662306a36Sopenharmony_ci if (sscanf(one_value, "%lu %llu", 74762306a36Sopenharmony_ci ¤t->frequency, 74862306a36Sopenharmony_ci ¤t->time_in_state) != 2) 74962306a36Sopenharmony_ci goto error_out; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci *total_time = *total_time + current->time_in_state; 75262306a36Sopenharmony_ci pos = i + 1; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci return first; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci error_out: 75962306a36Sopenharmony_ci while (first) { 76062306a36Sopenharmony_ci current = first->next; 76162306a36Sopenharmony_ci free(first); 76262306a36Sopenharmony_ci first = current; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci return NULL; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_civoid cpufreq_put_stats(struct cpufreq_stats *any) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci struct cpufreq_stats *tmp, *next; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (!any) 77262306a36Sopenharmony_ci return; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci tmp = any->first; 77562306a36Sopenharmony_ci while (tmp) { 77662306a36Sopenharmony_ci next = tmp->next; 77762306a36Sopenharmony_ci free(tmp); 77862306a36Sopenharmony_ci tmp = next; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ciunsigned long cpufreq_get_transitions(unsigned int cpu) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS); 78562306a36Sopenharmony_ci} 786