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 */
23static 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 */
35static 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
63enum 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
75static 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
86unsigned long cpufreq_get_sysfs_value_from_table(unsigned int cpu,
87 const char **table,
88 unsigned int index,
89 unsigned int size)
90{
91 unsigned long value;
92 unsigned int len;
93 char linebuf[MAX_LINE_LEN];
94 char *endp;
95
96 if (!table || index >= size || !table[index])
97 return 0;
98
99 len = sysfs_cpufreq_read_file(cpu, table[index], linebuf,
100 sizeof(linebuf));
101
102 if (len == 0)
103 return 0;
104
105 value = strtoul(linebuf, &endp, 0);
106
107 if (endp == linebuf || errno == ERANGE)
108 return 0;
109
110 return value;
111}
112
113static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,
114 enum cpufreq_value which)
115{
116 return cpufreq_get_sysfs_value_from_table(cpu, table: cpufreq_value_files,
117 index: which,
118 size: MAX_CPUFREQ_VALUE_READ_FILES);
119}
120
121/* read access to files which contain one string */
122
123enum cpufreq_string {
124 SCALING_DRIVER,
125 SCALING_GOVERNOR,
126 MAX_CPUFREQ_STRING_FILES
127};
128
129static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
130 [SCALING_DRIVER] = "scaling_driver",
131 [SCALING_GOVERNOR] = "scaling_governor",
132};
133
134
135static char *sysfs_cpufreq_get_one_string(unsigned int cpu,
136 enum cpufreq_string which)
137{
138 char linebuf[MAX_LINE_LEN];
139 char *result;
140 unsigned int len;
141
142 if (which >= MAX_CPUFREQ_STRING_FILES)
143 return NULL;
144
145 len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
146 linebuf, sizeof(linebuf));
147 if (len == 0)
148 return NULL;
149
150 result = strdup(linebuf);
151 if (result == NULL)
152 return NULL;
153
154 if (result[strlen(result) - 1] == '\n')
155 result[strlen(result) - 1] = '\0';
156
157 return result;
158}
159
160/* write access */
161
162enum cpufreq_write {
163 WRITE_SCALING_MIN_FREQ,
164 WRITE_SCALING_MAX_FREQ,
165 WRITE_SCALING_GOVERNOR,
166 WRITE_SCALING_SET_SPEED,
167 MAX_CPUFREQ_WRITE_FILES
168};
169
170static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
171 [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
172 [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
173 [WRITE_SCALING_GOVERNOR] = "scaling_governor",
174 [WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
175};
176
177static int sysfs_cpufreq_write_one_value(unsigned int cpu,
178 enum cpufreq_write which,
179 const char *new_value, size_t len)
180{
181 if (which >= MAX_CPUFREQ_WRITE_FILES)
182 return 0;
183
184 if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
185 new_value, len) != len)
186 return -ENODEV;
187
188 return 0;
189};
190
191unsigned long cpufreq_get_freq_kernel(unsigned int cpu)
192{
193 return sysfs_cpufreq_get_one_value(cpu, which: SCALING_CUR_FREQ);
194}
195
196unsigned long cpufreq_get_freq_hardware(unsigned int cpu)
197{
198 return sysfs_cpufreq_get_one_value(cpu, which: CPUINFO_CUR_FREQ);
199}
200
201unsigned long cpufreq_get_transition_latency(unsigned int cpu)
202{
203 return sysfs_cpufreq_get_one_value(cpu, which: CPUINFO_LATENCY);
204}
205
206int cpufreq_get_hardware_limits(unsigned int cpu,
207 unsigned long *min,
208 unsigned long *max)
209{
210 if ((!min) || (!max))
211 return -EINVAL;
212
213 *min = sysfs_cpufreq_get_one_value(cpu, which: CPUINFO_MIN_FREQ);
214 if (!*min)
215 return -ENODEV;
216
217 *max = sysfs_cpufreq_get_one_value(cpu, which: CPUINFO_MAX_FREQ);
218 if (!*max)
219 return -ENODEV;
220
221 return 0;
222}
223
224char *cpufreq_get_driver(unsigned int cpu)
225{
226 return sysfs_cpufreq_get_one_string(cpu, which: SCALING_DRIVER);
227}
228
229void cpufreq_put_driver(char *ptr)
230{
231 if (!ptr)
232 return;
233 free(ptr);
234}
235
236struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu)
237{
238 struct cpufreq_policy *policy;
239
240 policy = malloc(sizeof(struct cpufreq_policy));
241 if (!policy)
242 return NULL;
243
244 policy->governor = sysfs_cpufreq_get_one_string(cpu, which: SCALING_GOVERNOR);
245 if (!policy->governor) {
246 free(policy);
247 return NULL;
248 }
249 policy->min = sysfs_cpufreq_get_one_value(cpu, which: SCALING_MIN_FREQ);
250 policy->max = sysfs_cpufreq_get_one_value(cpu, which: SCALING_MAX_FREQ);
251 if ((!policy->min) || (!policy->max)) {
252 free(policy->governor);
253 free(policy);
254 return NULL;
255 }
256
257 return policy;
258}
259
260void cpufreq_put_policy(struct cpufreq_policy *policy)
261{
262 if ((!policy) || (!policy->governor))
263 return;
264
265 free(policy->governor);
266 policy->governor = NULL;
267 free(policy);
268}
269
270struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned
271 int cpu)
272{
273 struct cpufreq_available_governors *first = NULL;
274 struct cpufreq_available_governors *current = NULL;
275 char linebuf[MAX_LINE_LEN];
276 unsigned int pos, i;
277 unsigned int len;
278
279 len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
280 linebuf, sizeof(linebuf));
281 if (len == 0)
282 return NULL;
283
284 pos = 0;
285 for (i = 0; i < len; i++) {
286 if (linebuf[i] == ' ' || linebuf[i] == '\n') {
287 if (i - pos < 2)
288 continue;
289 if (current) {
290 current->next = malloc(sizeof(*current));
291 if (!current->next)
292 goto error_out;
293 current = current->next;
294 } else {
295 first = malloc(sizeof(*first));
296 if (!first)
297 return NULL;
298 current = first;
299 }
300 current->first = first;
301 current->next = NULL;
302
303 current->governor = malloc(i - pos + 1);
304 if (!current->governor)
305 goto error_out;
306
307 memcpy(current->governor, linebuf + pos, i - pos);
308 current->governor[i - pos] = '\0';
309 pos = i + 1;
310 }
311 }
312
313 return first;
314
315 error_out:
316 while (first) {
317 current = first->next;
318 if (first->governor)
319 free(first->governor);
320 free(first);
321 first = current;
322 }
323 return NULL;
324}
325
326void cpufreq_put_available_governors(struct cpufreq_available_governors *any)
327{
328 struct cpufreq_available_governors *tmp, *next;
329
330 if (!any)
331 return;
332
333 tmp = any->first;
334 while (tmp) {
335 next = tmp->next;
336 if (tmp->governor)
337 free(tmp->governor);
338 free(tmp);
339 tmp = next;
340 }
341}
342
343
344struct cpufreq_available_frequencies
345*cpufreq_get_available_frequencies(unsigned int cpu)
346{
347 struct cpufreq_available_frequencies *first = NULL;
348 struct cpufreq_available_frequencies *current = NULL;
349 char one_value[SYSFS_PATH_MAX];
350 char linebuf[MAX_LINE_LEN];
351 unsigned int pos, i;
352 unsigned int len;
353
354 len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies",
355 linebuf, sizeof(linebuf));
356 if (len == 0)
357 return NULL;
358
359 pos = 0;
360 for (i = 0; i < len; i++) {
361 if (linebuf[i] == ' ' || linebuf[i] == '\n') {
362 if (i - pos < 2)
363 continue;
364 if (i - pos >= SYSFS_PATH_MAX)
365 goto error_out;
366 if (current) {
367 current->next = malloc(sizeof(*current));
368 if (!current->next)
369 goto error_out;
370 current = current->next;
371 } else {
372 first = malloc(sizeof(*first));
373 if (!first)
374 return NULL;
375 current = first;
376 }
377 current->first = first;
378 current->next = NULL;
379
380 memcpy(one_value, linebuf + pos, i - pos);
381 one_value[i - pos] = '\0';
382 if (sscanf(one_value, "%lu", &current->frequency) != 1)
383 goto error_out;
384
385 pos = i + 1;
386 }
387 }
388
389 return first;
390
391 error_out:
392 while (first) {
393 current = first->next;
394 free(first);
395 first = current;
396 }
397 return NULL;
398}
399
400struct cpufreq_available_frequencies
401*cpufreq_get_boost_frequencies(unsigned int cpu)
402{
403 struct cpufreq_available_frequencies *first = NULL;
404 struct cpufreq_available_frequencies *current = NULL;
405 char one_value[SYSFS_PATH_MAX];
406 char linebuf[MAX_LINE_LEN];
407 unsigned int pos, i;
408 unsigned int len;
409
410 len = sysfs_cpufreq_read_file(cpu, "scaling_boost_frequencies",
411 linebuf, sizeof(linebuf));
412 if (len == 0)
413 return NULL;
414
415 pos = 0;
416 for (i = 0; i < len; i++) {
417 if (linebuf[i] == ' ' || linebuf[i] == '\n') {
418 if (i - pos < 2)
419 continue;
420 if (i - pos >= SYSFS_PATH_MAX)
421 goto error_out;
422 if (current) {
423 current->next = malloc(sizeof(*current));
424 if (!current->next)
425 goto error_out;
426 current = current->next;
427 } else {
428 first = malloc(sizeof(*first));
429 if (!first)
430 return NULL;
431 current = first;
432 }
433 current->first = first;
434 current->next = NULL;
435
436 memcpy(one_value, linebuf + pos, i - pos);
437 one_value[i - pos] = '\0';
438 if (sscanf(one_value, "%lu", &current->frequency) != 1)
439 goto error_out;
440
441 pos = i + 1;
442 }
443 }
444
445 return first;
446
447 error_out:
448 while (first) {
449 current = first->next;
450 free(first);
451 first = current;
452 }
453 return NULL;
454}
455
456void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies *any)
457{
458 struct cpufreq_available_frequencies *tmp, *next;
459
460 if (!any)
461 return;
462
463 tmp = any->first;
464 while (tmp) {
465 next = tmp->next;
466 free(tmp);
467 tmp = next;
468 }
469}
470
471void cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies *any)
472{
473 cpufreq_put_available_frequencies(any);
474}
475
476static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
477 const char *file)
478{
479 struct cpufreq_affected_cpus *first = NULL;
480 struct cpufreq_affected_cpus *current = NULL;
481 char one_value[SYSFS_PATH_MAX];
482 char linebuf[MAX_LINE_LEN];
483 unsigned int pos, i;
484 unsigned int len;
485
486 len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
487 if (len == 0)
488 return NULL;
489
490 pos = 0;
491 for (i = 0; i < len; i++) {
492 if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
493 if (i - pos < 1)
494 continue;
495 if (i - pos >= SYSFS_PATH_MAX)
496 goto error_out;
497 if (current) {
498 current->next = malloc(sizeof(*current));
499 if (!current->next)
500 goto error_out;
501 current = current->next;
502 } else {
503 first = malloc(sizeof(*first));
504 if (!first)
505 return NULL;
506 current = first;
507 }
508 current->first = first;
509 current->next = NULL;
510
511 memcpy(one_value, linebuf + pos, i - pos);
512 one_value[i - pos] = '\0';
513
514 if (sscanf(one_value, "%u", &current->cpu) != 1)
515 goto error_out;
516
517 pos = i + 1;
518 }
519 }
520
521 return first;
522
523 error_out:
524 while (first) {
525 current = first->next;
526 free(first);
527 first = current;
528 }
529 return NULL;
530}
531
532struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu)
533{
534 return sysfs_get_cpu_list(cpu, file: "affected_cpus");
535}
536
537void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any)
538{
539 struct cpufreq_affected_cpus *tmp, *next;
540
541 if (!any)
542 return;
543
544 tmp = any->first;
545 while (tmp) {
546 next = tmp->next;
547 free(tmp);
548 tmp = next;
549 }
550}
551
552
553struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu)
554{
555 return sysfs_get_cpu_list(cpu, file: "related_cpus");
556}
557
558void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any)
559{
560 cpufreq_put_affected_cpus(any);
561}
562
563static int verify_gov(char *new_gov, char *passed_gov)
564{
565 unsigned int i, j = 0;
566
567 if (!passed_gov || (strlen(passed_gov) > 19))
568 return -EINVAL;
569
570 strncpy(new_gov, passed_gov, 20);
571 for (i = 0; i < 20; i++) {
572 if (j) {
573 new_gov[i] = '\0';
574 continue;
575 }
576 if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))
577 continue;
578
579 if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))
580 continue;
581
582 if (new_gov[i] == '-')
583 continue;
584
585 if (new_gov[i] == '_')
586 continue;
587
588 if (new_gov[i] == '\0') {
589 j = 1;
590 continue;
591 }
592 return -EINVAL;
593 }
594 new_gov[19] = '\0';
595 return 0;
596}
597
598int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy)
599{
600 char min[SYSFS_PATH_MAX];
601 char max[SYSFS_PATH_MAX];
602 char gov[SYSFS_PATH_MAX];
603 int ret;
604 unsigned long old_min;
605 int write_max_first;
606
607 if (!policy || !(policy->governor))
608 return -EINVAL;
609
610 if (policy->max < policy->min)
611 return -EINVAL;
612
613 if (verify_gov(gov, policy->governor))
614 return -EINVAL;
615
616 snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
617 snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
618
619 old_min = sysfs_cpufreq_get_one_value(cpu, which: SCALING_MIN_FREQ);
620 write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
621
622 if (write_max_first) {
623 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
624 max, strlen(max));
625 if (ret)
626 return ret;
627 }
628
629 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
630 strlen(min));
631 if (ret)
632 return ret;
633
634 if (!write_max_first) {
635 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
636 max, strlen(max));
637 if (ret)
638 return ret;
639 }
640
641 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
642 gov, strlen(gov));
643}
644
645
646int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq)
647{
648 char value[SYSFS_PATH_MAX];
649
650 snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
651
652 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
653 value, strlen(value));
654}
655
656
657int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq)
658{
659 char value[SYSFS_PATH_MAX];
660
661 snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
662
663 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
664 value, strlen(value));
665}
666
667int cpufreq_modify_policy_governor(unsigned int cpu, char *governor)
668{
669 char new_gov[SYSFS_PATH_MAX];
670
671 if ((!governor) || (strlen(governor) > 19))
672 return -EINVAL;
673
674 if (verify_gov(new_gov, governor))
675 return -EINVAL;
676
677 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
678 new_gov, strlen(new_gov));
679}
680
681int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency)
682{
683 struct cpufreq_policy *pol = cpufreq_get_policy(cpu);
684 char userspace_gov[] = "userspace";
685 char freq[SYSFS_PATH_MAX];
686 int ret;
687
688 if (!pol)
689 return -ENODEV;
690
691 if (strncmp(pol->governor, userspace_gov, 9) != 0) {
692 ret = cpufreq_modify_policy_governor(cpu, governor: userspace_gov);
693 if (ret) {
694 cpufreq_put_policy(policy: pol);
695 return ret;
696 }
697 }
698
699 cpufreq_put_policy(policy: pol);
700
701 snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
702
703 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
704 freq, strlen(freq));
705}
706
707struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu,
708 unsigned long long *total_time)
709{
710 struct cpufreq_stats *first = NULL;
711 struct cpufreq_stats *current = NULL;
712 char one_value[SYSFS_PATH_MAX];
713 char linebuf[MAX_LINE_LEN];
714 unsigned int pos, i;
715 unsigned int len;
716
717 len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
718 linebuf, sizeof(linebuf));
719 if (len == 0)
720 return NULL;
721
722 *total_time = 0;
723 pos = 0;
724 for (i = 0; i < len; i++) {
725 if (i == strlen(linebuf) || linebuf[i] == '\n') {
726 if (i - pos < 2)
727 continue;
728 if ((i - pos) >= SYSFS_PATH_MAX)
729 goto error_out;
730 if (current) {
731 current->next = malloc(sizeof(*current));
732 if (!current->next)
733 goto error_out;
734 current = current->next;
735 } else {
736 first = malloc(sizeof(*first));
737 if (!first)
738 return NULL;
739 current = first;
740 }
741 current->first = first;
742 current->next = NULL;
743
744 memcpy(one_value, linebuf + pos, i - pos);
745 one_value[i - pos] = '\0';
746 if (sscanf(one_value, "%lu %llu",
747 &current->frequency,
748 &current->time_in_state) != 2)
749 goto error_out;
750
751 *total_time = *total_time + current->time_in_state;
752 pos = i + 1;
753 }
754 }
755
756 return first;
757
758 error_out:
759 while (first) {
760 current = first->next;
761 free(first);
762 first = current;
763 }
764 return NULL;
765}
766
767void cpufreq_put_stats(struct cpufreq_stats *any)
768{
769 struct cpufreq_stats *tmp, *next;
770
771 if (!any)
772 return;
773
774 tmp = any->first;
775 while (tmp) {
776 next = tmp->next;
777 free(tmp);
778 tmp = next;
779 }
780}
781
782unsigned long cpufreq_get_transitions(unsigned int cpu)
783{
784 return sysfs_cpufreq_get_one_value(cpu, which: STATS_NUM_TRANSITIONS);
785}
786

source code of linux/tools/power/cpupower/lib/cpufreq.c