Skip to content

Commit 7da7c15

Browse files
Bin GaoH. Peter Anvin
authored andcommitted
x86, tsc: Add static (MSR) TSC calibration on Intel Atom SoCs
On SoCs that have the calibration MSRs available, either there is no PIT, HPET or PMTIMER to calibrate against, or the PIT/HPET/PMTIMER is driven from the same clock as the TSC, so calibration is redundant and just slows down the boot. TSC rate is caculated by this formula: <maximum core-clock to bus-clock ratio> * <maximum resolved frequency> The ratio and the resolved frequency ID can be obtained from MSR. See Intel 64 and IA-32 System Programming Guid section 16.12 and 30.11.5 for details. Signed-off-by: Bin Gao <bin.gao@intel.com> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com> Link: http://lkml.kernel.org/n/tip-rgm7xmg7k6qnjlw3ynkcjsmh@git.kernel.org
1 parent 4618441 commit 7da7c15

File tree

4 files changed

+139
-1
lines changed

4 files changed

+139
-1
lines changed

arch/x86/include/asm/tsc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,7 @@ extern int notsc_setup(char *);
6565
extern void tsc_save_sched_clock_state(void);
6666
extern void tsc_restore_sched_clock_state(void);
6767

68+
/* MSR based TSC calibration for Intel Atom SoC platforms */
69+
int try_msr_calibrate_tsc(unsigned long *fast_calibrate);
70+
6871
#endif /* _ASM_X86_TSC_H */

arch/x86/kernel/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ obj-$(CONFIG_X86_64) += vsyscall_emu_64.o
3232
obj-y += bootflag.o e820.o
3333
obj-y += pci-dma.o quirks.o topology.o kdebugfs.o
3434
obj-y += alternative.o i8253.o pci-nommu.o hw_breakpoint.o
35-
obj-y += tsc.o io_delay.o rtc.o
35+
obj-y += tsc.o tsc_msr.o io_delay.o rtc.o
3636
obj-y += pci-iommu_table.o
3737
obj-y += resource.o
3838

arch/x86/kernel/tsc.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,16 @@ unsigned long native_calibrate_tsc(void)
419419
unsigned long flags, latch, ms, fast_calibrate;
420420
int hpet = is_hpet_enabled(), i, loopmin;
421421

422+
/* Calibrate TSC using MSR for Intel Atom SoCs */
423+
local_irq_save(flags);
424+
i = try_msr_calibrate_tsc(&fast_calibrate);
425+
local_irq_restore(flags);
426+
if (i >= 0) {
427+
if (i == 0)
428+
pr_warn("Fast TSC calibration using MSR failed\n");
429+
return fast_calibrate;
430+
}
431+
422432
local_irq_save(flags);
423433
fast_calibrate = quick_pit_calibrate();
424434
local_irq_restore(flags);

arch/x86/kernel/tsc_msr.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* tsc_msr.c - MSR based TSC calibration on Intel Atom SoC platforms.
3+
*
4+
* TSC in Intel Atom SoC runs at a constant rate which can be figured
5+
* by this formula:
6+
* <maximum core-clock to bus-clock ratio> * <maximum resolved frequency>
7+
* See Intel 64 and IA-32 System Programming Guid section 16.12 and 30.11.5
8+
* for details.
9+
* Especially some Intel Atom SoCs don't have PIT(i8254) or HPET, so MSR
10+
* based calibration is the only option.
11+
*
12+
*
13+
* Copyright (C) 2013 Intel Corporation
14+
* Author: Bin Gao <bin.gao@intel.com>
15+
*
16+
* This file is released under the GPLv2.
17+
*/
18+
19+
#include <linux/kernel.h>
20+
#include <asm/processor.h>
21+
#include <asm/setup.h>
22+
#include <asm/apic.h>
23+
#include <asm/param.h>
24+
25+
/* CPU reference clock frequency: in KHz */
26+
#define FREQ_83 83200
27+
#define FREQ_100 99840
28+
#define FREQ_133 133200
29+
#define FREQ_166 166400
30+
31+
#define MAX_NUM_FREQS 8
32+
33+
/*
34+
* According to Intel 64 and IA-32 System Programming Guide,
35+
* if MSR_PERF_STAT[31] is set, the maximum resolved bus ratio can be
36+
* read in MSR_PLATFORM_ID[12:8], otherwise in MSR_PERF_STAT[44:40].
37+
* Unfortunately some Intel Atom SoCs aren't quite compliant to this,
38+
* so we need manually differentiate SoC families. This is what the
39+
* field msr_plat does.
40+
*/
41+
struct freq_desc {
42+
u8 x86_family; /* CPU family */
43+
u8 x86_model; /* model */
44+
u8 msr_plat; /* 1: use MSR_PLATFORM_INFO, 0: MSR_IA32_PERF_STATUS */
45+
u32 freqs[MAX_NUM_FREQS];
46+
};
47+
48+
static struct freq_desc freq_desc_tables[] = {
49+
/* PNW */
50+
{ 6, 0x27, 0, { 0, 0, 0, 0, 0, FREQ_100, 0, FREQ_83 } },
51+
/* CLV+ */
52+
{ 6, 0x35, 0, { 0, FREQ_133, 0, 0, 0, FREQ_100, 0, FREQ_83 } },
53+
/* TNG */
54+
{ 6, 0x4a, 1, { 0, FREQ_100, FREQ_133, 0, 0, 0, 0, 0 } },
55+
/* VLV2 */
56+
{ 6, 0x37, 1, { 0, FREQ_100, FREQ_133, FREQ_166, 0, 0, 0, 0 } },
57+
/* ANN */
58+
{ 6, 0x5a, 1, { FREQ_83, FREQ_100, FREQ_133, FREQ_100, 0, 0, 0, 0 } },
59+
};
60+
61+
static int match_cpu(u8 family, u8 model)
62+
{
63+
int i;
64+
65+
for (i = 0; i < ARRAY_SIZE(freq_desc_tables); i++) {
66+
if ((family == freq_desc_tables[i].x86_family) &&
67+
(model == freq_desc_tables[i].x86_model))
68+
return i;
69+
}
70+
71+
return -1;
72+
}
73+
74+
/* Map CPU reference clock freq ID(0-7) to CPU reference clock freq(KHz) */
75+
#define id_to_freq(cpu_index, freq_id) \
76+
(freq_desc_tables[cpu_index].freqs[freq_id])
77+
78+
/*
79+
* Do MSR calibration only for known/supported CPUs.
80+
* Return values:
81+
* -1: CPU is unknown/unsupported for MSR based calibration
82+
* 0: CPU is known/supported, but calibration failed
83+
* 1: CPU is known/supported, and calibration succeeded
84+
*/
85+
int try_msr_calibrate_tsc(unsigned long *fast_calibrate)
86+
{
87+
int cpu_index;
88+
u32 lo, hi, ratio, freq_id, freq;
89+
90+
cpu_index = match_cpu(boot_cpu_data.x86, boot_cpu_data.x86_model);
91+
if (cpu_index < 0)
92+
return -1;
93+
94+
*fast_calibrate = 0;
95+
96+
if (freq_desc_tables[cpu_index].msr_plat) {
97+
rdmsr(MSR_PLATFORM_INFO, lo, hi);
98+
ratio = (lo >> 8) & 0x1f;
99+
} else {
100+
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
101+
ratio = (hi >> 8) & 0x1f;
102+
}
103+
pr_info("Maximum core-clock to bus-clock ratio: 0x%x\n", ratio);
104+
105+
if (!ratio)
106+
return 0;
107+
108+
/* Get FSB FREQ ID */
109+
rdmsr(MSR_FSB_FREQ, lo, hi);
110+
freq_id = lo & 0x7;
111+
freq = id_to_freq(cpu_index, freq_id);
112+
pr_info("Resolved frequency ID: %u, frequency: %u KHz\n",
113+
freq_id, freq);
114+
if (!freq)
115+
return 0;
116+
117+
/* TSC frequency = maximum resolved freq * maximum resolved bus ratio */
118+
*fast_calibrate = freq * ratio;
119+
pr_info("TSC runs at %lu KHz\n", *fast_calibrate);
120+
121+
lapic_timer_frequency = (freq * 1000) / HZ;
122+
pr_info("lapic_timer_frequency = %d\n", lapic_timer_frequency);
123+
124+
return 1;
125+
}

0 commit comments

Comments
 (0)