Merge branch 'fix/hda' of git://github.com/tiwai/sound
[pandora-kernel.git] / tools / power / cpupower / utils / cpufreq-set.c
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
23 static 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
32 static 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
43 struct freq_units {
44         char            *str_unit;
45         int             power_of_ten;
46 };
47
48 const 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
57 static void print_unknown_arg(void)
58 {
59         printf(_("invalid or unknown argument\n"));
60 }
61
62 static 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
140 static 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
167 static 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
192 int 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 }