1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 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 32 22 23 static 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 32 static 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 43 struct freq_units { 44 char *str_unit; 45 int power_of_ten; 46 }; 47 48 const 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 57 static void print_unknown_arg(void) 58 { 59 printf(_("invalid or unknown argument\n")); 60 } 61 62 static 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++, power--; 104 } 105 if (power >= -1) /* not enough => pad */ 106 pad = power + 1; 107 else /* to much => strip */ 108 pad = 0, cp += power + 1; 109 /* check bounds */ 110 if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1) 111 return 0; 112 113 /* copy digits */ 114 for (i = 0; i < cp; i++, str++) { 115 if (*str == '.') 116 str++; 117 normalized[i] = *str; 118 } 119 /* and pad */ 120 for (; i < cp + pad; i++) 121 normalized[i] = '0'; 122 123 /* round up, down ? */ 124 match_count = (normalized[i-1] >= '5'); 125 /* and drop the decimal part */ 126 normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */ 127 128 /* final conversion (and applying rounding) */ 129 errno = 0; 130 freq = strtoul(normalized, &end, 10); 131 if (errno) 132 return 0; 133 else { 134 if (match_count && freq != ULONG_MAX) 135 freq++; 136 return freq; 137 } 138 } 139 140 static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol) 141 { 142 struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu); 143 int ret; 144 145 if (!cur_pol) { 146 printf(_("wrong, unknown or unhandled CPU?\n")); 147 return -EINVAL; 148 } 149 150 if (!new_pol->min) 151 new_pol->min = cur_pol->min; 152 153 if (!new_pol->max) 154 new_pol->max = cur_pol->max; 155 156 if (!new_pol->governor) 157 new_pol->governor = cur_pol->governor; 158 159 ret = cpufreq_set_policy(cpu, new_pol); 160 161 cpufreq_put_policy(cur_pol); 162 163 return ret; 164 } 165 166 167 static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol, 168 unsigned long freq, unsigned int pc) 169 { 170 switch (pc) { 171 case 0: 172 return cpufreq_set_frequency(cpu, freq); 173 174 case 1: 175 /* if only one value of a policy is to be changed, we can 176 * use a "fast path". 177 */ 178 if (new_pol->min) 179 return cpufreq_modify_policy_min(cpu, new_pol->min); 180 else if (new_pol->max) 181 return cpufreq_modify_policy_max(cpu, new_pol->max); 182 else if (new_pol->governor) 183 return cpufreq_modify_policy_governor(cpu, 184 new_pol->governor); 185 186 default: 187 /* slow path */ 188 return do_new_policy(cpu, new_pol); 189 } 190 } 191 192 int cmd_freq_set(int argc, char **argv) 193 { 194 extern char *optarg; 195 extern int optind, opterr, optopt; 196 int ret = 0, cont = 1; 197 int double_parm = 0, related = 0, policychange = 0; 198 unsigned long freq = 0; 199 char gov[20]; 200 unsigned int cpu; 201 202 struct cpufreq_policy new_pol = { 203 .min = 0, 204 .max = 0, 205 .governor = NULL, 206 }; 207 208 /* parameter parsing */ 209 do { 210 ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL); 211 switch (ret) { 212 case '?': 213 print_unknown_arg(); 214 return -EINVAL; 215 case -1: 216 cont = 0; 217 break; 218 case 'r': 219 if (related) 220 double_parm++; 221 related++; 222 break; 223 case 'd': 224 if (new_pol.min) 225 double_parm++; 226 policychange++; 227 new_pol.min = string_to_frequency(optarg); 228 if (new_pol.min == 0) { 229 print_unknown_arg(); 230 return -EINVAL; 231 } 232 break; 233 case 'u': 234 if (new_pol.max) 235 double_parm++; 236 policychange++; 237 new_pol.max = string_to_frequency(optarg); 238 if (new_pol.max == 0) { 239 print_unknown_arg(); 240 return -EINVAL; 241 } 242 break; 243 case 'f': 244 if (freq) 245 double_parm++; 246 freq = string_to_frequency(optarg); 247 if (freq == 0) { 248 print_unknown_arg(); 249 return -EINVAL; 250 } 251 break; 252 case 'g': 253 if (new_pol.governor) 254 double_parm++; 255 policychange++; 256 if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) { 257 print_unknown_arg(); 258 return -EINVAL; 259 } 260 if ((sscanf(optarg, "%19s", gov)) != 1) { 261 print_unknown_arg(); 262 return -EINVAL; 263 } 264 new_pol.governor = gov; 265 break; 266 } 267 } while (cont); 268 269 /* parameter checking */ 270 if (double_parm) { 271 printf("the same parameter was passed more than once\n"); 272 return -EINVAL; 273 } 274 275 if (freq && policychange) { 276 printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n" 277 "-g/--governor parameters\n")); 278 return -EINVAL; 279 } 280 281 if (!freq && !policychange) { 282 printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n" 283 "-g/--governor must be passed\n")); 284 return -EINVAL; 285 } 286 287 /* Default is: set all CPUs */ 288 if (bitmask_isallclear(cpus_chosen)) 289 bitmask_setall(cpus_chosen); 290 291 /* Also set frequency settings for related CPUs if -r is passed */ 292 if (related) { 293 for (cpu = bitmask_first(cpus_chosen); 294 cpu <= bitmask_last(cpus_chosen); cpu++) { 295 struct cpufreq_affected_cpus *cpus; 296 297 if (!bitmask_isbitset(cpus_chosen, cpu) || 298 cpupower_is_cpu_online(cpu) != 1) 299 continue; 300 301 cpus = cpufreq_get_related_cpus(cpu); 302 if (!cpus) 303 break; 304 while (cpus->next) { 305 bitmask_setbit(cpus_chosen, cpus->cpu); 306 cpus = cpus->next; 307 } 308 /* Set the last cpu in related cpus list */ 309 bitmask_setbit(cpus_chosen, cpus->cpu); 310 cpufreq_put_related_cpus(cpus); 311 } 312 } 313 314 315 /* loop over CPUs */ 316 for (cpu = bitmask_first(cpus_chosen); 317 cpu <= bitmask_last(cpus_chosen); cpu++) { 318 319 if (!bitmask_isbitset(cpus_chosen, cpu) || 320 cpupower_is_cpu_online(cpu) != 1) 321 continue; 322 323 printf(_("Setting cpu: %d\n"), cpu); 324 ret = do_one_cpu(cpu, &new_pol, freq, policychange); 325 if (ret) { 326 print_error(); 327 return ret; 328 } 329 } 330 331 return 0; 332 } 333