linux/tools/power/cpupower/utils/cpufreq-set.c
<<
>>
Prefs
   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
  23static struct option set_opts[] = {
  24        { .name = "min",        .has_arg = required_argument,   .flag = NULL,   .val = 'd'},
  25        { .name = "max",        .has_arg = required_argument,   .flag = NULL,   .val = 'u'},
  26        { .name = "governor",   .has_arg = required_argument,   .flag = NULL,   .val = 'g'},
  27        { .name = "freq",       .has_arg = required_argument,   .flag = NULL,   .val = 'f'},
  28        { .name = "related",    .has_arg = no_argument,         .flag = NULL,   .val='r'},
  29        { },
  30};
  31
  32static 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
  43struct freq_units {
  44        char            *str_unit;
  45        int             power_of_ten;
  46};
  47
  48const 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
  57static void print_unknown_arg(void)
  58{
  59        printf(_("invalid or unknown argument\n"));
  60}
  61
  62static 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
 140static 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
 167static 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
 192int 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, "%s", 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                            cpufreq_cpu_exists(cpu))
 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                        cpufreq_put_related_cpus(cpus);
 309                }
 310        }
 311
 312
 313        /* loop over CPUs */
 314        for (cpu = bitmask_first(cpus_chosen);
 315             cpu <= bitmask_last(cpus_chosen); cpu++) {
 316
 317                if (!bitmask_isbitset(cpus_chosen, cpu) ||
 318                    cpufreq_cpu_exists(cpu))
 319                        continue;
 320
 321                printf(_("Setting cpu: %d\n"), cpu);
 322                ret = do_one_cpu(cpu, &new_pol, freq, policychange);
 323                if (ret)
 324                        break;
 325        }
 326
 327        if (ret)
 328                print_error();
 329
 330        return ret;
 331}
 332