162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <stdio.h> 362306a36Sopenharmony_ci#include <errno.h> 462306a36Sopenharmony_ci#include <string.h> 562306a36Sopenharmony_ci#include <unistd.h> 662306a36Sopenharmony_ci#include <stdlib.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "helpers/helpers.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistatic const char *cpu_vendor_table[X86_VENDOR_MAX] = { 1162306a36Sopenharmony_ci "Unknown", "GenuineIntel", "AuthenticAMD", "HygonGenuine", 1262306a36Sopenharmony_ci}; 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__) 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* from gcc */ 1762306a36Sopenharmony_ci#include <cpuid.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * CPUID functions returning a single datum 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Define unsigned int cpuid_e[abcd]x(unsigned int op) 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#define cpuid_func(reg) \ 2562306a36Sopenharmony_ci unsigned int cpuid_##reg(unsigned int op) \ 2662306a36Sopenharmony_ci { \ 2762306a36Sopenharmony_ci unsigned int eax, ebx, ecx, edx; \ 2862306a36Sopenharmony_ci __cpuid(op, eax, ebx, ecx, edx); \ 2962306a36Sopenharmony_ci return reg; \ 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_cicpuid_func(eax); 3262306a36Sopenharmony_cicpuid_func(ebx); 3362306a36Sopenharmony_cicpuid_func(ecx); 3462306a36Sopenharmony_cicpuid_func(edx); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#endif /* defined(__i386__) || defined(__x86_64__) */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* get_cpu_info 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * Returns 0 on success or a negativ error code 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * TBD: Should there be a cpuid alternative for this if /proc is not mounted? 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ciint get_cpu_info(struct cpupower_cpu_info *cpu_info) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci FILE *fp; 4962306a36Sopenharmony_ci char value[64]; 5062306a36Sopenharmony_ci unsigned int proc, x; 5162306a36Sopenharmony_ci unsigned int unknown = 0xffffff; 5262306a36Sopenharmony_ci unsigned int cpuid_level, ext_cpuid_level; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci int ret = -EINVAL; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci cpu_info->vendor = X86_VENDOR_UNKNOWN; 5762306a36Sopenharmony_ci cpu_info->family = unknown; 5862306a36Sopenharmony_ci cpu_info->model = unknown; 5962306a36Sopenharmony_ci cpu_info->stepping = unknown; 6062306a36Sopenharmony_ci cpu_info->caps = 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci fp = fopen("/proc/cpuinfo", "r"); 6362306a36Sopenharmony_ci if (!fp) 6462306a36Sopenharmony_ci return -EIO; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci while (!feof(fp)) { 6762306a36Sopenharmony_ci if (!fgets(value, 64, fp)) 6862306a36Sopenharmony_ci continue; 6962306a36Sopenharmony_ci value[63 - 1] = '\0'; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (!strncmp(value, "processor\t: ", 12)) 7262306a36Sopenharmony_ci sscanf(value, "processor\t: %u", &proc); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (proc != (unsigned int)base_cpu) 7562306a36Sopenharmony_ci continue; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Get CPU vendor */ 7862306a36Sopenharmony_ci if (!strncmp(value, "vendor_id", 9)) { 7962306a36Sopenharmony_ci for (x = 1; x < X86_VENDOR_MAX; x++) { 8062306a36Sopenharmony_ci if (strstr(value, cpu_vendor_table[x])) 8162306a36Sopenharmony_ci cpu_info->vendor = x; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci /* Get CPU family, etc. */ 8462306a36Sopenharmony_ci } else if (!strncmp(value, "cpu family\t: ", 13)) { 8562306a36Sopenharmony_ci sscanf(value, "cpu family\t: %u", 8662306a36Sopenharmony_ci &cpu_info->family); 8762306a36Sopenharmony_ci } else if (!strncmp(value, "model\t\t: ", 9)) { 8862306a36Sopenharmony_ci sscanf(value, "model\t\t: %u", 8962306a36Sopenharmony_ci &cpu_info->model); 9062306a36Sopenharmony_ci } else if (!strncmp(value, "stepping\t: ", 10)) { 9162306a36Sopenharmony_ci sscanf(value, "stepping\t: %u", 9262306a36Sopenharmony_ci &cpu_info->stepping); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Exit -> all values must have been set */ 9562306a36Sopenharmony_ci if (cpu_info->vendor == X86_VENDOR_UNKNOWN || 9662306a36Sopenharmony_ci cpu_info->family == unknown || 9762306a36Sopenharmony_ci cpu_info->model == unknown || 9862306a36Sopenharmony_ci cpu_info->stepping == unknown) { 9962306a36Sopenharmony_ci ret = -EINVAL; 10062306a36Sopenharmony_ci goto out; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ret = 0; 10462306a36Sopenharmony_ci goto out; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci ret = -ENODEV; 10862306a36Sopenharmony_ciout: 10962306a36Sopenharmony_ci fclose(fp); 11062306a36Sopenharmony_ci /* Get some useful CPU capabilities from cpuid */ 11162306a36Sopenharmony_ci if (cpu_info->vendor != X86_VENDOR_AMD && 11262306a36Sopenharmony_ci cpu_info->vendor != X86_VENDOR_HYGON && 11362306a36Sopenharmony_ci cpu_info->vendor != X86_VENDOR_INTEL) 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci cpuid_level = cpuid_eax(0); 11762306a36Sopenharmony_ci ext_cpuid_level = cpuid_eax(0x80000000); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Invariant TSC */ 12062306a36Sopenharmony_ci if (ext_cpuid_level >= 0x80000007 && 12162306a36Sopenharmony_ci (cpuid_edx(0x80000007) & (1 << 8))) 12262306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_INV_TSC; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Aperf/Mperf registers support */ 12562306a36Sopenharmony_ci if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1)) 12662306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_APERF; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* AMD or Hygon Boost state enable/disable register */ 12962306a36Sopenharmony_ci if (cpu_info->vendor == X86_VENDOR_AMD || 13062306a36Sopenharmony_ci cpu_info->vendor == X86_VENDOR_HYGON) { 13162306a36Sopenharmony_ci if (ext_cpuid_level >= 0x80000007) { 13262306a36Sopenharmony_ci if (cpuid_edx(0x80000007) & (1 << 9)) { 13362306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_AMD_CPB; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (cpu_info->family >= 0x17) 13662306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_AMD_CPB_MSR; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if ((cpuid_edx(0x80000007) & (1 << 7)) && 14062306a36Sopenharmony_ci cpu_info->family != 0x14) { 14162306a36Sopenharmony_ci /* HW pstate was not implemented in family 0x14 */ 14262306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_AMD_HW_PSTATE; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (cpu_info->family >= 0x17) 14562306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATEDEF; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (ext_cpuid_level >= 0x80000008 && 15062306a36Sopenharmony_ci cpuid_ebx(0x80000008) & (1 << 4)) 15162306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (cpupower_amd_pstate_enabled()) { 15462306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATE; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* 15762306a36Sopenharmony_ci * If AMD P-State is enabled, the firmware will treat 15862306a36Sopenharmony_ci * AMD P-State function as high priority. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB; 16162306a36Sopenharmony_ci cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB_MSR; 16262306a36Sopenharmony_ci cpu_info->caps &= ~CPUPOWER_CAP_AMD_HW_PSTATE; 16362306a36Sopenharmony_ci cpu_info->caps &= ~CPUPOWER_CAP_AMD_PSTATEDEF; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (cpu_info->vendor == X86_VENDOR_INTEL) { 16862306a36Sopenharmony_ci if (cpuid_level >= 6 && 16962306a36Sopenharmony_ci (cpuid_eax(6) & (1 << 1))) 17062306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (cpu_info->vendor == X86_VENDOR_INTEL) { 17462306a36Sopenharmony_ci /* Intel's perf-bias MSR support */ 17562306a36Sopenharmony_ci if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) 17662306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Intel's Turbo Ratio Limit support */ 17962306a36Sopenharmony_ci if (cpu_info->family == 6) { 18062306a36Sopenharmony_ci switch (cpu_info->model) { 18162306a36Sopenharmony_ci case 0x1A: /* Core i7, Xeon 5500 series 18262306a36Sopenharmony_ci * Bloomfield, Gainstown NHM-EP 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci case 0x1E: /* Core i7 and i5 Processor 18562306a36Sopenharmony_ci * Clarksfield, Lynnfield, Jasper Forest 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci case 0x1F: /* Core i7 and i5 Processor - Nehalem */ 18862306a36Sopenharmony_ci case 0x25: /* Westmere Client 18962306a36Sopenharmony_ci * Clarkdale, Arrandale 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci case 0x2C: /* Westmere EP - Gulftown */ 19262306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci case 0x2A: /* SNB */ 19562306a36Sopenharmony_ci case 0x2D: /* SNB Xeon */ 19662306a36Sopenharmony_ci case 0x3A: /* IVB */ 19762306a36Sopenharmony_ci case 0x3E: /* IVB Xeon */ 19862306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 19962306a36Sopenharmony_ci cpu_info->caps |= CPUPOWER_CAP_IS_SNB; 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci case 0x2E: /* Nehalem-EX Xeon - Beckton */ 20262306a36Sopenharmony_ci case 0x2F: /* Westmere-EX Xeon - Eagleton */ 20362306a36Sopenharmony_ci default: 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", 21062306a36Sopenharmony_ci cpuid_level, ext_cpuid_level, cpu_info->caps); 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci return ret; 21362306a36Sopenharmony_ci} 214