1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
27fe2f639SDominik Brodowski #include <stdio.h>
37fe2f639SDominik Brodowski #include <errno.h>
47fe2f639SDominik Brodowski #include <string.h>
57fe2f639SDominik Brodowski #include <unistd.h>
67fe2f639SDominik Brodowski #include <stdlib.h>
77fe2f639SDominik Brodowski
87fe2f639SDominik Brodowski #include "helpers/helpers.h"
97fe2f639SDominik Brodowski
107fe2f639SDominik Brodowski static const char *cpu_vendor_table[X86_VENDOR_MAX] = {
11995d5f64SPu Wen "Unknown", "GenuineIntel", "AuthenticAMD", "HygonGenuine",
127fe2f639SDominik Brodowski };
137fe2f639SDominik Brodowski
147fe2f639SDominik Brodowski #if defined(__i386__) || defined(__x86_64__)
157fe2f639SDominik Brodowski
167fe2f639SDominik Brodowski /* from gcc */
177fe2f639SDominik Brodowski #include <cpuid.h>
187fe2f639SDominik Brodowski
197fe2f639SDominik Brodowski /*
207fe2f639SDominik Brodowski * CPUID functions returning a single datum
217fe2f639SDominik Brodowski *
227fe2f639SDominik Brodowski * Define unsigned int cpuid_e[abcd]x(unsigned int op)
237fe2f639SDominik Brodowski */
247fe2f639SDominik Brodowski #define cpuid_func(reg) \
257fe2f639SDominik Brodowski unsigned int cpuid_##reg(unsigned int op) \
267fe2f639SDominik Brodowski { \
277fe2f639SDominik Brodowski unsigned int eax, ebx, ecx, edx; \
287fe2f639SDominik Brodowski __cpuid(op, eax, ebx, ecx, edx); \
297fe2f639SDominik Brodowski return reg; \
307fe2f639SDominik Brodowski }
317fe2f639SDominik Brodowski cpuid_func(eax);
327fe2f639SDominik Brodowski cpuid_func(ebx);
337fe2f639SDominik Brodowski cpuid_func(ecx);
347fe2f639SDominik Brodowski cpuid_func(edx);
357fe2f639SDominik Brodowski
367fe2f639SDominik Brodowski #endif /* defined(__i386__) || defined(__x86_64__) */
377fe2f639SDominik Brodowski
387fe2f639SDominik Brodowski /* get_cpu_info
397fe2f639SDominik Brodowski *
407fe2f639SDominik Brodowski * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo
417fe2f639SDominik Brodowski *
427fe2f639SDominik Brodowski * Returns 0 on success or a negativ error code
437fe2f639SDominik Brodowski *
447fe2f639SDominik Brodowski * TBD: Should there be a cpuid alternative for this if /proc is not mounted?
457fe2f639SDominik Brodowski */
get_cpu_info(struct cpupower_cpu_info * cpu_info)46d0e4a193SPrarit Bhargava int get_cpu_info(struct cpupower_cpu_info *cpu_info)
477fe2f639SDominik Brodowski {
487fe2f639SDominik Brodowski FILE *fp;
497fe2f639SDominik Brodowski char value[64];
507fe2f639SDominik Brodowski unsigned int proc, x;
517fe2f639SDominik Brodowski unsigned int unknown = 0xffffff;
527fe2f639SDominik Brodowski unsigned int cpuid_level, ext_cpuid_level;
537fe2f639SDominik Brodowski
547fe2f639SDominik Brodowski int ret = -EINVAL;
557fe2f639SDominik Brodowski
567fe2f639SDominik Brodowski cpu_info->vendor = X86_VENDOR_UNKNOWN;
577fe2f639SDominik Brodowski cpu_info->family = unknown;
587fe2f639SDominik Brodowski cpu_info->model = unknown;
597fe2f639SDominik Brodowski cpu_info->stepping = unknown;
607fe2f639SDominik Brodowski cpu_info->caps = 0;
617fe2f639SDominik Brodowski
627fe2f639SDominik Brodowski fp = fopen("/proc/cpuinfo", "r");
637fe2f639SDominik Brodowski if (!fp)
647fe2f639SDominik Brodowski return -EIO;
657fe2f639SDominik Brodowski
667fe2f639SDominik Brodowski while (!feof(fp)) {
677fe2f639SDominik Brodowski if (!fgets(value, 64, fp))
687fe2f639SDominik Brodowski continue;
697fe2f639SDominik Brodowski value[63 - 1] = '\0';
707fe2f639SDominik Brodowski
712cd005caSDominik Brodowski if (!strncmp(value, "processor\t: ", 12))
727fe2f639SDominik Brodowski sscanf(value, "processor\t: %u", &proc);
732cd005caSDominik Brodowski
74d0e4a193SPrarit Bhargava if (proc != (unsigned int)base_cpu)
757fe2f639SDominik Brodowski continue;
767fe2f639SDominik Brodowski
777fe2f639SDominik Brodowski /* Get CPU vendor */
782cd005caSDominik Brodowski if (!strncmp(value, "vendor_id", 9)) {
797fe2f639SDominik Brodowski for (x = 1; x < X86_VENDOR_MAX; x++) {
807fe2f639SDominik Brodowski if (strstr(value, cpu_vendor_table[x]))
817fe2f639SDominik Brodowski cpu_info->vendor = x;
827fe2f639SDominik Brodowski }
837fe2f639SDominik Brodowski /* Get CPU family, etc. */
842cd005caSDominik Brodowski } else if (!strncmp(value, "cpu family\t: ", 13)) {
857fe2f639SDominik Brodowski sscanf(value, "cpu family\t: %u",
867fe2f639SDominik Brodowski &cpu_info->family);
872cd005caSDominik Brodowski } else if (!strncmp(value, "model\t\t: ", 9)) {
887fe2f639SDominik Brodowski sscanf(value, "model\t\t: %u",
897fe2f639SDominik Brodowski &cpu_info->model);
902cd005caSDominik Brodowski } else if (!strncmp(value, "stepping\t: ", 10)) {
917fe2f639SDominik Brodowski sscanf(value, "stepping\t: %u",
927fe2f639SDominik Brodowski &cpu_info->stepping);
937fe2f639SDominik Brodowski
947fe2f639SDominik Brodowski /* Exit -> all values must have been set */
957fe2f639SDominik Brodowski if (cpu_info->vendor == X86_VENDOR_UNKNOWN ||
967fe2f639SDominik Brodowski cpu_info->family == unknown ||
977fe2f639SDominik Brodowski cpu_info->model == unknown ||
987fe2f639SDominik Brodowski cpu_info->stepping == unknown) {
997fe2f639SDominik Brodowski ret = -EINVAL;
1007fe2f639SDominik Brodowski goto out;
1017fe2f639SDominik Brodowski }
1027fe2f639SDominik Brodowski
1037fe2f639SDominik Brodowski ret = 0;
1047fe2f639SDominik Brodowski goto out;
1057fe2f639SDominik Brodowski }
1067fe2f639SDominik Brodowski }
1077fe2f639SDominik Brodowski ret = -ENODEV;
1087fe2f639SDominik Brodowski out:
1097fe2f639SDominik Brodowski fclose(fp);
1107fe2f639SDominik Brodowski /* Get some useful CPU capabilities from cpuid */
1117fe2f639SDominik Brodowski if (cpu_info->vendor != X86_VENDOR_AMD &&
112995d5f64SPu Wen cpu_info->vendor != X86_VENDOR_HYGON &&
1137fe2f639SDominik Brodowski cpu_info->vendor != X86_VENDOR_INTEL)
1147fe2f639SDominik Brodowski return ret;
1157fe2f639SDominik Brodowski
1167fe2f639SDominik Brodowski cpuid_level = cpuid_eax(0);
1177fe2f639SDominik Brodowski ext_cpuid_level = cpuid_eax(0x80000000);
1187fe2f639SDominik Brodowski
1197fe2f639SDominik Brodowski /* Invariant TSC */
1207fe2f639SDominik Brodowski if (ext_cpuid_level >= 0x80000007 &&
1217fe2f639SDominik Brodowski (cpuid_edx(0x80000007) & (1 << 8)))
1227fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_INV_TSC;
1237fe2f639SDominik Brodowski
1247fe2f639SDominik Brodowski /* Aperf/Mperf registers support */
1257fe2f639SDominik Brodowski if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1))
1267fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_APERF;
1277fe2f639SDominik Brodowski
128995d5f64SPu Wen /* AMD or Hygon Boost state enable/disable register */
129995d5f64SPu Wen if (cpu_info->vendor == X86_VENDOR_AMD ||
130995d5f64SPu Wen cpu_info->vendor == X86_VENDOR_HYGON) {
131a0255a76SNathan Fontenot if (ext_cpuid_level >= 0x80000007) {
1323a3ecfdbSNathan Fontenot if (cpuid_edx(0x80000007) & (1 << 9)) {
1337a136a8fSRobert Richter cpu_info->caps |= CPUPOWER_CAP_AMD_CPB;
1346af2ed53SJanakarajan Natarajan
1353a3ecfdbSNathan Fontenot if (cpu_info->family >= 0x17)
1363a3ecfdbSNathan Fontenot cpu_info->caps |= CPUPOWER_CAP_AMD_CPB_MSR;
1373a3ecfdbSNathan Fontenot }
1383a3ecfdbSNathan Fontenot
139a0255a76SNathan Fontenot if ((cpuid_edx(0x80000007) & (1 << 7)) &&
14023765b82SNathan Fontenot cpu_info->family != 0x14) {
141a0255a76SNathan Fontenot /* HW pstate was not implemented in family 0x14 */
142a0255a76SNathan Fontenot cpu_info->caps |= CPUPOWER_CAP_AMD_HW_PSTATE;
14323765b82SNathan Fontenot
14423765b82SNathan Fontenot if (cpu_info->family >= 0x17)
14523765b82SNathan Fontenot cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATEDEF;
14623765b82SNathan Fontenot }
147a0255a76SNathan Fontenot }
148a0255a76SNathan Fontenot
1496af2ed53SJanakarajan Natarajan if (ext_cpuid_level >= 0x80000008 &&
1506af2ed53SJanakarajan Natarajan cpuid_ebx(0x80000008) & (1 << 4))
1516af2ed53SJanakarajan Natarajan cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU;
152*083792f3SHuang Rui
153*083792f3SHuang Rui if (cpupower_amd_pstate_enabled()) {
154*083792f3SHuang Rui cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATE;
155*083792f3SHuang Rui
156*083792f3SHuang Rui /*
157*083792f3SHuang Rui * If AMD P-State is enabled, the firmware will treat
158*083792f3SHuang Rui * AMD P-State function as high priority.
159*083792f3SHuang Rui */
160*083792f3SHuang Rui cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB;
161*083792f3SHuang Rui cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB_MSR;
162*083792f3SHuang Rui cpu_info->caps &= ~CPUPOWER_CAP_AMD_HW_PSTATE;
163*083792f3SHuang Rui cpu_info->caps &= ~CPUPOWER_CAP_AMD_PSTATEDEF;
164*083792f3SHuang Rui }
1657fe2f639SDominik Brodowski }
1667fe2f639SDominik Brodowski
1677fe2f639SDominik Brodowski if (cpu_info->vendor == X86_VENDOR_INTEL) {
168029e9f73SThomas Renninger if (cpuid_level >= 6 &&
169029e9f73SThomas Renninger (cpuid_eax(6) & (1 << 1)))
170029e9f73SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA;
171029e9f73SThomas Renninger }
172029e9f73SThomas Renninger
173029e9f73SThomas Renninger if (cpu_info->vendor == X86_VENDOR_INTEL) {
1748fb2e440SThomas Renninger /* Intel's perf-bias MSR support */
1757fe2f639SDominik Brodowski if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3)))
1767fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS;
1778fb2e440SThomas Renninger
1788fb2e440SThomas Renninger /* Intel's Turbo Ratio Limit support */
1798fb2e440SThomas Renninger if (cpu_info->family == 6) {
1808fb2e440SThomas Renninger switch (cpu_info->model) {
1818fb2e440SThomas Renninger case 0x1A: /* Core i7, Xeon 5500 series
1828fb2e440SThomas Renninger * Bloomfield, Gainstown NHM-EP
1838fb2e440SThomas Renninger */
1848fb2e440SThomas Renninger case 0x1E: /* Core i7 and i5 Processor
1858fb2e440SThomas Renninger * Clarksfield, Lynnfield, Jasper Forest
1868fb2e440SThomas Renninger */
1878fb2e440SThomas Renninger case 0x1F: /* Core i7 and i5 Processor - Nehalem */
1888fb2e440SThomas Renninger case 0x25: /* Westmere Client
1898fb2e440SThomas Renninger * Clarkdale, Arrandale
1908fb2e440SThomas Renninger */
1918fb2e440SThomas Renninger case 0x2C: /* Westmere EP - Gulftown */
1928fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
1934cca0457SBen Hutchings break;
1948fb2e440SThomas Renninger case 0x2A: /* SNB */
1958fb2e440SThomas Renninger case 0x2D: /* SNB Xeon */
1968d219e36SThomas Renninger case 0x3A: /* IVB */
1978d219e36SThomas Renninger case 0x3E: /* IVB Xeon */
1988fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
1998fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_IS_SNB;
2008fb2e440SThomas Renninger break;
2018fb2e440SThomas Renninger case 0x2E: /* Nehalem-EX Xeon - Beckton */
2028fb2e440SThomas Renninger case 0x2F: /* Westmere-EX Xeon - Eagleton */
2038fb2e440SThomas Renninger default:
2048fb2e440SThomas Renninger break;
2058fb2e440SThomas Renninger }
2068fb2e440SThomas Renninger }
2077fe2f639SDominik Brodowski }
2087fe2f639SDominik Brodowski
2097fe2f639SDominik Brodowski /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n",
2107fe2f639SDominik Brodowski cpuid_level, ext_cpuid_level, cpu_info->caps);
2117fe2f639SDominik Brodowski */
2127fe2f639SDominik Brodowski return ret;
2137fe2f639SDominik Brodowski }
214