1 // SPDX-License-Identifier: GPL-2.0-only
3 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
11 #include <sys/types.h>
17 #include "cpupower_intern.h"
19 /* CPUFREQ sysfs access **************************************************/
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
)
26 char path
[SYSFS_PATH_MAX
];
28 snprintf(path
, sizeof(path
), PATH_TO_CPU
"cpu%u/cpufreq/%s",
30 return cpupower_read_sysfs(path
, buf
, buflen
);
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
,
37 const char *value
, size_t len
)
39 char path
[SYSFS_PATH_MAX
];
43 snprintf(path
, sizeof(path
), PATH_TO_CPU
"cpu%u/cpufreq/%s",
46 fd
= open(path
, O_WRONLY
);
50 numwrite
= write(fd
, value
, len
);
58 return (unsigned int) numwrite
;
61 /* read access to files which contain one numeric value */
71 STATS_NUM_TRANSITIONS
,
72 MAX_CPUFREQ_VALUE_READ_FILES
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"
87 static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu
,
88 enum cpufreq_value which
)
92 char linebuf
[MAX_LINE_LEN
];
95 if (which
>= MAX_CPUFREQ_VALUE_READ_FILES
)
98 len
= sysfs_cpufreq_read_file(cpu
, cpufreq_value_files
[which
],
99 linebuf
, sizeof(linebuf
));
104 value
= strtoul(linebuf
, &endp
, 0);
106 if (endp
== linebuf
|| errno
== ERANGE
)
112 /* read access to files which contain one string */
114 enum cpufreq_string
{
117 MAX_CPUFREQ_STRING_FILES
120 static const char *cpufreq_string_files
[MAX_CPUFREQ_STRING_FILES
] = {
121 [SCALING_DRIVER
] = "scaling_driver",
122 [SCALING_GOVERNOR
] = "scaling_governor",
126 static char *sysfs_cpufreq_get_one_string(unsigned int cpu
,
127 enum cpufreq_string which
)
129 char linebuf
[MAX_LINE_LEN
];
133 if (which
>= MAX_CPUFREQ_STRING_FILES
)
136 len
= sysfs_cpufreq_read_file(cpu
, cpufreq_string_files
[which
],
137 linebuf
, sizeof(linebuf
));
141 result
= strdup(linebuf
);
145 if (result
[strlen(result
) - 1] == '\n')
146 result
[strlen(result
) - 1] = '\0';
154 WRITE_SCALING_MIN_FREQ
,
155 WRITE_SCALING_MAX_FREQ
,
156 WRITE_SCALING_GOVERNOR
,
157 WRITE_SCALING_SET_SPEED
,
158 MAX_CPUFREQ_WRITE_FILES
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",
168 static int sysfs_cpufreq_write_one_value(unsigned int cpu
,
169 enum cpufreq_write which
,
170 const char *new_value
, size_t len
)
172 if (which
>= MAX_CPUFREQ_WRITE_FILES
)
175 if (sysfs_cpufreq_write_file(cpu
, cpufreq_write_files
[which
],
176 new_value
, len
) != len
)
182 unsigned long cpufreq_get_freq_kernel(unsigned int cpu
)
184 return sysfs_cpufreq_get_one_value(cpu
, SCALING_CUR_FREQ
);
187 unsigned long cpufreq_get_freq_hardware(unsigned int cpu
)
189 return sysfs_cpufreq_get_one_value(cpu
, CPUINFO_CUR_FREQ
);
192 unsigned long cpufreq_get_transition_latency(unsigned int cpu
)
194 return sysfs_cpufreq_get_one_value(cpu
, CPUINFO_LATENCY
);
197 int cpufreq_get_hardware_limits(unsigned int cpu
,
201 if ((!min
) || (!max
))
204 *min
= sysfs_cpufreq_get_one_value(cpu
, CPUINFO_MIN_FREQ
);
208 *max
= sysfs_cpufreq_get_one_value(cpu
, CPUINFO_MAX_FREQ
);
215 char *cpufreq_get_driver(unsigned int cpu
)
217 return sysfs_cpufreq_get_one_string(cpu
, SCALING_DRIVER
);
220 void cpufreq_put_driver(char *ptr
)
227 struct cpufreq_policy
*cpufreq_get_policy(unsigned int cpu
)
229 struct cpufreq_policy
*policy
;
231 policy
= malloc(sizeof(struct cpufreq_policy
));
235 policy
->governor
= sysfs_cpufreq_get_one_string(cpu
, SCALING_GOVERNOR
);
236 if (!policy
->governor
) {
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
);
251 void cpufreq_put_policy(struct cpufreq_policy
*policy
)
253 if ((!policy
) || (!policy
->governor
))
256 free(policy
->governor
);
257 policy
->governor
= NULL
;
261 struct cpufreq_available_governors
*cpufreq_get_available_governors(unsigned
264 struct cpufreq_available_governors
*first
= NULL
;
265 struct cpufreq_available_governors
*current
= NULL
;
266 char linebuf
[MAX_LINE_LEN
];
270 len
= sysfs_cpufreq_read_file(cpu
, "scaling_available_governors",
271 linebuf
, sizeof(linebuf
));
276 for (i
= 0; i
< len
; i
++) {
277 if (linebuf
[i
] == ' ' || linebuf
[i
] == '\n') {
281 current
->next
= malloc(sizeof(*current
));
284 current
= current
->next
;
286 first
= malloc(sizeof(*first
));
291 current
->first
= first
;
292 current
->next
= NULL
;
294 current
->governor
= malloc(i
- pos
+ 1);
295 if (!current
->governor
)
298 memcpy(current
->governor
, linebuf
+ pos
, i
- pos
);
299 current
->governor
[i
- pos
] = '\0';
308 current
= first
->next
;
310 free(first
->governor
);
317 void cpufreq_put_available_governors(struct cpufreq_available_governors
*any
)
319 struct cpufreq_available_governors
*tmp
, *next
;
335 struct cpufreq_available_frequencies
336 *cpufreq_get_available_frequencies(unsigned int cpu
)
338 struct cpufreq_available_frequencies
*first
= NULL
;
339 struct cpufreq_available_frequencies
*current
= NULL
;
340 char one_value
[SYSFS_PATH_MAX
];
341 char linebuf
[MAX_LINE_LEN
];
345 len
= sysfs_cpufreq_read_file(cpu
, "scaling_available_frequencies",
346 linebuf
, sizeof(linebuf
));
351 for (i
= 0; i
< len
; i
++) {
352 if (linebuf
[i
] == ' ' || linebuf
[i
] == '\n') {
355 if (i
- pos
>= SYSFS_PATH_MAX
)
358 current
->next
= malloc(sizeof(*current
));
361 current
= current
->next
;
363 first
= malloc(sizeof(*first
));
368 current
->first
= first
;
369 current
->next
= NULL
;
371 memcpy(one_value
, linebuf
+ pos
, i
- pos
);
372 one_value
[i
- pos
] = '\0';
373 if (sscanf(one_value
, "%lu", ¤t
->frequency
) != 1)
384 current
= first
->next
;
391 struct cpufreq_available_frequencies
392 *cpufreq_get_boost_frequencies(unsigned int cpu
)
394 struct cpufreq_available_frequencies
*first
= NULL
;
395 struct cpufreq_available_frequencies
*current
= NULL
;
396 char one_value
[SYSFS_PATH_MAX
];
397 char linebuf
[MAX_LINE_LEN
];
401 len
= sysfs_cpufreq_read_file(cpu
, "scaling_boost_frequencies",
402 linebuf
, sizeof(linebuf
));
407 for (i
= 0; i
< len
; i
++) {
408 if (linebuf
[i
] == ' ' || linebuf
[i
] == '\n') {
411 if (i
- pos
>= SYSFS_PATH_MAX
)
414 current
->next
= malloc(sizeof(*current
));
417 current
= current
->next
;
419 first
= malloc(sizeof(*first
));
424 current
->first
= first
;
425 current
->next
= NULL
;
427 memcpy(one_value
, linebuf
+ pos
, i
- pos
);
428 one_value
[i
- pos
] = '\0';
429 if (sscanf(one_value
, "%lu", ¤t
->frequency
) != 1)
440 current
= first
->next
;
447 void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies
*any
)
449 struct cpufreq_available_frequencies
*tmp
, *next
;
462 void cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies
*any
)
464 cpufreq_put_available_frequencies(any
);
467 static struct cpufreq_affected_cpus
*sysfs_get_cpu_list(unsigned int cpu
,
470 struct cpufreq_affected_cpus
*first
= NULL
;
471 struct cpufreq_affected_cpus
*current
= NULL
;
472 char one_value
[SYSFS_PATH_MAX
];
473 char linebuf
[MAX_LINE_LEN
];
477 len
= sysfs_cpufreq_read_file(cpu
, file
, linebuf
, sizeof(linebuf
));
482 for (i
= 0; i
< len
; i
++) {
483 if (i
== len
|| linebuf
[i
] == ' ' || linebuf
[i
] == '\n') {
486 if (i
- pos
>= SYSFS_PATH_MAX
)
489 current
->next
= malloc(sizeof(*current
));
492 current
= current
->next
;
494 first
= malloc(sizeof(*first
));
499 current
->first
= first
;
500 current
->next
= NULL
;
502 memcpy(one_value
, linebuf
+ pos
, i
- pos
);
503 one_value
[i
- pos
] = '\0';
505 if (sscanf(one_value
, "%u", ¤t
->cpu
) != 1)
516 current
= first
->next
;
523 struct cpufreq_affected_cpus
*cpufreq_get_affected_cpus(unsigned int cpu
)
525 return sysfs_get_cpu_list(cpu
, "affected_cpus");
528 void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus
*any
)
530 struct cpufreq_affected_cpus
*tmp
, *next
;
544 struct cpufreq_affected_cpus
*cpufreq_get_related_cpus(unsigned int cpu
)
546 return sysfs_get_cpu_list(cpu
, "related_cpus");
549 void cpufreq_put_related_cpus(struct cpufreq_affected_cpus
*any
)
551 cpufreq_put_affected_cpus(any
);
554 static int verify_gov(char *new_gov
, char *passed_gov
)
556 unsigned int i
, j
= 0;
558 if (!passed_gov
|| (strlen(passed_gov
) > 19))
561 strncpy(new_gov
, passed_gov
, 20);
562 for (i
= 0; i
< 20; i
++) {
567 if ((new_gov
[i
] >= 'a') && (new_gov
[i
] <= 'z'))
570 if ((new_gov
[i
] >= 'A') && (new_gov
[i
] <= 'Z'))
573 if (new_gov
[i
] == '-')
576 if (new_gov
[i
] == '_')
579 if (new_gov
[i
] == '\0') {
589 int cpufreq_set_policy(unsigned int cpu
, struct cpufreq_policy
*policy
)
591 char min
[SYSFS_PATH_MAX
];
592 char max
[SYSFS_PATH_MAX
];
593 char gov
[SYSFS_PATH_MAX
];
595 unsigned long old_min
;
598 if (!policy
|| !(policy
->governor
))
601 if (policy
->max
< policy
->min
)
604 if (verify_gov(gov
, policy
->governor
))
607 snprintf(min
, SYSFS_PATH_MAX
, "%lu", policy
->min
);
608 snprintf(max
, SYSFS_PATH_MAX
, "%lu", policy
->max
);
610 old_min
= sysfs_cpufreq_get_one_value(cpu
, SCALING_MIN_FREQ
);
611 write_max_first
= (old_min
&& (policy
->max
< old_min
) ? 0 : 1);
613 if (write_max_first
) {
614 ret
= sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_MAX_FREQ
,
620 ret
= sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_MIN_FREQ
, min
,
625 if (!write_max_first
) {
626 ret
= sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_MAX_FREQ
,
632 return sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_GOVERNOR
,
637 int cpufreq_modify_policy_min(unsigned int cpu
, unsigned long min_freq
)
639 char value
[SYSFS_PATH_MAX
];
641 snprintf(value
, SYSFS_PATH_MAX
, "%lu", min_freq
);
643 return sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_MIN_FREQ
,
644 value
, strlen(value
));
648 int cpufreq_modify_policy_max(unsigned int cpu
, unsigned long max_freq
)
650 char value
[SYSFS_PATH_MAX
];
652 snprintf(value
, SYSFS_PATH_MAX
, "%lu", max_freq
);
654 return sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_MAX_FREQ
,
655 value
, strlen(value
));
658 int cpufreq_modify_policy_governor(unsigned int cpu
, char *governor
)
660 char new_gov
[SYSFS_PATH_MAX
];
662 if ((!governor
) || (strlen(governor
) > 19))
665 if (verify_gov(new_gov
, governor
))
668 return sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_GOVERNOR
,
669 new_gov
, strlen(new_gov
));
672 int cpufreq_set_frequency(unsigned int cpu
, unsigned long target_frequency
)
674 struct cpufreq_policy
*pol
= cpufreq_get_policy(cpu
);
675 char userspace_gov
[] = "userspace";
676 char freq
[SYSFS_PATH_MAX
];
682 if (strncmp(pol
->governor
, userspace_gov
, 9) != 0) {
683 ret
= cpufreq_modify_policy_governor(cpu
, userspace_gov
);
685 cpufreq_put_policy(pol
);
690 cpufreq_put_policy(pol
);
692 snprintf(freq
, SYSFS_PATH_MAX
, "%lu", target_frequency
);
694 return sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_SET_SPEED
,
698 struct cpufreq_stats
*cpufreq_get_stats(unsigned int cpu
,
699 unsigned long long *total_time
)
701 struct cpufreq_stats
*first
= NULL
;
702 struct cpufreq_stats
*current
= NULL
;
703 char one_value
[SYSFS_PATH_MAX
];
704 char linebuf
[MAX_LINE_LEN
];
708 len
= sysfs_cpufreq_read_file(cpu
, "stats/time_in_state",
709 linebuf
, sizeof(linebuf
));
715 for (i
= 0; i
< len
; i
++) {
716 if (i
== strlen(linebuf
) || linebuf
[i
] == '\n') {
719 if ((i
- pos
) >= SYSFS_PATH_MAX
)
722 current
->next
= malloc(sizeof(*current
));
725 current
= current
->next
;
727 first
= malloc(sizeof(*first
));
732 current
->first
= first
;
733 current
->next
= NULL
;
735 memcpy(one_value
, linebuf
+ pos
, i
- pos
);
736 one_value
[i
- pos
] = '\0';
737 if (sscanf(one_value
, "%lu %llu",
739 ¤t
->time_in_state
) != 2)
742 *total_time
= *total_time
+ current
->time_in_state
;
751 current
= first
->next
;
758 void cpufreq_put_stats(struct cpufreq_stats
*any
)
760 struct cpufreq_stats
*tmp
, *next
;
773 unsigned long cpufreq_get_transitions(unsigned int cpu
)
775 return sysfs_cpufreq_get_one_value(cpu
, STATS_NUM_TRANSITIONS
);