]>
Commit | Line | Data |
---|---|---|
7fe2f639 DB |
1 | /* |
2 | * (C) 2004-2009 Dominik Brodowski <[email protected]> | |
3 | * | |
4 | * Licensed under the terms of the GNU GPL License version 2. | |
5 | */ | |
6 | ||
7 | ||
8 | #include <stdio.h> | |
9 | #include <errno.h> | |
10 | #include <stdlib.h> | |
11 | #include <string.h> | |
ac5a181d TR |
12 | #include <sys/types.h> |
13 | #include <sys/stat.h> | |
14 | #include <fcntl.h> | |
15 | #include <unistd.h> | |
7fe2f639 DB |
16 | |
17 | #include "cpufreq.h" | |
ac5a181d | 18 | #include "cpupower_intern.h" |
7fe2f639 | 19 | |
ac5a181d TR |
20 | /* CPUFREQ sysfs access **************************************************/ |
21 | ||
22 | /* helper function to read file from /sys into given buffer */ | |
23 | /* fname is a relative path under "cpuX/cpufreq" dir */ | |
24 | static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname, | |
25 | char *buf, size_t buflen) | |
7fe2f639 | 26 | { |
ac5a181d TR |
27 | char path[SYSFS_PATH_MAX]; |
28 | ||
29 | snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", | |
30 | cpu, fname); | |
9de9aa45 | 31 | return cpupower_read_sysfs(path, buf, buflen); |
7fe2f639 DB |
32 | } |
33 | ||
ac5a181d TR |
34 | /* helper function to write a new value to a /sys file */ |
35 | /* fname is a relative path under "cpuX/cpufreq" dir */ | |
36 | static unsigned int sysfs_cpufreq_write_file(unsigned int cpu, | |
37 | const char *fname, | |
38 | const char *value, size_t len) | |
39 | { | |
40 | char path[SYSFS_PATH_MAX]; | |
41 | int fd; | |
42 | ssize_t numwrite; | |
43 | ||
44 | snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", | |
45 | cpu, fname); | |
46 | ||
47 | fd = open(path, O_WRONLY); | |
48 | if (fd == -1) | |
49 | return 0; | |
50 | ||
51 | numwrite = write(fd, value, len); | |
52 | if (numwrite < 1) { | |
53 | close(fd); | |
54 | return 0; | |
55 | } | |
56 | ||
57 | close(fd); | |
58 | ||
59 | return (unsigned int) numwrite; | |
60 | } | |
61 | ||
62 | /* read access to files which contain one numeric value */ | |
63 | ||
64 | enum cpufreq_value { | |
65 | CPUINFO_CUR_FREQ, | |
66 | CPUINFO_MIN_FREQ, | |
67 | CPUINFO_MAX_FREQ, | |
68 | CPUINFO_LATENCY, | |
69 | SCALING_CUR_FREQ, | |
70 | SCALING_MIN_FREQ, | |
71 | SCALING_MAX_FREQ, | |
72 | STATS_NUM_TRANSITIONS, | |
73 | MAX_CPUFREQ_VALUE_READ_FILES | |
74 | }; | |
75 | ||
76 | static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = { | |
77 | [CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq", | |
78 | [CPUINFO_MIN_FREQ] = "cpuinfo_min_freq", | |
79 | [CPUINFO_MAX_FREQ] = "cpuinfo_max_freq", | |
80 | [CPUINFO_LATENCY] = "cpuinfo_transition_latency", | |
81 | [SCALING_CUR_FREQ] = "scaling_cur_freq", | |
82 | [SCALING_MIN_FREQ] = "scaling_min_freq", | |
83 | [SCALING_MAX_FREQ] = "scaling_max_freq", | |
84 | [STATS_NUM_TRANSITIONS] = "stats/total_trans" | |
85 | }; | |
86 | ||
87 | ||
88 | static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu, | |
89 | enum cpufreq_value which) | |
90 | { | |
91 | unsigned long value; | |
92 | unsigned int len; | |
93 | char linebuf[MAX_LINE_LEN]; | |
94 | char *endp; | |
95 | ||
96 | if (which >= MAX_CPUFREQ_VALUE_READ_FILES) | |
97 | return 0; | |
98 | ||
99 | len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which], | |
100 | linebuf, 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 | ||
113 | /* read access to files which contain one string */ | |
114 | ||
115 | enum cpufreq_string { | |
116 | SCALING_DRIVER, | |
117 | SCALING_GOVERNOR, | |
118 | MAX_CPUFREQ_STRING_FILES | |
119 | }; | |
120 | ||
121 | static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = { | |
122 | [SCALING_DRIVER] = "scaling_driver", | |
123 | [SCALING_GOVERNOR] = "scaling_governor", | |
124 | }; | |
125 | ||
126 | ||
127 | static char *sysfs_cpufreq_get_one_string(unsigned int cpu, | |
128 | enum cpufreq_string which) | |
129 | { | |
130 | char linebuf[MAX_LINE_LEN]; | |
131 | char *result; | |
132 | unsigned int len; | |
133 | ||
134 | if (which >= MAX_CPUFREQ_STRING_FILES) | |
135 | return NULL; | |
136 | ||
137 | len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which], | |
138 | linebuf, sizeof(linebuf)); | |
139 | if (len == 0) | |
140 | return NULL; | |
141 | ||
142 | result = strdup(linebuf); | |
143 | if (result == NULL) | |
144 | return NULL; | |
145 | ||
146 | if (result[strlen(result) - 1] == '\n') | |
147 | result[strlen(result) - 1] = '\0'; | |
148 | ||
149 | return result; | |
150 | } | |
151 | ||
152 | /* write access */ | |
153 | ||
154 | enum cpufreq_write { | |
155 | WRITE_SCALING_MIN_FREQ, | |
156 | WRITE_SCALING_MAX_FREQ, | |
157 | WRITE_SCALING_GOVERNOR, | |
158 | WRITE_SCALING_SET_SPEED, | |
159 | MAX_CPUFREQ_WRITE_FILES | |
160 | }; | |
161 | ||
162 | static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = { | |
163 | [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq", | |
164 | [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq", | |
165 | [WRITE_SCALING_GOVERNOR] = "scaling_governor", | |
166 | [WRITE_SCALING_SET_SPEED] = "scaling_setspeed", | |
167 | }; | |
168 | ||
169 | static int sysfs_cpufreq_write_one_value(unsigned int cpu, | |
170 | enum cpufreq_write which, | |
171 | const char *new_value, size_t len) | |
172 | { | |
173 | if (which >= MAX_CPUFREQ_WRITE_FILES) | |
174 | return 0; | |
175 | ||
176 | if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which], | |
177 | new_value, len) != len) | |
178 | return -ENODEV; | |
179 | ||
180 | return 0; | |
181 | }; | |
182 | ||
7fe2f639 DB |
183 | unsigned long cpufreq_get_freq_kernel(unsigned int cpu) |
184 | { | |
ac5a181d | 185 | return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ); |
7fe2f639 DB |
186 | } |
187 | ||
188 | unsigned long cpufreq_get_freq_hardware(unsigned int cpu) | |
189 | { | |
ac5a181d | 190 | return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ); |
7fe2f639 DB |
191 | } |
192 | ||
193 | unsigned long cpufreq_get_transition_latency(unsigned int cpu) | |
194 | { | |
ac5a181d | 195 | return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY); |
7fe2f639 DB |
196 | } |
197 | ||
198 | int cpufreq_get_hardware_limits(unsigned int cpu, | |
199 | unsigned long *min, | |
200 | unsigned long *max) | |
201 | { | |
202 | if ((!min) || (!max)) | |
203 | return -EINVAL; | |
ac5a181d TR |
204 | |
205 | *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ); | |
206 | if (!*min) | |
207 | return -ENODEV; | |
208 | ||
209 | *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ); | |
210 | if (!*max) | |
211 | return -ENODEV; | |
212 | ||
213 | return 0; | |
7fe2f639 DB |
214 | } |
215 | ||
6c2b8185 DB |
216 | char *cpufreq_get_driver(unsigned int cpu) |
217 | { | |
ac5a181d | 218 | return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER); |
7fe2f639 DB |
219 | } |
220 | ||
6c2b8185 DB |
221 | void cpufreq_put_driver(char *ptr) |
222 | { | |
7fe2f639 DB |
223 | if (!ptr) |
224 | return; | |
225 | free(ptr); | |
226 | } | |
227 | ||
6c2b8185 DB |
228 | struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu) |
229 | { | |
ac5a181d TR |
230 | struct cpufreq_policy *policy; |
231 | ||
232 | policy = malloc(sizeof(struct cpufreq_policy)); | |
233 | if (!policy) | |
234 | return NULL; | |
235 | ||
236 | policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR); | |
237 | if (!policy->governor) { | |
238 | free(policy); | |
239 | return NULL; | |
240 | } | |
241 | policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); | |
242 | policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ); | |
243 | if ((!policy->min) || (!policy->max)) { | |
244 | free(policy->governor); | |
245 | free(policy); | |
246 | return NULL; | |
247 | } | |
248 | ||
249 | return policy; | |
7fe2f639 DB |
250 | } |
251 | ||
6c2b8185 DB |
252 | void cpufreq_put_policy(struct cpufreq_policy *policy) |
253 | { | |
7fe2f639 DB |
254 | if ((!policy) || (!policy->governor)) |
255 | return; | |
256 | ||
257 | free(policy->governor); | |
258 | policy->governor = NULL; | |
259 | free(policy); | |
260 | } | |
261 | ||
6c2b8185 DB |
262 | struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned |
263 | int cpu) | |
264 | { | |
ac5a181d TR |
265 | struct cpufreq_available_governors *first = NULL; |
266 | struct cpufreq_available_governors *current = NULL; | |
267 | char linebuf[MAX_LINE_LEN]; | |
268 | unsigned int pos, i; | |
269 | unsigned int len; | |
270 | ||
271 | len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors", | |
272 | linebuf, sizeof(linebuf)); | |
273 | if (len == 0) | |
274 | return NULL; | |
275 | ||
276 | pos = 0; | |
277 | for (i = 0; i < len; i++) { | |
278 | if (linebuf[i] == ' ' || linebuf[i] == '\n') { | |
279 | if (i - pos < 2) | |
280 | continue; | |
281 | if (current) { | |
282 | current->next = malloc(sizeof(*current)); | |
283 | if (!current->next) | |
284 | goto error_out; | |
285 | current = current->next; | |
286 | } else { | |
287 | first = malloc(sizeof(*first)); | |
288 | if (!first) | |
289 | goto error_out; | |
290 | current = first; | |
291 | } | |
292 | current->first = first; | |
293 | current->next = NULL; | |
294 | ||
295 | current->governor = malloc(i - pos + 1); | |
296 | if (!current->governor) | |
297 | goto error_out; | |
298 | ||
299 | memcpy(current->governor, linebuf + pos, i - pos); | |
300 | current->governor[i - pos] = '\0'; | |
301 | pos = i + 1; | |
302 | } | |
303 | } | |
304 | ||
305 | return first; | |
306 | ||
307 | error_out: | |
308 | while (first) { | |
309 | current = first->next; | |
310 | if (first->governor) | |
311 | free(first->governor); | |
312 | free(first); | |
313 | first = current; | |
314 | } | |
315 | return NULL; | |
7fe2f639 DB |
316 | } |
317 | ||
6c2b8185 DB |
318 | void cpufreq_put_available_governors(struct cpufreq_available_governors *any) |
319 | { | |
7fe2f639 DB |
320 | struct cpufreq_available_governors *tmp, *next; |
321 | ||
322 | if (!any) | |
323 | return; | |
324 | ||
325 | tmp = any->first; | |
326 | while (tmp) { | |
327 | next = tmp->next; | |
328 | if (tmp->governor) | |
329 | free(tmp->governor); | |
330 | free(tmp); | |
331 | tmp = next; | |
332 | } | |
333 | } | |
334 | ||
335 | ||
6c2b8185 DB |
336 | struct cpufreq_available_frequencies |
337 | *cpufreq_get_available_frequencies(unsigned int cpu) | |
338 | { | |
ac5a181d TR |
339 | struct cpufreq_available_frequencies *first = NULL; |
340 | struct cpufreq_available_frequencies *current = NULL; | |
341 | char one_value[SYSFS_PATH_MAX]; | |
342 | char linebuf[MAX_LINE_LEN]; | |
343 | unsigned int pos, i; | |
344 | unsigned int len; | |
345 | ||
346 | len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies", | |
347 | linebuf, sizeof(linebuf)); | |
348 | if (len == 0) | |
349 | return NULL; | |
350 | ||
351 | pos = 0; | |
352 | for (i = 0; i < len; i++) { | |
353 | if (linebuf[i] == ' ' || linebuf[i] == '\n') { | |
354 | if (i - pos < 2) | |
355 | continue; | |
356 | if (i - pos >= SYSFS_PATH_MAX) | |
357 | goto error_out; | |
358 | if (current) { | |
359 | current->next = malloc(sizeof(*current)); | |
360 | if (!current->next) | |
361 | goto error_out; | |
362 | current = current->next; | |
363 | } else { | |
364 | first = malloc(sizeof(*first)); | |
365 | if (!first) | |
366 | goto error_out; | |
367 | current = first; | |
368 | } | |
369 | current->first = first; | |
370 | current->next = NULL; | |
371 | ||
372 | memcpy(one_value, linebuf + pos, i - pos); | |
373 | one_value[i - pos] = '\0'; | |
374 | if (sscanf(one_value, "%lu", ¤t->frequency) != 1) | |
375 | goto error_out; | |
376 | ||
377 | pos = i + 1; | |
378 | } | |
379 | } | |
380 | ||
381 | return first; | |
382 | ||
383 | error_out: | |
384 | while (first) { | |
385 | current = first->next; | |
386 | free(first); | |
387 | first = current; | |
388 | } | |
389 | return NULL; | |
7fe2f639 DB |
390 | } |
391 | ||
6c2b8185 DB |
392 | void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies |
393 | *any) { | |
7fe2f639 DB |
394 | struct cpufreq_available_frequencies *tmp, *next; |
395 | ||
396 | if (!any) | |
397 | return; | |
398 | ||
399 | tmp = any->first; | |
400 | while (tmp) { | |
401 | next = tmp->next; | |
402 | free(tmp); | |
403 | tmp = next; | |
404 | } | |
405 | } | |
406 | ||
ac5a181d TR |
407 | static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu, |
408 | const char *file) | |
409 | { | |
410 | struct cpufreq_affected_cpus *first = NULL; | |
411 | struct cpufreq_affected_cpus *current = NULL; | |
412 | char one_value[SYSFS_PATH_MAX]; | |
413 | char linebuf[MAX_LINE_LEN]; | |
414 | unsigned int pos, i; | |
415 | unsigned int len; | |
416 | ||
417 | len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf)); | |
418 | if (len == 0) | |
419 | return NULL; | |
420 | ||
421 | pos = 0; | |
422 | for (i = 0; i < len; i++) { | |
423 | if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') { | |
424 | if (i - pos < 1) | |
425 | continue; | |
426 | if (i - pos >= SYSFS_PATH_MAX) | |
427 | goto error_out; | |
428 | if (current) { | |
429 | current->next = malloc(sizeof(*current)); | |
430 | if (!current->next) | |
431 | goto error_out; | |
432 | current = current->next; | |
433 | } else { | |
434 | first = malloc(sizeof(*first)); | |
435 | if (!first) | |
436 | goto error_out; | |
437 | current = first; | |
438 | } | |
439 | current->first = first; | |
440 | current->next = NULL; | |
441 | ||
442 | memcpy(one_value, linebuf + pos, i - pos); | |
443 | one_value[i - pos] = '\0'; | |
444 | ||
445 | if (sscanf(one_value, "%u", ¤t->cpu) != 1) | |
446 | goto error_out; | |
447 | ||
448 | pos = i + 1; | |
449 | } | |
450 | } | |
451 | ||
452 | return first; | |
453 | ||
454 | error_out: | |
455 | while (first) { | |
456 | current = first->next; | |
457 | free(first); | |
458 | first = current; | |
459 | } | |
460 | return NULL; | |
461 | } | |
7fe2f639 | 462 | |
6c2b8185 DB |
463 | struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu) |
464 | { | |
ac5a181d | 465 | return sysfs_get_cpu_list(cpu, "affected_cpus"); |
7fe2f639 DB |
466 | } |
467 | ||
6c2b8185 DB |
468 | void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any) |
469 | { | |
7fe2f639 DB |
470 | struct cpufreq_affected_cpus *tmp, *next; |
471 | ||
472 | if (!any) | |
473 | return; | |
474 | ||
475 | tmp = any->first; | |
476 | while (tmp) { | |
477 | next = tmp->next; | |
478 | free(tmp); | |
479 | tmp = next; | |
480 | } | |
481 | } | |
482 | ||
483 | ||
6c2b8185 DB |
484 | struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu) |
485 | { | |
ac5a181d | 486 | return sysfs_get_cpu_list(cpu, "related_cpus"); |
7fe2f639 DB |
487 | } |
488 | ||
6c2b8185 DB |
489 | void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any) |
490 | { | |
7fe2f639 DB |
491 | cpufreq_put_affected_cpus(any); |
492 | } | |
493 | ||
ac5a181d TR |
494 | static int verify_gov(char *new_gov, char *passed_gov) |
495 | { | |
496 | unsigned int i, j = 0; | |
497 | ||
498 | if (!passed_gov || (strlen(passed_gov) > 19)) | |
499 | return -EINVAL; | |
500 | ||
501 | strncpy(new_gov, passed_gov, 20); | |
502 | for (i = 0; i < 20; i++) { | |
503 | if (j) { | |
504 | new_gov[i] = '\0'; | |
505 | continue; | |
506 | } | |
507 | if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z')) | |
508 | continue; | |
509 | ||
510 | if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z')) | |
511 | continue; | |
512 | ||
513 | if (new_gov[i] == '-') | |
514 | continue; | |
515 | ||
516 | if (new_gov[i] == '_') | |
517 | continue; | |
518 | ||
519 | if (new_gov[i] == '\0') { | |
520 | j = 1; | |
521 | continue; | |
522 | } | |
523 | return -EINVAL; | |
524 | } | |
525 | new_gov[19] = '\0'; | |
526 | return 0; | |
527 | } | |
7fe2f639 | 528 | |
6c2b8185 DB |
529 | int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy) |
530 | { | |
ac5a181d TR |
531 | char min[SYSFS_PATH_MAX]; |
532 | char max[SYSFS_PATH_MAX]; | |
533 | char gov[SYSFS_PATH_MAX]; | |
534 | int ret; | |
535 | unsigned long old_min; | |
536 | int write_max_first; | |
537 | ||
7fe2f639 DB |
538 | if (!policy || !(policy->governor)) |
539 | return -EINVAL; | |
540 | ||
ac5a181d TR |
541 | if (policy->max < policy->min) |
542 | return -EINVAL; | |
543 | ||
544 | if (verify_gov(gov, policy->governor)) | |
545 | return -EINVAL; | |
546 | ||
547 | snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min); | |
548 | snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max); | |
549 | ||
550 | old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); | |
551 | write_max_first = (old_min && (policy->max < old_min) ? 0 : 1); | |
552 | ||
553 | if (write_max_first) { | |
554 | ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, | |
555 | max, strlen(max)); | |
556 | if (ret) | |
557 | return ret; | |
558 | } | |
559 | ||
560 | ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min, | |
561 | strlen(min)); | |
562 | if (ret) | |
563 | return ret; | |
564 | ||
565 | if (!write_max_first) { | |
566 | ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, | |
567 | max, strlen(max)); | |
568 | if (ret) | |
569 | return ret; | |
570 | } | |
571 | ||
572 | return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, | |
573 | gov, strlen(gov)); | |
7fe2f639 DB |
574 | } |
575 | ||
576 | ||
6c2b8185 DB |
577 | int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq) |
578 | { | |
ac5a181d TR |
579 | char value[SYSFS_PATH_MAX]; |
580 | ||
581 | snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq); | |
582 | ||
583 | return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, | |
584 | value, strlen(value)); | |
7fe2f639 DB |
585 | } |
586 | ||
587 | ||
6c2b8185 DB |
588 | int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq) |
589 | { | |
ac5a181d TR |
590 | char value[SYSFS_PATH_MAX]; |
591 | ||
592 | snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq); | |
7fe2f639 | 593 | |
ac5a181d TR |
594 | return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, |
595 | value, strlen(value)); | |
596 | } | |
7fe2f639 | 597 | |
6c2b8185 DB |
598 | int cpufreq_modify_policy_governor(unsigned int cpu, char *governor) |
599 | { | |
ac5a181d TR |
600 | char new_gov[SYSFS_PATH_MAX]; |
601 | ||
7fe2f639 DB |
602 | if ((!governor) || (strlen(governor) > 19)) |
603 | return -EINVAL; | |
604 | ||
ac5a181d TR |
605 | if (verify_gov(new_gov, governor)) |
606 | return -EINVAL; | |
607 | ||
608 | return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, | |
609 | new_gov, strlen(new_gov)); | |
7fe2f639 DB |
610 | } |
611 | ||
6c2b8185 DB |
612 | int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency) |
613 | { | |
ac5a181d TR |
614 | struct cpufreq_policy *pol = cpufreq_get_policy(cpu); |
615 | char userspace_gov[] = "userspace"; | |
616 | char freq[SYSFS_PATH_MAX]; | |
617 | int ret; | |
618 | ||
619 | if (!pol) | |
620 | return -ENODEV; | |
621 | ||
622 | if (strncmp(pol->governor, userspace_gov, 9) != 0) { | |
623 | ret = cpufreq_modify_policy_governor(cpu, userspace_gov); | |
624 | if (ret) { | |
625 | cpufreq_put_policy(pol); | |
626 | return ret; | |
627 | } | |
628 | } | |
629 | ||
630 | cpufreq_put_policy(pol); | |
631 | ||
632 | snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency); | |
633 | ||
634 | return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED, | |
635 | freq, strlen(freq)); | |
7fe2f639 DB |
636 | } |
637 | ||
6c2b8185 DB |
638 | struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu, |
639 | unsigned long long *total_time) | |
640 | { | |
ac5a181d TR |
641 | struct cpufreq_stats *first = NULL; |
642 | struct cpufreq_stats *current = NULL; | |
643 | char one_value[SYSFS_PATH_MAX]; | |
644 | char linebuf[MAX_LINE_LEN]; | |
645 | unsigned int pos, i; | |
646 | unsigned int len; | |
647 | ||
648 | len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state", | |
649 | linebuf, sizeof(linebuf)); | |
650 | if (len == 0) | |
651 | return NULL; | |
652 | ||
653 | *total_time = 0; | |
654 | pos = 0; | |
655 | for (i = 0; i < len; i++) { | |
656 | if (i == strlen(linebuf) || linebuf[i] == '\n') { | |
657 | if (i - pos < 2) | |
658 | continue; | |
659 | if ((i - pos) >= SYSFS_PATH_MAX) | |
660 | goto error_out; | |
661 | if (current) { | |
662 | current->next = malloc(sizeof(*current)); | |
663 | if (!current->next) | |
664 | goto error_out; | |
665 | current = current->next; | |
666 | } else { | |
667 | first = malloc(sizeof(*first)); | |
668 | if (!first) | |
669 | goto error_out; | |
670 | current = first; | |
671 | } | |
672 | current->first = first; | |
673 | current->next = NULL; | |
674 | ||
675 | memcpy(one_value, linebuf + pos, i - pos); | |
676 | one_value[i - pos] = '\0'; | |
677 | if (sscanf(one_value, "%lu %llu", | |
678 | ¤t->frequency, | |
679 | ¤t->time_in_state) != 2) | |
680 | goto error_out; | |
681 | ||
682 | *total_time = *total_time + current->time_in_state; | |
683 | pos = i + 1; | |
684 | } | |
685 | } | |
686 | ||
687 | return first; | |
688 | ||
689 | error_out: | |
690 | while (first) { | |
691 | current = first->next; | |
692 | free(first); | |
693 | first = current; | |
694 | } | |
695 | return NULL; | |
7fe2f639 DB |
696 | } |
697 | ||
6c2b8185 DB |
698 | void cpufreq_put_stats(struct cpufreq_stats *any) |
699 | { | |
7fe2f639 DB |
700 | struct cpufreq_stats *tmp, *next; |
701 | ||
702 | if (!any) | |
703 | return; | |
704 | ||
705 | tmp = any->first; | |
706 | while (tmp) { | |
707 | next = tmp->next; | |
708 | free(tmp); | |
709 | tmp = next; | |
710 | } | |
711 | } | |
712 | ||
6c2b8185 DB |
713 | unsigned long cpufreq_get_transitions(unsigned int cpu) |
714 | { | |
ac5a181d | 715 | return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS); |
7fe2f639 | 716 | } |