1/*
2 * Copyright 2008, Intel Corporation
3 *
4 * This file is part of PowerTOP
5 *
6 * This program file is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 * for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program in a file named COPYING; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
20 *
21 * Authors:
22 * 	Arjan van de Ven <arjan@linux.intel.com>
23 */
24
25#include <unistd.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <stdint.h>
30#include <sys/types.h>
31#include <dirent.h>
32#include <ctype.h>
33
34#include "powertop.h"
35
36#ifdef __i386
37
38
39/*
40 * Perform a CPU ID operation; with various registers set
41 */
42static void cpuid(      unsigned int *eax,
43                        unsigned int *ebx,
44                        unsigned int *ecx,
45                        unsigned int *edx)
46{
47	/* call the cpuid instruction with the registers as input and output
48	 * modification by Dwokfur based on Sam Hocevar's discussion on
49	 * how to make Assemly code PIC compatible:
50	 * http://sam.zoy.org/blog/2007-04-13-shlib-with-non-pic-code-have-inline-assembly-and-pic-mix-well
51	 */
52	__asm__("pushl %%ebx	\n\t" /* save %ebx */
53		"cpuid		\n\t"
54		"movl %%ebx, %1	\n\t" /* save what cpuid just put in %ebx */
55		"popl %%ebx	\n\t" /* restore the old %ebx */
56		: "=a" (*eax),
57		  "=r" (*ebx),
58		  "=c" (*ecx),
59		  "=d" (*edx)
60		: "0" (*eax),
61		  "1" (*ebx),
62		  "2" (*ecx),
63		  "3" (*edx)
64		);
65}
66
67#endif
68
69
70void print_intel_cstates(void)
71{
72#ifdef __i386__
73
74        int bios_table[8];
75        int bioscount = 0;
76	DIR *cpudir;
77	DIR *dir;
78	struct dirent *entry;
79	FILE *file = NULL;
80	char line[4096];
81	char filename[128], *f;
82	int len, i;
83	unsigned int eax, ebx, ecx, edx;
84
85	memset(bios_table, 0, sizeof(bios_table));
86
87
88	cpudir = opendir("/sys/devices/system/cpu");
89	if (!cpudir)
90		return;
91
92	/* Loop over cpuN entries */
93	while ((entry = readdir(cpudir))) {
94		if (strlen(entry->d_name) < 3)
95			continue;
96
97		if (!isdigit(entry->d_name[3]))
98			continue;
99
100		len = sprintf(filename, "/sys/devices/system/cpu/%s/cpuidle",
101			      entry->d_name);
102
103		dir = opendir(filename);
104		if (!dir)
105			return;
106
107		/* For each C-state, there is a stateX directory which
108		 * contains a 'usage' and a 'time' (duration) file */
109		while ((entry = readdir(dir))) {
110			if (strlen(entry->d_name) < 3)
111				continue;
112			sprintf(filename + len, "/%s/desc", entry->d_name);
113			file = fopen(filename, "r");
114			if (file) {
115
116				memset(line, 0, 4096);
117				f = fgets(line, 4096, file);
118				fclose(file);
119				if (f == NULL)
120					break;
121
122				f = strstr(line, "MWAIT ");
123				if (f) {
124					f += 6;
125					bios_table[(strtoull(f, NULL, 16)>>4) + 1]++;
126					bioscount++;
127				}
128			}
129		}
130		closedir(dir);
131
132	}
133	closedir(cpudir);
134	if (!bioscount)
135		return;
136
137	eax = 5;
138	ebx = 0; ecx = 0; edx = 0;
139	cpuid(&eax, &ebx, &ecx, &edx);
140	if (!edx || ((ecx&1) == 0))
141		return;
142
143	printf(_("Your CPU supports the following C-states : "));
144	i = 0;
145	while (edx) {
146		if (edx&7)
147			printf("C%i ", i);
148		edx = edx >> 4;
149		i++;
150	}
151	printf("\n");
152	printf(_("Your BIOS reports the following C-states : "));
153	for (i = 0; i < 8; i++)
154		if (bios_table[i])
155			printf("C%i ", i);
156	printf("\n");
157#endif
158}
159