arm64: dts: qcom: sm8550: add TRNG node
[linux-modified.git] / tools / power / cpupower / lib / cpuidle.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
4  *  (C) 2011       Thomas Renninger <trenn@novell.com> Novell Inc.
5  */
6
7 #include <stdio.h>
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15
16 #include "cpuidle.h"
17 #include "cpupower_intern.h"
18
19 /*
20  * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir
21  * exists.
22  * For example the functionality to disable c-states was introduced in later
23  * kernel versions, this function can be used to explicitly check for this
24  * feature.
25  *
26  * returns 1 if the file exists, 0 otherwise.
27  */
28 static
29 unsigned int cpuidle_state_file_exists(unsigned int cpu,
30                                        unsigned int idlestate,
31                                        const char *fname)
32 {
33         char path[SYSFS_PATH_MAX];
34         struct stat statbuf;
35
36
37         snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
38                  cpu, idlestate, fname);
39         if (stat(path, &statbuf) != 0)
40                 return 0;
41         return 1;
42 }
43
44 /*
45  * helper function to read file from /sys into given buffer
46  * fname is a relative path under "cpuX/cpuidle/stateX/" dir
47  * cstates starting with 0, C0 is not counted as cstate.
48  * This means if you want C1 info, pass 0 as idlestate param
49  */
50 static
51 unsigned int cpuidle_state_read_file(unsigned int cpu,
52                                             unsigned int idlestate,
53                                             const char *fname, char *buf,
54                                             size_t buflen)
55 {
56         char path[SYSFS_PATH_MAX];
57         int fd;
58         ssize_t numread;
59
60         snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
61                  cpu, idlestate, fname);
62
63         fd = open(path, O_RDONLY);
64         if (fd == -1)
65                 return 0;
66
67         numread = read(fd, buf, buflen - 1);
68         if (numread < 1) {
69                 close(fd);
70                 return 0;
71         }
72
73         buf[numread] = '\0';
74         close(fd);
75
76         return (unsigned int) numread;
77 }
78
79 /*
80  * helper function to write a new value to a /sys file
81  * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir
82  *
83  * Returns the number of bytes written or 0 on error
84  */
85 static
86 unsigned int cpuidle_state_write_file(unsigned int cpu,
87                                       unsigned int idlestate,
88                                       const char *fname,
89                                       const char *value, size_t len)
90 {
91         char path[SYSFS_PATH_MAX];
92         int fd;
93         ssize_t numwrite;
94
95         snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
96                  cpu, idlestate, fname);
97
98         fd = open(path, O_WRONLY);
99         if (fd == -1)
100                 return 0;
101
102         numwrite = write(fd, value, len);
103         if (numwrite < 1) {
104                 close(fd);
105                 return 0;
106         }
107
108         close(fd);
109
110         return (unsigned int) numwrite;
111 }
112
113 /* read access to files which contain one numeric value */
114
115 enum idlestate_value {
116         IDLESTATE_USAGE,
117         IDLESTATE_POWER,
118         IDLESTATE_LATENCY,
119         IDLESTATE_TIME,
120         IDLESTATE_DISABLE,
121         MAX_IDLESTATE_VALUE_FILES
122 };
123
124 static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
125         [IDLESTATE_USAGE] = "usage",
126         [IDLESTATE_POWER] = "power",
127         [IDLESTATE_LATENCY] = "latency",
128         [IDLESTATE_TIME]  = "time",
129         [IDLESTATE_DISABLE]  = "disable",
130 };
131
132 static
133 unsigned long long cpuidle_state_get_one_value(unsigned int cpu,
134                                                unsigned int idlestate,
135                                                enum idlestate_value which)
136 {
137         unsigned long long value;
138         unsigned int len;
139         char linebuf[MAX_LINE_LEN];
140         char *endp;
141
142         if (which >= MAX_IDLESTATE_VALUE_FILES)
143                 return 0;
144
145         len = cpuidle_state_read_file(cpu, idlestate,
146                                       idlestate_value_files[which],
147                                       linebuf, sizeof(linebuf));
148         if (len == 0)
149                 return 0;
150
151         value = strtoull(linebuf, &endp, 0);
152
153         if (endp == linebuf || errno == ERANGE)
154                 return 0;
155
156         return value;
157 }
158
159 /* read access to files which contain one string */
160
161 enum idlestate_string {
162         IDLESTATE_DESC,
163         IDLESTATE_NAME,
164         MAX_IDLESTATE_STRING_FILES
165 };
166
167 static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
168         [IDLESTATE_DESC] = "desc",
169         [IDLESTATE_NAME] = "name",
170 };
171
172
173 static char *cpuidle_state_get_one_string(unsigned int cpu,
174                                         unsigned int idlestate,
175                                         enum idlestate_string which)
176 {
177         char linebuf[MAX_LINE_LEN];
178         char *result;
179         unsigned int len;
180
181         if (which >= MAX_IDLESTATE_STRING_FILES)
182                 return NULL;
183
184         len = cpuidle_state_read_file(cpu, idlestate,
185                                       idlestate_string_files[which],
186                                       linebuf, sizeof(linebuf));
187         if (len == 0)
188                 return NULL;
189
190         result = strdup(linebuf);
191         if (result == NULL)
192                 return NULL;
193
194         if (result[strlen(result) - 1] == '\n')
195                 result[strlen(result) - 1] = '\0';
196
197         return result;
198 }
199
200 /*
201  * Returns:
202  *    1  if disabled
203  *    0  if enabled
204  *    -1 if idlestate is not available
205  *    -2 if disabling is not supported by the kernel
206  */
207 int cpuidle_is_state_disabled(unsigned int cpu,
208                                 unsigned int idlestate)
209 {
210         if (cpuidle_state_count(cpu) <= idlestate)
211                 return -1;
212
213         if (!cpuidle_state_file_exists(cpu, idlestate,
214                                  idlestate_value_files[IDLESTATE_DISABLE]))
215                 return -2;
216         return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
217 }
218
219 /*
220  * Pass 1 as last argument to disable or 0 to enable the state
221  * Returns:
222  *    0  on success
223  *    negative values on error, for example:
224  *      -1 if idlestate is not available
225  *      -2 if disabling is not supported by the kernel
226  *      -3 No write access to disable/enable C-states
227  */
228 int cpuidle_state_disable(unsigned int cpu,
229                             unsigned int idlestate,
230                             unsigned int disable)
231 {
232         char value[SYSFS_PATH_MAX];
233         int bytes_written;
234
235         if (cpuidle_state_count(cpu) <= idlestate)
236                 return -1;
237
238         if (!cpuidle_state_file_exists(cpu, idlestate,
239                                  idlestate_value_files[IDLESTATE_DISABLE]))
240                 return -2;
241
242         snprintf(value, SYSFS_PATH_MAX, "%u", disable);
243
244         bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable",
245                                                    value, sizeof(disable));
246         if (bytes_written)
247                 return 0;
248         return -3;
249 }
250
251 unsigned long cpuidle_state_latency(unsigned int cpu,
252                                           unsigned int idlestate)
253 {
254         return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
255 }
256
257 unsigned long cpuidle_state_usage(unsigned int cpu,
258                                         unsigned int idlestate)
259 {
260         return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
261 }
262
263 unsigned long long cpuidle_state_time(unsigned int cpu,
264                                         unsigned int idlestate)
265 {
266         return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME);
267 }
268
269 char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate)
270 {
271         return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME);
272 }
273
274 char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate)
275 {
276         return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC);
277 }
278
279 /*
280  * Returns number of supported C-states of CPU core cpu
281  * Negativ in error case
282  * Zero if cpuidle does not export any C-states
283  */
284 unsigned int cpuidle_state_count(unsigned int cpu)
285 {
286         char file[SYSFS_PATH_MAX];
287         struct stat statbuf;
288         int idlestates = 1;
289
290
291         snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
292         if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
293                 return 0;
294
295         snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
296         if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
297                 return 0;
298
299         while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
300                 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
301                          "cpu%u/cpuidle/state%d", cpu, idlestates);
302                 idlestates++;
303         }
304         idlestates--;
305         return idlestates;
306 }
307
308 /* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
309
310 /*
311  * helper function to read file from /sys into given buffer
312  * fname is a relative path under "cpu/cpuidle/" dir
313  */
314 static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
315                                             size_t buflen)
316 {
317         char path[SYSFS_PATH_MAX];
318
319         snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
320
321         return cpupower_read_sysfs(path, buf, buflen);
322 }
323
324
325
326 /* read access to files which contain one string */
327
328 enum cpuidle_string {
329         CPUIDLE_GOVERNOR,
330         CPUIDLE_GOVERNOR_RO,
331         CPUIDLE_DRIVER,
332         MAX_CPUIDLE_STRING_FILES
333 };
334
335 static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
336         [CPUIDLE_GOVERNOR]      = "current_governor",
337         [CPUIDLE_GOVERNOR_RO]   = "current_governor_ro",
338         [CPUIDLE_DRIVER]        = "current_driver",
339 };
340
341
342 static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
343 {
344         char linebuf[MAX_LINE_LEN];
345         char *result;
346         unsigned int len;
347
348         if (which >= MAX_CPUIDLE_STRING_FILES)
349                 return NULL;
350
351         len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
352                                 linebuf, sizeof(linebuf));
353         if (len == 0)
354                 return NULL;
355
356         result = strdup(linebuf);
357         if (result == NULL)
358                 return NULL;
359
360         if (result[strlen(result) - 1] == '\n')
361                 result[strlen(result) - 1] = '\0';
362
363         return result;
364 }
365
366 char *cpuidle_get_governor(void)
367 {
368         char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
369         if (!tmp)
370                 return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
371         else
372                 return tmp;
373 }
374
375 char *cpuidle_get_driver(void)
376 {
377         return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
378 }
379 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */