diff options
Diffstat (limited to 'tools/power/cpupower/lib')
-rw-r--r-- | tools/power/cpupower/lib/cpufreq.c | 208 | ||||
-rw-r--r-- | tools/power/cpupower/lib/cpufreq.h | 223 | ||||
-rw-r--r-- | tools/power/cpupower/lib/sysfs.c | 672 | ||||
-rw-r--r-- | tools/power/cpupower/lib/sysfs.h | 31 |
4 files changed, 1134 insertions, 0 deletions
diff --git a/tools/power/cpupower/lib/cpufreq.c b/tools/power/cpupower/lib/cpufreq.c new file mode 100644 index 00000000000..d961101d1ce --- /dev/null +++ b/tools/power/cpupower/lib/cpufreq.c | |||
@@ -0,0 +1,208 @@ | |||
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 | |||
16 | int cpufreq_cpu_exists(unsigned int cpu) | ||
17 | { | ||
18 | return sysfs_cpu_exists(cpu); | ||
19 | } | ||
20 | |||
21 | unsigned long cpufreq_get_freq_kernel(unsigned int cpu) | ||
22 | { | ||
23 | return sysfs_get_freq_kernel(cpu); | ||
24 | } | ||
25 | |||
26 | unsigned long cpufreq_get_freq_hardware(unsigned int cpu) | ||
27 | { | ||
28 | return sysfs_get_freq_hardware(cpu); | ||
29 | } | ||
30 | |||
31 | unsigned long cpufreq_get_transition_latency(unsigned int cpu) | ||
32 | { | ||
33 | return sysfs_get_freq_transition_latency(cpu); | ||
34 | } | ||
35 | |||
36 | int 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 | |||
45 | char *cpufreq_get_driver(unsigned int cpu) | ||
46 | { | ||
47 | return sysfs_get_freq_driver(cpu); | ||
48 | } | ||
49 | |||
50 | void cpufreq_put_driver(char *ptr) | ||
51 | { | ||
52 | if (!ptr) | ||
53 | return; | ||
54 | free(ptr); | ||
55 | } | ||
56 | |||
57 | struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu) | ||
58 | { | ||
59 | return sysfs_get_freq_policy(cpu); | ||
60 | } | ||
61 | |||
62 | void cpufreq_put_policy(struct cpufreq_policy *policy) | ||
63 | { | ||
64 | if ((!policy) || (!policy->governor)) | ||
65 | return; | ||
66 | |||
67 | free(policy->governor); | ||
68 | policy->governor = NULL; | ||
69 | free(policy); | ||
70 | } | ||
71 | |||
72 | struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned | ||
73 | int cpu) | ||
74 | { | ||
75 | return sysfs_get_freq_available_governors(cpu); | ||
76 | } | ||
77 | |||
78 | void cpufreq_put_available_governors(struct cpufreq_available_governors *any) | ||
79 | { | ||
80 | struct cpufreq_available_governors *tmp, *next; | ||
81 | |||
82 | if (!any) | ||
83 | return; | ||
84 | |||
85 | tmp = any->first; | ||
86 | while (tmp) { | ||
87 | next = tmp->next; | ||
88 | if (tmp->governor) | ||
89 | free(tmp->governor); | ||
90 | free(tmp); | ||
91 | tmp = next; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | |||
96 | struct cpufreq_available_frequencies | ||
97 | *cpufreq_get_available_frequencies(unsigned int cpu) | ||
98 | { | ||
99 | return sysfs_get_available_frequencies(cpu); | ||
100 | } | ||
101 | |||
102 | void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies | ||
103 | *any) { | ||
104 | struct cpufreq_available_frequencies *tmp, *next; | ||
105 | |||
106 | if (!any) | ||
107 | return; | ||
108 | |||
109 | tmp = any->first; | ||
110 | while (tmp) { | ||
111 | next = tmp->next; | ||
112 | free(tmp); | ||
113 | tmp = next; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | |||
118 | struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu) | ||
119 | { | ||
120 | return sysfs_get_freq_affected_cpus(cpu); | ||
121 | } | ||
122 | |||
123 | void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any) | ||
124 | { | ||
125 | struct cpufreq_affected_cpus *tmp, *next; | ||
126 | |||
127 | if (!any) | ||
128 | return; | ||
129 | |||
130 | tmp = any->first; | ||
131 | while (tmp) { | ||
132 | next = tmp->next; | ||
133 | free(tmp); | ||
134 | tmp = next; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | |||
139 | struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu) | ||
140 | { | ||
141 | return sysfs_get_freq_related_cpus(cpu); | ||
142 | } | ||
143 | |||
144 | void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any) | ||
145 | { | ||
146 | cpufreq_put_affected_cpus(any); | ||
147 | } | ||
148 | |||
149 | |||
150 | int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy) | ||
151 | { | ||
152 | if (!policy || !(policy->governor)) | ||
153 | return -EINVAL; | ||
154 | |||
155 | return sysfs_set_freq_policy(cpu, policy); | ||
156 | } | ||
157 | |||
158 | |||
159 | int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq) | ||
160 | { | ||
161 | return sysfs_modify_freq_policy_min(cpu, min_freq); | ||
162 | } | ||
163 | |||
164 | |||
165 | int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq) | ||
166 | { | ||
167 | return sysfs_modify_freq_policy_max(cpu, max_freq); | ||
168 | } | ||
169 | |||
170 | |||
171 | int cpufreq_modify_policy_governor(unsigned int cpu, char *governor) | ||
172 | { | ||
173 | if ((!governor) || (strlen(governor) > 19)) | ||
174 | return -EINVAL; | ||
175 | |||
176 | return sysfs_modify_freq_policy_governor(cpu, governor); | ||
177 | } | ||
178 | |||
179 | int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency) | ||
180 | { | ||
181 | return sysfs_set_frequency(cpu, target_frequency); | ||
182 | } | ||
183 | |||
184 | struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu, | ||
185 | unsigned long long *total_time) | ||
186 | { | ||
187 | return sysfs_get_freq_stats(cpu, total_time); | ||
188 | } | ||
189 | |||
190 | void cpufreq_put_stats(struct cpufreq_stats *any) | ||
191 | { | ||
192 | struct cpufreq_stats *tmp, *next; | ||
193 | |||
194 | if (!any) | ||
195 | return; | ||
196 | |||
197 | tmp = any->first; | ||
198 | while (tmp) { | ||
199 | next = tmp->next; | ||
200 | free(tmp); | ||
201 | tmp = next; | ||
202 | } | ||
203 | } | ||
204 | |||
205 | unsigned long cpufreq_get_transitions(unsigned int cpu) | ||
206 | { | ||
207 | return sysfs_get_freq_transitions(cpu); | ||
208 | } | ||
diff --git a/tools/power/cpupower/lib/cpufreq.h b/tools/power/cpupower/lib/cpufreq.h new file mode 100644 index 00000000000..3aae8e7a083 --- /dev/null +++ b/tools/power/cpupower/lib/cpufreq.h | |||
@@ -0,0 +1,223 @@ | |||
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 | |||
23 | struct cpufreq_policy { | ||
24 | unsigned long min; | ||
25 | unsigned long max; | ||
26 | char *governor; | ||
27 | }; | ||
28 | |||
29 | struct cpufreq_available_governors { | ||
30 | char *governor; | ||
31 | struct cpufreq_available_governors *next; | ||
32 | struct cpufreq_available_governors *first; | ||
33 | }; | ||
34 | |||
35 | struct cpufreq_available_frequencies { | ||
36 | unsigned long frequency; | ||
37 | struct cpufreq_available_frequencies *next; | ||
38 | struct cpufreq_available_frequencies *first; | ||
39 | }; | ||
40 | |||
41 | |||
42 | struct cpufreq_affected_cpus { | ||
43 | unsigned int cpu; | ||
44 | struct cpufreq_affected_cpus *next; | ||
45 | struct cpufreq_affected_cpus *first; | ||
46 | }; | ||
47 | |||
48 | struct 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 | ||
58 | extern "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 | |||
66 | extern 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 | |||
76 | extern unsigned long cpufreq_get_freq_kernel(unsigned int cpu); | ||
77 | |||
78 | extern 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 | */ | ||
87 | extern 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 | |||
96 | extern 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 | |||
107 | extern char *cpufreq_get_driver(unsigned int cpu); | ||
108 | |||
109 | extern 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 | |||
119 | extern struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu); | ||
120 | |||
121 | extern 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 | |||
132 | extern struct cpufreq_available_governors | ||
133 | *cpufreq_get_available_governors(unsigned int cpu); | ||
134 | |||
135 | extern void cpufreq_put_available_governors( | ||
136 | struct cpufreq_available_governors *first); | ||
137 | |||
138 | |||
139 | /* determine CPU frequency states available | ||
140 | * | ||
141 | * Only present on _some_ ->target() cpufreq drivers. For information purposes | ||
142 | * only. Please free allocated memory by calling | ||
143 | * cpufreq_put_available_frequencies after use. | ||
144 | */ | ||
145 | |||
146 | extern struct cpufreq_available_frequencies | ||
147 | *cpufreq_get_available_frequencies(unsigned int cpu); | ||
148 | |||
149 | extern void cpufreq_put_available_frequencies( | ||
150 | struct cpufreq_available_frequencies *first); | ||
151 | |||
152 | |||
153 | /* determine affected CPUs | ||
154 | * | ||
155 | * Remember to call cpufreq_put_affected_cpus when no longer needed | ||
156 | * to avoid memory leakage, please. | ||
157 | */ | ||
158 | |||
159 | extern struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned | ||
160 | int cpu); | ||
161 | |||
162 | extern void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *first); | ||
163 | |||
164 | |||
165 | /* determine related CPUs | ||
166 | * | ||
167 | * Remember to call cpufreq_put_related_cpus when no longer needed | ||
168 | * to avoid memory leakage, please. | ||
169 | */ | ||
170 | |||
171 | extern struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned | ||
172 | int cpu); | ||
173 | |||
174 | extern void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *first); | ||
175 | |||
176 | |||
177 | /* determine stats for cpufreq subsystem | ||
178 | * | ||
179 | * This is not available in all kernel versions or configurations. | ||
180 | */ | ||
181 | |||
182 | extern struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu, | ||
183 | unsigned long long *total_time); | ||
184 | |||
185 | extern void cpufreq_put_stats(struct cpufreq_stats *stats); | ||
186 | |||
187 | extern unsigned long cpufreq_get_transitions(unsigned int cpu); | ||
188 | |||
189 | |||
190 | /* set new cpufreq policy | ||
191 | * | ||
192 | * Tries to set the passed policy as new policy as close as possible, | ||
193 | * but results may differ depending e.g. on governors being available. | ||
194 | */ | ||
195 | |||
196 | extern int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy); | ||
197 | |||
198 | |||
199 | /* modify a policy by only changing min/max freq or governor | ||
200 | * | ||
201 | * Does not check whether result is what was intended. | ||
202 | */ | ||
203 | |||
204 | extern int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq); | ||
205 | extern int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq); | ||
206 | extern int cpufreq_modify_policy_governor(unsigned int cpu, char *governor); | ||
207 | |||
208 | |||
209 | /* set a specific frequency | ||
210 | * | ||
211 | * Does only work if userspace governor can be used and no external | ||
212 | * interference (other calls to this function or to set/modify_policy) | ||
213 | * occurs. Also does not work on ->range() cpufreq drivers. | ||
214 | */ | ||
215 | |||
216 | extern int cpufreq_set_frequency(unsigned int cpu, | ||
217 | unsigned long target_frequency); | ||
218 | |||
219 | #ifdef __cplusplus | ||
220 | } | ||
221 | #endif | ||
222 | |||
223 | #endif /* _CPUFREQ_H */ | ||
diff --git a/tools/power/cpupower/lib/sysfs.c b/tools/power/cpupower/lib/sysfs.c new file mode 100644 index 00000000000..870713a75a8 --- /dev/null +++ b/tools/power/cpupower/lib/sysfs.c | |||
@@ -0,0 +1,672 @@ | |||
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 4096 | ||
21 | #define SYSFS_PATH_MAX 255 | ||
22 | |||
23 | |||
24 | static unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen) | ||
25 | { | ||
26 | int fd; | ||
27 | ssize_t numread; | ||
28 | |||
29 | fd = open(path, O_RDONLY); | ||
30 | if (fd == -1) | ||
31 | return 0; | ||
32 | |||
33 | numread = read(fd, buf, buflen - 1); | ||
34 | if (numread < 1) { | ||
35 | close(fd); | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | buf[numread] = '\0'; | ||
40 | close(fd); | ||
41 | |||
42 | return (unsigned int) 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 */ | ||
50 | static 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 */ | ||
62 | static 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 | ssize_t numwrite; | ||
69 | |||
70 | snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", | ||
71 | cpu, fname); | ||
72 | |||
73 | fd = open(path, O_WRONLY); | ||
74 | if (fd == -1) | ||
75 | return 0; | ||
76 | |||
77 | numwrite = write(fd, value, len); | ||
78 | if (numwrite < 1) { | ||
79 | close(fd); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | close(fd); | ||
84 | |||
85 | return (unsigned int) numwrite; | ||
86 | } | ||
87 | |||
88 | /* read access to files which contain one numeric value */ | ||
89 | |||
90 | enum 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 | |||
102 | static 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 | |||
114 | static 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 | len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which], | ||
126 | linebuf, sizeof(linebuf)); | ||
127 | |||
128 | if (len == 0) | ||
129 | return 0; | ||
130 | |||
131 | value = strtoul(linebuf, &endp, 0); | ||
132 | |||
133 | if (endp == linebuf || errno == ERANGE) | ||
134 | return 0; | ||
135 | |||
136 | return value; | ||
137 | } | ||
138 | |||
139 | /* read access to files which contain one string */ | ||
140 | |||
141 | enum cpufreq_string { | ||
142 | SCALING_DRIVER, | ||
143 | SCALING_GOVERNOR, | ||
144 | MAX_CPUFREQ_STRING_FILES | ||
145 | }; | ||
146 | |||
147 | static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = { | ||
148 | [SCALING_DRIVER] = "scaling_driver", | ||
149 | [SCALING_GOVERNOR] = "scaling_governor", | ||
150 | }; | ||
151 | |||
152 | |||
153 | static char *sysfs_cpufreq_get_one_string(unsigned int cpu, | ||
154 | enum cpufreq_string which) | ||
155 | { | ||
156 | char linebuf[MAX_LINE_LEN]; | ||
157 | char *result; | ||
158 | unsigned int len; | ||
159 | |||
160 | if (which >= MAX_CPUFREQ_STRING_FILES) | ||
161 | return NULL; | ||
162 | |||
163 | len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which], | ||
164 | linebuf, sizeof(linebuf)); | ||
165 | if (len == 0) | ||
166 | return NULL; | ||
167 | |||
168 | result = strdup(linebuf); | ||
169 | if (result == NULL) | ||
170 | return NULL; | ||
171 | |||
172 | if (result[strlen(result) - 1] == '\n') | ||
173 | result[strlen(result) - 1] = '\0'; | ||
174 | |||
175 | return result; | ||
176 | } | ||
177 | |||
178 | /* write access */ | ||
179 | |||
180 | enum cpufreq_write { | ||
181 | WRITE_SCALING_MIN_FREQ, | ||
182 | WRITE_SCALING_MAX_FREQ, | ||
183 | WRITE_SCALING_GOVERNOR, | ||
184 | WRITE_SCALING_SET_SPEED, | ||
185 | MAX_CPUFREQ_WRITE_FILES | ||
186 | }; | ||
187 | |||
188 | static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = { | ||
189 | [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq", | ||
190 | [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq", | ||
191 | [WRITE_SCALING_GOVERNOR] = "scaling_governor", | ||
192 | [WRITE_SCALING_SET_SPEED] = "scaling_setspeed", | ||
193 | }; | ||
194 | |||
195 | static int sysfs_cpufreq_write_one_value(unsigned int cpu, | ||
196 | enum cpufreq_write which, | ||
197 | const char *new_value, size_t len) | ||
198 | { | ||
199 | if (which >= MAX_CPUFREQ_WRITE_FILES) | ||
200 | return 0; | ||
201 | |||
202 | if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which], | ||
203 | new_value, len) != len) | ||
204 | return -ENODEV; | ||
205 | |||
206 | return 0; | ||
207 | }; | ||
208 | |||
209 | unsigned long sysfs_get_freq_kernel(unsigned int cpu) | ||
210 | { | ||
211 | return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ); | ||
212 | } | ||
213 | |||
214 | unsigned long sysfs_get_freq_hardware(unsigned int cpu) | ||
215 | { | ||
216 | return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ); | ||
217 | } | ||
218 | |||
219 | unsigned long sysfs_get_freq_transition_latency(unsigned int cpu) | ||
220 | { | ||
221 | return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY); | ||
222 | } | ||
223 | |||
224 | int sysfs_get_freq_hardware_limits(unsigned int cpu, | ||
225 | unsigned long *min, | ||
226 | unsigned long *max) | ||
227 | { | ||
228 | if ((!min) || (!max)) | ||
229 | return -EINVAL; | ||
230 | |||
231 | *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ); | ||
232 | if (!*min) | ||
233 | return -ENODEV; | ||
234 | |||
235 | *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ); | ||
236 | if (!*max) | ||
237 | return -ENODEV; | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | char *sysfs_get_freq_driver(unsigned int cpu) | ||
243 | { | ||
244 | return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER); | ||
245 | } | ||
246 | |||
247 | struct cpufreq_policy *sysfs_get_freq_policy(unsigned int cpu) | ||
248 | { | ||
249 | struct cpufreq_policy *policy; | ||
250 | |||
251 | policy = malloc(sizeof(struct cpufreq_policy)); | ||
252 | if (!policy) | ||
253 | return NULL; | ||
254 | |||
255 | policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR); | ||
256 | if (!policy->governor) { | ||
257 | free(policy); | ||
258 | return NULL; | ||
259 | } | ||
260 | policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); | ||
261 | policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ); | ||
262 | if ((!policy->min) || (!policy->max)) { | ||
263 | free(policy->governor); | ||
264 | free(policy); | ||
265 | return NULL; | ||
266 | } | ||
267 | |||
268 | return policy; | ||
269 | } | ||
270 | |||
271 | struct cpufreq_available_governors * | ||
272 | sysfs_get_freq_available_governors(unsigned int cpu) { | ||
273 | struct cpufreq_available_governors *first = NULL; | ||
274 | struct cpufreq_available_governors *current = NULL; | ||
275 | char linebuf[MAX_LINE_LEN]; | ||
276 | unsigned int pos, i; | ||
277 | unsigned int len; | ||
278 | |||
279 | len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors", | ||
280 | linebuf, sizeof(linebuf)); | ||
281 | if (len == 0) | ||
282 | return NULL; | ||
283 | |||
284 | pos = 0; | ||
285 | for (i = 0; i < len; i++) { | ||
286 | if (linebuf[i] == ' ' || linebuf[i] == '\n') { | ||
287 | if (i - pos < 2) | ||
288 | continue; | ||
289 | if (current) { | ||
290 | current->next = malloc(sizeof(*current)); | ||
291 | if (!current->next) | ||
292 | goto error_out; | ||
293 | current = current->next; | ||
294 | } else { | ||
295 | first = malloc(sizeof(*first)); | ||
296 | if (!first) | ||
297 | goto error_out; | ||
298 | current = first; | ||
299 | } | ||
300 | current->first = first; | ||
301 | current->next = NULL; | ||
302 | |||
303 | current->governor = malloc(i - pos + 1); | ||
304 | if (!current->governor) | ||
305 | goto error_out; | ||
306 | |||
307 | memcpy(current->governor, linebuf + pos, i - pos); | ||
308 | current->governor[i - pos] = '\0'; | ||
309 | pos = i + 1; | ||
310 | } | ||
311 | } | ||
312 | |||
313 | return first; | ||
314 | |||
315 | error_out: | ||
316 | while (first) { | ||
317 | current = first->next; | ||
318 | if (first->governor) | ||
319 | free(first->governor); | ||
320 | free(first); | ||
321 | first = current; | ||
322 | } | ||
323 | return NULL; | ||
324 | } | ||
325 | |||
326 | |||
327 | struct cpufreq_available_frequencies * | ||
328 | sysfs_get_available_frequencies(unsigned int cpu) { | ||
329 | struct cpufreq_available_frequencies *first = NULL; | ||
330 | struct cpufreq_available_frequencies *current = NULL; | ||
331 | char one_value[SYSFS_PATH_MAX]; | ||
332 | char linebuf[MAX_LINE_LEN]; | ||
333 | unsigned int pos, i; | ||
334 | unsigned int len; | ||
335 | |||
336 | len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies", | ||
337 | linebuf, sizeof(linebuf)); | ||
338 | if (len == 0) | ||
339 | return NULL; | ||
340 | |||
341 | pos = 0; | ||
342 | for (i = 0; i < len; i++) { | ||
343 | if (linebuf[i] == ' ' || linebuf[i] == '\n') { | ||
344 | if (i - pos < 2) | ||
345 | continue; | ||
346 | if (i - pos >= SYSFS_PATH_MAX) | ||
347 | goto error_out; | ||
348 | if (current) { | ||
349 | current->next = malloc(sizeof(*current)); | ||
350 | if (!current->next) | ||
351 | goto error_out; | ||
352 | current = current->next; | ||
353 | } else { | ||
354 | first = malloc(sizeof(*first)); | ||
355 | if (!first) | ||
356 | goto error_out; | ||
357 | current = first; | ||
358 | } | ||
359 | current->first = first; | ||
360 | current->next = NULL; | ||
361 | |||
362 | memcpy(one_value, linebuf + pos, i - pos); | ||
363 | one_value[i - pos] = '\0'; | ||
364 | if (sscanf(one_value, "%lu", ¤t->frequency) != 1) | ||
365 | goto error_out; | ||
366 | |||
367 | pos = i + 1; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | return first; | ||
372 | |||
373 | error_out: | ||
374 | while (first) { | ||
375 | current = first->next; | ||
376 | free(first); | ||
377 | first = current; | ||
378 | } | ||
379 | return NULL; | ||
380 | } | ||
381 | |||
382 | static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu, | ||
383 | const char *file) | ||
384 | { | ||
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 | len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf)); | ||
393 | if (len == 0) | ||
394 | return NULL; | ||
395 | |||
396 | pos = 0; | ||
397 | for (i = 0; i < len; i++) { | ||
398 | if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') { | ||
399 | if (i - pos < 1) | ||
400 | continue; | ||
401 | if (i - pos >= SYSFS_PATH_MAX) | ||
402 | goto error_out; | ||
403 | if (current) { | ||
404 | current->next = malloc(sizeof(*current)); | ||
405 | if (!current->next) | ||
406 | goto error_out; | ||
407 | current = current->next; | ||
408 | } else { | ||
409 | first = malloc(sizeof(*first)); | ||
410 | if (!first) | ||
411 | goto error_out; | ||
412 | current = first; | ||
413 | } | ||
414 | current->first = first; | ||
415 | current->next = NULL; | ||
416 | |||
417 | memcpy(one_value, linebuf + pos, i - pos); | ||
418 | one_value[i - pos] = '\0'; | ||
419 | |||
420 | if (sscanf(one_value, "%u", ¤t->cpu) != 1) | ||
421 | goto error_out; | ||
422 | |||
423 | pos = i + 1; | ||
424 | } | ||
425 | } | ||
426 | |||
427 | return first; | ||
428 | |||
429 | error_out: | ||
430 | while (first) { | ||
431 | current = first->next; | ||
432 | free(first); | ||
433 | first = current; | ||
434 | } | ||
435 | return NULL; | ||
436 | } | ||
437 | |||
438 | struct cpufreq_affected_cpus *sysfs_get_freq_affected_cpus(unsigned int cpu) | ||
439 | { | ||
440 | return sysfs_get_cpu_list(cpu, "affected_cpus"); | ||
441 | } | ||
442 | |||
443 | struct cpufreq_affected_cpus *sysfs_get_freq_related_cpus(unsigned int cpu) | ||
444 | { | ||
445 | return sysfs_get_cpu_list(cpu, "related_cpus"); | ||
446 | } | ||
447 | |||
448 | struct cpufreq_stats *sysfs_get_freq_stats(unsigned int cpu, | ||
449 | unsigned long long *total_time) { | ||
450 | struct cpufreq_stats *first = NULL; | ||
451 | struct cpufreq_stats *current = NULL; | ||
452 | char one_value[SYSFS_PATH_MAX]; | ||
453 | char linebuf[MAX_LINE_LEN]; | ||
454 | unsigned int pos, i; | ||
455 | unsigned int len; | ||
456 | |||
457 | len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state", | ||
458 | linebuf, sizeof(linebuf)); | ||
459 | if (len == 0) | ||
460 | return NULL; | ||
461 | |||
462 | *total_time = 0; | ||
463 | pos = 0; | ||
464 | for (i = 0; i < len; i++) { | ||
465 | if (i == strlen(linebuf) || linebuf[i] == '\n') { | ||
466 | if (i - pos < 2) | ||
467 | continue; | ||
468 | if ((i - pos) >= SYSFS_PATH_MAX) | ||
469 | goto error_out; | ||
470 | if (current) { | ||
471 | current->next = malloc(sizeof(*current)); | ||
472 | if (!current->next) | ||
473 | goto error_out; | ||
474 | current = current->next; | ||
475 | } else { | ||
476 | first = malloc(sizeof(*first)); | ||
477 | if (!first) | ||
478 | goto error_out; | ||
479 | current = first; | ||
480 | } | ||
481 | current->first = first; | ||
482 | current->next = NULL; | ||
483 | |||
484 | memcpy(one_value, linebuf + pos, i - pos); | ||
485 | one_value[i - pos] = '\0'; | ||
486 | if (sscanf(one_value, "%lu %llu", | ||
487 | ¤t->frequency, | ||
488 | ¤t->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 | |||
507 | unsigned long sysfs_get_freq_transitions(unsigned int cpu) | ||
508 | { | ||
509 | return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS); | ||
510 | } | ||
511 | |||
512 | static 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 | |||
547 | int 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 | |||
561 | int 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 | |||
572 | int 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 | |||
583 | int 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 | |||
630 | int sysfs_set_frequency(unsigned int cpu, unsigned long target_frequency) | ||
631 | { | ||
632 | struct cpufreq_policy *pol = sysfs_get_freq_policy(cpu); | ||
633 | char userspace_gov[] = "userspace"; | ||
634 | char freq[SYSFS_PATH_MAX]; | ||
635 | int ret; | ||
636 | |||
637 | if (!pol) | ||
638 | return -ENODEV; | ||
639 | |||
640 | if (strncmp(pol->governor, userspace_gov, 9) != 0) { | ||
641 | ret = sysfs_modify_freq_policy_governor(cpu, userspace_gov); | ||
642 | if (ret) { | ||
643 | cpufreq_put_policy(pol); | ||
644 | return ret; | ||
645 | } | ||
646 | } | ||
647 | |||
648 | cpufreq_put_policy(pol); | ||
649 | |||
650 | snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency); | ||
651 | |||
652 | return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED, | ||
653 | freq, strlen(freq)); | ||
654 | } | ||
655 | |||
656 | /* CPUFREQ sysfs access **************************************************/ | ||
657 | |||
658 | /* General sysfs access **************************************************/ | ||
659 | int sysfs_cpu_exists(unsigned int cpu) | ||
660 | { | ||
661 | char file[SYSFS_PATH_MAX]; | ||
662 | struct stat statbuf; | ||
663 | |||
664 | snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/", cpu); | ||
665 | |||
666 | if (stat(file, &statbuf) != 0) | ||
667 | return -ENOSYS; | ||
668 | |||
669 | return S_ISDIR(statbuf.st_mode) ? 0 : -ENOSYS; | ||
670 | } | ||
671 | |||
672 | /* General sysfs access **************************************************/ | ||
diff --git a/tools/power/cpupower/lib/sysfs.h b/tools/power/cpupower/lib/sysfs.h new file mode 100644 index 00000000000..c76a5e0af50 --- /dev/null +++ b/tools/power/cpupower/lib/sysfs.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* General */ | ||
2 | extern unsigned int sysfs_cpu_exists(unsigned int cpu); | ||
3 | |||
4 | /* CPUfreq */ | ||
5 | extern unsigned long sysfs_get_freq_kernel(unsigned int cpu); | ||
6 | extern unsigned long sysfs_get_freq_hardware(unsigned int cpu); | ||
7 | extern unsigned long sysfs_get_freq_transition_latency(unsigned int cpu); | ||
8 | extern int sysfs_get_freq_hardware_limits(unsigned int cpu, | ||
9 | unsigned long *min, unsigned long *max); | ||
10 | extern char *sysfs_get_freq_driver(unsigned int cpu); | ||
11 | extern struct cpufreq_policy *sysfs_get_freq_policy(unsigned int cpu); | ||
12 | extern struct cpufreq_available_governors *sysfs_get_freq_available_governors( | ||
13 | unsigned int cpu); | ||
14 | extern struct cpufreq_available_frequencies *sysfs_get_available_frequencies( | ||
15 | unsigned int cpu); | ||
16 | extern struct cpufreq_affected_cpus *sysfs_get_freq_affected_cpus( | ||
17 | unsigned int cpu); | ||
18 | extern struct cpufreq_affected_cpus *sysfs_get_freq_related_cpus( | ||
19 | unsigned int cpu); | ||
20 | extern struct cpufreq_stats *sysfs_get_freq_stats(unsigned int cpu, | ||
21 | unsigned long long *total_time); | ||
22 | extern unsigned long sysfs_get_freq_transitions(unsigned int cpu); | ||
23 | extern int sysfs_set_freq_policy(unsigned int cpu, | ||
24 | struct cpufreq_policy *policy); | ||
25 | extern int sysfs_modify_freq_policy_min(unsigned int cpu, | ||
26 | unsigned long min_freq); | ||
27 | extern int sysfs_modify_freq_policy_max(unsigned int cpu, | ||
28 | unsigned long max_freq); | ||
29 | extern int sysfs_modify_freq_policy_governor(unsigned int cpu, char *governor); | ||
30 | extern int sysfs_set_frequency(unsigned int cpu, | ||
31 | unsigned long target_frequency); | ||