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