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 <unistd.h> 862306a36Sopenharmony_ci#include <stdio.h> 962306a36Sopenharmony_ci#include <errno.h> 1062306a36Sopenharmony_ci#include <stdlib.h> 1162306a36Sopenharmony_ci#include <limits.h> 1262306a36Sopenharmony_ci#include <string.h> 1362306a36Sopenharmony_ci#include <ctype.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <getopt.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "cpufreq.h" 1862306a36Sopenharmony_ci#include "cpuidle.h" 1962306a36Sopenharmony_ci#include "helpers/helpers.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define NORM_FREQ_LEN 32 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic struct option set_opts[] = { 2462306a36Sopenharmony_ci {"min", required_argument, NULL, 'd'}, 2562306a36Sopenharmony_ci {"max", required_argument, NULL, 'u'}, 2662306a36Sopenharmony_ci {"governor", required_argument, NULL, 'g'}, 2762306a36Sopenharmony_ci {"freq", required_argument, NULL, 'f'}, 2862306a36Sopenharmony_ci {"related", no_argument, NULL, 'r'}, 2962306a36Sopenharmony_ci { }, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void print_error(void) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci printf(_("Error setting new values. Common errors:\n" 3562306a36Sopenharmony_ci "- Do you have proper administration rights? (super-user?)\n" 3662306a36Sopenharmony_ci "- Is the governor you requested available and modprobed?\n" 3762306a36Sopenharmony_ci "- Trying to set an invalid policy?\n" 3862306a36Sopenharmony_ci "- Trying to set a specific frequency, but userspace governor is not available,\n" 3962306a36Sopenharmony_ci " for example because of hardware which cannot be set to a specific frequency\n" 4062306a36Sopenharmony_ci " or because the userspace governor isn't loaded?\n")); 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct freq_units { 4462306a36Sopenharmony_ci char *str_unit; 4562306a36Sopenharmony_ci int power_of_ten; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciconst struct freq_units def_units[] = { 4962306a36Sopenharmony_ci {"hz", -3}, 5062306a36Sopenharmony_ci {"khz", 0}, /* default */ 5162306a36Sopenharmony_ci {"mhz", 3}, 5262306a36Sopenharmony_ci {"ghz", 6}, 5362306a36Sopenharmony_ci {"thz", 9}, 5462306a36Sopenharmony_ci {NULL, 0} 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void print_unknown_arg(void) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci printf(_("invalid or unknown argument\n")); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic unsigned long string_to_frequency(const char *str) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci char normalized[NORM_FREQ_LEN]; 6562306a36Sopenharmony_ci const struct freq_units *unit; 6662306a36Sopenharmony_ci const char *scan; 6762306a36Sopenharmony_ci char *end; 6862306a36Sopenharmony_ci unsigned long freq; 6962306a36Sopenharmony_ci int power = 0, match_count = 0, i, cp, pad; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci while (*str == '0') 7262306a36Sopenharmony_ci str++; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci for (scan = str; isdigit(*scan) || *scan == '.'; scan++) { 7562306a36Sopenharmony_ci if (*scan == '.' && match_count == 0) 7662306a36Sopenharmony_ci match_count = 1; 7762306a36Sopenharmony_ci else if (*scan == '.' && match_count == 1) 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (*scan) { 8262306a36Sopenharmony_ci match_count = 0; 8362306a36Sopenharmony_ci for (unit = def_units; unit->str_unit; unit++) { 8462306a36Sopenharmony_ci for (i = 0; 8562306a36Sopenharmony_ci scan[i] && tolower(scan[i]) == unit->str_unit[i]; 8662306a36Sopenharmony_ci ++i) 8762306a36Sopenharmony_ci continue; 8862306a36Sopenharmony_ci if (scan[i]) 8962306a36Sopenharmony_ci continue; 9062306a36Sopenharmony_ci match_count++; 9162306a36Sopenharmony_ci power = unit->power_of_ten; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci if (match_count != 1) 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* count the number of digits to be copied */ 9862306a36Sopenharmony_ci for (cp = 0; isdigit(str[cp]); cp++) 9962306a36Sopenharmony_ci continue; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (str[cp] == '.') { 10262306a36Sopenharmony_ci while (power > -1 && isdigit(str[cp+1])) { 10362306a36Sopenharmony_ci cp++; 10462306a36Sopenharmony_ci power--; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci if (power >= -1) { /* not enough => pad */ 10862306a36Sopenharmony_ci pad = power + 1; 10962306a36Sopenharmony_ci } else { /* too much => strip */ 11062306a36Sopenharmony_ci pad = 0; 11162306a36Sopenharmony_ci cp += power + 1; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci /* check bounds */ 11462306a36Sopenharmony_ci if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1) 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* copy digits */ 11862306a36Sopenharmony_ci for (i = 0; i < cp; i++, str++) { 11962306a36Sopenharmony_ci if (*str == '.') 12062306a36Sopenharmony_ci str++; 12162306a36Sopenharmony_ci normalized[i] = *str; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci /* and pad */ 12462306a36Sopenharmony_ci for (; i < cp + pad; i++) 12562306a36Sopenharmony_ci normalized[i] = '0'; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* round up, down ? */ 12862306a36Sopenharmony_ci match_count = (normalized[i-1] >= '5'); 12962306a36Sopenharmony_ci /* and drop the decimal part */ 13062306a36Sopenharmony_ci normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */ 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* final conversion (and applying rounding) */ 13362306a36Sopenharmony_ci errno = 0; 13462306a36Sopenharmony_ci freq = strtoul(normalized, &end, 10); 13562306a36Sopenharmony_ci if (errno) 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci else { 13862306a36Sopenharmony_ci if (match_count && freq != ULONG_MAX) 13962306a36Sopenharmony_ci freq++; 14062306a36Sopenharmony_ci return freq; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu); 14762306a36Sopenharmony_ci int ret; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (!cur_pol) { 15062306a36Sopenharmony_ci printf(_("wrong, unknown or unhandled CPU?\n")); 15162306a36Sopenharmony_ci return -EINVAL; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!new_pol->min) 15562306a36Sopenharmony_ci new_pol->min = cur_pol->min; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (!new_pol->max) 15862306a36Sopenharmony_ci new_pol->max = cur_pol->max; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (!new_pol->governor) 16162306a36Sopenharmony_ci new_pol->governor = cur_pol->governor; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = cpufreq_set_policy(cpu, new_pol); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci cpufreq_put_policy(cur_pol); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return ret; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol, 17262306a36Sopenharmony_ci unsigned long freq, unsigned int pc) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci switch (pc) { 17562306a36Sopenharmony_ci case 0: 17662306a36Sopenharmony_ci return cpufreq_set_frequency(cpu, freq); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci case 1: 17962306a36Sopenharmony_ci /* if only one value of a policy is to be changed, we can 18062306a36Sopenharmony_ci * use a "fast path". 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci if (new_pol->min) 18362306a36Sopenharmony_ci return cpufreq_modify_policy_min(cpu, new_pol->min); 18462306a36Sopenharmony_ci else if (new_pol->max) 18562306a36Sopenharmony_ci return cpufreq_modify_policy_max(cpu, new_pol->max); 18662306a36Sopenharmony_ci else if (new_pol->governor) 18762306a36Sopenharmony_ci return cpufreq_modify_policy_governor(cpu, 18862306a36Sopenharmony_ci new_pol->governor); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci default: 19162306a36Sopenharmony_ci /* slow path */ 19262306a36Sopenharmony_ci return do_new_policy(cpu, new_pol); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ciint cmd_freq_set(int argc, char **argv) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci extern char *optarg; 19962306a36Sopenharmony_ci extern int optind, opterr, optopt; 20062306a36Sopenharmony_ci int ret = 0, cont = 1; 20162306a36Sopenharmony_ci int double_parm = 0, related = 0, policychange = 0; 20262306a36Sopenharmony_ci unsigned long freq = 0; 20362306a36Sopenharmony_ci char gov[20]; 20462306a36Sopenharmony_ci unsigned int cpu; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci struct cpufreq_policy new_pol = { 20762306a36Sopenharmony_ci .min = 0, 20862306a36Sopenharmony_ci .max = 0, 20962306a36Sopenharmony_ci .governor = NULL, 21062306a36Sopenharmony_ci }; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* parameter parsing */ 21362306a36Sopenharmony_ci do { 21462306a36Sopenharmony_ci ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL); 21562306a36Sopenharmony_ci switch (ret) { 21662306a36Sopenharmony_ci case '?': 21762306a36Sopenharmony_ci print_unknown_arg(); 21862306a36Sopenharmony_ci return -EINVAL; 21962306a36Sopenharmony_ci case -1: 22062306a36Sopenharmony_ci cont = 0; 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci case 'r': 22362306a36Sopenharmony_ci if (related) 22462306a36Sopenharmony_ci double_parm++; 22562306a36Sopenharmony_ci related++; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci case 'd': 22862306a36Sopenharmony_ci if (new_pol.min) 22962306a36Sopenharmony_ci double_parm++; 23062306a36Sopenharmony_ci policychange++; 23162306a36Sopenharmony_ci new_pol.min = string_to_frequency(optarg); 23262306a36Sopenharmony_ci if (new_pol.min == 0) { 23362306a36Sopenharmony_ci print_unknown_arg(); 23462306a36Sopenharmony_ci return -EINVAL; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci case 'u': 23862306a36Sopenharmony_ci if (new_pol.max) 23962306a36Sopenharmony_ci double_parm++; 24062306a36Sopenharmony_ci policychange++; 24162306a36Sopenharmony_ci new_pol.max = string_to_frequency(optarg); 24262306a36Sopenharmony_ci if (new_pol.max == 0) { 24362306a36Sopenharmony_ci print_unknown_arg(); 24462306a36Sopenharmony_ci return -EINVAL; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci case 'f': 24862306a36Sopenharmony_ci if (freq) 24962306a36Sopenharmony_ci double_parm++; 25062306a36Sopenharmony_ci freq = string_to_frequency(optarg); 25162306a36Sopenharmony_ci if (freq == 0) { 25262306a36Sopenharmony_ci print_unknown_arg(); 25362306a36Sopenharmony_ci return -EINVAL; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci case 'g': 25762306a36Sopenharmony_ci if (new_pol.governor) 25862306a36Sopenharmony_ci double_parm++; 25962306a36Sopenharmony_ci policychange++; 26062306a36Sopenharmony_ci if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) { 26162306a36Sopenharmony_ci print_unknown_arg(); 26262306a36Sopenharmony_ci return -EINVAL; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci if ((sscanf(optarg, "%19s", gov)) != 1) { 26562306a36Sopenharmony_ci print_unknown_arg(); 26662306a36Sopenharmony_ci return -EINVAL; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci new_pol.governor = gov; 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci } while (cont); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* parameter checking */ 27462306a36Sopenharmony_ci if (double_parm) { 27562306a36Sopenharmony_ci printf("the same parameter was passed more than once\n"); 27662306a36Sopenharmony_ci return -EINVAL; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (freq && policychange) { 28062306a36Sopenharmony_ci printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n" 28162306a36Sopenharmony_ci "-g/--governor parameters\n")); 28262306a36Sopenharmony_ci return -EINVAL; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (!freq && !policychange) { 28662306a36Sopenharmony_ci printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n" 28762306a36Sopenharmony_ci "-g/--governor must be passed\n")); 28862306a36Sopenharmony_ci return -EINVAL; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Default is: set all CPUs */ 29262306a36Sopenharmony_ci if (bitmask_isallclear(cpus_chosen)) 29362306a36Sopenharmony_ci bitmask_setall(cpus_chosen); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Also set frequency settings for related CPUs if -r is passed */ 29662306a36Sopenharmony_ci if (related) { 29762306a36Sopenharmony_ci for (cpu = bitmask_first(cpus_chosen); 29862306a36Sopenharmony_ci cpu <= bitmask_last(cpus_chosen); cpu++) { 29962306a36Sopenharmony_ci struct cpufreq_affected_cpus *cpus; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (!bitmask_isbitset(cpus_chosen, cpu) || 30262306a36Sopenharmony_ci cpupower_is_cpu_online(cpu) != 1) 30362306a36Sopenharmony_ci continue; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci cpus = cpufreq_get_related_cpus(cpu); 30662306a36Sopenharmony_ci if (!cpus) 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci while (cpus->next) { 30962306a36Sopenharmony_ci bitmask_setbit(cpus_chosen, cpus->cpu); 31062306a36Sopenharmony_ci cpus = cpus->next; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci /* Set the last cpu in related cpus list */ 31362306a36Sopenharmony_ci bitmask_setbit(cpus_chosen, cpus->cpu); 31462306a36Sopenharmony_ci cpufreq_put_related_cpus(cpus); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci get_cpustate(); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* loop over CPUs */ 32162306a36Sopenharmony_ci for (cpu = bitmask_first(cpus_chosen); 32262306a36Sopenharmony_ci cpu <= bitmask_last(cpus_chosen); cpu++) { 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!bitmask_isbitset(cpus_chosen, cpu) || 32562306a36Sopenharmony_ci cpupower_is_cpu_online(cpu) != 1) 32662306a36Sopenharmony_ci continue; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci printf(_("Setting cpu: %d\n"), cpu); 32962306a36Sopenharmony_ci ret = do_one_cpu(cpu, &new_pol, freq, policychange); 33062306a36Sopenharmony_ci if (ret) { 33162306a36Sopenharmony_ci print_error(); 33262306a36Sopenharmony_ci return ret; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci print_offline_cpus(); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return 0; 33962306a36Sopenharmony_ci} 340