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 **************************************************/