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