xref: /linux/tools/power/cpupower/lib/cpufreq.c (revision 22d55f02b8922a097cd4be1e2f131dfa7ef65901)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
4  */
5 
6 
7 #include <stdio.h>
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 
16 #include "cpufreq.h"
17 #include "cpupower_intern.h"
18 
19 /* CPUFREQ sysfs access **************************************************/
20 
21 /* helper function to read file from /sys into given buffer */
22 /* fname is a relative path under "cpuX/cpufreq" dir */
23 static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,
24 					    char *buf, size_t buflen)
25 {
26 	char path[SYSFS_PATH_MAX];
27 
28 	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
29 			 cpu, fname);
30 	return cpupower_read_sysfs(path, buf, buflen);
31 }
32 
33 /* helper function to write a new value to a /sys file */
34 /* fname is a relative path under "cpuX/cpufreq" dir */
35 static unsigned int sysfs_cpufreq_write_file(unsigned int cpu,
36 					     const char *fname,
37 					     const char *value, size_t len)
38 {
39 	char path[SYSFS_PATH_MAX];
40 	int fd;
41 	ssize_t numwrite;
42 
43 	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
44 			 cpu, fname);
45 
46 	fd = open(path, O_WRONLY);
47 	if (fd == -1)
48 		return 0;
49 
50 	numwrite = write(fd, value, len);
51 	if (numwrite < 1) {
52 		close(fd);
53 		return 0;
54 	}
55 
56 	close(fd);
57 
58 	return (unsigned int) numwrite;
59 }
60 
61 /* read access to files which contain one numeric value */
62 
63 enum cpufreq_value {
64 	CPUINFO_CUR_FREQ,
65 	CPUINFO_MIN_FREQ,
66 	CPUINFO_MAX_FREQ,
67 	CPUINFO_LATENCY,
68 	SCALING_CUR_FREQ,
69 	SCALING_MIN_FREQ,
70 	SCALING_MAX_FREQ,
71 	STATS_NUM_TRANSITIONS,
72 	MAX_CPUFREQ_VALUE_READ_FILES
73 };
74 
75 static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {
76 	[CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq",
77 	[CPUINFO_MIN_FREQ] = "cpuinfo_min_freq",
78 	[CPUINFO_MAX_FREQ] = "cpuinfo_max_freq",
79 	[CPUINFO_LATENCY]  = "cpuinfo_transition_latency",
80 	[SCALING_CUR_FREQ] = "scaling_cur_freq",
81 	[SCALING_MIN_FREQ] = "scaling_min_freq",
82 	[SCALING_MAX_FREQ] = "scaling_max_freq",
83 	[STATS_NUM_TRANSITIONS] = "stats/total_trans"
84 };
85 
86 
87 static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,
88 						 enum cpufreq_value which)
89 {
90 	unsigned long value;
91 	unsigned int len;
92 	char linebuf[MAX_LINE_LEN];
93 	char *endp;
94 
95 	if (which >= MAX_CPUFREQ_VALUE_READ_FILES)
96 		return 0;
97 
98 	len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which],
99 				linebuf, sizeof(linebuf));
100 
101 	if (len == 0)
102 		return 0;
103 
104 	value = strtoul(linebuf, &endp, 0);
105 
106 	if (endp == linebuf || errno == ERANGE)
107 		return 0;
108 
109 	return value;
110 }
111 
112 /* read access to files which contain one string */
113 
114 enum cpufreq_string {
115 	SCALING_DRIVER,
116 	SCALING_GOVERNOR,
117 	MAX_CPUFREQ_STRING_FILES
118 };
119 
120 static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
121 	[SCALING_DRIVER] = "scaling_driver",
122 	[SCALING_GOVERNOR] = "scaling_governor",
123 };
124 
125 
126 static char *sysfs_cpufreq_get_one_string(unsigned int cpu,
127 					   enum cpufreq_string which)
128 {
129 	char linebuf[MAX_LINE_LEN];
130 	char *result;
131 	unsigned int len;
132 
133 	if (which >= MAX_CPUFREQ_STRING_FILES)
134 		return NULL;
135 
136 	len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
137 				linebuf, sizeof(linebuf));
138 	if (len == 0)
139 		return NULL;
140 
141 	result = strdup(linebuf);
142 	if (result == NULL)
143 		return NULL;
144 
145 	if (result[strlen(result) - 1] == '\n')
146 		result[strlen(result) - 1] = '\0';
147 
148 	return result;
149 }
150 
151 /* write access */
152 
153 enum cpufreq_write {
154 	WRITE_SCALING_MIN_FREQ,
155 	WRITE_SCALING_MAX_FREQ,
156 	WRITE_SCALING_GOVERNOR,
157 	WRITE_SCALING_SET_SPEED,
158 	MAX_CPUFREQ_WRITE_FILES
159 };
160 
161 static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
162 	[WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
163 	[WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
164 	[WRITE_SCALING_GOVERNOR] = "scaling_governor",
165 	[WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
166 };
167 
168 static int sysfs_cpufreq_write_one_value(unsigned int cpu,
169 					 enum cpufreq_write which,
170 					 const char *new_value, size_t len)
171 {
172 	if (which >= MAX_CPUFREQ_WRITE_FILES)
173 		return 0;
174 
175 	if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
176 					new_value, len) != len)
177 		return -ENODEV;
178 
179 	return 0;
180 };
181 
182 unsigned long cpufreq_get_freq_kernel(unsigned int cpu)
183 {
184 	return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
185 }
186 
187 unsigned long cpufreq_get_freq_hardware(unsigned int cpu)
188 {
189 	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
190 }
191 
192 unsigned long cpufreq_get_transition_latency(unsigned int cpu)
193 {
194 	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
195 }
196 
197 int cpufreq_get_hardware_limits(unsigned int cpu,
198 				unsigned long *min,
199 				unsigned long *max)
200 {
201 	if ((!min) || (!max))
202 		return -EINVAL;
203 
204 	*min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);
205 	if (!*min)
206 		return -ENODEV;
207 
208 	*max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
209 	if (!*max)
210 		return -ENODEV;
211 
212 	return 0;
213 }
214 
215 char *cpufreq_get_driver(unsigned int cpu)
216 {
217 	return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
218 }
219 
220 void cpufreq_put_driver(char *ptr)
221 {
222 	if (!ptr)
223 		return;
224 	free(ptr);
225 }
226 
227 struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu)
228 {
229 	struct cpufreq_policy *policy;
230 
231 	policy = malloc(sizeof(struct cpufreq_policy));
232 	if (!policy)
233 		return NULL;
234 
235 	policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
236 	if (!policy->governor) {
237 		free(policy);
238 		return NULL;
239 	}
240 	policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
241 	policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);
242 	if ((!policy->min) || (!policy->max)) {
243 		free(policy->governor);
244 		free(policy);
245 		return NULL;
246 	}
247 
248 	return policy;
249 }
250 
251 void cpufreq_put_policy(struct cpufreq_policy *policy)
252 {
253 	if ((!policy) || (!policy->governor))
254 		return;
255 
256 	free(policy->governor);
257 	policy->governor = NULL;
258 	free(policy);
259 }
260 
261 struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned
262 								int cpu)
263 {
264 	struct cpufreq_available_governors *first = NULL;
265 	struct cpufreq_available_governors *current = NULL;
266 	char linebuf[MAX_LINE_LEN];
267 	unsigned int pos, i;
268 	unsigned int len;
269 
270 	len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
271 				linebuf, sizeof(linebuf));
272 	if (len == 0)
273 		return NULL;
274 
275 	pos = 0;
276 	for (i = 0; i < len; i++) {
277 		if (linebuf[i] == ' ' || linebuf[i] == '\n') {
278 			if (i - pos < 2)
279 				continue;
280 			if (current) {
281 				current->next = malloc(sizeof(*current));
282 				if (!current->next)
283 					goto error_out;
284 				current = current->next;
285 			} else {
286 				first = malloc(sizeof(*first));
287 				if (!first)
288 					goto error_out;
289 				current = first;
290 			}
291 			current->first = first;
292 			current->next = NULL;
293 
294 			current->governor = malloc(i - pos + 1);
295 			if (!current->governor)
296 				goto error_out;
297 
298 			memcpy(current->governor, linebuf + pos, i - pos);
299 			current->governor[i - pos] = '\0';
300 			pos = i + 1;
301 		}
302 	}
303 
304 	return first;
305 
306  error_out:
307 	while (first) {
308 		current = first->next;
309 		if (first->governor)
310 			free(first->governor);
311 		free(first);
312 		first = current;
313 	}
314 	return NULL;
315 }
316 
317 void cpufreq_put_available_governors(struct cpufreq_available_governors *any)
318 {
319 	struct cpufreq_available_governors *tmp, *next;
320 
321 	if (!any)
322 		return;
323 
324 	tmp = any->first;
325 	while (tmp) {
326 		next = tmp->next;
327 		if (tmp->governor)
328 			free(tmp->governor);
329 		free(tmp);
330 		tmp = next;
331 	}
332 }
333 
334 
335 struct cpufreq_frequencies
336 *cpufreq_get_frequencies(const char *type, unsigned int cpu)
337 {
338 	struct cpufreq_frequencies *first = NULL;
339 	struct cpufreq_frequencies *current = NULL;
340 	char one_value[SYSFS_PATH_MAX];
341 	char linebuf[MAX_LINE_LEN];
342 	char fname[MAX_LINE_LEN];
343 	unsigned int pos, i;
344 	unsigned int len;
345 
346 	snprintf(fname, MAX_LINE_LEN, "scaling_%s_frequencies", type);
347 
348 	len = sysfs_cpufreq_read_file(cpu, fname,
349 				linebuf, sizeof(linebuf));
350 	if (len == 0)
351 		return NULL;
352 
353 	pos = 0;
354 	for (i = 0; i < len; i++) {
355 		if (linebuf[i] == ' ' || linebuf[i] == '\n') {
356 			if (i - pos < 2)
357 				continue;
358 			if (i - pos >= SYSFS_PATH_MAX)
359 				goto error_out;
360 			if (current) {
361 				current->next = malloc(sizeof(*current));
362 				if (!current->next)
363 					goto error_out;
364 				current = current->next;
365 			} else {
366 				first = malloc(sizeof(*first));
367 				if (!first)
368 					goto error_out;
369 				current = first;
370 			}
371 			current->first = first;
372 			current->next = NULL;
373 
374 			memcpy(one_value, linebuf + pos, i - pos);
375 			one_value[i - pos] = '\0';
376 			if (sscanf(one_value, "%lu", &current->frequency) != 1)
377 				goto error_out;
378 
379 			pos = i + 1;
380 		}
381 	}
382 
383 	return first;
384 
385  error_out:
386 	while (first) {
387 		current = first->next;
388 		free(first);
389 		first = current;
390 	}
391 	return NULL;
392 }
393 
394 void cpufreq_put_frequencies(struct cpufreq_frequencies *any)
395 {
396 	struct cpufreq_frequencies *tmp, *next;
397 
398 	if (!any)
399 		return;
400 
401 	tmp = any->first;
402 	while (tmp) {
403 		next = tmp->next;
404 		free(tmp);
405 		tmp = next;
406 	}
407 }
408 
409 static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
410 							const char *file)
411 {
412 	struct cpufreq_affected_cpus *first = NULL;
413 	struct cpufreq_affected_cpus *current = NULL;
414 	char one_value[SYSFS_PATH_MAX];
415 	char linebuf[MAX_LINE_LEN];
416 	unsigned int pos, i;
417 	unsigned int len;
418 
419 	len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
420 	if (len == 0)
421 		return NULL;
422 
423 	pos = 0;
424 	for (i = 0; i < len; i++) {
425 		if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
426 			if (i - pos  < 1)
427 				continue;
428 			if (i - pos >= SYSFS_PATH_MAX)
429 				goto error_out;
430 			if (current) {
431 				current->next = malloc(sizeof(*current));
432 				if (!current->next)
433 					goto error_out;
434 				current = current->next;
435 			} else {
436 				first = malloc(sizeof(*first));
437 				if (!first)
438 					goto error_out;
439 				current = first;
440 			}
441 			current->first = first;
442 			current->next = NULL;
443 
444 			memcpy(one_value, linebuf + pos, i - pos);
445 			one_value[i - pos] = '\0';
446 
447 			if (sscanf(one_value, "%u", &current->cpu) != 1)
448 				goto error_out;
449 
450 			pos = i + 1;
451 		}
452 	}
453 
454 	return first;
455 
456  error_out:
457 	while (first) {
458 		current = first->next;
459 		free(first);
460 		first = current;
461 	}
462 	return NULL;
463 }
464 
465 struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu)
466 {
467 	return sysfs_get_cpu_list(cpu, "affected_cpus");
468 }
469 
470 void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any)
471 {
472 	struct cpufreq_affected_cpus *tmp, *next;
473 
474 	if (!any)
475 		return;
476 
477 	tmp = any->first;
478 	while (tmp) {
479 		next = tmp->next;
480 		free(tmp);
481 		tmp = next;
482 	}
483 }
484 
485 
486 struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu)
487 {
488 	return sysfs_get_cpu_list(cpu, "related_cpus");
489 }
490 
491 void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any)
492 {
493 	cpufreq_put_affected_cpus(any);
494 }
495 
496 static int verify_gov(char *new_gov, char *passed_gov)
497 {
498 	unsigned int i, j = 0;
499 
500 	if (!passed_gov || (strlen(passed_gov) > 19))
501 		return -EINVAL;
502 
503 	strncpy(new_gov, passed_gov, 20);
504 	for (i = 0; i < 20; i++) {
505 		if (j) {
506 			new_gov[i] = '\0';
507 			continue;
508 		}
509 		if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))
510 			continue;
511 
512 		if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))
513 			continue;
514 
515 		if (new_gov[i] == '-')
516 			continue;
517 
518 		if (new_gov[i] == '_')
519 			continue;
520 
521 		if (new_gov[i] == '\0') {
522 			j = 1;
523 			continue;
524 		}
525 		return -EINVAL;
526 	}
527 	new_gov[19] = '\0';
528 	return 0;
529 }
530 
531 int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy)
532 {
533 	char min[SYSFS_PATH_MAX];
534 	char max[SYSFS_PATH_MAX];
535 	char gov[SYSFS_PATH_MAX];
536 	int ret;
537 	unsigned long old_min;
538 	int write_max_first;
539 
540 	if (!policy || !(policy->governor))
541 		return -EINVAL;
542 
543 	if (policy->max < policy->min)
544 		return -EINVAL;
545 
546 	if (verify_gov(gov, policy->governor))
547 		return -EINVAL;
548 
549 	snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
550 	snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
551 
552 	old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
553 	write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
554 
555 	if (write_max_first) {
556 		ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
557 						    max, strlen(max));
558 		if (ret)
559 			return ret;
560 	}
561 
562 	ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
563 					    strlen(min));
564 	if (ret)
565 		return ret;
566 
567 	if (!write_max_first) {
568 		ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
569 						    max, strlen(max));
570 		if (ret)
571 			return ret;
572 	}
573 
574 	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
575 					     gov, strlen(gov));
576 }
577 
578 
579 int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq)
580 {
581 	char value[SYSFS_PATH_MAX];
582 
583 	snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
584 
585 	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
586 					     value, strlen(value));
587 }
588 
589 
590 int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq)
591 {
592 	char value[SYSFS_PATH_MAX];
593 
594 	snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
595 
596 	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
597 					     value, strlen(value));
598 }
599 
600 int cpufreq_modify_policy_governor(unsigned int cpu, char *governor)
601 {
602 	char new_gov[SYSFS_PATH_MAX];
603 
604 	if ((!governor) || (strlen(governor) > 19))
605 		return -EINVAL;
606 
607 	if (verify_gov(new_gov, governor))
608 		return -EINVAL;
609 
610 	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
611 					     new_gov, strlen(new_gov));
612 }
613 
614 int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency)
615 {
616 	struct cpufreq_policy *pol = cpufreq_get_policy(cpu);
617 	char userspace_gov[] = "userspace";
618 	char freq[SYSFS_PATH_MAX];
619 	int ret;
620 
621 	if (!pol)
622 		return -ENODEV;
623 
624 	if (strncmp(pol->governor, userspace_gov, 9) != 0) {
625 		ret = cpufreq_modify_policy_governor(cpu, userspace_gov);
626 		if (ret) {
627 			cpufreq_put_policy(pol);
628 			return ret;
629 		}
630 	}
631 
632 	cpufreq_put_policy(pol);
633 
634 	snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
635 
636 	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
637 					     freq, strlen(freq));
638 }
639 
640 struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu,
641 					unsigned long long *total_time)
642 {
643 	struct cpufreq_stats *first = NULL;
644 	struct cpufreq_stats *current = NULL;
645 	char one_value[SYSFS_PATH_MAX];
646 	char linebuf[MAX_LINE_LEN];
647 	unsigned int pos, i;
648 	unsigned int len;
649 
650 	len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
651 				linebuf, sizeof(linebuf));
652 	if (len == 0)
653 		return NULL;
654 
655 	*total_time = 0;
656 	pos = 0;
657 	for (i = 0; i < len; i++) {
658 		if (i == strlen(linebuf) || linebuf[i] == '\n')	{
659 			if (i - pos < 2)
660 				continue;
661 			if ((i - pos) >= SYSFS_PATH_MAX)
662 				goto error_out;
663 			if (current) {
664 				current->next = malloc(sizeof(*current));
665 				if (!current->next)
666 					goto error_out;
667 				current = current->next;
668 			} else {
669 				first = malloc(sizeof(*first));
670 				if (!first)
671 					goto error_out;
672 				current = first;
673 			}
674 			current->first = first;
675 			current->next = NULL;
676 
677 			memcpy(one_value, linebuf + pos, i - pos);
678 			one_value[i - pos] = '\0';
679 			if (sscanf(one_value, "%lu %llu",
680 					&current->frequency,
681 					&current->time_in_state) != 2)
682 				goto error_out;
683 
684 			*total_time = *total_time + current->time_in_state;
685 			pos = i + 1;
686 		}
687 	}
688 
689 	return first;
690 
691  error_out:
692 	while (first) {
693 		current = first->next;
694 		free(first);
695 		first = current;
696 	}
697 	return NULL;
698 }
699 
700 void cpufreq_put_stats(struct cpufreq_stats *any)
701 {
702 	struct cpufreq_stats *tmp, *next;
703 
704 	if (!any)
705 		return;
706 
707 	tmp = any->first;
708 	while (tmp) {
709 		next = tmp->next;
710 		free(tmp);
711 		tmp = next;
712 	}
713 }
714 
715 unsigned long cpufreq_get_transitions(unsigned int cpu)
716 {
717 	return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
718 }
719