xref: /openbmc/linux/tools/power/cpupower/lib/cpufreq.c (revision da1d9caf)
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, cpufreq_value_files,
117 						  which,
118 						  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, SCALING_CUR_FREQ);
194 }
195 
196 unsigned long cpufreq_get_freq_hardware(unsigned int cpu)
197 {
198 	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
199 }
200 
201 unsigned long cpufreq_get_transition_latency(unsigned int cpu)
202 {
203 	return sysfs_cpufreq_get_one_value(cpu, 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, CPUINFO_MIN_FREQ);
214 	if (!*min)
215 		return -ENODEV;
216 
217 	*max = sysfs_cpufreq_get_one_value(cpu, 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, 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, SCALING_GOVERNOR);
245 	if (!policy->governor) {
246 		free(policy);
247 		return NULL;
248 	}
249 	policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
250 	policy->max = sysfs_cpufreq_get_one_value(cpu, 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", &current->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", &current->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", &current->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, "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, "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, 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, userspace_gov);
693 		if (ret) {
694 			cpufreq_put_policy(pol);
695 			return ret;
696 		}
697 	}
698 
699 	cpufreq_put_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 					&current->frequency,
748 					&current->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, STATS_NUM_TRANSITIONS);
785 }
786