xref: /openbmc/linux/tools/power/cpupower/lib/cpufreq.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
14f19048fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27fe2f639SDominik Brodowski /*
37fe2f639SDominik Brodowski  *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
47fe2f639SDominik Brodowski  */
57fe2f639SDominik Brodowski 
67fe2f639SDominik Brodowski 
77fe2f639SDominik Brodowski #include <stdio.h>
87fe2f639SDominik Brodowski #include <errno.h>
97fe2f639SDominik Brodowski #include <stdlib.h>
107fe2f639SDominik Brodowski #include <string.h>
11ac5a181dSThomas Renninger #include <sys/types.h>
12ac5a181dSThomas Renninger #include <sys/stat.h>
13ac5a181dSThomas Renninger #include <fcntl.h>
14ac5a181dSThomas Renninger #include <unistd.h>
157fe2f639SDominik Brodowski 
167fe2f639SDominik Brodowski #include "cpufreq.h"
17ac5a181dSThomas Renninger #include "cpupower_intern.h"
187fe2f639SDominik Brodowski 
19ac5a181dSThomas Renninger /* CPUFREQ sysfs access **************************************************/
20ac5a181dSThomas Renninger 
21ac5a181dSThomas Renninger /* helper function to read file from /sys into given buffer */
22ac5a181dSThomas Renninger /* fname is a relative path under "cpuX/cpufreq" dir */
sysfs_cpufreq_read_file(unsigned int cpu,const char * fname,char * buf,size_t buflen)23ac5a181dSThomas Renninger static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,
24ac5a181dSThomas Renninger 					    char *buf, size_t buflen)
257fe2f639SDominik Brodowski {
26ac5a181dSThomas Renninger 	char path[SYSFS_PATH_MAX];
27ac5a181dSThomas Renninger 
28ac5a181dSThomas Renninger 	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
29ac5a181dSThomas Renninger 			 cpu, fname);
309de9aa45SKonstantin Khlebnikov 	return cpupower_read_sysfs(path, buf, buflen);
317fe2f639SDominik Brodowski }
327fe2f639SDominik Brodowski 
33ac5a181dSThomas Renninger /* helper function to write a new value to a /sys file */
34ac5a181dSThomas Renninger /* fname is a relative path under "cpuX/cpufreq" dir */
sysfs_cpufreq_write_file(unsigned int cpu,const char * fname,const char * value,size_t len)35ac5a181dSThomas Renninger static unsigned int sysfs_cpufreq_write_file(unsigned int cpu,
36ac5a181dSThomas Renninger 					     const char *fname,
37ac5a181dSThomas Renninger 					     const char *value, size_t len)
38ac5a181dSThomas Renninger {
39ac5a181dSThomas Renninger 	char path[SYSFS_PATH_MAX];
40ac5a181dSThomas Renninger 	int fd;
41ac5a181dSThomas Renninger 	ssize_t numwrite;
42ac5a181dSThomas Renninger 
43ac5a181dSThomas Renninger 	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
44ac5a181dSThomas Renninger 			 cpu, fname);
45ac5a181dSThomas Renninger 
46ac5a181dSThomas Renninger 	fd = open(path, O_WRONLY);
47ac5a181dSThomas Renninger 	if (fd == -1)
48ac5a181dSThomas Renninger 		return 0;
49ac5a181dSThomas Renninger 
50ac5a181dSThomas Renninger 	numwrite = write(fd, value, len);
51ac5a181dSThomas Renninger 	if (numwrite < 1) {
52ac5a181dSThomas Renninger 		close(fd);
53ac5a181dSThomas Renninger 		return 0;
54ac5a181dSThomas Renninger 	}
55ac5a181dSThomas Renninger 
56ac5a181dSThomas Renninger 	close(fd);
57ac5a181dSThomas Renninger 
58ac5a181dSThomas Renninger 	return (unsigned int) numwrite;
59ac5a181dSThomas Renninger }
60ac5a181dSThomas Renninger 
61ac5a181dSThomas Renninger /* read access to files which contain one numeric value */
62ac5a181dSThomas Renninger 
63ac5a181dSThomas Renninger enum cpufreq_value {
64ac5a181dSThomas Renninger 	CPUINFO_CUR_FREQ,
65ac5a181dSThomas Renninger 	CPUINFO_MIN_FREQ,
66ac5a181dSThomas Renninger 	CPUINFO_MAX_FREQ,
67ac5a181dSThomas Renninger 	CPUINFO_LATENCY,
68ac5a181dSThomas Renninger 	SCALING_CUR_FREQ,
69ac5a181dSThomas Renninger 	SCALING_MIN_FREQ,
70ac5a181dSThomas Renninger 	SCALING_MAX_FREQ,
71ac5a181dSThomas Renninger 	STATS_NUM_TRANSITIONS,
72ac5a181dSThomas Renninger 	MAX_CPUFREQ_VALUE_READ_FILES
73ac5a181dSThomas Renninger };
74ac5a181dSThomas Renninger 
75ac5a181dSThomas Renninger static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {
76ac5a181dSThomas Renninger 	[CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq",
77ac5a181dSThomas Renninger 	[CPUINFO_MIN_FREQ] = "cpuinfo_min_freq",
78ac5a181dSThomas Renninger 	[CPUINFO_MAX_FREQ] = "cpuinfo_max_freq",
79ac5a181dSThomas Renninger 	[CPUINFO_LATENCY]  = "cpuinfo_transition_latency",
80ac5a181dSThomas Renninger 	[SCALING_CUR_FREQ] = "scaling_cur_freq",
81ac5a181dSThomas Renninger 	[SCALING_MIN_FREQ] = "scaling_min_freq",
82ac5a181dSThomas Renninger 	[SCALING_MAX_FREQ] = "scaling_max_freq",
83ac5a181dSThomas Renninger 	[STATS_NUM_TRANSITIONS] = "stats/total_trans"
84ac5a181dSThomas Renninger };
85ac5a181dSThomas Renninger 
cpufreq_get_sysfs_value_from_table(unsigned int cpu,const char ** table,unsigned int index,unsigned int size)86*e3ede976SHuang Rui unsigned long cpufreq_get_sysfs_value_from_table(unsigned int cpu,
87*e3ede976SHuang Rui 						 const char **table,
88*e3ede976SHuang Rui 						 unsigned int index,
89*e3ede976SHuang Rui 						 unsigned int size)
90ac5a181dSThomas Renninger {
91ac5a181dSThomas Renninger 	unsigned long value;
92ac5a181dSThomas Renninger 	unsigned int len;
93ac5a181dSThomas Renninger 	char linebuf[MAX_LINE_LEN];
94ac5a181dSThomas Renninger 	char *endp;
95ac5a181dSThomas Renninger 
96*e3ede976SHuang Rui 	if (!table || index >= size || !table[index])
97ac5a181dSThomas Renninger 		return 0;
98ac5a181dSThomas Renninger 
99*e3ede976SHuang Rui 	len = sysfs_cpufreq_read_file(cpu, table[index], linebuf,
100*e3ede976SHuang Rui 				      sizeof(linebuf));
101ac5a181dSThomas Renninger 
102ac5a181dSThomas Renninger 	if (len == 0)
103ac5a181dSThomas Renninger 		return 0;
104ac5a181dSThomas Renninger 
105ac5a181dSThomas Renninger 	value = strtoul(linebuf, &endp, 0);
106ac5a181dSThomas Renninger 
107ac5a181dSThomas Renninger 	if (endp == linebuf || errno == ERANGE)
108ac5a181dSThomas Renninger 		return 0;
109ac5a181dSThomas Renninger 
110ac5a181dSThomas Renninger 	return value;
111ac5a181dSThomas Renninger }
112ac5a181dSThomas Renninger 
sysfs_cpufreq_get_one_value(unsigned int cpu,enum cpufreq_value which)113*e3ede976SHuang Rui static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,
114*e3ede976SHuang Rui 						 enum cpufreq_value which)
115*e3ede976SHuang Rui {
116*e3ede976SHuang Rui 	return cpufreq_get_sysfs_value_from_table(cpu, cpufreq_value_files,
117*e3ede976SHuang Rui 						  which,
118*e3ede976SHuang Rui 						  MAX_CPUFREQ_VALUE_READ_FILES);
119*e3ede976SHuang Rui }
120*e3ede976SHuang Rui 
121ac5a181dSThomas Renninger /* read access to files which contain one string */
122ac5a181dSThomas Renninger 
123ac5a181dSThomas Renninger enum cpufreq_string {
124ac5a181dSThomas Renninger 	SCALING_DRIVER,
125ac5a181dSThomas Renninger 	SCALING_GOVERNOR,
126ac5a181dSThomas Renninger 	MAX_CPUFREQ_STRING_FILES
127ac5a181dSThomas Renninger };
128ac5a181dSThomas Renninger 
129ac5a181dSThomas Renninger static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
130ac5a181dSThomas Renninger 	[SCALING_DRIVER] = "scaling_driver",
131ac5a181dSThomas Renninger 	[SCALING_GOVERNOR] = "scaling_governor",
132ac5a181dSThomas Renninger };
133ac5a181dSThomas Renninger 
134ac5a181dSThomas Renninger 
sysfs_cpufreq_get_one_string(unsigned int cpu,enum cpufreq_string which)135ac5a181dSThomas Renninger static char *sysfs_cpufreq_get_one_string(unsigned int cpu,
136ac5a181dSThomas Renninger 					  enum cpufreq_string which)
137ac5a181dSThomas Renninger {
138ac5a181dSThomas Renninger 	char linebuf[MAX_LINE_LEN];
139ac5a181dSThomas Renninger 	char *result;
140ac5a181dSThomas Renninger 	unsigned int len;
141ac5a181dSThomas Renninger 
142ac5a181dSThomas Renninger 	if (which >= MAX_CPUFREQ_STRING_FILES)
143ac5a181dSThomas Renninger 		return NULL;
144ac5a181dSThomas Renninger 
145ac5a181dSThomas Renninger 	len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
146ac5a181dSThomas Renninger 				linebuf, sizeof(linebuf));
147ac5a181dSThomas Renninger 	if (len == 0)
148ac5a181dSThomas Renninger 		return NULL;
149ac5a181dSThomas Renninger 
150ac5a181dSThomas Renninger 	result = strdup(linebuf);
151ac5a181dSThomas Renninger 	if (result == NULL)
152ac5a181dSThomas Renninger 		return NULL;
153ac5a181dSThomas Renninger 
154ac5a181dSThomas Renninger 	if (result[strlen(result) - 1] == '\n')
155ac5a181dSThomas Renninger 		result[strlen(result) - 1] = '\0';
156ac5a181dSThomas Renninger 
157ac5a181dSThomas Renninger 	return result;
158ac5a181dSThomas Renninger }
159ac5a181dSThomas Renninger 
160ac5a181dSThomas Renninger /* write access */
161ac5a181dSThomas Renninger 
162ac5a181dSThomas Renninger enum cpufreq_write {
163ac5a181dSThomas Renninger 	WRITE_SCALING_MIN_FREQ,
164ac5a181dSThomas Renninger 	WRITE_SCALING_MAX_FREQ,
165ac5a181dSThomas Renninger 	WRITE_SCALING_GOVERNOR,
166ac5a181dSThomas Renninger 	WRITE_SCALING_SET_SPEED,
167ac5a181dSThomas Renninger 	MAX_CPUFREQ_WRITE_FILES
168ac5a181dSThomas Renninger };
169ac5a181dSThomas Renninger 
170ac5a181dSThomas Renninger static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
171ac5a181dSThomas Renninger 	[WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
172ac5a181dSThomas Renninger 	[WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
173ac5a181dSThomas Renninger 	[WRITE_SCALING_GOVERNOR] = "scaling_governor",
174ac5a181dSThomas Renninger 	[WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
175ac5a181dSThomas Renninger };
176ac5a181dSThomas Renninger 
sysfs_cpufreq_write_one_value(unsigned int cpu,enum cpufreq_write which,const char * new_value,size_t len)177ac5a181dSThomas Renninger static int sysfs_cpufreq_write_one_value(unsigned int cpu,
178ac5a181dSThomas Renninger 					 enum cpufreq_write which,
179ac5a181dSThomas Renninger 					 const char *new_value, size_t len)
180ac5a181dSThomas Renninger {
181ac5a181dSThomas Renninger 	if (which >= MAX_CPUFREQ_WRITE_FILES)
182ac5a181dSThomas Renninger 		return 0;
183ac5a181dSThomas Renninger 
184ac5a181dSThomas Renninger 	if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
185ac5a181dSThomas Renninger 					new_value, len) != len)
186ac5a181dSThomas Renninger 		return -ENODEV;
187ac5a181dSThomas Renninger 
188ac5a181dSThomas Renninger 	return 0;
189ac5a181dSThomas Renninger };
190ac5a181dSThomas Renninger 
cpufreq_get_freq_kernel(unsigned int cpu)1917fe2f639SDominik Brodowski unsigned long cpufreq_get_freq_kernel(unsigned int cpu)
1927fe2f639SDominik Brodowski {
193ac5a181dSThomas Renninger 	return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
1947fe2f639SDominik Brodowski }
1957fe2f639SDominik Brodowski 
cpufreq_get_freq_hardware(unsigned int cpu)1967fe2f639SDominik Brodowski unsigned long cpufreq_get_freq_hardware(unsigned int cpu)
1977fe2f639SDominik Brodowski {
198ac5a181dSThomas Renninger 	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
1997fe2f639SDominik Brodowski }
2007fe2f639SDominik Brodowski 
cpufreq_get_transition_latency(unsigned int cpu)2017fe2f639SDominik Brodowski unsigned long cpufreq_get_transition_latency(unsigned int cpu)
2027fe2f639SDominik Brodowski {
203ac5a181dSThomas Renninger 	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
2047fe2f639SDominik Brodowski }
2057fe2f639SDominik Brodowski 
cpufreq_get_hardware_limits(unsigned int cpu,unsigned long * min,unsigned long * max)2067fe2f639SDominik Brodowski int cpufreq_get_hardware_limits(unsigned int cpu,
2077fe2f639SDominik Brodowski 				unsigned long *min,
2087fe2f639SDominik Brodowski 				unsigned long *max)
2097fe2f639SDominik Brodowski {
2107fe2f639SDominik Brodowski 	if ((!min) || (!max))
2117fe2f639SDominik Brodowski 		return -EINVAL;
212ac5a181dSThomas Renninger 
213ac5a181dSThomas Renninger 	*min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);
214ac5a181dSThomas Renninger 	if (!*min)
215ac5a181dSThomas Renninger 		return -ENODEV;
216ac5a181dSThomas Renninger 
217ac5a181dSThomas Renninger 	*max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
218ac5a181dSThomas Renninger 	if (!*max)
219ac5a181dSThomas Renninger 		return -ENODEV;
220ac5a181dSThomas Renninger 
221ac5a181dSThomas Renninger 	return 0;
2227fe2f639SDominik Brodowski }
2237fe2f639SDominik Brodowski 
cpufreq_get_driver(unsigned int cpu)2246c2b8185SDominik Brodowski char *cpufreq_get_driver(unsigned int cpu)
2256c2b8185SDominik Brodowski {
226ac5a181dSThomas Renninger 	return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
2277fe2f639SDominik Brodowski }
2287fe2f639SDominik Brodowski 
cpufreq_put_driver(char * ptr)2296c2b8185SDominik Brodowski void cpufreq_put_driver(char *ptr)
2306c2b8185SDominik Brodowski {
2317fe2f639SDominik Brodowski 	if (!ptr)
2327fe2f639SDominik Brodowski 		return;
2337fe2f639SDominik Brodowski 	free(ptr);
2347fe2f639SDominik Brodowski }
2357fe2f639SDominik Brodowski 
cpufreq_get_policy(unsigned int cpu)2366c2b8185SDominik Brodowski struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu)
2376c2b8185SDominik Brodowski {
238ac5a181dSThomas Renninger 	struct cpufreq_policy *policy;
239ac5a181dSThomas Renninger 
240ac5a181dSThomas Renninger 	policy = malloc(sizeof(struct cpufreq_policy));
241ac5a181dSThomas Renninger 	if (!policy)
242ac5a181dSThomas Renninger 		return NULL;
243ac5a181dSThomas Renninger 
244ac5a181dSThomas Renninger 	policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
245ac5a181dSThomas Renninger 	if (!policy->governor) {
246ac5a181dSThomas Renninger 		free(policy);
247ac5a181dSThomas Renninger 		return NULL;
248ac5a181dSThomas Renninger 	}
249ac5a181dSThomas Renninger 	policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
250ac5a181dSThomas Renninger 	policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);
251ac5a181dSThomas Renninger 	if ((!policy->min) || (!policy->max)) {
252ac5a181dSThomas Renninger 		free(policy->governor);
253ac5a181dSThomas Renninger 		free(policy);
254ac5a181dSThomas Renninger 		return NULL;
255ac5a181dSThomas Renninger 	}
256ac5a181dSThomas Renninger 
257ac5a181dSThomas Renninger 	return policy;
2587fe2f639SDominik Brodowski }
2597fe2f639SDominik Brodowski 
cpufreq_put_policy(struct cpufreq_policy * policy)2606c2b8185SDominik Brodowski void cpufreq_put_policy(struct cpufreq_policy *policy)
2616c2b8185SDominik Brodowski {
2627fe2f639SDominik Brodowski 	if ((!policy) || (!policy->governor))
2637fe2f639SDominik Brodowski 		return;
2647fe2f639SDominik Brodowski 
2657fe2f639SDominik Brodowski 	free(policy->governor);
2667fe2f639SDominik Brodowski 	policy->governor = NULL;
2677fe2f639SDominik Brodowski 	free(policy);
2687fe2f639SDominik Brodowski }
2697fe2f639SDominik Brodowski 
cpufreq_get_available_governors(unsigned int cpu)2706c2b8185SDominik Brodowski struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned
2716c2b8185SDominik Brodowski 								int cpu)
2726c2b8185SDominik Brodowski {
273ac5a181dSThomas Renninger 	struct cpufreq_available_governors *first = NULL;
274ac5a181dSThomas Renninger 	struct cpufreq_available_governors *current = NULL;
275ac5a181dSThomas Renninger 	char linebuf[MAX_LINE_LEN];
276ac5a181dSThomas Renninger 	unsigned int pos, i;
277ac5a181dSThomas Renninger 	unsigned int len;
278ac5a181dSThomas Renninger 
279ac5a181dSThomas Renninger 	len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
280ac5a181dSThomas Renninger 				linebuf, sizeof(linebuf));
281ac5a181dSThomas Renninger 	if (len == 0)
282ac5a181dSThomas Renninger 		return NULL;
283ac5a181dSThomas Renninger 
284ac5a181dSThomas Renninger 	pos = 0;
285ac5a181dSThomas Renninger 	for (i = 0; i < len; i++) {
286ac5a181dSThomas Renninger 		if (linebuf[i] == ' ' || linebuf[i] == '\n') {
287ac5a181dSThomas Renninger 			if (i - pos < 2)
288ac5a181dSThomas Renninger 				continue;
289ac5a181dSThomas Renninger 			if (current) {
290ac5a181dSThomas Renninger 				current->next = malloc(sizeof(*current));
291ac5a181dSThomas Renninger 				if (!current->next)
292ac5a181dSThomas Renninger 					goto error_out;
293ac5a181dSThomas Renninger 				current = current->next;
294ac5a181dSThomas Renninger 			} else {
295ac5a181dSThomas Renninger 				first = malloc(sizeof(*first));
296ac5a181dSThomas Renninger 				if (!first)
297cbf25270SShuah Khan 					return NULL;
298ac5a181dSThomas Renninger 				current = first;
299ac5a181dSThomas Renninger 			}
300ac5a181dSThomas Renninger 			current->first = first;
301ac5a181dSThomas Renninger 			current->next = NULL;
302ac5a181dSThomas Renninger 
303ac5a181dSThomas Renninger 			current->governor = malloc(i - pos + 1);
304ac5a181dSThomas Renninger 			if (!current->governor)
305ac5a181dSThomas Renninger 				goto error_out;
306ac5a181dSThomas Renninger 
307ac5a181dSThomas Renninger 			memcpy(current->governor, linebuf + pos, i - pos);
308ac5a181dSThomas Renninger 			current->governor[i - pos] = '\0';
309ac5a181dSThomas Renninger 			pos = i + 1;
310ac5a181dSThomas Renninger 		}
311ac5a181dSThomas Renninger 	}
312ac5a181dSThomas Renninger 
313ac5a181dSThomas Renninger 	return first;
314ac5a181dSThomas Renninger 
315ac5a181dSThomas Renninger  error_out:
316ac5a181dSThomas Renninger 	while (first) {
317ac5a181dSThomas Renninger 		current = first->next;
318ac5a181dSThomas Renninger 		if (first->governor)
319ac5a181dSThomas Renninger 			free(first->governor);
320ac5a181dSThomas Renninger 		free(first);
321ac5a181dSThomas Renninger 		first = current;
322ac5a181dSThomas Renninger 	}
323ac5a181dSThomas Renninger 	return NULL;
3247fe2f639SDominik Brodowski }
3257fe2f639SDominik Brodowski 
cpufreq_put_available_governors(struct cpufreq_available_governors * any)3266c2b8185SDominik Brodowski void cpufreq_put_available_governors(struct cpufreq_available_governors *any)
3276c2b8185SDominik Brodowski {
3287fe2f639SDominik Brodowski 	struct cpufreq_available_governors *tmp, *next;
3297fe2f639SDominik Brodowski 
3307fe2f639SDominik Brodowski 	if (!any)
3317fe2f639SDominik Brodowski 		return;
3327fe2f639SDominik Brodowski 
3337fe2f639SDominik Brodowski 	tmp = any->first;
3347fe2f639SDominik Brodowski 	while (tmp) {
3357fe2f639SDominik Brodowski 		next = tmp->next;
3367fe2f639SDominik Brodowski 		if (tmp->governor)
3377fe2f639SDominik Brodowski 			free(tmp->governor);
3387fe2f639SDominik Brodowski 		free(tmp);
3397fe2f639SDominik Brodowski 		tmp = next;
3407fe2f639SDominik Brodowski 	}
3417fe2f639SDominik Brodowski }
3427fe2f639SDominik Brodowski 
3437fe2f639SDominik Brodowski 
34441ddb7e1SThomas Renninger struct cpufreq_available_frequencies
cpufreq_get_available_frequencies(unsigned int cpu)34541ddb7e1SThomas Renninger *cpufreq_get_available_frequencies(unsigned int cpu)
3466c2b8185SDominik Brodowski {
34741ddb7e1SThomas Renninger 	struct cpufreq_available_frequencies *first = NULL;
34841ddb7e1SThomas Renninger 	struct cpufreq_available_frequencies *current = NULL;
349ac5a181dSThomas Renninger 	char one_value[SYSFS_PATH_MAX];
350ac5a181dSThomas Renninger 	char linebuf[MAX_LINE_LEN];
351ac5a181dSThomas Renninger 	unsigned int pos, i;
352ac5a181dSThomas Renninger 	unsigned int len;
353ac5a181dSThomas Renninger 
35441ddb7e1SThomas Renninger 	len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies",
355ac5a181dSThomas Renninger 				      linebuf, sizeof(linebuf));
356ac5a181dSThomas Renninger 	if (len == 0)
357ac5a181dSThomas Renninger 		return NULL;
358ac5a181dSThomas Renninger 
359ac5a181dSThomas Renninger 	pos = 0;
360ac5a181dSThomas Renninger 	for (i = 0; i < len; i++) {
361ac5a181dSThomas Renninger 		if (linebuf[i] == ' ' || linebuf[i] == '\n') {
362ac5a181dSThomas Renninger 			if (i - pos < 2)
363ac5a181dSThomas Renninger 				continue;
364ac5a181dSThomas Renninger 			if (i - pos >= SYSFS_PATH_MAX)
365ac5a181dSThomas Renninger 				goto error_out;
366ac5a181dSThomas Renninger 			if (current) {
367ac5a181dSThomas Renninger 				current->next = malloc(sizeof(*current));
368ac5a181dSThomas Renninger 				if (!current->next)
369ac5a181dSThomas Renninger 					goto error_out;
370ac5a181dSThomas Renninger 				current = current->next;
371ac5a181dSThomas Renninger 			} else {
372ac5a181dSThomas Renninger 				first = malloc(sizeof(*first));
373ac5a181dSThomas Renninger 				if (!first)
374cbf25270SShuah Khan 					return NULL;
375ac5a181dSThomas Renninger 				current = first;
376ac5a181dSThomas Renninger 			}
377ac5a181dSThomas Renninger 			current->first = first;
378ac5a181dSThomas Renninger 			current->next = NULL;
379ac5a181dSThomas Renninger 
380ac5a181dSThomas Renninger 			memcpy(one_value, linebuf + pos, i - pos);
381ac5a181dSThomas Renninger 			one_value[i - pos] = '\0';
382ac5a181dSThomas Renninger 			if (sscanf(one_value, "%lu", &current->frequency) != 1)
383ac5a181dSThomas Renninger 				goto error_out;
384ac5a181dSThomas Renninger 
385ac5a181dSThomas Renninger 			pos = i + 1;
386ac5a181dSThomas Renninger 		}
387ac5a181dSThomas Renninger 	}
388ac5a181dSThomas Renninger 
389ac5a181dSThomas Renninger 	return first;
390ac5a181dSThomas Renninger 
391ac5a181dSThomas Renninger  error_out:
392ac5a181dSThomas Renninger 	while (first) {
393ac5a181dSThomas Renninger 		current = first->next;
394ac5a181dSThomas Renninger 		free(first);
395ac5a181dSThomas Renninger 		first = current;
396ac5a181dSThomas Renninger 	}
397ac5a181dSThomas Renninger 	return NULL;
3987fe2f639SDominik Brodowski }
3997fe2f639SDominik Brodowski 
40041ddb7e1SThomas Renninger struct cpufreq_available_frequencies
cpufreq_get_boost_frequencies(unsigned int cpu)40141ddb7e1SThomas Renninger *cpufreq_get_boost_frequencies(unsigned int cpu)
402ae291709SAbhishek Goel {
40341ddb7e1SThomas Renninger 	struct cpufreq_available_frequencies *first = NULL;
40441ddb7e1SThomas Renninger 	struct cpufreq_available_frequencies *current = NULL;
40541ddb7e1SThomas Renninger 	char one_value[SYSFS_PATH_MAX];
40641ddb7e1SThomas Renninger 	char linebuf[MAX_LINE_LEN];
40741ddb7e1SThomas Renninger 	unsigned int pos, i;
40841ddb7e1SThomas Renninger 	unsigned int len;
40941ddb7e1SThomas Renninger 
41041ddb7e1SThomas Renninger 	len = sysfs_cpufreq_read_file(cpu, "scaling_boost_frequencies",
41141ddb7e1SThomas Renninger 				      linebuf, sizeof(linebuf));
41241ddb7e1SThomas Renninger 	if (len == 0)
41341ddb7e1SThomas Renninger 		return NULL;
41441ddb7e1SThomas Renninger 
41541ddb7e1SThomas Renninger 	pos = 0;
41641ddb7e1SThomas Renninger 	for (i = 0; i < len; i++) {
41741ddb7e1SThomas Renninger 		if (linebuf[i] == ' ' || linebuf[i] == '\n') {
41841ddb7e1SThomas Renninger 			if (i - pos < 2)
41941ddb7e1SThomas Renninger 				continue;
42041ddb7e1SThomas Renninger 			if (i - pos >= SYSFS_PATH_MAX)
42141ddb7e1SThomas Renninger 				goto error_out;
42241ddb7e1SThomas Renninger 			if (current) {
42341ddb7e1SThomas Renninger 				current->next = malloc(sizeof(*current));
42441ddb7e1SThomas Renninger 				if (!current->next)
42541ddb7e1SThomas Renninger 					goto error_out;
42641ddb7e1SThomas Renninger 				current = current->next;
42741ddb7e1SThomas Renninger 			} else {
42841ddb7e1SThomas Renninger 				first = malloc(sizeof(*first));
42941ddb7e1SThomas Renninger 				if (!first)
430cbf25270SShuah Khan 					return NULL;
43141ddb7e1SThomas Renninger 				current = first;
43241ddb7e1SThomas Renninger 			}
43341ddb7e1SThomas Renninger 			current->first = first;
43441ddb7e1SThomas Renninger 			current->next = NULL;
43541ddb7e1SThomas Renninger 
43641ddb7e1SThomas Renninger 			memcpy(one_value, linebuf + pos, i - pos);
43741ddb7e1SThomas Renninger 			one_value[i - pos] = '\0';
43841ddb7e1SThomas Renninger 			if (sscanf(one_value, "%lu", &current->frequency) != 1)
43941ddb7e1SThomas Renninger 				goto error_out;
44041ddb7e1SThomas Renninger 
44141ddb7e1SThomas Renninger 			pos = i + 1;
44241ddb7e1SThomas Renninger 		}
44341ddb7e1SThomas Renninger 	}
44441ddb7e1SThomas Renninger 
44541ddb7e1SThomas Renninger 	return first;
44641ddb7e1SThomas Renninger 
44741ddb7e1SThomas Renninger  error_out:
44841ddb7e1SThomas Renninger 	while (first) {
44941ddb7e1SThomas Renninger 		current = first->next;
45041ddb7e1SThomas Renninger 		free(first);
45141ddb7e1SThomas Renninger 		first = current;
45241ddb7e1SThomas Renninger 	}
45341ddb7e1SThomas Renninger 	return NULL;
45441ddb7e1SThomas Renninger }
45541ddb7e1SThomas Renninger 
cpufreq_put_available_frequencies(struct cpufreq_available_frequencies * any)45641ddb7e1SThomas Renninger void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies *any)
45741ddb7e1SThomas Renninger {
45841ddb7e1SThomas Renninger 	struct cpufreq_available_frequencies *tmp, *next;
4597fe2f639SDominik Brodowski 
4607fe2f639SDominik Brodowski 	if (!any)
4617fe2f639SDominik Brodowski 		return;
4627fe2f639SDominik Brodowski 
4637fe2f639SDominik Brodowski 	tmp = any->first;
4647fe2f639SDominik Brodowski 	while (tmp) {
4657fe2f639SDominik Brodowski 		next = tmp->next;
4667fe2f639SDominik Brodowski 		free(tmp);
4677fe2f639SDominik Brodowski 		tmp = next;
4687fe2f639SDominik Brodowski 	}
4697fe2f639SDominik Brodowski }
4707fe2f639SDominik Brodowski 
cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies * any)47141ddb7e1SThomas Renninger void cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies *any)
47241ddb7e1SThomas Renninger {
47341ddb7e1SThomas Renninger 	cpufreq_put_available_frequencies(any);
47441ddb7e1SThomas Renninger }
47541ddb7e1SThomas Renninger 
sysfs_get_cpu_list(unsigned int cpu,const char * file)476ac5a181dSThomas Renninger static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
477ac5a181dSThomas Renninger 							const char *file)
478ac5a181dSThomas Renninger {
479ac5a181dSThomas Renninger 	struct cpufreq_affected_cpus *first = NULL;
480ac5a181dSThomas Renninger 	struct cpufreq_affected_cpus *current = NULL;
481ac5a181dSThomas Renninger 	char one_value[SYSFS_PATH_MAX];
482ac5a181dSThomas Renninger 	char linebuf[MAX_LINE_LEN];
483ac5a181dSThomas Renninger 	unsigned int pos, i;
484ac5a181dSThomas Renninger 	unsigned int len;
485ac5a181dSThomas Renninger 
486ac5a181dSThomas Renninger 	len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
487ac5a181dSThomas Renninger 	if (len == 0)
488ac5a181dSThomas Renninger 		return NULL;
489ac5a181dSThomas Renninger 
490ac5a181dSThomas Renninger 	pos = 0;
491ac5a181dSThomas Renninger 	for (i = 0; i < len; i++) {
492ac5a181dSThomas Renninger 		if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
493ac5a181dSThomas Renninger 			if (i - pos  < 1)
494ac5a181dSThomas Renninger 				continue;
495ac5a181dSThomas Renninger 			if (i - pos >= SYSFS_PATH_MAX)
496ac5a181dSThomas Renninger 				goto error_out;
497ac5a181dSThomas Renninger 			if (current) {
498ac5a181dSThomas Renninger 				current->next = malloc(sizeof(*current));
499ac5a181dSThomas Renninger 				if (!current->next)
500ac5a181dSThomas Renninger 					goto error_out;
501ac5a181dSThomas Renninger 				current = current->next;
502ac5a181dSThomas Renninger 			} else {
503ac5a181dSThomas Renninger 				first = malloc(sizeof(*first));
504ac5a181dSThomas Renninger 				if (!first)
505cbf25270SShuah Khan 					return NULL;
506ac5a181dSThomas Renninger 				current = first;
507ac5a181dSThomas Renninger 			}
508ac5a181dSThomas Renninger 			current->first = first;
509ac5a181dSThomas Renninger 			current->next = NULL;
510ac5a181dSThomas Renninger 
511ac5a181dSThomas Renninger 			memcpy(one_value, linebuf + pos, i - pos);
512ac5a181dSThomas Renninger 			one_value[i - pos] = '\0';
513ac5a181dSThomas Renninger 
514ac5a181dSThomas Renninger 			if (sscanf(one_value, "%u", &current->cpu) != 1)
515ac5a181dSThomas Renninger 				goto error_out;
516ac5a181dSThomas Renninger 
517ac5a181dSThomas Renninger 			pos = i + 1;
518ac5a181dSThomas Renninger 		}
519ac5a181dSThomas Renninger 	}
520ac5a181dSThomas Renninger 
521ac5a181dSThomas Renninger 	return first;
522ac5a181dSThomas Renninger 
523ac5a181dSThomas Renninger  error_out:
524ac5a181dSThomas Renninger 	while (first) {
525ac5a181dSThomas Renninger 		current = first->next;
526ac5a181dSThomas Renninger 		free(first);
527ac5a181dSThomas Renninger 		first = current;
528ac5a181dSThomas Renninger 	}
529ac5a181dSThomas Renninger 	return NULL;
530ac5a181dSThomas Renninger }
5317fe2f639SDominik Brodowski 
cpufreq_get_affected_cpus(unsigned int cpu)5326c2b8185SDominik Brodowski struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu)
5336c2b8185SDominik Brodowski {
534ac5a181dSThomas Renninger 	return sysfs_get_cpu_list(cpu, "affected_cpus");
5357fe2f639SDominik Brodowski }
5367fe2f639SDominik Brodowski 
cpufreq_put_affected_cpus(struct cpufreq_affected_cpus * any)5376c2b8185SDominik Brodowski void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any)
5386c2b8185SDominik Brodowski {
5397fe2f639SDominik Brodowski 	struct cpufreq_affected_cpus *tmp, *next;
5407fe2f639SDominik Brodowski 
5417fe2f639SDominik Brodowski 	if (!any)
5427fe2f639SDominik Brodowski 		return;
5437fe2f639SDominik Brodowski 
5447fe2f639SDominik Brodowski 	tmp = any->first;
5457fe2f639SDominik Brodowski 	while (tmp) {
5467fe2f639SDominik Brodowski 		next = tmp->next;
5477fe2f639SDominik Brodowski 		free(tmp);
5487fe2f639SDominik Brodowski 		tmp = next;
5497fe2f639SDominik Brodowski 	}
5507fe2f639SDominik Brodowski }
5517fe2f639SDominik Brodowski 
5527fe2f639SDominik Brodowski 
cpufreq_get_related_cpus(unsigned int cpu)5536c2b8185SDominik Brodowski struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu)
5546c2b8185SDominik Brodowski {
555ac5a181dSThomas Renninger 	return sysfs_get_cpu_list(cpu, "related_cpus");
5567fe2f639SDominik Brodowski }
5577fe2f639SDominik Brodowski 
cpufreq_put_related_cpus(struct cpufreq_affected_cpus * any)5586c2b8185SDominik Brodowski void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any)
5596c2b8185SDominik Brodowski {
5607fe2f639SDominik Brodowski 	cpufreq_put_affected_cpus(any);
5617fe2f639SDominik Brodowski }
5627fe2f639SDominik Brodowski 
verify_gov(char * new_gov,char * passed_gov)563ac5a181dSThomas Renninger static int verify_gov(char *new_gov, char *passed_gov)
564ac5a181dSThomas Renninger {
565ac5a181dSThomas Renninger 	unsigned int i, j = 0;
566ac5a181dSThomas Renninger 
567ac5a181dSThomas Renninger 	if (!passed_gov || (strlen(passed_gov) > 19))
568ac5a181dSThomas Renninger 		return -EINVAL;
569ac5a181dSThomas Renninger 
570ac5a181dSThomas Renninger 	strncpy(new_gov, passed_gov, 20);
571ac5a181dSThomas Renninger 	for (i = 0; i < 20; i++) {
572ac5a181dSThomas Renninger 		if (j) {
573ac5a181dSThomas Renninger 			new_gov[i] = '\0';
574ac5a181dSThomas Renninger 			continue;
575ac5a181dSThomas Renninger 		}
576ac5a181dSThomas Renninger 		if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))
577ac5a181dSThomas Renninger 			continue;
578ac5a181dSThomas Renninger 
579ac5a181dSThomas Renninger 		if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))
580ac5a181dSThomas Renninger 			continue;
581ac5a181dSThomas Renninger 
582ac5a181dSThomas Renninger 		if (new_gov[i] == '-')
583ac5a181dSThomas Renninger 			continue;
584ac5a181dSThomas Renninger 
585ac5a181dSThomas Renninger 		if (new_gov[i] == '_')
586ac5a181dSThomas Renninger 			continue;
587ac5a181dSThomas Renninger 
588ac5a181dSThomas Renninger 		if (new_gov[i] == '\0') {
589ac5a181dSThomas Renninger 			j = 1;
590ac5a181dSThomas Renninger 			continue;
591ac5a181dSThomas Renninger 		}
592ac5a181dSThomas Renninger 		return -EINVAL;
593ac5a181dSThomas Renninger 	}
594ac5a181dSThomas Renninger 	new_gov[19] = '\0';
595ac5a181dSThomas Renninger 	return 0;
596ac5a181dSThomas Renninger }
5977fe2f639SDominik Brodowski 
cpufreq_set_policy(unsigned int cpu,struct cpufreq_policy * policy)5986c2b8185SDominik Brodowski int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy)
5996c2b8185SDominik Brodowski {
600ac5a181dSThomas Renninger 	char min[SYSFS_PATH_MAX];
601ac5a181dSThomas Renninger 	char max[SYSFS_PATH_MAX];
602ac5a181dSThomas Renninger 	char gov[SYSFS_PATH_MAX];
603ac5a181dSThomas Renninger 	int ret;
604ac5a181dSThomas Renninger 	unsigned long old_min;
605ac5a181dSThomas Renninger 	int write_max_first;
606ac5a181dSThomas Renninger 
6077fe2f639SDominik Brodowski 	if (!policy || !(policy->governor))
6087fe2f639SDominik Brodowski 		return -EINVAL;
6097fe2f639SDominik Brodowski 
610ac5a181dSThomas Renninger 	if (policy->max < policy->min)
611ac5a181dSThomas Renninger 		return -EINVAL;
612ac5a181dSThomas Renninger 
613ac5a181dSThomas Renninger 	if (verify_gov(gov, policy->governor))
614ac5a181dSThomas Renninger 		return -EINVAL;
615ac5a181dSThomas Renninger 
616ac5a181dSThomas Renninger 	snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
617ac5a181dSThomas Renninger 	snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
618ac5a181dSThomas Renninger 
619ac5a181dSThomas Renninger 	old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
620ac5a181dSThomas Renninger 	write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
621ac5a181dSThomas Renninger 
622ac5a181dSThomas Renninger 	if (write_max_first) {
623ac5a181dSThomas Renninger 		ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
624ac5a181dSThomas Renninger 						    max, strlen(max));
625ac5a181dSThomas Renninger 		if (ret)
626ac5a181dSThomas Renninger 			return ret;
627ac5a181dSThomas Renninger 	}
628ac5a181dSThomas Renninger 
629ac5a181dSThomas Renninger 	ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
630ac5a181dSThomas Renninger 					    strlen(min));
631ac5a181dSThomas Renninger 	if (ret)
632ac5a181dSThomas Renninger 		return ret;
633ac5a181dSThomas Renninger 
634ac5a181dSThomas Renninger 	if (!write_max_first) {
635ac5a181dSThomas Renninger 		ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
636ac5a181dSThomas Renninger 						    max, strlen(max));
637ac5a181dSThomas Renninger 		if (ret)
638ac5a181dSThomas Renninger 			return ret;
639ac5a181dSThomas Renninger 	}
640ac5a181dSThomas Renninger 
641ac5a181dSThomas Renninger 	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
642ac5a181dSThomas Renninger 					     gov, strlen(gov));
6437fe2f639SDominik Brodowski }
6447fe2f639SDominik Brodowski 
6457fe2f639SDominik Brodowski 
cpufreq_modify_policy_min(unsigned int cpu,unsigned long min_freq)6466c2b8185SDominik Brodowski int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq)
6476c2b8185SDominik Brodowski {
648ac5a181dSThomas Renninger 	char value[SYSFS_PATH_MAX];
649ac5a181dSThomas Renninger 
650ac5a181dSThomas Renninger 	snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
651ac5a181dSThomas Renninger 
652ac5a181dSThomas Renninger 	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
653ac5a181dSThomas Renninger 					     value, strlen(value));
6547fe2f639SDominik Brodowski }
6557fe2f639SDominik Brodowski 
6567fe2f639SDominik Brodowski 
cpufreq_modify_policy_max(unsigned int cpu,unsigned long max_freq)6576c2b8185SDominik Brodowski int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq)
6586c2b8185SDominik Brodowski {
659ac5a181dSThomas Renninger 	char value[SYSFS_PATH_MAX];
6607fe2f639SDominik Brodowski 
661ac5a181dSThomas Renninger 	snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
662ac5a181dSThomas Renninger 
663ac5a181dSThomas Renninger 	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
664ac5a181dSThomas Renninger 					     value, strlen(value));
665ac5a181dSThomas Renninger }
6667fe2f639SDominik Brodowski 
cpufreq_modify_policy_governor(unsigned int cpu,char * governor)6676c2b8185SDominik Brodowski int cpufreq_modify_policy_governor(unsigned int cpu, char *governor)
6686c2b8185SDominik Brodowski {
669ac5a181dSThomas Renninger 	char new_gov[SYSFS_PATH_MAX];
670ac5a181dSThomas Renninger 
6717fe2f639SDominik Brodowski 	if ((!governor) || (strlen(governor) > 19))
6727fe2f639SDominik Brodowski 		return -EINVAL;
6737fe2f639SDominik Brodowski 
674ac5a181dSThomas Renninger 	if (verify_gov(new_gov, governor))
675ac5a181dSThomas Renninger 		return -EINVAL;
676ac5a181dSThomas Renninger 
677ac5a181dSThomas Renninger 	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
678ac5a181dSThomas Renninger 					     new_gov, strlen(new_gov));
6797fe2f639SDominik Brodowski }
6807fe2f639SDominik Brodowski 
cpufreq_set_frequency(unsigned int cpu,unsigned long target_frequency)6816c2b8185SDominik Brodowski int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency)
6826c2b8185SDominik Brodowski {
683ac5a181dSThomas Renninger 	struct cpufreq_policy *pol = cpufreq_get_policy(cpu);
684ac5a181dSThomas Renninger 	char userspace_gov[] = "userspace";
685ac5a181dSThomas Renninger 	char freq[SYSFS_PATH_MAX];
686ac5a181dSThomas Renninger 	int ret;
687ac5a181dSThomas Renninger 
688ac5a181dSThomas Renninger 	if (!pol)
689ac5a181dSThomas Renninger 		return -ENODEV;
690ac5a181dSThomas Renninger 
691ac5a181dSThomas Renninger 	if (strncmp(pol->governor, userspace_gov, 9) != 0) {
692ac5a181dSThomas Renninger 		ret = cpufreq_modify_policy_governor(cpu, userspace_gov);
693ac5a181dSThomas Renninger 		if (ret) {
694ac5a181dSThomas Renninger 			cpufreq_put_policy(pol);
695ac5a181dSThomas Renninger 			return ret;
696ac5a181dSThomas Renninger 		}
697ac5a181dSThomas Renninger 	}
698ac5a181dSThomas Renninger 
699ac5a181dSThomas Renninger 	cpufreq_put_policy(pol);
700ac5a181dSThomas Renninger 
701ac5a181dSThomas Renninger 	snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
702ac5a181dSThomas Renninger 
703ac5a181dSThomas Renninger 	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
704ac5a181dSThomas Renninger 					     freq, strlen(freq));
7057fe2f639SDominik Brodowski }
7067fe2f639SDominik Brodowski 
cpufreq_get_stats(unsigned int cpu,unsigned long long * total_time)7076c2b8185SDominik Brodowski struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu,
7086c2b8185SDominik Brodowski 					unsigned long long *total_time)
7096c2b8185SDominik Brodowski {
710ac5a181dSThomas Renninger 	struct cpufreq_stats *first = NULL;
711ac5a181dSThomas Renninger 	struct cpufreq_stats *current = NULL;
712ac5a181dSThomas Renninger 	char one_value[SYSFS_PATH_MAX];
713ac5a181dSThomas Renninger 	char linebuf[MAX_LINE_LEN];
714ac5a181dSThomas Renninger 	unsigned int pos, i;
715ac5a181dSThomas Renninger 	unsigned int len;
716ac5a181dSThomas Renninger 
717ac5a181dSThomas Renninger 	len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
718ac5a181dSThomas Renninger 				linebuf, sizeof(linebuf));
719ac5a181dSThomas Renninger 	if (len == 0)
720ac5a181dSThomas Renninger 		return NULL;
721ac5a181dSThomas Renninger 
722ac5a181dSThomas Renninger 	*total_time = 0;
723ac5a181dSThomas Renninger 	pos = 0;
724ac5a181dSThomas Renninger 	for (i = 0; i < len; i++) {
725ac5a181dSThomas Renninger 		if (i == strlen(linebuf) || linebuf[i] == '\n')	{
726ac5a181dSThomas Renninger 			if (i - pos < 2)
727ac5a181dSThomas Renninger 				continue;
728ac5a181dSThomas Renninger 			if ((i - pos) >= SYSFS_PATH_MAX)
729ac5a181dSThomas Renninger 				goto error_out;
730ac5a181dSThomas Renninger 			if (current) {
731ac5a181dSThomas Renninger 				current->next = malloc(sizeof(*current));
732ac5a181dSThomas Renninger 				if (!current->next)
733ac5a181dSThomas Renninger 					goto error_out;
734ac5a181dSThomas Renninger 				current = current->next;
735ac5a181dSThomas Renninger 			} else {
736ac5a181dSThomas Renninger 				first = malloc(sizeof(*first));
737ac5a181dSThomas Renninger 				if (!first)
738cbf25270SShuah Khan 					return NULL;
739ac5a181dSThomas Renninger 				current = first;
740ac5a181dSThomas Renninger 			}
741ac5a181dSThomas Renninger 			current->first = first;
742ac5a181dSThomas Renninger 			current->next = NULL;
743ac5a181dSThomas Renninger 
744ac5a181dSThomas Renninger 			memcpy(one_value, linebuf + pos, i - pos);
745ac5a181dSThomas Renninger 			one_value[i - pos] = '\0';
746ac5a181dSThomas Renninger 			if (sscanf(one_value, "%lu %llu",
747ac5a181dSThomas Renninger 					&current->frequency,
748ac5a181dSThomas Renninger 					&current->time_in_state) != 2)
749ac5a181dSThomas Renninger 				goto error_out;
750ac5a181dSThomas Renninger 
751ac5a181dSThomas Renninger 			*total_time = *total_time + current->time_in_state;
752ac5a181dSThomas Renninger 			pos = i + 1;
753ac5a181dSThomas Renninger 		}
754ac5a181dSThomas Renninger 	}
755ac5a181dSThomas Renninger 
756ac5a181dSThomas Renninger 	return first;
757ac5a181dSThomas Renninger 
758ac5a181dSThomas Renninger  error_out:
759ac5a181dSThomas Renninger 	while (first) {
760ac5a181dSThomas Renninger 		current = first->next;
761ac5a181dSThomas Renninger 		free(first);
762ac5a181dSThomas Renninger 		first = current;
763ac5a181dSThomas Renninger 	}
764ac5a181dSThomas Renninger 	return NULL;
7657fe2f639SDominik Brodowski }
7667fe2f639SDominik Brodowski 
cpufreq_put_stats(struct cpufreq_stats * any)7676c2b8185SDominik Brodowski void cpufreq_put_stats(struct cpufreq_stats *any)
7686c2b8185SDominik Brodowski {
7697fe2f639SDominik Brodowski 	struct cpufreq_stats *tmp, *next;
7707fe2f639SDominik Brodowski 
7717fe2f639SDominik Brodowski 	if (!any)
7727fe2f639SDominik Brodowski 		return;
7737fe2f639SDominik Brodowski 
7747fe2f639SDominik Brodowski 	tmp = any->first;
7757fe2f639SDominik Brodowski 	while (tmp) {
7767fe2f639SDominik Brodowski 		next = tmp->next;
7777fe2f639SDominik Brodowski 		free(tmp);
7787fe2f639SDominik Brodowski 		tmp = next;
7797fe2f639SDominik Brodowski 	}
7807fe2f639SDominik Brodowski }
7817fe2f639SDominik Brodowski 
cpufreq_get_transitions(unsigned int cpu)7826c2b8185SDominik Brodowski unsigned long cpufreq_get_transitions(unsigned int cpu)
7836c2b8185SDominik Brodowski {
784ac5a181dSThomas Renninger 	return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
7857fe2f639SDominik Brodowski }
786