/*
* Copyright 2008, Intel Corporation
*
* This file is part of PowerTOP
*
* This program file is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program in a file named COPYING; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* Authors:
* Arjan van de Ven <arjan@linux.intel.com>
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>
#include "powertop.h"
#ifdef __i386
/*
* Perform a CPU ID operation; with various registers set
*/
static void cpuid( unsigned int *eax,
unsigned int *ebx,
unsigned int *ecx,
unsigned int *edx)
{
/* call the cpuid instruction with the registers as input and output
* modification by Dwokfur based on Sam Hocevar's discussion on
* how to make Assemly code PIC compatible:
* http://sam.zoy.org/blog/2007-04-13-shlib-with-non-pic-code-have-inline-assembly-and-pic-mix-well
*/
__asm__("pushl %%ebx \n\t" /* save %ebx */
"cpuid \n\t"
"movl %%ebx, %1 \n\t" /* save what cpuid just put in %ebx */
"popl %%ebx \n\t" /* restore the old %ebx */
: "=a" (*eax),
"=r" (*ebx),
"=c" (*ecx),
"=d" (*edx)
: "0" (*eax),
"1" (*ebx),
"2" (*ecx),
"3" (*edx)
);
}
#endif
void print_intel_cstates(void)
{
#ifdef __i386__
int bios_table[8];
int bioscount = 0;
DIR *cpudir;
DIR *dir;
struct dirent *entry;
FILE *file = NULL;
char line[4096];
char filename[128], *f;
int len, i;
unsigned int eax, ebx, ecx, edx;
memset(bios_table, 0, sizeof(bios_table));
cpudir = opendir("/sys/devices/system/cpu");
if (!cpudir)
return;
/* Loop over cpuN entries */
while ((entry = readdir(cpudir))) {
if (strlen(entry->d_name) < 3)
continue;
if (!isdigit(entry->d_name[3]))
continue;
len = sprintf(filename, "/sys/devices/system/cpu/%s/cpuidle",
entry->d_name);
dir = opendir(filename);
if (!dir)
return;
/* For each C-state, there is a stateX directory which
* contains a 'usage' and a 'time' (duration) file */
while ((entry = readdir(dir))) {
if (strlen(entry->d_name) < 3)
continue;
sprintf(filename + len, "/%s/desc", entry->d_name);
file = fopen(filename, "r");
if (file) {
memset(line, 0, 4096);
f = fgets(line, 4096, file);
fclose(file);
if (f == NULL)
break;
f = strstr(line, "MWAIT ");
if (f) {
f += 6;
bios_table[(strtoull(f, NULL, 16)>>4) + 1]++;
bioscount++;
}
}
}
closedir(dir);
}
closedir(cpudir);
if (!bioscount)
return;
eax = 5;
ebx = 0; ecx = 0; edx = 0;
cpuid(&eax, &ebx, &ecx, &edx);
if (!edx || ((ecx&1) == 0))
return;
printf(_("Your CPU supports the following C-states : "));
i = 0;
while (edx) {
if (edx&7)
printf("C%i ", i);
edx = edx >> 4;
i++;
}
printf("\n");
printf(_("Your BIOS reports the following C-states : "));
for (i = 0; i < 8; i++)
if (bios_table[i])
printf("C%i ", i);
printf("\n");
#endif
}