Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[zen-stable.git] / tools / power / cpupower / lib / sysfs.c
blob870713a75a81c458256c0f589223cb9926635b6f
1 /*
2 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
4 * Licensed under the terms of the GNU GPL License version 2.
5 */
7 #include <stdio.h>
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <limits.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <unistd.h>
17 #include "cpufreq.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)
26 int fd;
27 ssize_t numread;
29 fd = open(path, O_RDONLY);
30 if (fd == -1)
31 return 0;
33 numread = read(fd, buf, buflen - 1);
34 if (numread < 1) {
35 close(fd);
36 return 0;
39 buf[numread] = '\0';
40 close(fd);
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",
56 cpu, fname);
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,
63 const char *fname,
64 const char *value, size_t len)
66 char path[SYSFS_PATH_MAX];
67 int fd;
68 ssize_t numwrite;
70 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
71 cpu, fname);
73 fd = open(path, O_WRONLY);
74 if (fd == -1)
75 return 0;
77 numwrite = write(fd, value, len);
78 if (numwrite < 1) {
79 close(fd);
80 return 0;
83 close(fd);
85 return (unsigned int) numwrite;
88 /* read access to files which contain one numeric value */
90 enum cpufreq_value {
91 CPUINFO_CUR_FREQ,
92 CPUINFO_MIN_FREQ,
93 CPUINFO_MAX_FREQ,
94 CPUINFO_LATENCY,
95 SCALING_CUR_FREQ,
96 SCALING_MIN_FREQ,
97 SCALING_MAX_FREQ,
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)
117 unsigned long value;
118 unsigned int len;
119 char linebuf[MAX_LINE_LEN];
120 char *endp;
122 if (which >= MAX_CPUFREQ_VALUE_READ_FILES)
123 return 0;
125 len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which],
126 linebuf, sizeof(linebuf));
128 if (len == 0)
129 return 0;
131 value = strtoul(linebuf, &endp, 0);
133 if (endp == linebuf || errno == ERANGE)
134 return 0;
136 return value;
139 /* read access to files which contain one string */
141 enum cpufreq_string {
142 SCALING_DRIVER,
143 SCALING_GOVERNOR,
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];
157 char *result;
158 unsigned int len;
160 if (which >= MAX_CPUFREQ_STRING_FILES)
161 return NULL;
163 len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
164 linebuf, sizeof(linebuf));
165 if (len == 0)
166 return NULL;
168 result = strdup(linebuf);
169 if (result == NULL)
170 return NULL;
172 if (result[strlen(result) - 1] == '\n')
173 result[strlen(result) - 1] = '\0';
175 return result;
178 /* write access */
180 enum cpufreq_write {
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)
200 return 0;
202 if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
203 new_value, len) != len)
204 return -ENODEV;
206 return 0;
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,
225 unsigned long *min,
226 unsigned long *max)
228 if ((!min) || (!max))
229 return -EINVAL;
231 *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);
232 if (!*min)
233 return -ENODEV;
235 *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
236 if (!*max)
237 return -ENODEV;
239 return 0;
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));
252 if (!policy)
253 return NULL;
255 policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
256 if (!policy->governor) {
257 free(policy);
258 return NULL;
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);
264 free(policy);
265 return NULL;
268 return policy;
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];
276 unsigned int pos, i;
277 unsigned int len;
279 len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
280 linebuf, sizeof(linebuf));
281 if (len == 0)
282 return NULL;
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 goto error_out;
298 current = first;
300 current->first = first;
301 current->next = NULL;
303 current->governor = malloc(i - pos + 1);
304 if (!current->governor)
305 goto error_out;
307 memcpy(current->governor, linebuf + pos, i - pos);
308 current->governor[i - pos] = '\0';
309 pos = i + 1;
313 return first;
315 error_out:
316 while (first) {
317 current = first->next;
318 if (first->governor)
319 free(first->governor);
320 free(first);
321 first = current;
323 return NULL;
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];
333 unsigned int pos, i;
334 unsigned int len;
336 len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies",
337 linebuf, sizeof(linebuf));
338 if (len == 0)
339 return NULL;
341 pos = 0;
342 for (i = 0; i < len; i++) {
343 if (linebuf[i] == ' ' || linebuf[i] == '\n') {
344 if (i - pos < 2)
345 continue;
346 if (i - pos >= SYSFS_PATH_MAX)
347 goto error_out;
348 if (current) {
349 current->next = malloc(sizeof(*current));
350 if (!current->next)
351 goto error_out;
352 current = current->next;
353 } else {
354 first = malloc(sizeof(*first));
355 if (!first)
356 goto error_out;
357 current = 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", &current->frequency) != 1)
365 goto error_out;
367 pos = i + 1;
371 return first;
373 error_out:
374 while (first) {
375 current = first->next;
376 free(first);
377 first = current;
379 return NULL;
382 static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
383 const char *file)
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];
389 unsigned int pos, i;
390 unsigned int len;
392 len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
393 if (len == 0)
394 return NULL;
396 pos = 0;
397 for (i = 0; i < len; i++) {
398 if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
399 if (i - pos < 1)
400 continue;
401 if (i - pos >= SYSFS_PATH_MAX)
402 goto error_out;
403 if (current) {
404 current->next = malloc(sizeof(*current));
405 if (!current->next)
406 goto error_out;
407 current = current->next;
408 } else {
409 first = malloc(sizeof(*first));
410 if (!first)
411 goto error_out;
412 current = 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", &current->cpu) != 1)
421 goto error_out;
423 pos = i + 1;
427 return first;
429 error_out:
430 while (first) {
431 current = first->next;
432 free(first);
433 first = current;
435 return NULL;
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];
454 unsigned int pos, i;
455 unsigned int len;
457 len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
458 linebuf, sizeof(linebuf));
459 if (len == 0)
460 return NULL;
462 *total_time = 0;
463 pos = 0;
464 for (i = 0; i < len; i++) {
465 if (i == strlen(linebuf) || linebuf[i] == '\n') {
466 if (i - pos < 2)
467 continue;
468 if ((i - pos) >= SYSFS_PATH_MAX)
469 goto error_out;
470 if (current) {
471 current->next = malloc(sizeof(*current));
472 if (!current->next)
473 goto error_out;
474 current = current->next;
475 } else {
476 first = malloc(sizeof(*first));
477 if (!first)
478 goto error_out;
479 current = 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",
487 &current->frequency,
488 &current->time_in_state) != 2)
489 goto error_out;
491 *total_time = *total_time + current->time_in_state;
492 pos = i + 1;
496 return first;
498 error_out:
499 while (first) {
500 current = first->next;
501 free(first);
502 first = current;
504 return NULL;
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))
517 return -EINVAL;
519 strncpy(new_gov, passed_gov, 20);
520 for (i = 0; i < 20; i++) {
521 if (j) {
522 new_gov[i] = '\0';
523 continue;
525 if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))
526 continue;
528 if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))
529 continue;
531 if (new_gov[i] == '-')
532 continue;
534 if (new_gov[i] == '_')
535 continue;
537 if (new_gov[i] == '\0') {
538 j = 1;
539 continue;
541 return -EINVAL;
543 new_gov[19] = '\0';
544 return 0;
547 int sysfs_modify_freq_policy_governor(unsigned int cpu, char *governor)
549 char new_gov[SYSFS_PATH_MAX];
551 if (!governor)
552 return -EINVAL;
554 if (verify_gov(new_gov, governor))
555 return -EINVAL;
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];
588 int ret;
589 unsigned long old_min;
590 int write_max_first;
592 if (!policy || !(policy->governor))
593 return -EINVAL;
595 if (policy->max < policy->min)
596 return -EINVAL;
598 if (verify_gov(gov, policy->governor))
599 return -EINVAL;
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,
609 max, strlen(max));
610 if (ret)
611 return ret;
614 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
615 strlen(min));
616 if (ret)
617 return ret;
619 if (!write_max_first) {
620 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
621 max, strlen(max));
622 if (ret)
623 return ret;
626 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
627 gov, strlen(gov));
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];
635 int ret;
637 if (!pol)
638 return -ENODEV;
640 if (strncmp(pol->governor, userspace_gov, 9) != 0) {
641 ret = sysfs_modify_freq_policy_governor(cpu, userspace_gov);
642 if (ret) {
643 cpufreq_put_policy(pol);
644 return ret;
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,
653 freq, strlen(freq));
656 /* CPUFREQ sysfs access **************************************************/
658 /* General sysfs access **************************************************/
659 int sysfs_cpu_exists(unsigned int cpu)
661 char file[SYSFS_PATH_MAX];
662 struct stat statbuf;
664 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/", cpu);
666 if (stat(file, &statbuf) != 0)
667 return -ENOSYS;
669 return S_ISDIR(statbuf.st_mode) ? 0 : -ENOSYS;
672 /* General sysfs access **************************************************/