340 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>4 */5 6 7#include <unistd.h>8#include <stdio.h>9#include <errno.h>10#include <stdlib.h>11#include <limits.h>12#include <string.h>13#include <ctype.h>14 15#include <getopt.h>16 17#include "cpufreq.h"18#include "cpuidle.h"19#include "helpers/helpers.h"20 21#define NORM_FREQ_LEN 3222 23static struct option set_opts[] = {24 {"min", required_argument, NULL, 'd'},25 {"max", required_argument, NULL, 'u'},26 {"governor", required_argument, NULL, 'g'},27 {"freq", required_argument, NULL, 'f'},28 {"related", no_argument, NULL, 'r'},29 { },30};31 32static void print_error(void)33{34 printf(_("Error setting new values. Common errors:\n"35 "- Do you have proper administration rights? (super-user?)\n"36 "- Is the governor you requested available and modprobed?\n"37 "- Trying to set an invalid policy?\n"38 "- Trying to set a specific frequency, but userspace governor is not available,\n"39 " for example because of hardware which cannot be set to a specific frequency\n"40 " or because the userspace governor isn't loaded?\n"));41};42 43struct freq_units {44 char *str_unit;45 int power_of_ten;46};47 48const struct freq_units def_units[] = {49 {"hz", -3},50 {"khz", 0}, /* default */51 {"mhz", 3},52 {"ghz", 6},53 {"thz", 9},54 {NULL, 0}55};56 57static void print_unknown_arg(void)58{59 printf(_("invalid or unknown argument\n"));60}61 62static unsigned long string_to_frequency(const char *str)63{64 char normalized[NORM_FREQ_LEN];65 const struct freq_units *unit;66 const char *scan;67 char *end;68 unsigned long freq;69 int power = 0, match_count = 0, i, cp, pad;70 71 while (*str == '0')72 str++;73 74 for (scan = str; isdigit(*scan) || *scan == '.'; scan++) {75 if (*scan == '.' && match_count == 0)76 match_count = 1;77 else if (*scan == '.' && match_count == 1)78 return 0;79 }80 81 if (*scan) {82 match_count = 0;83 for (unit = def_units; unit->str_unit; unit++) {84 for (i = 0;85 scan[i] && tolower(scan[i]) == unit->str_unit[i];86 ++i)87 continue;88 if (scan[i])89 continue;90 match_count++;91 power = unit->power_of_ten;92 }93 if (match_count != 1)94 return 0;95 }96 97 /* count the number of digits to be copied */98 for (cp = 0; isdigit(str[cp]); cp++)99 continue;100 101 if (str[cp] == '.') {102 while (power > -1 && isdigit(str[cp+1])) {103 cp++;104 power--;105 }106 }107 if (power >= -1) { /* not enough => pad */108 pad = power + 1;109 } else { /* too much => strip */110 pad = 0;111 cp += power + 1;112 }113 /* check bounds */114 if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1)115 return 0;116 117 /* copy digits */118 for (i = 0; i < cp; i++, str++) {119 if (*str == '.')120 str++;121 normalized[i] = *str;122 }123 /* and pad */124 for (; i < cp + pad; i++)125 normalized[i] = '0';126 127 /* round up, down ? */128 match_count = (normalized[i-1] >= '5');129 /* and drop the decimal part */130 normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */131 132 /* final conversion (and applying rounding) */133 errno = 0;134 freq = strtoul(normalized, &end, 10);135 if (errno)136 return 0;137 else {138 if (match_count && freq != ULONG_MAX)139 freq++;140 return freq;141 }142}143 144static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol)145{146 struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu);147 int ret;148 149 if (!cur_pol) {150 printf(_("wrong, unknown or unhandled CPU?\n"));151 return -EINVAL;152 }153 154 if (!new_pol->min)155 new_pol->min = cur_pol->min;156 157 if (!new_pol->max)158 new_pol->max = cur_pol->max;159 160 if (!new_pol->governor)161 new_pol->governor = cur_pol->governor;162 163 ret = cpufreq_set_policy(cpu, new_pol);164 165 cpufreq_put_policy(cur_pol);166 167 return ret;168}169 170 171static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol,172 unsigned long freq, unsigned int pc)173{174 switch (pc) {175 case 0:176 return cpufreq_set_frequency(cpu, freq);177 178 case 1:179 /* if only one value of a policy is to be changed, we can180 * use a "fast path".181 */182 if (new_pol->min)183 return cpufreq_modify_policy_min(cpu, new_pol->min);184 else if (new_pol->max)185 return cpufreq_modify_policy_max(cpu, new_pol->max);186 else if (new_pol->governor)187 return cpufreq_modify_policy_governor(cpu,188 new_pol->governor);189 190 default:191 /* slow path */192 return do_new_policy(cpu, new_pol);193 }194}195 196int cmd_freq_set(int argc, char **argv)197{198 extern char *optarg;199 extern int optind, opterr, optopt;200 int ret = 0, cont = 1;201 int double_parm = 0, related = 0, policychange = 0;202 unsigned long freq = 0;203 char gov[20];204 unsigned int cpu;205 206 struct cpufreq_policy new_pol = {207 .min = 0,208 .max = 0,209 .governor = NULL,210 };211 212 /* parameter parsing */213 do {214 ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL);215 switch (ret) {216 case '?':217 print_unknown_arg();218 return -EINVAL;219 case -1:220 cont = 0;221 break;222 case 'r':223 if (related)224 double_parm++;225 related++;226 break;227 case 'd':228 if (new_pol.min)229 double_parm++;230 policychange++;231 new_pol.min = string_to_frequency(optarg);232 if (new_pol.min == 0) {233 print_unknown_arg();234 return -EINVAL;235 }236 break;237 case 'u':238 if (new_pol.max)239 double_parm++;240 policychange++;241 new_pol.max = string_to_frequency(optarg);242 if (new_pol.max == 0) {243 print_unknown_arg();244 return -EINVAL;245 }246 break;247 case 'f':248 if (freq)249 double_parm++;250 freq = string_to_frequency(optarg);251 if (freq == 0) {252 print_unknown_arg();253 return -EINVAL;254 }255 break;256 case 'g':257 if (new_pol.governor)258 double_parm++;259 policychange++;260 if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) {261 print_unknown_arg();262 return -EINVAL;263 }264 if ((sscanf(optarg, "%19s", gov)) != 1) {265 print_unknown_arg();266 return -EINVAL;267 }268 new_pol.governor = gov;269 break;270 }271 } while (cont);272 273 /* parameter checking */274 if (double_parm) {275 printf("the same parameter was passed more than once\n");276 return -EINVAL;277 }278 279 if (freq && policychange) {280 printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n"281 "-g/--governor parameters\n"));282 return -EINVAL;283 }284 285 if (!freq && !policychange) {286 printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n"287 "-g/--governor must be passed\n"));288 return -EINVAL;289 }290 291 /* Default is: set all CPUs */292 if (bitmask_isallclear(cpus_chosen))293 bitmask_setall(cpus_chosen);294 295 /* Also set frequency settings for related CPUs if -r is passed */296 if (related) {297 for (cpu = bitmask_first(cpus_chosen);298 cpu <= bitmask_last(cpus_chosen); cpu++) {299 struct cpufreq_affected_cpus *cpus;300 301 if (!bitmask_isbitset(cpus_chosen, cpu) ||302 cpupower_is_cpu_online(cpu) != 1)303 continue;304 305 cpus = cpufreq_get_related_cpus(cpu);306 if (!cpus)307 break;308 while (cpus->next) {309 bitmask_setbit(cpus_chosen, cpus->cpu);310 cpus = cpus->next;311 }312 /* Set the last cpu in related cpus list */313 bitmask_setbit(cpus_chosen, cpus->cpu);314 cpufreq_put_related_cpus(cpus);315 }316 }317 318 get_cpustate();319 320 /* loop over CPUs */321 for (cpu = bitmask_first(cpus_chosen);322 cpu <= bitmask_last(cpus_chosen); cpu++) {323 324 if (!bitmask_isbitset(cpus_chosen, cpu) ||325 cpupower_is_cpu_online(cpu) != 1)326 continue;327 328 printf(_("Setting cpu: %d\n"), cpu);329 ret = do_one_cpu(cpu, &new_pol, freq, policychange);330 if (ret) {331 print_error();332 return ret;333 }334 }335 336 print_offline_cpus();337 338 return 0;339}340