| /* |
| * 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 |
| } |