1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
4 */
5
6
7#include <unistd.h>
8#include <stdio.h>
9#include <errno.h>
10#include <stdlib.h>
11#include <limits.h>
12#include <string.h>
13#include <ctype.h>
14
15#include <getopt.h>
16
17#include "cpufreq.h"
18#include "cpuidle.h"
19#include "helpers/helpers.h"
20
21#define NORM_FREQ_LEN 32
22
23static struct option set_opts[] = {
24 {"min", required_argument, NULL, 'd'},
25 {"max", required_argument, NULL, 'u'},
26 {"governor", required_argument, NULL, 'g'},
27 {"freq", required_argument, NULL, 'f'},
28 {"related", no_argument, NULL, '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++;
104 power--;
105 }
106 }
107 if (power >= -1) { /* not enough => pad */
108 pad = power + 1;
109 } else { /* too much => strip */
110 pad = 0;
111 cp += power + 1;
112 }
113 /* check bounds */
114 if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1)
115 return 0;
116
117 /* copy digits */
118 for (i = 0; i < cp; i++, str++) {
119 if (*str == '.')
120 str++;
121 normalized[i] = *str;
122 }
123 /* and pad */
124 for (; i < cp + pad; i++)
125 normalized[i] = '0';
126
127 /* round up, down ? */
128 match_count = (normalized[i-1] >= '5');
129 /* and drop the decimal part */
130 normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */
131
132 /* final conversion (and applying rounding) */
133 errno = 0;
134 freq = strtoul(normalized, &end, 10);
135 if (errno)
136 return 0;
137 else {
138 if (match_count && freq != ULONG_MAX)
139 freq++;
140 return freq;
141 }
142}
143
144static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol)
145{
146 struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu);
147 int ret;
148
149 if (!cur_pol) {
150 printf(_("wrong, unknown or unhandled CPU?\n"));
151 return -EINVAL;
152 }
153
154 if (!new_pol->min)
155 new_pol->min = cur_pol->min;
156
157 if (!new_pol->max)
158 new_pol->max = cur_pol->max;
159
160 if (!new_pol->governor)
161 new_pol->governor = cur_pol->governor;
162
163 ret = cpufreq_set_policy(cpu, new_pol);
164
165 cpufreq_put_policy(cur_pol);
166
167 return ret;
168}
169
170
171static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol,
172 unsigned long freq, unsigned int pc)
173{
174 switch (pc) {
175 case 0:
176 return cpufreq_set_frequency(cpu, freq);
177
178 case 1:
179 /* if only one value of a policy is to be changed, we can
180 * use a "fast path".
181 */
182 if (new_pol->min)
183 return cpufreq_modify_policy_min(cpu, new_pol->min);
184 else if (new_pol->max)
185 return cpufreq_modify_policy_max(cpu, new_pol->max);
186 else if (new_pol->governor)
187 return cpufreq_modify_policy_governor(cpu,
188 new_pol->governor);
189
190 default:
191 /* slow path */
192 return do_new_policy(cpu, new_pol);
193 }
194}
195
196int cmd_freq_set(int argc, char **argv)
197{
198 extern char *optarg;
199 extern int optind, opterr, optopt;
200 int ret = 0, cont = 1;
201 int double_parm = 0, related = 0, policychange = 0;
202 unsigned long freq = 0;
203 char gov[20];
204 unsigned int cpu;
205
206 struct cpufreq_policy new_pol = {
207 .min = 0,
208 .max = 0,
209 .governor = NULL,
210 };
211
212 /* parameter parsing */
213 do {
214 ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL);
215 switch (ret) {
216 case '?':
217 print_unknown_arg();
218 return -EINVAL;
219 case -1:
220 cont = 0;
221 break;
222 case 'r':
223 if (related)
224 double_parm++;
225 related++;
226 break;
227 case 'd':
228 if (new_pol.min)
229 double_parm++;
230 policychange++;
231 new_pol.min = string_to_frequency(str: optarg);
232 if (new_pol.min == 0) {
233 print_unknown_arg();
234 return -EINVAL;
235 }
236 break;
237 case 'u':
238 if (new_pol.max)
239 double_parm++;
240 policychange++;
241 new_pol.max = string_to_frequency(str: optarg);
242 if (new_pol.max == 0) {
243 print_unknown_arg();
244 return -EINVAL;
245 }
246 break;
247 case 'f':
248 if (freq)
249 double_parm++;
250 freq = string_to_frequency(str: optarg);
251 if (freq == 0) {
252 print_unknown_arg();
253 return -EINVAL;
254 }
255 break;
256 case 'g':
257 if (new_pol.governor)
258 double_parm++;
259 policychange++;
260 if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) {
261 print_unknown_arg();
262 return -EINVAL;
263 }
264 if ((sscanf(optarg, "%19s", gov)) != 1) {
265 print_unknown_arg();
266 return -EINVAL;
267 }
268 new_pol.governor = gov;
269 break;
270 }
271 } while (cont);
272
273 /* parameter checking */
274 if (double_parm) {
275 printf("the same parameter was passed more than once\n");
276 return -EINVAL;
277 }
278
279 if (freq && policychange) {
280 printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n"
281 "-g/--governor parameters\n"));
282 return -EINVAL;
283 }
284
285 if (!freq && !policychange) {
286 printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n"
287 "-g/--governor must be passed\n"));
288 return -EINVAL;
289 }
290
291 /* Default is: set all CPUs */
292 if (bitmask_isallclear(cpus_chosen))
293 bitmask_setall(cpus_chosen);
294
295 /* Also set frequency settings for related CPUs if -r is passed */
296 if (related) {
297 for (cpu = bitmask_first(cpus_chosen);
298 cpu <= bitmask_last(cpus_chosen); cpu++) {
299 struct cpufreq_affected_cpus *cpus;
300
301 if (!bitmask_isbitset(cpus_chosen, cpu) ||
302 cpupower_is_cpu_online(cpu) != 1)
303 continue;
304
305 cpus = cpufreq_get_related_cpus(cpu);
306 if (!cpus)
307 break;
308 while (cpus->next) {
309 bitmask_setbit(cpus_chosen, cpus->cpu);
310 cpus = cpus->next;
311 }
312 /* Set the last cpu in related cpus list */
313 bitmask_setbit(cpus_chosen, cpus->cpu);
314 cpufreq_put_related_cpus(cpus);
315 }
316 }
317
318 get_cpustate();
319
320 /* loop over CPUs */
321 for (cpu = bitmask_first(cpus_chosen);
322 cpu <= bitmask_last(cpus_chosen); cpu++) {
323
324 if (!bitmask_isbitset(cpus_chosen, cpu) ||
325 cpupower_is_cpu_online(cpu) != 1)
326 continue;
327
328 printf(_("Setting cpu: %d\n"), cpu);
329 ret = do_one_cpu(cpu, new_pol: &new_pol, freq, pc: policychange);
330 if (ret) {
331 print_error();
332 return ret;
333 }
334 }
335
336 print_offline_cpus();
337
338 return 0;
339}
340

source code of linux/tools/power/cpupower/utils/cpufreq-set.c