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++; 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 144 static 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 171 static 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 can 180 * 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 196 int cmd_freq_set(int argc, char **argv) 197 { 198 int ret = 0, cont = 1; 199 int double_parm = 0, related = 0, policychange = 0; 200 unsigned long freq = 0; 201 char gov[20]; 202 unsigned int cpu; 203 204 struct cpufreq_policy new_pol = { 205 .min = 0, 206 .max = 0, 207 .governor = NULL, 208 }; 209 210 /* parameter parsing */ 211 do { 212 ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL); 213 switch (ret) { 214 case '?': 215 print_unknown_arg(); 216 return -EINVAL; 217 case -1: 218 cont = 0; 219 break; 220 case 'r': 221 if (related) 222 double_parm++; 223 related++; 224 break; 225 case 'd': 226 if (new_pol.min) 227 double_parm++; 228 policychange++; 229 new_pol.min = string_to_frequency(optarg); 230 if (new_pol.min == 0) { 231 print_unknown_arg(); 232 return -EINVAL; 233 } 234 break; 235 case 'u': 236 if (new_pol.max) 237 double_parm++; 238 policychange++; 239 new_pol.max = string_to_frequency(optarg); 240 if (new_pol.max == 0) { 241 print_unknown_arg(); 242 return -EINVAL; 243 } 244 break; 245 case 'f': 246 if (freq) 247 double_parm++; 248 freq = string_to_frequency(optarg); 249 if (freq == 0) { 250 print_unknown_arg(); 251 return -EINVAL; 252 } 253 break; 254 case 'g': 255 if (new_pol.governor) 256 double_parm++; 257 policychange++; 258 if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) { 259 print_unknown_arg(); 260 return -EINVAL; 261 } 262 if ((sscanf(optarg, "%19s", gov)) != 1) { 263 print_unknown_arg(); 264 return -EINVAL; 265 } 266 new_pol.governor = gov; 267 break; 268 } 269 } while (cont); 270 271 /* parameter checking */ 272 if (double_parm) { 273 printf("the same parameter was passed more than once\n"); 274 return -EINVAL; 275 } 276 277 if (freq && policychange) { 278 printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n" 279 "-g/--governor parameters\n")); 280 return -EINVAL; 281 } 282 283 if (!freq && !policychange) { 284 printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n" 285 "-g/--governor must be passed\n")); 286 return -EINVAL; 287 } 288 289 /* Default is: set all CPUs */ 290 if (bitmask_isallclear(cpus_chosen)) 291 bitmask_setall(cpus_chosen); 292 293 /* Also set frequency settings for related CPUs if -r is passed */ 294 if (related) { 295 for (cpu = bitmask_first(cpus_chosen); 296 cpu <= bitmask_last(cpus_chosen); cpu++) { 297 struct cpufreq_affected_cpus *cpus; 298 299 if (!bitmask_isbitset(cpus_chosen, cpu) || 300 cpupower_is_cpu_online(cpu) != 1) 301 continue; 302 303 cpus = cpufreq_get_related_cpus(cpu); 304 if (!cpus) 305 break; 306 while (cpus->next) { 307 bitmask_setbit(cpus_chosen, cpus->cpu); 308 cpus = cpus->next; 309 } 310 /* Set the last cpu in related cpus list */ 311 bitmask_setbit(cpus_chosen, cpus->cpu); 312 cpufreq_put_related_cpus(cpus); 313 } 314 } 315 316 get_cpustate(); 317 318 /* loop over CPUs */ 319 for (cpu = bitmask_first(cpus_chosen); 320 cpu <= bitmask_last(cpus_chosen); cpu++) { 321 322 if (!bitmask_isbitset(cpus_chosen, cpu) || 323 cpupower_is_cpu_online(cpu) != 1) 324 continue; 325 326 printf(_("Setting cpu: %d\n"), cpu); 327 ret = do_one_cpu(cpu, &new_pol, freq, policychange); 328 if (ret) { 329 print_error(); 330 return ret; 331 } 332 } 333 334 print_offline_cpus(); 335 336 return 0; 337 } 338