 8d219e3658
			
		
	
	
	8d219e3658
	
	
	
		
			
			Signed-off-by: Thomas Renninger <trenn@suse.de> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
		
			
				
	
	
		
			178 lines
		
	
	
	
		
			4.4 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
	
		
			4.4 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <stdio.h>
 | |
| #include <errno.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| #include <stdlib.h>
 | |
| 
 | |
| #include "helpers/helpers.h"
 | |
| 
 | |
| static const char *cpu_vendor_table[X86_VENDOR_MAX] = {
 | |
| 	"Unknown", "GenuineIntel", "AuthenticAMD",
 | |
| };
 | |
| 
 | |
| #if defined(__i386__) || defined(__x86_64__)
 | |
| 
 | |
| /* from gcc */
 | |
| #include <cpuid.h>
 | |
| 
 | |
| /*
 | |
|  * CPUID functions returning a single datum
 | |
|  *
 | |
|  * Define unsigned int cpuid_e[abcd]x(unsigned int op)
 | |
|  */
 | |
| #define cpuid_func(reg)					\
 | |
| 	unsigned int cpuid_##reg(unsigned int op)	\
 | |
| 	{						\
 | |
| 	unsigned int eax, ebx, ecx, edx;		\
 | |
| 	__cpuid(op, eax, ebx, ecx, edx);		\
 | |
| 	return reg;					\
 | |
| 	}
 | |
| cpuid_func(eax);
 | |
| cpuid_func(ebx);
 | |
| cpuid_func(ecx);
 | |
| cpuid_func(edx);
 | |
| 
 | |
| #endif /* defined(__i386__) || defined(__x86_64__) */
 | |
| 
 | |
| /* get_cpu_info
 | |
|  *
 | |
|  * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo
 | |
|  *
 | |
|  * Returns 0 on success or a negativ error code
 | |
|  *
 | |
|  * TBD: Should there be a cpuid alternative for this if /proc is not mounted?
 | |
|  */
 | |
| int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info)
 | |
| {
 | |
| 	FILE *fp;
 | |
| 	char value[64];
 | |
| 	unsigned int proc, x;
 | |
| 	unsigned int unknown = 0xffffff;
 | |
| 	unsigned int cpuid_level, ext_cpuid_level;
 | |
| 
 | |
| 	int ret = -EINVAL;
 | |
| 
 | |
| 	cpu_info->vendor		= X86_VENDOR_UNKNOWN;
 | |
| 	cpu_info->family		= unknown;
 | |
| 	cpu_info->model			= unknown;
 | |
| 	cpu_info->stepping		= unknown;
 | |
| 	cpu_info->caps			= 0;
 | |
| 
 | |
| 	fp = fopen("/proc/cpuinfo", "r");
 | |
| 	if (!fp)
 | |
| 		return -EIO;
 | |
| 
 | |
| 	while (!feof(fp)) {
 | |
| 		if (!fgets(value, 64, fp))
 | |
| 			continue;
 | |
| 		value[63 - 1] = '\0';
 | |
| 
 | |
| 		if (!strncmp(value, "processor\t: ", 12))
 | |
| 			sscanf(value, "processor\t: %u", &proc);
 | |
| 
 | |
| 		if (proc != cpu)
 | |
| 			continue;
 | |
| 
 | |
| 		/* Get CPU vendor */
 | |
| 		if (!strncmp(value, "vendor_id", 9)) {
 | |
| 			for (x = 1; x < X86_VENDOR_MAX; x++) {
 | |
| 				if (strstr(value, cpu_vendor_table[x]))
 | |
| 					cpu_info->vendor = x;
 | |
| 			}
 | |
| 		/* Get CPU family, etc. */
 | |
| 		} else if (!strncmp(value, "cpu family\t: ", 13)) {
 | |
| 			sscanf(value, "cpu family\t: %u",
 | |
| 			       &cpu_info->family);
 | |
| 		} else if (!strncmp(value, "model\t\t: ", 9)) {
 | |
| 			sscanf(value, "model\t\t: %u",
 | |
| 			       &cpu_info->model);
 | |
| 		} else if (!strncmp(value, "stepping\t: ", 10)) {
 | |
| 			sscanf(value, "stepping\t: %u",
 | |
| 			       &cpu_info->stepping);
 | |
| 
 | |
| 			/* Exit -> all values must have been set */
 | |
| 			if (cpu_info->vendor == X86_VENDOR_UNKNOWN ||
 | |
| 			    cpu_info->family == unknown ||
 | |
| 			    cpu_info->model == unknown ||
 | |
| 			    cpu_info->stepping == unknown) {
 | |
| 				ret = -EINVAL;
 | |
| 				goto out;
 | |
| 			}
 | |
| 
 | |
| 			ret = 0;
 | |
| 			goto out;
 | |
| 		}
 | |
| 	}
 | |
| 	ret = -ENODEV;
 | |
| out:
 | |
| 	fclose(fp);
 | |
| 	/* Get some useful CPU capabilities from cpuid */
 | |
| 	if (cpu_info->vendor != X86_VENDOR_AMD &&
 | |
| 	    cpu_info->vendor != X86_VENDOR_INTEL)
 | |
| 		return ret;
 | |
| 
 | |
| 	cpuid_level	= cpuid_eax(0);
 | |
| 	ext_cpuid_level	= cpuid_eax(0x80000000);
 | |
| 
 | |
| 	/* Invariant TSC */
 | |
| 	if (ext_cpuid_level >= 0x80000007 &&
 | |
| 	    (cpuid_edx(0x80000007) & (1 << 8)))
 | |
| 		cpu_info->caps |= CPUPOWER_CAP_INV_TSC;
 | |
| 
 | |
| 	/* Aperf/Mperf registers support */
 | |
| 	if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1))
 | |
| 		cpu_info->caps |= CPUPOWER_CAP_APERF;
 | |
| 
 | |
| 	/* AMD Boost state enable/disable register */
 | |
| 	if (cpu_info->vendor == X86_VENDOR_AMD) {
 | |
| 		if (ext_cpuid_level >= 0x80000007 &&
 | |
| 		    (cpuid_edx(0x80000007) & (1 << 9)))
 | |
| 			cpu_info->caps |= CPUPOWER_CAP_AMD_CBP;
 | |
| 	}
 | |
| 
 | |
| 	if (cpu_info->vendor == X86_VENDOR_INTEL) {
 | |
| 		if (cpuid_level >= 6 &&
 | |
| 		    (cpuid_eax(6) & (1 << 1)))
 | |
| 			cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA;
 | |
| 	}
 | |
| 
 | |
| 	if (cpu_info->vendor == X86_VENDOR_INTEL) {
 | |
| 		/* Intel's perf-bias MSR support */
 | |
| 		if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3)))
 | |
| 			cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS;
 | |
| 
 | |
| 		/* Intel's Turbo Ratio Limit support */
 | |
| 		if (cpu_info->family == 6) {
 | |
| 			switch (cpu_info->model) {
 | |
| 			case 0x1A:	/* Core i7, Xeon 5500 series
 | |
| 					 * Bloomfield, Gainstown NHM-EP
 | |
| 					 */
 | |
| 			case 0x1E:	/* Core i7 and i5 Processor
 | |
| 					 * Clarksfield, Lynnfield, Jasper Forest
 | |
| 					 */
 | |
| 			case 0x1F:	/* Core i7 and i5 Processor - Nehalem */
 | |
| 			case 0x25:	/* Westmere Client
 | |
| 					 * Clarkdale, Arrandale
 | |
| 					 */
 | |
| 			case 0x2C:	/* Westmere EP - Gulftown */
 | |
| 				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
 | |
| 			case 0x2A:	/* SNB */
 | |
| 			case 0x2D:	/* SNB Xeon */
 | |
| 			case 0x3A:	/* IVB */
 | |
| 			case 0x3E:	/* IVB Xeon */
 | |
| 				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
 | |
| 				cpu_info->caps |= CPUPOWER_CAP_IS_SNB;
 | |
| 				break;
 | |
| 			case 0x2E:	/* Nehalem-EX Xeon - Beckton */
 | |
| 			case 0x2F:	/* Westmere-EX Xeon - Eagleton */
 | |
| 			default:
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*	printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n",
 | |
| 		cpuid_level, ext_cpuid_level, cpu_info->caps);
 | |
| 	*/
 | |
| 	return ret;
 | |
| }
 |