/* * (C) 2004-2009 Dominik Brodowski * * Licensed under the terms of the GNU GPL License version 2. */ #include #include #include #include #include #include #include #include #include "cpufreq.h" #include "helpers/helpers.h" #define NORM_FREQ_LEN 32 void freq_set_help(void) { printf(_("Usage: cpupower frequency-set [options]\n")); printf(_("Options:\n")); printf(_(" -d FREQ, --min FREQ new minimum CPU frequency the governor may select\n")); printf(_(" -u FREQ, --max FREQ new maximum CPU frequency the governor may select\n")); printf(_(" -g GOV, --governor GOV new cpufreq governor\n")); printf(_(" -f FREQ, --freq FREQ specific frequency to be set. Requires userspace\n" " governor to be available and loaded\n")); printf(_(" -r, --related Switches all hardware-related CPUs\n")); printf(_(" -h, --help Prints out this screen\n")); printf("\n"); printf(_("Notes:\n" "1. Omitting the -c or --cpu argument is equivalent to setting it to \"all\"\n")); printf(_("2. The -f FREQ, --freq FREQ parameter cannot be combined with any other parameter\n" " except the -c CPU, --cpu CPU parameter\n" "3. FREQuencies can be passed in Hz, kHz (default), MHz, GHz, or THz\n" " by postfixing the value with the wanted unit name, without any space\n" " (FREQuency in kHz =^ Hz * 0.001 =^ MHz * 1000 =^ GHz * 1000000).\n")); } static struct option set_opts[] = { { .name="min", .has_arg=required_argument, .flag=NULL, .val='d'}, { .name="max", .has_arg=required_argument, .flag=NULL, .val='u'}, { .name="governor", .has_arg=required_argument, .flag=NULL, .val='g'}, { .name="freq", .has_arg=required_argument, .flag=NULL, .val='f'}, { .name="help", .has_arg=no_argument, .flag=NULL, .val='h'}, { .name="related", .has_arg=no_argument, .flag=NULL, .val='r'}, { }, }; static void print_error(void) { printf(_("Error setting new values. Common errors:\n" "- Do you have proper administration rights? (super-user?)\n" "- Is the governor you requested available and modprobed?\n" "- Trying to set an invalid policy?\n" "- Trying to set a specific frequency, but userspace governor is not available,\n" " for example because of hardware which cannot be set to a specific frequency\n" " or because the userspace governor isn't loaded?\n")); }; struct freq_units { char* str_unit; int power_of_ten; }; const struct freq_units def_units[] = { {"hz", -3}, {"khz", 0}, /* default */ {"mhz", 3}, {"ghz", 6}, {"thz", 9}, {NULL, 0} }; static void print_unknown_arg(void) { printf(_("invalid or unknown argument\n")); freq_set_help(); } static unsigned long string_to_frequency(const char *str) { char normalized[NORM_FREQ_LEN]; const struct freq_units *unit; const char *scan; char *end; unsigned long freq; int power = 0, match_count = 0, i, cp, pad; while (*str == '0') str++; for (scan = str; isdigit(*scan) || *scan == '.'; scan++) { if (*scan == '.' && match_count == 0) match_count = 1; else if (*scan == '.' && match_count == 1) return 0; } if (*scan) { match_count = 0; for (unit = def_units; unit->str_unit; unit++) { for (i = 0; scan[i] && tolower(scan[i]) == unit->str_unit[i]; ++i) continue; if (scan[i]) continue; match_count++; power = unit->power_of_ten; } if (match_count != 1) return 0; } /* count the number of digits to be copied */ for (cp = 0; isdigit(str[cp]); cp++) continue; if (str[cp] == '.') { while (power > -1 && isdigit(str[cp+1])) cp++, power--; } if (power >= -1) /* not enough => pad */ pad = power + 1; else /* to much => strip */ pad = 0, cp += power + 1; /* check bounds */ if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1) return 0; /* copy digits */ for (i = 0; i < cp; i++, str++) { if (*str == '.') str++; normalized[i] = *str; } /* and pad */ for (; i < cp + pad; i++) normalized[i] = '0'; /* round up, down ? */ match_count = (normalized[i-1] >= '5'); /* and drop the decimal part */ normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */ /* final conversion (and applying rounding) */ errno = 0; freq = strtoul(normalized, &end, 10); if (errno) return 0; else { if (match_count && freq != ULONG_MAX) freq++; return freq; } } static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol) { struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu); int ret; if (!cur_pol) { printf(_("wrong, unknown or unhandled CPU?\n")); return -EINVAL; } if (!new_pol->min) new_pol->min = cur_pol->min; if (!new_pol->max) new_pol->max = cur_pol->max; if (!new_pol->governor) new_pol->governor = cur_pol->governor; ret = cpufreq_set_policy(cpu, new_pol); cpufreq_put_policy(cur_pol); return ret; } static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol, unsigned long freq, unsigned int pc) { switch (pc) { case 0: return cpufreq_set_frequency(cpu, freq); case 1: /* if only one value of a policy is to be changed, we can * use a "fast path". */ if (new_pol->min) return cpufreq_modify_policy_min(cpu, new_pol->min); else if (new_pol->max) return cpufreq_modify_policy_max(cpu, new_pol->max); else if (new_pol->governor) return cpufreq_modify_policy_governor(cpu, new_pol->governor); default: /* slow path */ return do_new_policy(cpu, new_pol); } } int cmd_freq_set(int argc, char **argv) { extern char *optarg; extern int optind, opterr, optopt; int ret = 0, cont = 1; int double_parm = 0, related = 0, policychange = 0; unsigned long freq = 0; char gov[20]; unsigned int cpu; struct cpufreq_policy new_pol = { .min = 0, .max = 0, .governor = NULL, }; /* parameter parsing */ do { ret = getopt_long(argc, argv, "d:u:g:f:hr", set_opts, NULL); switch (ret) { case '?': print_unknown_arg(); return -EINVAL; case 'h': freq_set_help(); return 0; case -1: cont = 0; break; case 'r': if (related) double_parm++; related++; break; case 'd': if (new_pol.min) double_parm++; policychange++; new_pol.min = string_to_frequency(optarg); if (new_pol.min == 0) { print_unknown_arg(); return -EINVAL; } break; case 'u': if (new_pol.max) double_parm++; policychange++; new_pol.max = string_to_frequency(optarg); if (new_pol.max == 0) { print_unknown_arg(); return -EINVAL; } break; case 'f': if (freq) double_parm++; freq = string_to_frequency(optarg); if (freq == 0) { print_unknown_arg(); return -EINVAL; } break; case 'g': if (new_pol.governor) double_parm++; policychange++; if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) { print_unknown_arg(); return -EINVAL; } if ((sscanf(optarg, "%s", gov)) != 1) { print_unknown_arg(); return -EINVAL; } new_pol.governor = gov; break; } } while(cont); /* parameter checking */ if (double_parm) { printf("the same parameter was passed more than once\n"); return -EINVAL; } if (freq && policychange) { printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n" "-g/--governor parameters\n")); return -EINVAL; } if (!freq && !policychange) { printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n" "-g/--governor must be passed\n")); return -EINVAL; } /* Default is: set all CPUs */ if (bitmask_isallclear(cpus_chosen)) bitmask_setall(cpus_chosen); /* Also set frequency settings for related CPUs if -r is passed */ if (related) { for (cpu = bitmask_first(cpus_chosen); cpu <= bitmask_last(cpus_chosen); cpu++) { struct cpufreq_affected_cpus *cpus; if (!bitmask_isbitset(cpus_chosen, cpu) || cpufreq_cpu_exists(cpu)) continue; cpus = cpufreq_get_related_cpus(cpu); if (!cpus) break; while (cpus->next) { bitmask_setbit(cpus_chosen, cpus->cpu); cpus = cpus->next; } cpufreq_put_related_cpus(cpus); } } /* loop over CPUs */ for (cpu = bitmask_first(cpus_chosen); cpu <= bitmask_last(cpus_chosen); cpu++) { if (!bitmask_isbitset(cpus_chosen, cpu) || cpufreq_cpu_exists(cpu)) continue; printf(_("Setting cpu: %d\n"), cpu); ret = do_one_cpu(cpu, &new_pol, freq, policychange); if (ret) break; } if (ret) print_error(); return ret; }