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 <unistd.h>
9#include <stdio.h>
10#include <errno.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include <getopt.h>
15
16#include "cpufreq.h"
17#include "helpers/helpers.h"
18#include "helpers/bitmask.h"
19
20#define LINE_LEN 10
21
22static unsigned int count_cpus(void)
23{
24	FILE *fp;
25	char value[LINE_LEN];
26	unsigned int ret = 0;
27	unsigned int cpunr = 0;
28
29	fp = fopen("/proc/stat", "r");
30	if (!fp) {
31		printf(_("Couldn't count the number of CPUs (%s: %s), assuming 1\n"), "/proc/stat", strerror(errno));
32		return 1;
33	}
34
35	while (!feof(fp)) {
36		if (!fgets(value, LINE_LEN, fp))
37			continue;
38		value[LINE_LEN - 1] = '\0';
39		if (strlen(value) < (LINE_LEN - 2))
40			continue;
41		if (strstr(value, "cpu "))
42			continue;
43		if (sscanf(value, "cpu%d ", &cpunr) != 1)
44			continue;
45		if (cpunr > ret)
46			ret = cpunr;
47	}
48	fclose(fp);
49
50	/* cpu count starts from 0, on error return 1 (UP) */
51	return ret + 1;
52}
53
54
55static void proc_cpufreq_output(void)
56{
57	unsigned int cpu, nr_cpus;
58	struct cpufreq_policy *policy;
59	unsigned int min_pctg = 0;
60	unsigned int max_pctg = 0;
61	unsigned long min, max;
62
63	printf(_("          minimum CPU frequency  -  maximum CPU frequency  -  governor\n"));
64
65	nr_cpus = count_cpus();
66	for (cpu = 0; cpu < nr_cpus; cpu++) {
67		policy = cpufreq_get_policy(cpu);
68		if (!policy)
69			continue;
70
71		if (cpufreq_get_hardware_limits(cpu, &min, &max)) {
72			max = 0;
73		} else {
74			min_pctg = (policy->min * 100) / max;
75			max_pctg = (policy->max * 100) / max;
76		}
77		printf("CPU%3d    %9lu kHz (%3d %%)  -  %9lu kHz (%3d %%)  -  %s\n",
78			cpu , policy->min, max ? min_pctg : 0, policy->max,
79			max ? max_pctg : 0, policy->governor);
80
81		cpufreq_put_policy(policy);
82	}
83}
84
85static int no_rounding;
86static void print_speed(unsigned long speed)
87{
88	unsigned long tmp;
89
90	if (no_rounding) {
91		if (speed > 1000000)
92			printf("%u.%06u GHz", ((unsigned int) speed/1000000),
93				((unsigned int) speed%1000000));
94		else if (speed > 100000)
95			printf("%u MHz", (unsigned int) speed);
96		else if (speed > 1000)
97			printf("%u.%03u MHz", ((unsigned int) speed/1000),
98				(unsigned int) (speed%1000));
99		else
100			printf("%lu kHz", speed);
101	} else {
102		if (speed > 1000000) {
103			tmp = speed%10000;
104			if (tmp >= 5000)
105				speed += 10000;
106			printf("%u.%02u GHz", ((unsigned int) speed/1000000),
107				((unsigned int) (speed%1000000)/10000));
108		} else if (speed > 100000) {
109			tmp = speed%1000;
110			if (tmp >= 500)
111				speed += 1000;
112			printf("%u MHz", ((unsigned int) speed/1000));
113		} else if (speed > 1000) {
114			tmp = speed%100;
115			if (tmp >= 50)
116				speed += 100;
117			printf("%u.%01u MHz", ((unsigned int) speed/1000),
118				((unsigned int) (speed%1000)/100));
119		}
120	}
121
122	return;
123}
124
125static void print_duration(unsigned long duration)
126{
127	unsigned long tmp;
128
129	if (no_rounding) {
130		if (duration > 1000000)
131			printf("%u.%06u ms", ((unsigned int) duration/1000000),
132				((unsigned int) duration%1000000));
133		else if (duration > 100000)
134			printf("%u us", ((unsigned int) duration/1000));
135		else if (duration > 1000)
136			printf("%u.%03u us", ((unsigned int) duration/1000),
137				((unsigned int) duration%1000));
138		else
139			printf("%lu ns", duration);
140	} else {
141		if (duration > 1000000) {
142			tmp = duration%10000;
143			if (tmp >= 5000)
144				duration += 10000;
145			printf("%u.%02u ms", ((unsigned int) duration/1000000),
146				((unsigned int) (duration%1000000)/10000));
147		} else if (duration > 100000) {
148			tmp = duration%1000;
149			if (tmp >= 500)
150				duration += 1000;
151			printf("%u us", ((unsigned int) duration / 1000));
152		} else if (duration > 1000) {
153			tmp = duration%100;
154			if (tmp >= 50)
155				duration += 100;
156			printf("%u.%01u us", ((unsigned int) duration/1000),
157				((unsigned int) (duration%1000)/100));
158		} else
159			printf("%lu ns", duration);
160	}
161	return;
162}
163
164/* --boost / -b */
165
166static int get_boost_mode(unsigned int cpu)
167{
168	int support, active, b_states = 0, ret, pstate_no, i;
169	/* ToDo: Make this more global */
170	unsigned long pstates[MAX_HW_PSTATES] = {0,};
171
172	if (cpupower_cpu_info.vendor != X86_VENDOR_AMD &&
173	    cpupower_cpu_info.vendor != X86_VENDOR_INTEL)
174		return 0;
175
176	ret = cpufreq_has_boost_support(cpu, &support, &active, &b_states);
177	if (ret) {
178		printf(_("Error while evaluating Boost Capabilities"
179				" on CPU %d -- are you root?\n"), cpu);
180		return ret;
181	}
182	/* P state changes via MSR are identified via cpuid 80000007
183	   on Intel and AMD, but we assume boost capable machines can do that
184	   if (cpuid_eax(0x80000000) >= 0x80000007
185	   && (cpuid_edx(0x80000007) & (1 << 7)))
186	*/
187
188	printf(_("  boost state support:\n"));
189
190	printf(_("    Supported: %s\n"), support ? _("yes") : _("no"));
191	printf(_("    Active: %s\n"), active ? _("yes") : _("no"));
192
193	if (cpupower_cpu_info.vendor == X86_VENDOR_AMD &&
194	    cpupower_cpu_info.family >= 0x10) {
195		ret = decode_pstates(cpu, cpupower_cpu_info.family, b_states,
196				     pstates, &pstate_no);
197		if (ret)
198			return ret;
199
200		printf(_("    Boost States: %d\n"), b_states);
201		printf(_("    Total States: %d\n"), pstate_no);
202		for (i = 0; i < pstate_no; i++) {
203			if (i < b_states)
204				printf(_("    Pstate-Pb%d: %luMHz (boost state)"
205					 "\n"), i, pstates[i]);
206			else
207				printf(_("    Pstate-P%d:  %luMHz\n"),
208				       i - b_states, pstates[i]);
209		}
210	} else if (cpupower_cpu_info.caps & CPUPOWER_CAP_HAS_TURBO_RATIO) {
211		double bclk;
212		unsigned long long intel_turbo_ratio = 0;
213		unsigned int ratio;
214
215		/* Any way to autodetect this ? */
216		if (cpupower_cpu_info.caps & CPUPOWER_CAP_IS_SNB)
217			bclk = 100.00;
218		else
219			bclk = 133.33;
220		intel_turbo_ratio = msr_intel_get_turbo_ratio(cpu);
221		dprint ("    Ratio: 0x%llx - bclk: %f\n",
222			intel_turbo_ratio, bclk);
223
224		ratio = (intel_turbo_ratio >> 24) & 0xFF;
225		if (ratio)
226			printf(_("    %.0f MHz max turbo 4 active cores\n"),
227			       ratio * bclk);
228
229		ratio = (intel_turbo_ratio >> 16) & 0xFF;
230		if (ratio)
231			printf(_("    %.0f MHz max turbo 3 active cores\n"),
232			       ratio * bclk);
233
234		ratio = (intel_turbo_ratio >> 8) & 0xFF;
235		if (ratio)
236			printf(_("    %.0f MHz max turbo 2 active cores\n"),
237			       ratio * bclk);
238
239		ratio = (intel_turbo_ratio >> 0) & 0xFF;
240		if (ratio)
241			printf(_("    %.0f MHz max turbo 1 active cores\n"),
242			       ratio * bclk);
243	}
244	return 0;
245}
246
247static void debug_output_one(unsigned int cpu)
248{
249	char *driver;
250	struct cpufreq_affected_cpus *cpus;
251	struct cpufreq_available_frequencies *freqs;
252	unsigned long min, max, freq_kernel, freq_hardware;
253	unsigned long total_trans, latency;
254	unsigned long long total_time;
255	struct cpufreq_policy *policy;
256	struct cpufreq_available_governors *governors;
257	struct cpufreq_stats *stats;
258
259	if (cpufreq_cpu_exists(cpu))
260		return;
261
262	freq_kernel = cpufreq_get_freq_kernel(cpu);
263	freq_hardware = cpufreq_get_freq_hardware(cpu);
264
265	driver = cpufreq_get_driver(cpu);
266	if (!driver) {
267		printf(_("  no or unknown cpufreq driver is active on this CPU\n"));
268	} else {
269		printf(_("  driver: %s\n"), driver);
270		cpufreq_put_driver(driver);
271	}
272
273	cpus = cpufreq_get_related_cpus(cpu);
274	if (cpus) {
275		printf(_("  CPUs which run at the same hardware frequency: "));
276		while (cpus->next) {
277			printf("%d ", cpus->cpu);
278			cpus = cpus->next;
279		}
280		printf("%d\n", cpus->cpu);
281		cpufreq_put_related_cpus(cpus);
282	}
283
284	cpus = cpufreq_get_affected_cpus(cpu);
285	if (cpus) {
286		printf(_("  CPUs which need to have their frequency coordinated by software: "));
287		while (cpus->next) {
288			printf("%d ", cpus->cpu);
289			cpus = cpus->next;
290		}
291		printf("%d\n", cpus->cpu);
292		cpufreq_put_affected_cpus(cpus);
293	}
294
295	latency = cpufreq_get_transition_latency(cpu);
296	if (latency) {
297		printf(_("  maximum transition latency: "));
298		print_duration(latency);
299		printf(".\n");
300	}
301
302	if (!(cpufreq_get_hardware_limits(cpu, &min, &max))) {
303		printf(_("  hardware limits: "));
304		print_speed(min);
305		printf(" - ");
306		print_speed(max);
307		printf("\n");
308	}
309
310	freqs = cpufreq_get_available_frequencies(cpu);
311	if (freqs) {
312		printf(_("  available frequency steps: "));
313		while (freqs->next) {
314			print_speed(freqs->frequency);
315			printf(", ");
316			freqs = freqs->next;
317		}
318		print_speed(freqs->frequency);
319		printf("\n");
320		cpufreq_put_available_frequencies(freqs);
321	}
322
323	governors = cpufreq_get_available_governors(cpu);
324	if (governors) {
325		printf(_("  available cpufreq governors: "));
326		while (governors->next) {
327			printf("%s, ", governors->governor);
328			governors = governors->next;
329		}
330		printf("%s\n", governors->governor);
331		cpufreq_put_available_governors(governors);
332	}
333
334	policy = cpufreq_get_policy(cpu);
335	if (policy) {
336		printf(_("  current policy: frequency should be within "));
337		print_speed(policy->min);
338		printf(_(" and "));
339		print_speed(policy->max);
340
341		printf(".\n                  ");
342		printf(_("The governor \"%s\" may"
343		       " decide which speed to use\n                  within this range.\n"),
344		       policy->governor);
345		cpufreq_put_policy(policy);
346	}
347
348	if (freq_kernel || freq_hardware) {
349		printf(_("  current CPU frequency is "));
350		if (freq_hardware) {
351			print_speed(freq_hardware);
352			printf(_(" (asserted by call to hardware)"));
353		} else
354			print_speed(freq_kernel);
355		printf(".\n");
356	}
357	stats = cpufreq_get_stats(cpu, &total_time);
358	if (stats) {
359		printf(_("  cpufreq stats: "));
360		while (stats) {
361			print_speed(stats->frequency);
362			printf(":%.2f%%", (100.0 * stats->time_in_state) / total_time);
363			stats = stats->next;
364			if (stats)
365				printf(", ");
366		}
367		cpufreq_put_stats(stats);
368		total_trans = cpufreq_get_transitions(cpu);
369		if (total_trans)
370			printf("  (%lu)\n", total_trans);
371		else
372			printf("\n");
373	}
374	get_boost_mode(cpu);
375
376}
377
378/* --freq / -f */
379
380static int get_freq_kernel(unsigned int cpu, unsigned int human)
381{
382	unsigned long freq = cpufreq_get_freq_kernel(cpu);
383	if (!freq)
384		return -EINVAL;
385	if (human) {
386		print_speed(freq);
387		printf("\n");
388	} else
389		printf("%lu\n", freq);
390	return 0;
391}
392
393
394/* --hwfreq / -w */
395
396static int get_freq_hardware(unsigned int cpu, unsigned int human)
397{
398	unsigned long freq = cpufreq_get_freq_hardware(cpu);
399	if (!freq)
400		return -EINVAL;
401	if (human) {
402		print_speed(freq);
403		printf("\n");
404	} else
405		printf("%lu\n", freq);
406	return 0;
407}
408
409/* --hwlimits / -l */
410
411static int get_hardware_limits(unsigned int cpu)
412{
413	unsigned long min, max;
414	if (cpufreq_get_hardware_limits(cpu, &min, &max))
415		return -EINVAL;
416	printf("%lu %lu\n", min, max);
417	return 0;
418}
419
420/* --driver / -d */
421
422static int get_driver(unsigned int cpu)
423{
424	char *driver = cpufreq_get_driver(cpu);
425	if (!driver)
426		return -EINVAL;
427	printf("%s\n", driver);
428	cpufreq_put_driver(driver);
429	return 0;
430}
431
432/* --policy / -p */
433
434static int get_policy(unsigned int cpu)
435{
436	struct cpufreq_policy *policy = cpufreq_get_policy(cpu);
437	if (!policy)
438		return -EINVAL;
439	printf("%lu %lu %s\n", policy->min, policy->max, policy->governor);
440	cpufreq_put_policy(policy);
441	return 0;
442}
443
444/* --governors / -g */
445
446static int get_available_governors(unsigned int cpu)
447{
448	struct cpufreq_available_governors *governors =
449		cpufreq_get_available_governors(cpu);
450	if (!governors)
451		return -EINVAL;
452
453	while (governors->next) {
454		printf("%s ", governors->governor);
455		governors = governors->next;
456	}
457	printf("%s\n", governors->governor);
458	cpufreq_put_available_governors(governors);
459	return 0;
460}
461
462
463/* --affected-cpus  / -a */
464
465static int get_affected_cpus(unsigned int cpu)
466{
467	struct cpufreq_affected_cpus *cpus = cpufreq_get_affected_cpus(cpu);
468	if (!cpus)
469		return -EINVAL;
470
471	while (cpus->next) {
472		printf("%d ", cpus->cpu);
473		cpus = cpus->next;
474	}
475	printf("%d\n", cpus->cpu);
476	cpufreq_put_affected_cpus(cpus);
477	return 0;
478}
479
480/* --related-cpus  / -r */
481
482static int get_related_cpus(unsigned int cpu)
483{
484	struct cpufreq_affected_cpus *cpus = cpufreq_get_related_cpus(cpu);
485	if (!cpus)
486		return -EINVAL;
487
488	while (cpus->next) {
489		printf("%d ", cpus->cpu);
490		cpus = cpus->next;
491	}
492	printf("%d\n", cpus->cpu);
493	cpufreq_put_related_cpus(cpus);
494	return 0;
495}
496
497/* --stats / -s */
498
499static int get_freq_stats(unsigned int cpu, unsigned int human)
500{
501	unsigned long total_trans = cpufreq_get_transitions(cpu);
502	unsigned long long total_time;
503	struct cpufreq_stats *stats = cpufreq_get_stats(cpu, &total_time);
504	while (stats) {
505		if (human) {
506			print_speed(stats->frequency);
507			printf(":%.2f%%",
508				(100.0 * stats->time_in_state) / total_time);
509		} else
510			printf("%lu:%llu",
511				stats->frequency, stats->time_in_state);
512		stats = stats->next;
513		if (stats)
514			printf(", ");
515	}
516	cpufreq_put_stats(stats);
517	if (total_trans)
518		printf("  (%lu)\n", total_trans);
519	return 0;
520}
521
522/* --latency / -y */
523
524static int get_latency(unsigned int cpu, unsigned int human)
525{
526	unsigned long latency = cpufreq_get_transition_latency(cpu);
527	if (!latency)
528		return -EINVAL;
529
530	if (human) {
531		print_duration(latency);
532		printf("\n");
533	} else
534		printf("%lu\n", latency);
535	return 0;
536}
537
538static struct option info_opts[] = {
539	{ .name = "debug",	.has_arg = no_argument,		.flag = NULL,	.val = 'e'},
540	{ .name = "boost",	.has_arg = no_argument,		.flag = NULL,	.val = 'b'},
541	{ .name = "freq",	.has_arg = no_argument,		.flag = NULL,	.val = 'f'},
542	{ .name = "hwfreq",	.has_arg = no_argument,		.flag = NULL,	.val = 'w'},
543	{ .name = "hwlimits",	.has_arg = no_argument,		.flag = NULL,	.val = 'l'},
544	{ .name = "driver",	.has_arg = no_argument,		.flag = NULL,	.val = 'd'},
545	{ .name = "policy",	.has_arg = no_argument,		.flag = NULL,	.val = 'p'},
546	{ .name = "governors",	.has_arg = no_argument,		.flag = NULL,	.val = 'g'},
547	{ .name = "related-cpus", .has_arg = no_argument,	.flag = NULL,	.val = 'r'},
548	{ .name = "affected-cpus",.has_arg = no_argument,	.flag = NULL,	.val = 'a'},
549	{ .name = "stats",	.has_arg = no_argument,		.flag = NULL,	.val = 's'},
550	{ .name = "latency",	.has_arg = no_argument,		.flag = NULL,	.val = 'y'},
551	{ .name = "proc",	.has_arg = no_argument,		.flag = NULL,	.val = 'o'},
552	{ .name = "human",	.has_arg = no_argument,		.flag = NULL,	.val = 'm'},
553	{ .name = "no-rounding", .has_arg = no_argument,	.flag = NULL,	.val = 'n'},
554	{ },
555};
556
557int cmd_freq_info(int argc, char **argv)
558{
559	extern char *optarg;
560	extern int optind, opterr, optopt;
561	int ret = 0, cont = 1;
562	unsigned int cpu = 0;
563	unsigned int human = 0;
564	int output_param = 0;
565
566	do {
567		ret = getopt_long(argc, argv, "oefwldpgrasmybn", info_opts,
568				  NULL);
569		switch (ret) {
570		case '?':
571			output_param = '?';
572			cont = 0;
573			break;
574		case -1:
575			cont = 0;
576			break;
577		case 'b':
578		case 'o':
579		case 'a':
580		case 'r':
581		case 'g':
582		case 'p':
583		case 'd':
584		case 'l':
585		case 'w':
586		case 'f':
587		case 'e':
588		case 's':
589		case 'y':
590			if (output_param) {
591				output_param = -1;
592				cont = 0;
593				break;
594			}
595			output_param = ret;
596			break;
597		case 'm':
598			if (human) {
599				output_param = -1;
600				cont = 0;
601				break;
602			}
603			human = 1;
604			break;
605		case 'n':
606			no_rounding = 1;
607			break;
608		default:
609			fprintf(stderr, "invalid or unknown argument\n");
610			return EXIT_FAILURE;
611		}
612	} while (cont);
613
614	switch (output_param) {
615	case 'o':
616		if (!bitmask_isallclear(cpus_chosen)) {
617			printf(_("The argument passed to this tool can't be "
618				 "combined with passing a --cpu argument\n"));
619			return -EINVAL;
620		}
621		break;
622	case 0:
623		output_param = 'e';
624	}
625
626	ret = 0;
627
628	/* Default is: show output of CPU 0 only */
629	if (bitmask_isallclear(cpus_chosen))
630		bitmask_setbit(cpus_chosen, 0);
631
632	switch (output_param) {
633	case -1:
634		printf(_("You can't specify more than one --cpu parameter and/or\n"
635		       "more than one output-specific argument\n"));
636		return -EINVAL;
637	case '?':
638		printf(_("invalid or unknown argument\n"));
639		return -EINVAL;
640	case 'o':
641		proc_cpufreq_output();
642		return EXIT_SUCCESS;
643	}
644
645	for (cpu = bitmask_first(cpus_chosen);
646	     cpu <= bitmask_last(cpus_chosen); cpu++) {
647
648		if (!bitmask_isbitset(cpus_chosen, cpu))
649			continue;
650		if (cpufreq_cpu_exists(cpu)) {
651			printf(_("couldn't analyze CPU %d as it doesn't seem to be present\n"), cpu);
652			continue;
653		}
654		printf(_("analyzing CPU %d:\n"), cpu);
655
656		switch (output_param) {
657		case 'b':
658			get_boost_mode(cpu);
659			break;
660		case 'e':
661			debug_output_one(cpu);
662			break;
663		case 'a':
664			ret = get_affected_cpus(cpu);
665			break;
666		case 'r':
667			ret = get_related_cpus(cpu);
668			break;
669		case 'g':
670			ret = get_available_governors(cpu);
671			break;
672		case 'p':
673			ret = get_policy(cpu);
674			break;
675		case 'd':
676			ret = get_driver(cpu);
677			break;
678		case 'l':
679			ret = get_hardware_limits(cpu);
680			break;
681		case 'w':
682			ret = get_freq_hardware(cpu, human);
683			break;
684		case 'f':
685			ret = get_freq_kernel(cpu, human);
686			break;
687		case 's':
688			ret = get_freq_stats(cpu, human);
689			break;
690		case 'y':
691			ret = get_latency(cpu, human);
692			break;
693		}
694		if (ret)
695			return ret;
696	}
697	return ret;
698}
699