Linux 6.7-rc7
[linux-modified.git] / tools / power / cpupower / utils / cpufreq-set.c
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
23 static 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
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++;
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
144 static 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
171 static 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
196 int 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(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(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(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, freq, policychange);
330                 if (ret) {
331                         print_error();
332                         return ret;
333                 }
334         }
335
336         print_offline_cpus();
337
338         return 0;
339 }