2 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
4 * Licensed under the terms of the GNU GPL License version 2.
12 #include <sys/types.h>
19 #define PATH_TO_CPU "/sys/devices/system/cpu/"
20 #define MAX_LINE_LEN 4096
21 #define SYSFS_PATH_MAX 255
24 static unsigned int sysfs_read_file(const char *path
, char *buf
, size_t buflen
)
29 fd
= open(path
, O_RDONLY
);
33 numread
= read(fd
, buf
, buflen
- 1);
42 return (unsigned int) numread
;
46 /* CPUFREQ sysfs access **************************************************/
48 /* helper function to read file from /sys into given buffer */
49 /* fname is a relative path under "cpuX/cpufreq" dir */
50 static unsigned int sysfs_cpufreq_read_file(unsigned int cpu
, const char *fname
,
51 char *buf
, size_t buflen
)
53 char path
[SYSFS_PATH_MAX
];
55 snprintf(path
, sizeof(path
), PATH_TO_CPU
"cpu%u/cpufreq/%s",
57 return sysfs_read_file(path
, buf
, buflen
);
60 /* helper function to write a new value to a /sys file */
61 /* fname is a relative path under "cpuX/cpufreq" dir */
62 static unsigned int sysfs_cpufreq_write_file(unsigned int cpu
,
64 const char *value
, size_t len
)
66 char path
[SYSFS_PATH_MAX
];
70 snprintf(path
, sizeof(path
), PATH_TO_CPU
"cpu%u/cpufreq/%s",
73 fd
= open(path
, O_WRONLY
);
77 numwrite
= write(fd
, value
, len
);
85 return (unsigned int) numwrite
;
88 /* read access to files which contain one numeric value */
98 STATS_NUM_TRANSITIONS
,
99 MAX_CPUFREQ_VALUE_READ_FILES
102 static const char *cpufreq_value_files
[MAX_CPUFREQ_VALUE_READ_FILES
] = {
103 [CPUINFO_CUR_FREQ
] = "cpuinfo_cur_freq",
104 [CPUINFO_MIN_FREQ
] = "cpuinfo_min_freq",
105 [CPUINFO_MAX_FREQ
] = "cpuinfo_max_freq",
106 [CPUINFO_LATENCY
] = "cpuinfo_transition_latency",
107 [SCALING_CUR_FREQ
] = "scaling_cur_freq",
108 [SCALING_MIN_FREQ
] = "scaling_min_freq",
109 [SCALING_MAX_FREQ
] = "scaling_max_freq",
110 [STATS_NUM_TRANSITIONS
] = "stats/total_trans"
114 static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu
,
115 enum cpufreq_value which
)
119 char linebuf
[MAX_LINE_LEN
];
122 if (which
>= MAX_CPUFREQ_VALUE_READ_FILES
)
125 len
= sysfs_cpufreq_read_file(cpu
, cpufreq_value_files
[which
],
126 linebuf
, sizeof(linebuf
));
131 value
= strtoul(linebuf
, &endp
, 0);
133 if (endp
== linebuf
|| errno
== ERANGE
)
139 /* read access to files which contain one string */
141 enum cpufreq_string
{
144 MAX_CPUFREQ_STRING_FILES
147 static const char *cpufreq_string_files
[MAX_CPUFREQ_STRING_FILES
] = {
148 [SCALING_DRIVER
] = "scaling_driver",
149 [SCALING_GOVERNOR
] = "scaling_governor",
153 static char *sysfs_cpufreq_get_one_string(unsigned int cpu
,
154 enum cpufreq_string which
)
156 char linebuf
[MAX_LINE_LEN
];
160 if (which
>= MAX_CPUFREQ_STRING_FILES
)
163 len
= sysfs_cpufreq_read_file(cpu
, cpufreq_string_files
[which
],
164 linebuf
, sizeof(linebuf
));
168 result
= strdup(linebuf
);
172 if (result
[strlen(result
) - 1] == '\n')
173 result
[strlen(result
) - 1] = '\0';
181 WRITE_SCALING_MIN_FREQ
,
182 WRITE_SCALING_MAX_FREQ
,
183 WRITE_SCALING_GOVERNOR
,
184 WRITE_SCALING_SET_SPEED
,
185 MAX_CPUFREQ_WRITE_FILES
188 static const char *cpufreq_write_files
[MAX_CPUFREQ_WRITE_FILES
] = {
189 [WRITE_SCALING_MIN_FREQ
] = "scaling_min_freq",
190 [WRITE_SCALING_MAX_FREQ
] = "scaling_max_freq",
191 [WRITE_SCALING_GOVERNOR
] = "scaling_governor",
192 [WRITE_SCALING_SET_SPEED
] = "scaling_setspeed",
195 static int sysfs_cpufreq_write_one_value(unsigned int cpu
,
196 enum cpufreq_write which
,
197 const char *new_value
, size_t len
)
199 if (which
>= MAX_CPUFREQ_WRITE_FILES
)
202 if (sysfs_cpufreq_write_file(cpu
, cpufreq_write_files
[which
],
203 new_value
, len
) != len
)
209 unsigned long sysfs_get_freq_kernel(unsigned int cpu
)
211 return sysfs_cpufreq_get_one_value(cpu
, SCALING_CUR_FREQ
);
214 unsigned long sysfs_get_freq_hardware(unsigned int cpu
)
216 return sysfs_cpufreq_get_one_value(cpu
, CPUINFO_CUR_FREQ
);
219 unsigned long sysfs_get_freq_transition_latency(unsigned int cpu
)
221 return sysfs_cpufreq_get_one_value(cpu
, CPUINFO_LATENCY
);
224 int sysfs_get_freq_hardware_limits(unsigned int cpu
,
228 if ((!min
) || (!max
))
231 *min
= sysfs_cpufreq_get_one_value(cpu
, CPUINFO_MIN_FREQ
);
235 *max
= sysfs_cpufreq_get_one_value(cpu
, CPUINFO_MAX_FREQ
);
242 char *sysfs_get_freq_driver(unsigned int cpu
)
244 return sysfs_cpufreq_get_one_string(cpu
, SCALING_DRIVER
);
247 struct cpufreq_policy
*sysfs_get_freq_policy(unsigned int cpu
)
249 struct cpufreq_policy
*policy
;
251 policy
= malloc(sizeof(struct cpufreq_policy
));
255 policy
->governor
= sysfs_cpufreq_get_one_string(cpu
, SCALING_GOVERNOR
);
256 if (!policy
->governor
) {
260 policy
->min
= sysfs_cpufreq_get_one_value(cpu
, SCALING_MIN_FREQ
);
261 policy
->max
= sysfs_cpufreq_get_one_value(cpu
, SCALING_MAX_FREQ
);
262 if ((!policy
->min
) || (!policy
->max
)) {
263 free(policy
->governor
);
271 struct cpufreq_available_governors
*
272 sysfs_get_freq_available_governors(unsigned int cpu
) {
273 struct cpufreq_available_governors
*first
= NULL
;
274 struct cpufreq_available_governors
*current
= NULL
;
275 char linebuf
[MAX_LINE_LEN
];
279 len
= sysfs_cpufreq_read_file(cpu
, "scaling_available_governors",
280 linebuf
, sizeof(linebuf
));
285 for (i
= 0; i
< len
; i
++) {
286 if (linebuf
[i
] == ' ' || linebuf
[i
] == '\n') {
290 current
->next
= malloc(sizeof(*current
));
293 current
= current
->next
;
295 first
= malloc(sizeof(*first
));
300 current
->first
= first
;
301 current
->next
= NULL
;
303 current
->governor
= malloc(i
- pos
+ 1);
304 if (!current
->governor
)
307 memcpy(current
->governor
, linebuf
+ pos
, i
- pos
);
308 current
->governor
[i
- pos
] = '\0';
317 current
= first
->next
;
319 free(first
->governor
);
327 struct cpufreq_available_frequencies
*
328 sysfs_get_available_frequencies(unsigned int cpu
) {
329 struct cpufreq_available_frequencies
*first
= NULL
;
330 struct cpufreq_available_frequencies
*current
= NULL
;
331 char one_value
[SYSFS_PATH_MAX
];
332 char linebuf
[MAX_LINE_LEN
];
336 len
= sysfs_cpufreq_read_file(cpu
, "scaling_available_frequencies",
337 linebuf
, sizeof(linebuf
));
342 for (i
= 0; i
< len
; i
++) {
343 if (linebuf
[i
] == ' ' || linebuf
[i
] == '\n') {
346 if (i
- pos
>= SYSFS_PATH_MAX
)
349 current
->next
= malloc(sizeof(*current
));
352 current
= current
->next
;
354 first
= malloc(sizeof(*first
));
359 current
->first
= first
;
360 current
->next
= NULL
;
362 memcpy(one_value
, linebuf
+ pos
, i
- pos
);
363 one_value
[i
- pos
] = '\0';
364 if (sscanf(one_value
, "%lu", ¤t
->frequency
) != 1)
375 current
= first
->next
;
382 static struct cpufreq_affected_cpus
*sysfs_get_cpu_list(unsigned int cpu
,
385 struct cpufreq_affected_cpus
*first
= NULL
;
386 struct cpufreq_affected_cpus
*current
= NULL
;
387 char one_value
[SYSFS_PATH_MAX
];
388 char linebuf
[MAX_LINE_LEN
];
392 len
= sysfs_cpufreq_read_file(cpu
, file
, linebuf
, sizeof(linebuf
));
397 for (i
= 0; i
< len
; i
++) {
398 if (i
== len
|| linebuf
[i
] == ' ' || linebuf
[i
] == '\n') {
401 if (i
- pos
>= SYSFS_PATH_MAX
)
404 current
->next
= malloc(sizeof(*current
));
407 current
= current
->next
;
409 first
= malloc(sizeof(*first
));
414 current
->first
= first
;
415 current
->next
= NULL
;
417 memcpy(one_value
, linebuf
+ pos
, i
- pos
);
418 one_value
[i
- pos
] = '\0';
420 if (sscanf(one_value
, "%u", ¤t
->cpu
) != 1)
431 current
= first
->next
;
438 struct cpufreq_affected_cpus
*sysfs_get_freq_affected_cpus(unsigned int cpu
)
440 return sysfs_get_cpu_list(cpu
, "affected_cpus");
443 struct cpufreq_affected_cpus
*sysfs_get_freq_related_cpus(unsigned int cpu
)
445 return sysfs_get_cpu_list(cpu
, "related_cpus");
448 struct cpufreq_stats
*sysfs_get_freq_stats(unsigned int cpu
,
449 unsigned long long *total_time
) {
450 struct cpufreq_stats
*first
= NULL
;
451 struct cpufreq_stats
*current
= NULL
;
452 char one_value
[SYSFS_PATH_MAX
];
453 char linebuf
[MAX_LINE_LEN
];
457 len
= sysfs_cpufreq_read_file(cpu
, "stats/time_in_state",
458 linebuf
, sizeof(linebuf
));
464 for (i
= 0; i
< len
; i
++) {
465 if (i
== strlen(linebuf
) || linebuf
[i
] == '\n') {
468 if ((i
- pos
) >= SYSFS_PATH_MAX
)
471 current
->next
= malloc(sizeof(*current
));
474 current
= current
->next
;
476 first
= malloc(sizeof(*first
));
481 current
->first
= first
;
482 current
->next
= NULL
;
484 memcpy(one_value
, linebuf
+ pos
, i
- pos
);
485 one_value
[i
- pos
] = '\0';
486 if (sscanf(one_value
, "%lu %llu",
488 ¤t
->time_in_state
) != 2)
491 *total_time
= *total_time
+ current
->time_in_state
;
500 current
= first
->next
;
507 unsigned long sysfs_get_freq_transitions(unsigned int cpu
)
509 return sysfs_cpufreq_get_one_value(cpu
, STATS_NUM_TRANSITIONS
);
512 static int verify_gov(char *new_gov
, char *passed_gov
)
514 unsigned int i
, j
= 0;
516 if (!passed_gov
|| (strlen(passed_gov
) > 19))
519 strncpy(new_gov
, passed_gov
, 20);
520 for (i
= 0; i
< 20; i
++) {
525 if ((new_gov
[i
] >= 'a') && (new_gov
[i
] <= 'z'))
528 if ((new_gov
[i
] >= 'A') && (new_gov
[i
] <= 'Z'))
531 if (new_gov
[i
] == '-')
534 if (new_gov
[i
] == '_')
537 if (new_gov
[i
] == '\0') {
547 int sysfs_modify_freq_policy_governor(unsigned int cpu
, char *governor
)
549 char new_gov
[SYSFS_PATH_MAX
];
554 if (verify_gov(new_gov
, governor
))
557 return sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_GOVERNOR
,
558 new_gov
, strlen(new_gov
));
561 int sysfs_modify_freq_policy_max(unsigned int cpu
, unsigned long max_freq
)
563 char value
[SYSFS_PATH_MAX
];
565 snprintf(value
, SYSFS_PATH_MAX
, "%lu", max_freq
);
567 return sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_MAX_FREQ
,
568 value
, strlen(value
));
572 int sysfs_modify_freq_policy_min(unsigned int cpu
, unsigned long min_freq
)
574 char value
[SYSFS_PATH_MAX
];
576 snprintf(value
, SYSFS_PATH_MAX
, "%lu", min_freq
);
578 return sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_MIN_FREQ
,
579 value
, strlen(value
));
583 int sysfs_set_freq_policy(unsigned int cpu
, struct cpufreq_policy
*policy
)
585 char min
[SYSFS_PATH_MAX
];
586 char max
[SYSFS_PATH_MAX
];
587 char gov
[SYSFS_PATH_MAX
];
589 unsigned long old_min
;
592 if (!policy
|| !(policy
->governor
))
595 if (policy
->max
< policy
->min
)
598 if (verify_gov(gov
, policy
->governor
))
601 snprintf(min
, SYSFS_PATH_MAX
, "%lu", policy
->min
);
602 snprintf(max
, SYSFS_PATH_MAX
, "%lu", policy
->max
);
604 old_min
= sysfs_cpufreq_get_one_value(cpu
, SCALING_MIN_FREQ
);
605 write_max_first
= (old_min
&& (policy
->max
< old_min
) ? 0 : 1);
607 if (write_max_first
) {
608 ret
= sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_MAX_FREQ
,
614 ret
= sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_MIN_FREQ
, min
,
619 if (!write_max_first
) {
620 ret
= sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_MAX_FREQ
,
626 return sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_GOVERNOR
,
630 int sysfs_set_frequency(unsigned int cpu
, unsigned long target_frequency
)
632 struct cpufreq_policy
*pol
= sysfs_get_freq_policy(cpu
);
633 char userspace_gov
[] = "userspace";
634 char freq
[SYSFS_PATH_MAX
];
640 if (strncmp(pol
->governor
, userspace_gov
, 9) != 0) {
641 ret
= sysfs_modify_freq_policy_governor(cpu
, userspace_gov
);
643 cpufreq_put_policy(pol
);
648 cpufreq_put_policy(pol
);
650 snprintf(freq
, SYSFS_PATH_MAX
, "%lu", target_frequency
);
652 return sysfs_cpufreq_write_one_value(cpu
, WRITE_SCALING_SET_SPEED
,
656 /* CPUFREQ sysfs access **************************************************/
658 /* General sysfs access **************************************************/
659 int sysfs_cpu_exists(unsigned int cpu
)
661 char file
[SYSFS_PATH_MAX
];
664 snprintf(file
, SYSFS_PATH_MAX
, PATH_TO_CPU
"cpu%u/", cpu
);
666 if (stat(file
, &statbuf
) != 0)
669 return S_ISDIR(statbuf
.st_mode
) ? 0 : -ENOSYS
;
672 /* General sysfs access **************************************************/