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