| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * tsc_msr.c - MSR based TSC calibration on Intel Atom SoC platforms. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * TSC in Intel Atom SoC runs at a constant rate which can be figured | 
					
						
							|  |  |  |  * by this formula: | 
					
						
							|  |  |  |  * <maximum core-clock to bus-clock ratio> * <maximum resolved frequency> | 
					
						
							|  |  |  |  * See Intel 64 and IA-32 System Programming Guid section 16.12 and 30.11.5 | 
					
						
							|  |  |  |  * for details. | 
					
						
							|  |  |  |  * Especially some Intel Atom SoCs don't have PIT(i8254) or HPET, so MSR | 
					
						
							|  |  |  |  * based calibration is the only option. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2013 Intel Corporation | 
					
						
							|  |  |  |  * Author: Bin Gao <bin.gao@intel.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This file is released under the GPLv2. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <asm/processor.h>
 | 
					
						
							|  |  |  | #include <asm/setup.h>
 | 
					
						
							|  |  |  | #include <asm/apic.h>
 | 
					
						
							|  |  |  | #include <asm/param.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* CPU reference clock frequency: in KHz */ | 
					
						
							|  |  |  | #define FREQ_83		83200
 | 
					
						
							|  |  |  | #define FREQ_100	99840
 | 
					
						
							|  |  |  | #define FREQ_133	133200
 | 
					
						
							|  |  |  | #define FREQ_166	166400
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define MAX_NUM_FREQS	8
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * According to Intel 64 and IA-32 System Programming Guide, | 
					
						
							|  |  |  |  * if MSR_PERF_STAT[31] is set, the maximum resolved bus ratio can be | 
					
						
							|  |  |  |  * read in MSR_PLATFORM_ID[12:8], otherwise in MSR_PERF_STAT[44:40]. | 
					
						
							|  |  |  |  * Unfortunately some Intel Atom SoCs aren't quite compliant to this, | 
					
						
							|  |  |  |  * so we need manually differentiate SoC families. This is what the | 
					
						
							|  |  |  |  * field msr_plat does. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct freq_desc { | 
					
						
							|  |  |  | 	u8 x86_family;	/* CPU family */ | 
					
						
							|  |  |  | 	u8 x86_model;	/* model */ | 
					
						
							|  |  |  | 	u8 msr_plat;	/* 1: use MSR_PLATFORM_INFO, 0: MSR_IA32_PERF_STATUS */ | 
					
						
							|  |  |  | 	u32 freqs[MAX_NUM_FREQS]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct freq_desc freq_desc_tables[] = { | 
					
						
							|  |  |  | 	/* PNW */ | 
					
						
							|  |  |  | 	{ 6, 0x27, 0, { 0, 0, 0, 0, 0, FREQ_100, 0, FREQ_83 } }, | 
					
						
							|  |  |  | 	/* CLV+ */ | 
					
						
							|  |  |  | 	{ 6, 0x35, 0, { 0, FREQ_133, 0, 0, 0, FREQ_100, 0, FREQ_83 } }, | 
					
						
							|  |  |  | 	/* TNG */ | 
					
						
							|  |  |  | 	{ 6, 0x4a, 1, { 0, FREQ_100, FREQ_133, 0, 0, 0, 0, 0 } }, | 
					
						
							|  |  |  | 	/* VLV2 */ | 
					
						
							| 
									
										
										
										
											2014-02-19 13:52:30 +02:00
										 |  |  | 	{ 6, 0x37, 1, { FREQ_83, FREQ_100, FREQ_133, FREQ_166, 0, 0, 0, 0 } }, | 
					
						
							| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  | 	/* ANN */ | 
					
						
							|  |  |  | 	{ 6, 0x5a, 1, { FREQ_83, FREQ_100, FREQ_133, FREQ_100, 0, 0, 0, 0 } }, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int match_cpu(u8 family, u8 model) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ARRAY_SIZE(freq_desc_tables); i++) { | 
					
						
							|  |  |  | 		if ((family == freq_desc_tables[i].x86_family) && | 
					
						
							|  |  |  | 			(model == freq_desc_tables[i].x86_model)) | 
					
						
							|  |  |  | 			return i; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Map CPU reference clock freq ID(0-7) to CPU reference clock freq(KHz) */ | 
					
						
							|  |  |  | #define id_to_freq(cpu_index, freq_id) \
 | 
					
						
							|  |  |  | 	(freq_desc_tables[cpu_index].freqs[freq_id]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Do MSR calibration only for known/supported CPUs. | 
					
						
							| 
									
										
										
										
											2014-02-19 13:52:29 +02:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Returns the calibration value or 0 if MSR calibration failed. | 
					
						
							| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-02-19 13:52:29 +02:00
										 |  |  | unsigned long try_msr_calibrate_tsc(void) | 
					
						
							| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	u32 lo, hi, ratio, freq_id, freq; | 
					
						
							| 
									
										
										
										
											2014-02-19 13:52:29 +02:00
										 |  |  | 	unsigned long res; | 
					
						
							|  |  |  | 	int cpu_index; | 
					
						
							| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	cpu_index = match_cpu(boot_cpu_data.x86, boot_cpu_data.x86_model); | 
					
						
							|  |  |  | 	if (cpu_index < 0) | 
					
						
							| 
									
										
										
										
											2014-02-19 13:52:29 +02:00
										 |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (freq_desc_tables[cpu_index].msr_plat) { | 
					
						
							|  |  |  | 		rdmsr(MSR_PLATFORM_INFO, lo, hi); | 
					
						
							|  |  |  | 		ratio = (lo >> 8) & 0x1f; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		rdmsr(MSR_IA32_PERF_STATUS, lo, hi); | 
					
						
							|  |  |  | 		ratio = (hi >> 8) & 0x1f; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pr_info("Maximum core-clock to bus-clock ratio: 0x%x\n", ratio); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ratio) | 
					
						
							| 
									
										
										
										
											2014-02-19 13:52:29 +02:00
										 |  |  | 		goto fail; | 
					
						
							| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Get FSB FREQ ID */ | 
					
						
							|  |  |  | 	rdmsr(MSR_FSB_FREQ, lo, hi); | 
					
						
							|  |  |  | 	freq_id = lo & 0x7; | 
					
						
							|  |  |  | 	freq = id_to_freq(cpu_index, freq_id); | 
					
						
							|  |  |  | 	pr_info("Resolved frequency ID: %u, frequency: %u KHz\n", | 
					
						
							|  |  |  | 				freq_id, freq); | 
					
						
							|  |  |  | 	if (!freq) | 
					
						
							| 
									
										
										
										
											2014-02-19 13:52:29 +02:00
										 |  |  | 		goto fail; | 
					
						
							| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* TSC frequency = maximum resolved freq * maximum resolved bus ratio */ | 
					
						
							| 
									
										
										
										
											2014-02-19 13:52:29 +02:00
										 |  |  | 	res = freq * ratio; | 
					
						
							|  |  |  | 	pr_info("TSC runs at %lu KHz\n", res); | 
					
						
							| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-16 13:00:21 -08:00
										 |  |  | #ifdef CONFIG_X86_LOCAL_APIC
 | 
					
						
							| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  | 	lapic_timer_frequency = (freq * 1000) / HZ; | 
					
						
							|  |  |  | 	pr_info("lapic_timer_frequency = %d\n", lapic_timer_frequency); | 
					
						
							| 
									
										
										
										
											2014-01-16 13:00:21 -08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2014-02-19 13:52:29 +02:00
										 |  |  | 	return res; | 
					
						
							| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 13:52:29 +02:00
										 |  |  | fail: | 
					
						
							|  |  |  | 	pr_warn("Fast TSC calibration using MSR failed\n"); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2013-10-21 09:16:33 -07:00
										 |  |  | } |