blob: dd1539eb8c63de3e8d166b24a5f79b65702a4685 [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
Dominik Brodowski7fe2f632011-03-30 16:30:11 +020023static struct option set_opts[] = {
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +020024 { .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'},
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +020028 { .name = "related", .has_arg = no_argument, .flag = NULL, .val='r'},
Dominik Brodowski7fe2f632011-03-30 16:30:11 +020029 { },
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 {
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +020044 char *str_unit;
Dominik Brodowski7fe2f632011-03-30 16:30:11 +020045 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"));
Dominik Brodowski7fe2f632011-03-30 16:30:11 +020060}
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)
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +0200183 return cpufreq_modify_policy_governor(cpu,
184 new_pol->governor);
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200185
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 {
Dominik Brodowski498ca792011-08-06 18:11:43 +0200210 ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL);
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200211 switch (ret) {
212 case '?':
213 print_unknown_arg();
214 return -EINVAL;
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200215 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;
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +0200259 }
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200260 if ((sscanf(optarg, "%s", gov)) != 1) {
261 print_unknown_arg();
262 return -EINVAL;
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +0200263 }
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200264 new_pol.governor = gov;
265 break;
266 }
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +0200267 } while (cont);
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200268
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++) {
Dominik Brodowskia1ce5ba2011-04-19 20:33:50 +0200316
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200317 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}