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
24static 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 */
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	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
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	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
141enum cpufreq_string {
142	SCALING_DRIVER,
143	SCALING_GOVERNOR,
144	MAX_CPUFREQ_STRING_FILES
145};
146
147static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
148	[SCALING_DRIVER] = "scaling_driver",
149	[SCALING_GOVERNOR] = "scaling_governor",
150};
151
152
153static 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
180enum 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
188static 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
195static 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
209unsigned long sysfs_get_freq_kernel(unsigned int cpu)
210{
211	return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
212}
213
214unsigned long sysfs_get_freq_hardware(unsigned int cpu)
215{
216	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
217}
218
219unsigned long sysfs_get_freq_transition_latency(unsigned int cpu)
220{
221	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
222}
223
224int 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
242char *sysfs_get_freq_driver(unsigned int cpu)
243{
244	return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
245}
246
247struct 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
271struct cpufreq_available_governors *
272sysfs_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
327struct cpufreq_available_frequencies *
328sysfs_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", &current->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
382static 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", &current->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
438struct cpufreq_affected_cpus *sysfs_get_freq_affected_cpus(unsigned int cpu)
439{
440	return sysfs_get_cpu_list(cpu, "affected_cpus");
441}
442
443struct cpufreq_affected_cpus *sysfs_get_freq_related_cpus(unsigned int cpu)
444{
445	return sysfs_get_cpu_list(cpu, "related_cpus");
446}
447
448struct 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					&current->frequency,
488					&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{
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 **************************************************/
659int 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 **************************************************/
673