aboutsummaryrefslogtreecommitdiffstats
path: root/tools/power/cpupower/lib
diff options
context:
space:
mode:
authorDominik Brodowski <linux@dominikbrodowski.net>2011-03-30 10:30:11 -0400
committerDominik Brodowski <linux@dominikbrodowski.net>2011-07-29 12:35:36 -0400
commit7fe2f6399a84760a9af8896ac152728250f82adb (patch)
treefa4bf236359b8d6d9f8d6ff823ddd3e839da5768 /tools/power/cpupower/lib
parent02f8c6aee8df3cdc935e9bdd4f2d020306035dbe (diff)
cpupowerutils - cpufrequtils extended with quite some features
CPU power consumption vs performance tuning is no longer limited to CPU frequency switching anymore: deep sleep states, traditional dynamic frequency scaling and hidden turbo/boost frequencies are tied close together and depend on each other. The first two exist on different architectures like PPC, Itanium and ARM, the latter (so far) only on X86. On X86 the APU (CPU+GPU) will only run most efficiently if CPU and GPU has proper power management in place. Users and Developers want to have *one* tool to get an overview what their system supports and to monitor and debug CPU power management in detail. The tool should compile and work on as many architectures as possible. Once this tool stabilizes a bit, it is intended to replace the Intel-specific tools in tools/power/x86 Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Diffstat (limited to 'tools/power/cpupower/lib')
-rw-r--r--tools/power/cpupower/lib/cpufreq.c190
-rw-r--r--tools/power/cpupower/lib/cpufreq.h215
-rw-r--r--tools/power/cpupower/lib/sysfs.c671
-rw-r--r--tools/power/cpupower/lib/sysfs.h21
4 files changed, 1097 insertions, 0 deletions
diff --git a/tools/power/cpupower/lib/cpufreq.c b/tools/power/cpupower/lib/cpufreq.c
new file mode 100644
index 000000000000..ae7d8c57b447
--- /dev/null
+++ b/tools/power/cpupower/lib/cpufreq.c
@@ -0,0 +1,190 @@
1/*
2 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
3 *
4 * Licensed under the terms of the GNU GPL License version 2.
5 */
6
7
8#include <stdio.h>
9#include <errno.h>
10#include <stdlib.h>
11#include <string.h>
12
13#include "cpufreq.h"
14#include "sysfs.h"
15
16int cpufreq_cpu_exists(unsigned int cpu)
17{
18 return sysfs_cpu_exists(cpu);
19}
20
21unsigned long cpufreq_get_freq_kernel(unsigned int cpu)
22{
23 return sysfs_get_freq_kernel(cpu);
24}
25
26unsigned long cpufreq_get_freq_hardware(unsigned int cpu)
27{
28 return sysfs_get_freq_hardware(cpu);
29}
30
31unsigned long cpufreq_get_transition_latency(unsigned int cpu)
32{
33 return sysfs_get_freq_transition_latency(cpu);
34}
35
36int cpufreq_get_hardware_limits(unsigned int cpu,
37 unsigned long *min,
38 unsigned long *max)
39{
40 if ((!min) || (!max))
41 return -EINVAL;
42 return sysfs_get_freq_hardware_limits(cpu, min, max);
43}
44
45char * cpufreq_get_driver(unsigned int cpu) {
46 return sysfs_get_freq_driver(cpu);
47}
48
49void cpufreq_put_driver(char * ptr) {
50 if (!ptr)
51 return;
52 free(ptr);
53}
54
55struct cpufreq_policy * cpufreq_get_policy(unsigned int cpu) {
56 return sysfs_get_freq_policy(cpu);
57}
58
59void cpufreq_put_policy(struct cpufreq_policy *policy) {
60 if ((!policy) || (!policy->governor))
61 return;
62
63 free(policy->governor);
64 policy->governor = NULL;
65 free(policy);
66}
67
68struct cpufreq_available_governors * cpufreq_get_available_governors(unsigned int cpu) {
69 return sysfs_get_freq_available_governors(cpu);
70}
71
72void cpufreq_put_available_governors(struct cpufreq_available_governors *any) {
73 struct cpufreq_available_governors *tmp, *next;
74
75 if (!any)
76 return;
77
78 tmp = any->first;
79 while (tmp) {
80 next = tmp->next;
81 if (tmp->governor)
82 free(tmp->governor);
83 free(tmp);
84 tmp = next;
85 }
86}
87
88
89struct cpufreq_available_frequencies * cpufreq_get_available_frequencies(unsigned int cpu) {
90 return sysfs_get_available_frequencies(cpu);
91}
92
93void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies *any) {
94 struct cpufreq_available_frequencies *tmp, *next;
95
96 if (!any)
97 return;
98
99 tmp = any->first;
100 while (tmp) {
101 next = tmp->next;
102 free(tmp);
103 tmp = next;
104 }
105}
106
107
108struct cpufreq_affected_cpus * cpufreq_get_affected_cpus(unsigned int cpu) {
109 return sysfs_get_freq_affected_cpus(cpu);
110}
111
112void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any) {
113 struct cpufreq_affected_cpus *tmp, *next;
114
115 if (!any)
116 return;
117
118 tmp = any->first;
119 while (tmp) {
120 next = tmp->next;
121 free(tmp);
122 tmp = next;
123 }
124}
125
126
127struct cpufreq_affected_cpus * cpufreq_get_related_cpus(unsigned int cpu) {
128 return sysfs_get_freq_related_cpus(cpu);
129}
130
131void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any) {
132 cpufreq_put_affected_cpus(any);
133}
134
135
136int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy) {
137 if (!policy || !(policy->governor))
138 return -EINVAL;
139
140 return sysfs_set_freq_policy(cpu, policy);
141}
142
143
144int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq) {
145 return sysfs_modify_freq_policy_min(cpu, min_freq);
146}
147
148
149int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq) {
150 return sysfs_modify_freq_policy_max(cpu, max_freq);
151}
152
153
154int cpufreq_modify_policy_governor(unsigned int cpu, char *governor) {
155 if ((!governor) || (strlen(governor) > 19))
156 return -EINVAL;
157
158 return sysfs_modify_freq_policy_governor(cpu, governor);
159}
160
161int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency) {
162 return sysfs_set_frequency(cpu, target_frequency);
163}
164
165struct cpufreq_stats * cpufreq_get_stats(unsigned int cpu, unsigned long long *total_time) {
166 struct cpufreq_stats *ret;
167
168 ret = sysfs_get_freq_stats(cpu, total_time);
169 return (ret);
170}
171
172void cpufreq_put_stats(struct cpufreq_stats *any) {
173 struct cpufreq_stats *tmp, *next;
174
175 if (!any)
176 return;
177
178 tmp = any->first;
179 while (tmp) {
180 next = tmp->next;
181 free(tmp);
182 tmp = next;
183 }
184}
185
186unsigned long cpufreq_get_transitions(unsigned int cpu) {
187 unsigned long ret = sysfs_get_freq_transitions(cpu);
188
189 return (ret);
190}
diff --git a/tools/power/cpupower/lib/cpufreq.h b/tools/power/cpupower/lib/cpufreq.h
new file mode 100644
index 000000000000..03be906581b5
--- /dev/null
+++ b/tools/power/cpupower/lib/cpufreq.h
@@ -0,0 +1,215 @@
1/*
2 * cpufreq.h - definitions for libcpufreq
3 *
4 * Copyright (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#ifndef _CPUFREQ_H
21#define _CPUFREQ_H 1
22
23struct cpufreq_policy {
24 unsigned long min;
25 unsigned long max;
26 char *governor;
27};
28
29struct cpufreq_available_governors {
30 char *governor;
31 struct cpufreq_available_governors *next;
32 struct cpufreq_available_governors *first;
33};
34
35struct cpufreq_available_frequencies {
36 unsigned long frequency;
37 struct cpufreq_available_frequencies *next;
38 struct cpufreq_available_frequencies *first;
39};
40
41
42struct cpufreq_affected_cpus {
43 unsigned int cpu;
44 struct cpufreq_affected_cpus *next;
45 struct cpufreq_affected_cpus *first;
46};
47
48struct cpufreq_stats {
49 unsigned long frequency;
50 unsigned long long time_in_state;
51 struct cpufreq_stats *next;
52 struct cpufreq_stats *first;
53};
54
55
56
57#ifdef __cplusplus
58extern "C" {
59#endif
60
61/*
62 * returns 0 if the specified CPU is present (it doesn't say
63 * whether it is online!), and an error value if not.
64 */
65
66extern int cpufreq_cpu_exists(unsigned int cpu);
67
68/* determine current CPU frequency
69 * - _kernel variant means kernel's opinion of CPU frequency
70 * - _hardware variant means actual hardware CPU frequency,
71 * which is only available to root.
72 *
73 * returns 0 on failure, else frequency in kHz.
74 */
75
76extern unsigned long cpufreq_get_freq_kernel(unsigned int cpu);
77
78extern unsigned long cpufreq_get_freq_hardware(unsigned int cpu);
79
80#define cpufreq_get(cpu) cpufreq_get_freq_kernel(cpu);
81
82
83/* determine CPU transition latency
84 *
85 * returns 0 on failure, else transition latency in 10^(-9) s = nanoseconds
86 */
87extern unsigned long cpufreq_get_transition_latency(unsigned int cpu);
88
89
90/* determine hardware CPU frequency limits
91 *
92 * These may be limited further by thermal, energy or other
93 * considerations by cpufreq policy notifiers in the kernel.
94 */
95
96extern int cpufreq_get_hardware_limits(unsigned int cpu,
97 unsigned long *min,
98 unsigned long *max);
99
100
101/* determine CPUfreq driver used
102 *
103 * Remember to call cpufreq_put_driver when no longer needed
104 * to avoid memory leakage, please.
105 */
106
107extern char * cpufreq_get_driver(unsigned int cpu);
108
109extern void cpufreq_put_driver(char * ptr);
110
111
112/* determine CPUfreq policy currently used
113 *
114 * Remember to call cpufreq_put_policy when no longer needed
115 * to avoid memory leakage, please.
116 */
117
118
119extern struct cpufreq_policy * cpufreq_get_policy(unsigned int cpu);
120
121extern void cpufreq_put_policy(struct cpufreq_policy *policy);
122
123
124/* determine CPUfreq governors currently available
125 *
126 * may be modified by modprobe'ing or rmmod'ing other governors. Please
127 * free allocated memory by calling cpufreq_put_available_governors
128 * after use.
129 */
130
131
132extern struct cpufreq_available_governors * cpufreq_get_available_governors(unsigned int cpu);
133
134extern void cpufreq_put_available_governors(struct cpufreq_available_governors *first);
135
136
137/* determine CPU frequency states available
138 *
139 * only present on _some_ ->target() cpufreq drivers. For information purposes
140 * only. Please free allocated memory by calling cpufreq_put_available_frequencies
141 * after use.
142 */
143
144extern struct cpufreq_available_frequencies * cpufreq_get_available_frequencies(unsigned int cpu);
145
146extern void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies *first);
147
148
149/* determine affected CPUs
150 *
151 * Remember to call cpufreq_put_affected_cpus when no longer needed
152 * to avoid memory leakage, please.
153 */
154
155extern struct cpufreq_affected_cpus * cpufreq_get_affected_cpus(unsigned int cpu);
156
157extern void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *first);
158
159
160/* determine related CPUs
161 *
162 * Remember to call cpufreq_put_related_cpus when no longer needed
163 * to avoid memory leakage, please.
164 */
165
166extern struct cpufreq_affected_cpus * cpufreq_get_related_cpus(unsigned int cpu);
167
168extern void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *first);
169
170
171/* determine stats for cpufreq subsystem
172 *
173 * This is not available in all kernel versions or configurations.
174 */
175
176extern struct cpufreq_stats * cpufreq_get_stats(unsigned int cpu, unsigned long long *total_time);
177
178extern void cpufreq_put_stats(struct cpufreq_stats *stats);
179
180extern unsigned long cpufreq_get_transitions(unsigned int cpu);
181
182
183/* set new cpufreq policy
184 *
185 * Tries to set the passed policy as new policy as close as possible,
186 * but results may differ depending e.g. on governors being available.
187 */
188
189extern int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy);
190
191
192/* modify a policy by only changing min/max freq or governor
193 *
194 * Does not check whether result is what was intended.
195 */
196
197extern int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq);
198extern int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq);
199extern int cpufreq_modify_policy_governor(unsigned int cpu, char *governor);
200
201
202/* set a specific frequency
203 *
204 * Does only work if userspace governor can be used and no external
205 * interference (other calls to this function or to set/modify_policy)
206 * occurs. Also does not work on ->range() cpufreq drivers.
207 */
208
209extern int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency);
210
211#ifdef __cplusplus
212}
213#endif
214
215#endif /* _CPUFREQ_H */
diff --git a/tools/power/cpupower/lib/sysfs.c b/tools/power/cpupower/lib/sysfs.c
new file mode 100644
index 000000000000..c9b061fe87b1
--- /dev/null
+++ b/tools/power/cpupower/lib/sysfs.c
@@ -0,0 +1,671 @@
1/*
2 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
3 *
4 * Licensed under the terms of the GNU GPL License version 2.
5 */
6
7#include <stdio.h>
8#include <errno.h>
9#include <stdlib.h>
10#include <string.h>
11#include <limits.h>
12#include <sys/types.h>
13#include <sys/stat.h>
14#include <fcntl.h>
15#include <unistd.h>
16
17#include "cpufreq.h"
18
19#define PATH_TO_CPU "/sys/devices/system/cpu/"
20#define MAX_LINE_LEN 255
21#define SYSFS_PATH_MAX 255
22
23
24static unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
25{
26 int fd;
27 size_t numread;
28
29 if ( ( fd = open(path, O_RDONLY) ) == -1 )
30 return 0;
31
32 numread = read(fd, buf, buflen - 1);
33 if ( numread < 1 )
34 {
35 close(fd);
36 return 0;
37 }
38
39 buf[numread] = '\0';
40 close(fd);
41
42 return numread;
43}
44
45
46/* CPUFREQ sysfs access **************************************************/
47
48/* helper function to read file from /sys into given buffer */
49/* fname is a relative path under "cpuX/cpufreq" dir */
50static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,
51 char *buf, size_t buflen)
52{
53 char path[SYSFS_PATH_MAX];
54
55 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
56 cpu, fname);
57 return sysfs_read_file(path, buf, buflen);
58}
59
60/* helper function to write a new value to a /sys file */
61/* fname is a relative path under "cpuX/cpufreq" dir */
62static unsigned int sysfs_cpufreq_write_file(unsigned int cpu,
63 const char *fname,
64 const char *value, size_t len)
65{
66 char path[SYSFS_PATH_MAX];
67 int fd;
68 size_t numwrite;
69
70 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
71 cpu, fname);
72
73 if ( ( fd = open(path, O_WRONLY) ) == -1 )
74 return 0;
75
76 numwrite = write(fd, value, len);
77 if ( numwrite < 1 )
78 {
79 close(fd);
80 return 0;
81 }
82
83 close(fd);
84
85 return numwrite;
86}
87
88/* read access to files which contain one numeric value */
89
90enum cpufreq_value {
91 CPUINFO_CUR_FREQ,
92 CPUINFO_MIN_FREQ,
93 CPUINFO_MAX_FREQ,
94 CPUINFO_LATENCY,
95 SCALING_CUR_FREQ,
96 SCALING_MIN_FREQ,
97 SCALING_MAX_FREQ,
98 STATS_NUM_TRANSITIONS,
99 MAX_CPUFREQ_VALUE_READ_FILES
100};
101
102static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {
103 [CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq",
104 [CPUINFO_MIN_FREQ] = "cpuinfo_min_freq",
105 [CPUINFO_MAX_FREQ] = "cpuinfo_max_freq",
106 [CPUINFO_LATENCY] = "cpuinfo_transition_latency",
107 [SCALING_CUR_FREQ] = "scaling_cur_freq",
108 [SCALING_MIN_FREQ] = "scaling_min_freq",
109 [SCALING_MAX_FREQ] = "scaling_max_freq",
110 [STATS_NUM_TRANSITIONS] = "stats/total_trans"
111};
112
113
114static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,
115 enum cpufreq_value which)
116{
117 unsigned long value;
118 unsigned int len;
119 char linebuf[MAX_LINE_LEN];
120 char *endp;
121
122 if ( which >= MAX_CPUFREQ_VALUE_READ_FILES )
123 return 0;
124
125 if ( ( len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which],
126 linebuf, sizeof(linebuf))) == 0 )
127 return 0;
128
129 value = strtoul(linebuf, &endp, 0);
130
131 if ( endp == linebuf || errno == ERANGE )
132 return 0;
133
134 return value;
135}
136
137/* read access to files which contain one string */
138
139enum cpufreq_string {
140 SCALING_DRIVER,
141 SCALING_GOVERNOR,
142 MAX_CPUFREQ_STRING_FILES
143};
144
145static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
146 [SCALING_DRIVER] = "scaling_driver",
147 [SCALING_GOVERNOR] = "scaling_governor",
148};
149
150
151static char * sysfs_cpufreq_get_one_string(unsigned int cpu,
152 enum cpufreq_string which)
153{
154 char linebuf[MAX_LINE_LEN];
155 char *result;
156 unsigned int len;
157
158 if (which >= MAX_CPUFREQ_STRING_FILES)
159 return NULL;
160
161 if ( ( len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
162 linebuf, sizeof(linebuf))) == 0 )
163 return NULL;
164
165 if ( ( result = strdup(linebuf) ) == NULL )
166 return NULL;
167
168 if (result[strlen(result) - 1] == '\n')
169 result[strlen(result) - 1] = '\0';
170
171 return result;
172}
173
174/* write access */
175
176enum cpufreq_write {
177 WRITE_SCALING_MIN_FREQ,
178 WRITE_SCALING_MAX_FREQ,
179 WRITE_SCALING_GOVERNOR,
180 WRITE_SCALING_SET_SPEED,
181 MAX_CPUFREQ_WRITE_FILES
182};
183
184static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
185 [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
186 [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
187 [WRITE_SCALING_GOVERNOR] = "scaling_governor",
188 [WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
189};
190
191static int sysfs_cpufreq_write_one_value(unsigned int cpu,
192 enum cpufreq_write which,
193 const char *new_value, size_t len)
194{
195 if (which >= MAX_CPUFREQ_WRITE_FILES)
196 return 0;
197
198 if ( sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
199 new_value, len) != len )
200 return -ENODEV;
201
202 return 0;
203};
204
205unsigned long sysfs_get_freq_kernel(unsigned int cpu)
206{
207 return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
208}
209
210unsigned long sysfs_get_freq_hardware(unsigned int cpu)
211{
212 return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
213}
214
215unsigned long sysfs_get_freq_transition_latency(unsigned int cpu)
216{
217 return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
218}
219
220int sysfs_get_freq_hardware_limits(unsigned int cpu,
221 unsigned long *min,
222 unsigned long *max)
223{
224 if ((!min) || (!max))
225 return -EINVAL;
226
227 *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);
228 if (!*min)
229 return -ENODEV;
230
231 *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
232 if (!*max)
233 return -ENODEV;
234
235 return 0;
236}
237
238char * sysfs_get_freq_driver(unsigned int cpu) {
239 return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
240}
241
242struct cpufreq_policy * sysfs_get_freq_policy(unsigned int cpu) {
243 struct cpufreq_policy *policy;
244
245 policy = malloc(sizeof(struct cpufreq_policy));
246 if (!policy)
247 return NULL;
248
249 policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
250 if (!policy->governor) {
251 free(policy);
252 return NULL;
253 }
254 policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
255 policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);
256 if ((!policy->min) || (!policy->max)) {
257 free(policy->governor);
258 free(policy);
259 return NULL;
260 }
261
262 return policy;
263}
264
265struct cpufreq_available_governors *
266sysfs_get_freq_available_governors(unsigned int cpu) {
267 struct cpufreq_available_governors *first = NULL;
268 struct cpufreq_available_governors *current = NULL;
269 char linebuf[MAX_LINE_LEN];
270 unsigned int pos, i;
271 unsigned int len;
272
273 if ( ( len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
274 linebuf, sizeof(linebuf))) == 0 )
275 {
276 return NULL;
277 }
278
279 pos = 0;
280 for ( i = 0; i < len; i++ )
281 {
282 if ( linebuf[i] == ' ' || linebuf[i] == '\n' )
283 {
284 if ( i - pos < 2 )
285 continue;
286 if ( current ) {
287 current->next = malloc(sizeof *current );
288 if ( ! current->next )
289 goto error_out;
290 current = current->next;
291 } else {
292 first = malloc( sizeof *first );
293 if ( ! first )
294 goto error_out;
295 current = first;
296 }
297 current->first = first;
298 current->next = NULL;
299
300 current->governor = malloc(i - pos + 1);
301 if ( ! current->governor )
302 goto error_out;
303
304 memcpy( current->governor, linebuf + pos, i - pos);
305 current->governor[i - pos] = '\0';
306 pos = i + 1;
307 }
308 }
309
310 return first;
311
312 error_out:
313 while ( first ) {
314 current = first->next;
315 if ( first->governor )
316 free( first->governor );
317 free( first );
318 first = current;
319 }
320 return NULL;
321}
322
323
324struct cpufreq_available_frequencies *
325sysfs_get_available_frequencies(unsigned int cpu) {
326 struct cpufreq_available_frequencies *first = NULL;
327 struct cpufreq_available_frequencies *current = NULL;
328 char one_value[SYSFS_PATH_MAX];
329 char linebuf[MAX_LINE_LEN];
330 unsigned int pos, i;
331 unsigned int len;
332
333 if ( ( len = sysfs_cpufreq_read_file(cpu,
334 "scaling_available_frequencies",
335 linebuf, sizeof(linebuf))) == 0 )
336 {
337 return NULL;
338 }
339
340 pos = 0;
341 for ( i = 0; i < len; i++ )
342 {
343 if ( linebuf[i] == ' ' || linebuf[i] == '\n' )
344 {
345 if ( i - pos < 2 )
346 continue;
347 if ( i - pos >= SYSFS_PATH_MAX )
348 goto error_out;
349 if ( current ) {
350 current->next = malloc(sizeof *current );
351 if ( ! current->next )
352 goto error_out;
353 current = current->next;
354 } else {
355 first = malloc(sizeof *first );
356 if ( ! first )
357 goto error_out;
358 current = first;
359 }
360 current->first = first;
361 current->next = NULL;
362
363 memcpy(one_value, linebuf + pos, i - pos);
364 one_value[i - pos] = '\0';
365 if ( sscanf(one_value, "%lu", &current->frequency) != 1 )
366 goto error_out;
367
368 pos = i + 1;
369 }
370 }
371
372 return first;
373
374 error_out:
375 while ( first ) {
376 current = first->next;
377 free(first);
378 first = current;
379 }
380 return NULL;
381}
382
383static struct cpufreq_affected_cpus * sysfs_get_cpu_list(unsigned int cpu,
384 const char *file) {
385 struct cpufreq_affected_cpus *first = NULL;
386 struct cpufreq_affected_cpus *current = NULL;
387 char one_value[SYSFS_PATH_MAX];
388 char linebuf[MAX_LINE_LEN];
389 unsigned int pos, i;
390 unsigned int len;
391
392 if ( ( len = sysfs_cpufreq_read_file(cpu, file, linebuf,
393 sizeof(linebuf))) == 0 )
394 {
395 return NULL;
396 }
397
398 pos = 0;
399 for ( i = 0; i < len; i++ )
400 {
401 if ( i == len || linebuf[i] == ' ' || linebuf[i] == '\n' )
402 {
403 if ( i - pos < 1 )
404 continue;
405 if ( i - pos >= SYSFS_PATH_MAX )
406 goto error_out;
407 if ( current ) {
408 current->next = malloc(sizeof *current);
409 if ( ! current->next )
410 goto error_out;
411 current = current->next;
412 } else {
413 first = malloc(sizeof *first);
414 if ( ! first )
415 goto error_out;
416 current = first;
417 }
418 current->first = first;
419 current->next = NULL;
420
421 memcpy(one_value, linebuf + pos, i - pos);
422 one_value[i - pos] = '\0';
423
424 if ( sscanf(one_value, "%u", &current->cpu) != 1 )
425 goto error_out;
426
427 pos = i + 1;
428 }
429 }
430
431 return first;
432
433 error_out:
434 while (first) {
435 current = first->next;
436 free(first);
437 first = current;
438 }
439 return NULL;
440}
441
442struct cpufreq_affected_cpus * sysfs_get_freq_affected_cpus(unsigned int cpu) {
443 return sysfs_get_cpu_list(cpu, "affected_cpus");
444}
445
446struct cpufreq_affected_cpus * sysfs_get_freq_related_cpus(unsigned int cpu) {
447 return sysfs_get_cpu_list(cpu, "related_cpus");
448}
449
450struct cpufreq_stats * sysfs_get_freq_stats(unsigned int cpu, unsigned long long *total_time) {
451 struct cpufreq_stats *first = NULL;
452 struct cpufreq_stats *current = NULL;
453 char one_value[SYSFS_PATH_MAX];
454 char linebuf[MAX_LINE_LEN];
455 unsigned int pos, i;
456 unsigned int len;
457
458 if ( ( len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
459 linebuf, sizeof(linebuf))) == 0 )
460 return NULL;
461
462 *total_time = 0;
463 pos = 0;
464 for ( i = 0; i < len; i++ )
465 {
466 if ( i == strlen(linebuf) || linebuf[i] == '\n' )
467 {
468 if ( i - pos < 2 )
469 continue;
470 if ( (i - pos) >= SYSFS_PATH_MAX )
471 goto error_out;
472 if ( current ) {
473 current->next = malloc(sizeof *current );
474 if ( ! current->next )
475 goto error_out;
476 current = current->next;
477 } else {
478 first = malloc(sizeof *first );
479 if ( ! first )
480 goto error_out;
481 current = first;
482 }
483 current->first = first;
484 current->next = NULL;
485
486 memcpy(one_value, linebuf + pos, i - pos);
487 one_value[i - pos] = '\0';
488 if ( sscanf(one_value, "%lu %llu", &current->frequency, &current->time_in_state) != 2 )
489 goto error_out;
490
491 *total_time = *total_time + current->time_in_state;
492 pos = i + 1;
493 }
494 }
495
496 return first;
497
498 error_out:
499 while ( first ) {
500 current = first->next;
501 free(first);
502 first = current;
503 }
504 return NULL;
505}
506
507unsigned long sysfs_get_freq_transitions(unsigned int cpu)
508{
509 return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
510}
511
512static int verify_gov(char *new_gov, char *passed_gov)
513{
514 unsigned int i, j=0;
515
516 if (!passed_gov || (strlen(passed_gov) > 19))
517 return -EINVAL;
518
519 strncpy(new_gov, passed_gov, 20);
520 for (i=0;i<20;i++) {
521 if (j) {
522 new_gov[i] = '\0';
523 continue;
524 }
525 if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z')) {
526 continue;
527 }
528 if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z')) {
529 continue;
530 }
531 if (new_gov[i] == '-') {
532 continue;
533 }
534 if (new_gov[i] == '_') {
535 continue;
536 }
537 if (new_gov[i] == '\0') {
538 j = 1;
539 continue;
540 }
541 return -EINVAL;
542 }
543 new_gov[19] = '\0';
544 return 0;
545}
546
547int sysfs_modify_freq_policy_governor(unsigned int cpu, char *governor)
548{
549 char new_gov[SYSFS_PATH_MAX];
550
551 if (!governor)
552 return -EINVAL;
553
554 if (verify_gov(new_gov, governor))
555 return -EINVAL;
556
557 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
558 new_gov, strlen(new_gov));
559};
560
561int sysfs_modify_freq_policy_max(unsigned int cpu, unsigned long max_freq)
562{
563 char value[SYSFS_PATH_MAX];
564
565 snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
566
567 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
568 value, strlen(value));
569};
570
571
572int sysfs_modify_freq_policy_min(unsigned int cpu, unsigned long min_freq)
573{
574 char value[SYSFS_PATH_MAX];
575
576 snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
577
578 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
579 value, strlen(value));
580};
581
582
583int sysfs_set_freq_policy(unsigned int cpu, struct cpufreq_policy *policy)
584{
585 char min[SYSFS_PATH_MAX];
586 char max[SYSFS_PATH_MAX];
587 char gov[SYSFS_PATH_MAX];
588 int ret;
589 unsigned long old_min;
590 int write_max_first;
591
592 if (!policy || !(policy->governor))
593 return -EINVAL;
594
595 if (policy->max < policy->min)
596 return -EINVAL;
597
598 if (verify_gov(gov, policy->governor))
599 return -EINVAL;
600
601 snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
602 snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
603
604 old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
605 write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
606
607 if (write_max_first) {
608 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
609 max, strlen(max));
610 if (ret)
611 return ret;
612 }
613
614 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
615 strlen(min));
616 if (ret)
617 return ret;
618
619 if (!write_max_first) {
620 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
621 max, strlen(max));
622 if (ret)
623 return ret;
624 }
625
626 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
627 gov, strlen(gov));
628}
629
630int sysfs_set_frequency(unsigned int cpu, unsigned long target_frequency) {
631 struct cpufreq_policy *pol = sysfs_get_freq_policy(cpu);
632 char userspace_gov[] = "userspace";
633 char freq[SYSFS_PATH_MAX];
634 int ret;
635
636 if (!pol)
637 return -ENODEV;
638
639 if (strncmp(pol->governor, userspace_gov, 9) != 0) {
640 ret = sysfs_modify_freq_policy_governor(cpu, userspace_gov);
641 if (ret) {
642 cpufreq_put_policy(pol);
643 return (ret);
644 }
645 }
646
647 cpufreq_put_policy(pol);
648
649 snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
650
651 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
652 freq, strlen(freq));
653}
654
655/* CPUFREQ sysfs access **************************************************/
656
657/* General sysfs access **************************************************/
658int sysfs_cpu_exists(unsigned int cpu)
659{
660 char file[SYSFS_PATH_MAX];
661 struct stat statbuf;
662
663 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/", cpu);
664
665 if ( stat(file, &statbuf) != 0 )
666 return -ENOSYS;
667
668 return S_ISDIR(statbuf.st_mode) ? 0 : -ENOSYS;
669}
670
671/* General sysfs access **************************************************/
diff --git a/tools/power/cpupower/lib/sysfs.h b/tools/power/cpupower/lib/sysfs.h
new file mode 100644
index 000000000000..c29e5575be8a
--- /dev/null
+++ b/tools/power/cpupower/lib/sysfs.h
@@ -0,0 +1,21 @@
1/* General */
2extern unsigned int sysfs_cpu_exists(unsigned int cpu);
3
4/* CPUfreq */
5extern unsigned long sysfs_get_freq_kernel(unsigned int cpu);
6extern unsigned long sysfs_get_freq_hardware(unsigned int cpu);
7extern unsigned long sysfs_get_freq_transition_latency(unsigned int cpu);
8extern int sysfs_get_freq_hardware_limits(unsigned int cpu, unsigned long *min, unsigned long *max);
9extern char * sysfs_get_freq_driver(unsigned int cpu);
10extern struct cpufreq_policy * sysfs_get_freq_policy(unsigned int cpu);
11extern struct cpufreq_available_governors * sysfs_get_freq_available_governors(unsigned int cpu);
12extern struct cpufreq_available_frequencies * sysfs_get_available_frequencies(unsigned int cpu);
13extern struct cpufreq_affected_cpus * sysfs_get_freq_affected_cpus(unsigned int cpu);
14extern struct cpufreq_affected_cpus * sysfs_get_freq_related_cpus(unsigned int cpu);
15extern struct cpufreq_stats * sysfs_get_freq_stats(unsigned int cpu, unsigned long long *total_time);
16extern unsigned long sysfs_get_freq_transitions(unsigned int cpu);
17extern int sysfs_set_freq_policy(unsigned int cpu, struct cpufreq_policy *policy);
18extern int sysfs_modify_freq_policy_min(unsigned int cpu, unsigned long min_freq);
19extern int sysfs_modify_freq_policy_max(unsigned int cpu, unsigned long max_freq);
20extern int sysfs_modify_freq_policy_governor(unsigned int cpu, char *governor);
21extern int sysfs_set_frequency(unsigned int cpu, unsigned long target_frequency);