xref: /linux/tools/power/cpupower/utils/cpufreq-set.c (revision 1ccd4b7bfdcfcc8cc7ffc4a9c11d3ac5b6da8ca0)
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 void 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 
45 static struct option set_opts[] = {
46 	{ .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'},
52 	{ },
53 };
54 
55 static 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 
66 struct freq_units {
67 	char		*str_unit;
68 	int		power_of_ten;
69 };
70 
71 const 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 
80 static void print_unknown_arg(void)
81 {
82 	printf(_("invalid or unknown argument\n"));
83 	freq_set_help();
84 }
85 
86 static 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 
164 static 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 
191 static 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)
207 			return cpufreq_modify_policy_governor(cpu,
208 							new_pol->governor);
209 
210 	default:
211 		/* slow path */
212 		return do_new_policy(cpu, new_pol);
213 	}
214 }
215 
216 int 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;
286 			}
287 			if ((sscanf(optarg, "%s", gov)) != 1) {
288 				print_unknown_arg();
289 				return -EINVAL;
290 			}
291 			new_pol.governor = gov;
292 			break;
293 		}
294 	} while (cont);
295 
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++) {
343 
344 		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 }
359