Path: blob/master/tools/power/cpupower/utils/cpufreq-set.c
26295 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* (C) 2004-2009 Dominik Brodowski <[email protected]>3*/456#include <unistd.h>7#include <stdio.h>8#include <errno.h>9#include <stdlib.h>10#include <limits.h>11#include <string.h>12#include <ctype.h>1314#include <getopt.h>1516#include "cpufreq.h"17#include "cpuidle.h"18#include "helpers/helpers.h"1920#define NORM_FREQ_LEN 322122static struct option set_opts[] = {23{"min", required_argument, NULL, 'd'},24{"max", required_argument, NULL, 'u'},25{"governor", required_argument, NULL, 'g'},26{"freq", required_argument, NULL, 'f'},27{"related", no_argument, NULL, 'r'},28{ },29};3031static void print_error(void)32{33printf(_("Error setting new values. Common errors:\n"34"- Do you have proper administration rights? (super-user?)\n"35"- Is the governor you requested available and modprobed?\n"36"- Trying to set an invalid policy?\n"37"- Trying to set a specific frequency, but userspace governor is not available,\n"38" for example because of hardware which cannot be set to a specific frequency\n"39" or because the userspace governor isn't loaded?\n"));40};4142struct freq_units {43char *str_unit;44int power_of_ten;45};4647const struct freq_units def_units[] = {48{"hz", -3},49{"khz", 0}, /* default */50{"mhz", 3},51{"ghz", 6},52{"thz", 9},53{NULL, 0}54};5556static void print_unknown_arg(void)57{58printf(_("invalid or unknown argument\n"));59}6061static unsigned long string_to_frequency(const char *str)62{63char normalized[NORM_FREQ_LEN];64const struct freq_units *unit;65const char *scan;66char *end;67unsigned long freq;68int power = 0, match_count = 0, i, cp, pad;6970while (*str == '0')71str++;7273for (scan = str; isdigit(*scan) || *scan == '.'; scan++) {74if (*scan == '.' && match_count == 0)75match_count = 1;76else if (*scan == '.' && match_count == 1)77return 0;78}7980if (*scan) {81match_count = 0;82for (unit = def_units; unit->str_unit; unit++) {83for (i = 0;84scan[i] && tolower(scan[i]) == unit->str_unit[i];85++i)86continue;87if (scan[i])88continue;89match_count++;90power = unit->power_of_ten;91}92if (match_count != 1)93return 0;94}9596/* count the number of digits to be copied */97for (cp = 0; isdigit(str[cp]); cp++)98continue;99100if (str[cp] == '.') {101while (power > -1 && isdigit(str[cp+1])) {102cp++;103power--;104}105}106if (power >= -1) { /* not enough => pad */107pad = power + 1;108} else { /* too much => strip */109pad = 0;110cp += power + 1;111}112/* check bounds */113if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1)114return 0;115116/* copy digits */117for (i = 0; i < cp; i++, str++) {118if (*str == '.')119str++;120normalized[i] = *str;121}122/* and pad */123for (; i < cp + pad; i++)124normalized[i] = '0';125126/* round up, down ? */127match_count = (normalized[i-1] >= '5');128/* and drop the decimal part */129normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */130131/* final conversion (and applying rounding) */132errno = 0;133freq = strtoul(normalized, &end, 10);134if (errno)135return 0;136else {137if (match_count && freq != ULONG_MAX)138freq++;139return freq;140}141}142143static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol)144{145struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu);146int ret;147148if (!cur_pol) {149printf(_("wrong, unknown or unhandled CPU?\n"));150return -EINVAL;151}152153if (!new_pol->min)154new_pol->min = cur_pol->min;155156if (!new_pol->max)157new_pol->max = cur_pol->max;158159if (!new_pol->governor)160new_pol->governor = cur_pol->governor;161162ret = cpufreq_set_policy(cpu, new_pol);163164cpufreq_put_policy(cur_pol);165166return ret;167}168169170static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol,171unsigned long freq, unsigned int pc)172{173switch (pc) {174case 0:175return cpufreq_set_frequency(cpu, freq);176177case 1:178/* if only one value of a policy is to be changed, we can179* use a "fast path".180*/181if (new_pol->min)182return cpufreq_modify_policy_min(cpu, new_pol->min);183else if (new_pol->max)184return cpufreq_modify_policy_max(cpu, new_pol->max);185else if (new_pol->governor)186return cpufreq_modify_policy_governor(cpu,187new_pol->governor);188189default:190/* slow path */191return do_new_policy(cpu, new_pol);192}193}194195int cmd_freq_set(int argc, char **argv)196{197extern char *optarg;198extern int optind, opterr, optopt;199int ret = 0, cont = 1;200int double_parm = 0, related = 0, policychange = 0;201unsigned long freq = 0;202char gov[20];203unsigned int cpu;204205struct cpufreq_policy new_pol = {206.min = 0,207.max = 0,208.governor = NULL,209};210211/* parameter parsing */212do {213ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL);214switch (ret) {215case '?':216print_unknown_arg();217return -EINVAL;218case -1:219cont = 0;220break;221case 'r':222if (related)223double_parm++;224related++;225break;226case 'd':227if (new_pol.min)228double_parm++;229policychange++;230new_pol.min = string_to_frequency(optarg);231if (new_pol.min == 0) {232print_unknown_arg();233return -EINVAL;234}235break;236case 'u':237if (new_pol.max)238double_parm++;239policychange++;240new_pol.max = string_to_frequency(optarg);241if (new_pol.max == 0) {242print_unknown_arg();243return -EINVAL;244}245break;246case 'f':247if (freq)248double_parm++;249freq = string_to_frequency(optarg);250if (freq == 0) {251print_unknown_arg();252return -EINVAL;253}254break;255case 'g':256if (new_pol.governor)257double_parm++;258policychange++;259if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) {260print_unknown_arg();261return -EINVAL;262}263if ((sscanf(optarg, "%19s", gov)) != 1) {264print_unknown_arg();265return -EINVAL;266}267new_pol.governor = gov;268break;269}270} while (cont);271272/* parameter checking */273if (double_parm) {274printf("the same parameter was passed more than once\n");275return -EINVAL;276}277278if (freq && policychange) {279printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n"280"-g/--governor parameters\n"));281return -EINVAL;282}283284if (!freq && !policychange) {285printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n"286"-g/--governor must be passed\n"));287return -EINVAL;288}289290/* Default is: set all CPUs */291if (bitmask_isallclear(cpus_chosen))292bitmask_setall(cpus_chosen);293294/* Also set frequency settings for related CPUs if -r is passed */295if (related) {296for (cpu = bitmask_first(cpus_chosen);297cpu <= bitmask_last(cpus_chosen); cpu++) {298struct cpufreq_affected_cpus *cpus;299300if (!bitmask_isbitset(cpus_chosen, cpu) ||301cpupower_is_cpu_online(cpu) != 1)302continue;303304cpus = cpufreq_get_related_cpus(cpu);305if (!cpus)306break;307while (cpus->next) {308bitmask_setbit(cpus_chosen, cpus->cpu);309cpus = cpus->next;310}311/* Set the last cpu in related cpus list */312bitmask_setbit(cpus_chosen, cpus->cpu);313cpufreq_put_related_cpus(cpus);314}315}316317get_cpustate();318319/* loop over CPUs */320for (cpu = bitmask_first(cpus_chosen);321cpu <= bitmask_last(cpus_chosen); cpu++) {322323if (!bitmask_isbitset(cpus_chosen, cpu) ||324cpupower_is_cpu_online(cpu) != 1)325continue;326327printf(_("Setting cpu: %d\n"), cpu);328ret = do_one_cpu(cpu, &new_pol, freq, policychange);329if (ret) {330print_error();331return ret;332}333}334335print_offline_cpus();336337return 0;338}339340341