blob: 5f783622bf31f89e2618ff5b84a50fb37221957b [file] [log] [blame]
Dominik Brodowski7fe2f632011-03-30 16:30:11 +02001/*
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
23void 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
45static struct option set_opts[] = {
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +020046 { .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'},
Dominik Brodowski7fe2f632011-03-30 16:30:11 +020052 { },
53};
54
55static 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
66struct freq_units {
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +020067 char *str_unit;
Dominik Brodowski7fe2f632011-03-30 16:30:11 +020068 int power_of_ten;
69};
70
71const 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
80static void print_unknown_arg(void)
81{
82 printf(_("invalid or unknown argument\n"));
83 freq_set_help();
84}
85
86static 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
164static 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
191static 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)
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +0200207 return cpufreq_modify_policy_governor(cpu,
208 new_pol->governor);
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200209
210 default:
211 /* slow path */
212 return do_new_policy(cpu, new_pol);
213 }
214}
215
216int 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;
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +0200286 }
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200287 if ((sscanf(optarg, "%s", gov)) != 1) {
288 print_unknown_arg();
289 return -EINVAL;
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +0200290 }
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200291 new_pol.governor = gov;
292 break;
293 }
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +0200294 } while (cont);
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200295
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++) {
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +0200343
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200344 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}