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