cpupower: Do detect IDA (opportunistic processor performance) via cpuid
[pandora-kernel.git] / tools / power / cpupower / utils / helpers / cpuid.c
1 #include <stdio.h>
2 #include <errno.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6
7 #include "helpers/helpers.h"
8
9 static const char *cpu_vendor_table[X86_VENDOR_MAX] = {
10         "Unknown", "GenuineIntel", "AuthenticAMD",
11 };
12
13 #if defined(__i386__) || defined(__x86_64__)
14
15 /* from gcc */
16 #include <cpuid.h>
17
18 /*
19  * CPUID functions returning a single datum
20  *
21  * Define unsigned int cpuid_e[abcd]x(unsigned int op)
22  */
23 #define cpuid_func(reg)                                 \
24         unsigned int cpuid_##reg(unsigned int op)       \
25         {                                               \
26         unsigned int eax, ebx, ecx, edx;                \
27         __cpuid(op, eax, ebx, ecx, edx);                \
28         return reg;                                     \
29         }
30 cpuid_func(eax);
31 cpuid_func(ebx);
32 cpuid_func(ecx);
33 cpuid_func(edx);
34
35 #endif /* defined(__i386__) || defined(__x86_64__) */
36
37 /* get_cpu_info
38  *
39  * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo
40  *
41  * Returns 0 on success or a negativ error code
42  *
43  * TBD: Should there be a cpuid alternative for this if /proc is not mounted?
44  */
45 int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info)
46 {
47         FILE *fp;
48         char value[64];
49         unsigned int proc, x;
50         unsigned int unknown = 0xffffff;
51         unsigned int cpuid_level, ext_cpuid_level;
52
53         int ret = -EINVAL;
54
55         cpu_info->vendor                = X86_VENDOR_UNKNOWN;
56         cpu_info->family                = unknown;
57         cpu_info->model                 = unknown;
58         cpu_info->stepping              = unknown;
59         cpu_info->caps                  = 0;
60
61         fp = fopen("/proc/cpuinfo", "r");
62         if (!fp)
63                 return -EIO;
64
65         while (!feof(fp)) {
66                 if (!fgets(value, 64, fp))
67                         continue;
68                 value[63 - 1] = '\0';
69
70                 if (!strncmp(value, "processor\t: ", 12))
71                         sscanf(value, "processor\t: %u", &proc);
72
73                 if (proc != cpu)
74                         continue;
75
76                 /* Get CPU vendor */
77                 if (!strncmp(value, "vendor_id", 9)) {
78                         for (x = 1; x < X86_VENDOR_MAX; x++) {
79                                 if (strstr(value, cpu_vendor_table[x]))
80                                         cpu_info->vendor = x;
81                         }
82                 /* Get CPU family, etc. */
83                 } else if (!strncmp(value, "cpu family\t: ", 13)) {
84                         sscanf(value, "cpu family\t: %u",
85                                &cpu_info->family);
86                 } else if (!strncmp(value, "model\t\t: ", 9)) {
87                         sscanf(value, "model\t\t: %u",
88                                &cpu_info->model);
89                 } else if (!strncmp(value, "stepping\t: ", 10)) {
90                         sscanf(value, "stepping\t: %u",
91                                &cpu_info->stepping);
92
93                         /* Exit -> all values must have been set */
94                         if (cpu_info->vendor == X86_VENDOR_UNKNOWN ||
95                             cpu_info->family == unknown ||
96                             cpu_info->model == unknown ||
97                             cpu_info->stepping == unknown) {
98                                 ret = -EINVAL;
99                                 goto out;
100                         }
101
102                         ret = 0;
103                         goto out;
104                 }
105         }
106         ret = -ENODEV;
107 out:
108         fclose(fp);
109         /* Get some useful CPU capabilities from cpuid */
110         if (cpu_info->vendor != X86_VENDOR_AMD &&
111             cpu_info->vendor != X86_VENDOR_INTEL)
112                 return ret;
113
114         cpuid_level     = cpuid_eax(0);
115         ext_cpuid_level = cpuid_eax(0x80000000);
116
117         /* Invariant TSC */
118         if (ext_cpuid_level >= 0x80000007 &&
119             (cpuid_edx(0x80000007) & (1 << 8)))
120                 cpu_info->caps |= CPUPOWER_CAP_INV_TSC;
121
122         /* Aperf/Mperf registers support */
123         if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1))
124                 cpu_info->caps |= CPUPOWER_CAP_APERF;
125
126         /* AMD Boost state enable/disable register */
127         if (cpu_info->vendor == X86_VENDOR_AMD) {
128                 if (ext_cpuid_level >= 0x80000007 &&
129                     (cpuid_edx(0x80000007) & (1 << 9)))
130                         cpu_info->caps |= CPUPOWER_CAP_AMD_CBP;
131         }
132
133         if (cpu_info->vendor == X86_VENDOR_INTEL) {
134                 if (cpuid_level >= 6 &&
135                     (cpuid_eax(6) & (1 << 1)))
136                         cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA;
137         }
138
139         if (cpu_info->vendor == X86_VENDOR_INTEL) {
140                 /* Intel's perf-bias MSR support */
141                 if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3)))
142                         cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS;
143
144                 /* Intel's Turbo Ratio Limit support */
145                 if (cpu_info->family == 6) {
146                         switch (cpu_info->model) {
147                         case 0x1A:      /* Core i7, Xeon 5500 series
148                                          * Bloomfield, Gainstown NHM-EP
149                                          */
150                         case 0x1E:      /* Core i7 and i5 Processor
151                                          * Clarksfield, Lynnfield, Jasper Forest
152                                          */
153                         case 0x1F:      /* Core i7 and i5 Processor - Nehalem */
154                         case 0x25:      /* Westmere Client
155                                          * Clarkdale, Arrandale
156                                          */
157                         case 0x2C:      /* Westmere EP - Gulftown */
158                                 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
159                         case 0x2A:      /* SNB */
160                         case 0x2D:      /* SNB Xeon */
161                                 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
162                                 cpu_info->caps |= CPUPOWER_CAP_IS_SNB;
163                                 break;
164                         case 0x2E:      /* Nehalem-EX Xeon - Beckton */
165                         case 0x2F:      /* Westmere-EX Xeon - Eagleton */
166                         default:
167                                 break;
168                         }
169                 }
170         }
171
172         /*      printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n",
173                 cpuid_level, ext_cpuid_level, cpu_info->caps);
174         */
175         return ret;
176 }