1515#include <asm/param.h>
1616#include <asm/tsc.h>
1717
18- #define MAX_NUM_FREQS 9
18+ #define MAX_NUM_FREQS 16 /* 4 bits to select the frequency */
19+
20+ /*
21+ * The frequency numbers in the SDM are e.g. 83.3 MHz, which does not contain a
22+ * lot of accuracy which leads to clock drift. As far as we know Bay Trail SoCs
23+ * use a 25 MHz crystal and Cherry Trail uses a 19.2 MHz crystal, the crystal
24+ * is the source clk for a root PLL which outputs 1600 and 100 MHz. It is
25+ * unclear if the root PLL outputs are used directly by the CPU clock PLL or
26+ * if there is another PLL in between.
27+ * This does not matter though, we can model the chain of PLLs as a single PLL
28+ * with a quotient equal to the quotients of all PLLs in the chain multiplied.
29+ * So we can create a simplified model of the CPU clock setup using a reference
30+ * clock of 100 MHz plus a quotient which gets us as close to the frequency
31+ * from the SDM as possible.
32+ * For the 83.3 MHz example from above this would give us 100 MHz * 5 / 6 =
33+ * 83 and 1/3 MHz, which matches exactly what has been measured on actual hw.
34+ */
35+ #define TSC_REFERENCE_KHZ 100000
36+
37+ struct muldiv {
38+ u32 multiplier ;
39+ u32 divider ;
40+ };
1941
2042/*
2143 * If MSR_PERF_STAT[31] is set, the maximum resolved bus ratio can be
2244 * read in MSR_PLATFORM_ID[12:8], otherwise in MSR_PERF_STAT[44:40].
2345 * Unfortunately some Intel Atom SoCs aren't quite compliant to this,
2446 * so we need manually differentiate SoC families. This is what the
25- * field msr_plat does.
47+ * field use_msr_plat does.
2648 */
2749struct freq_desc {
28- u8 msr_plat ; /* 1: use MSR_PLATFORM_INFO, 0: MSR_IA32_PERF_STATUS */
50+ bool use_msr_plat ;
51+ struct muldiv muldiv [MAX_NUM_FREQS ];
52+ /*
53+ * Some CPU frequencies in the SDM do not map to known PLL freqs, in
54+ * that case the muldiv array is empty and the freqs array is used.
55+ */
2956 u32 freqs [MAX_NUM_FREQS ];
57+ u32 mask ;
3058};
3159
3260/*
@@ -35,31 +63,81 @@ struct freq_desc {
3563 * by MSR based on SDM.
3664 */
3765static const struct freq_desc freq_desc_pnw = {
38- 0 , { 0 , 0 , 0 , 0 , 0 , 99840 , 0 , 83200 }
66+ .use_msr_plat = false,
67+ .freqs = { 0 , 0 , 0 , 0 , 0 , 99840 , 0 , 83200 },
68+ .mask = 0x07 ,
3969};
4070
4171static const struct freq_desc freq_desc_clv = {
42- 0 , { 0 , 133200 , 0 , 0 , 0 , 99840 , 0 , 83200 }
72+ .use_msr_plat = false,
73+ .freqs = { 0 , 133200 , 0 , 0 , 0 , 99840 , 0 , 83200 },
74+ .mask = 0x07 ,
4375};
4476
77+ /*
78+ * Bay Trail SDM MSR_FSB_FREQ frequencies simplified PLL model:
79+ * 000: 100 * 5 / 6 = 83.3333 MHz
80+ * 001: 100 * 1 / 1 = 100.0000 MHz
81+ * 010: 100 * 4 / 3 = 133.3333 MHz
82+ * 011: 100 * 7 / 6 = 116.6667 MHz
83+ * 100: 100 * 4 / 5 = 80.0000 MHz
84+ */
4585static const struct freq_desc freq_desc_byt = {
46- 1 , { 83300 , 100000 , 133300 , 116700 , 80000 , 0 , 0 , 0 }
86+ .use_msr_plat = true,
87+ .muldiv = { { 5 , 6 }, { 1 , 1 }, { 4 , 3 }, { 7 , 6 },
88+ { 4 , 5 } },
89+ .mask = 0x07 ,
4790};
4891
92+ /*
93+ * Cherry Trail SDM MSR_FSB_FREQ frequencies simplified PLL model:
94+ * 0000: 100 * 5 / 6 = 83.3333 MHz
95+ * 0001: 100 * 1 / 1 = 100.0000 MHz
96+ * 0010: 100 * 4 / 3 = 133.3333 MHz
97+ * 0011: 100 * 7 / 6 = 116.6667 MHz
98+ * 0100: 100 * 4 / 5 = 80.0000 MHz
99+ * 0101: 100 * 14 / 15 = 93.3333 MHz
100+ * 0110: 100 * 9 / 10 = 90.0000 MHz
101+ * 0111: 100 * 8 / 9 = 88.8889 MHz
102+ * 1000: 100 * 7 / 8 = 87.5000 MHz
103+ */
49104static const struct freq_desc freq_desc_cht = {
50- 1 , { 83300 , 100000 , 133300 , 116700 , 80000 , 93300 , 90000 , 88900 , 87500 }
105+ .use_msr_plat = true,
106+ .muldiv = { { 5 , 6 }, { 1 , 1 }, { 4 , 3 }, { 7 , 6 },
107+ { 4 , 5 }, { 14 , 15 }, { 9 , 10 }, { 8 , 9 },
108+ { 7 , 8 } },
109+ .mask = 0x0f ,
51110};
52111
112+ /*
113+ * Merriefield SDM MSR_FSB_FREQ frequencies simplified PLL model:
114+ * 0001: 100 * 1 / 1 = 100.0000 MHz
115+ * 0010: 100 * 4 / 3 = 133.3333 MHz
116+ */
53117static const struct freq_desc freq_desc_tng = {
54- 1 , { 0 , 100000 , 133300 , 0 , 0 , 0 , 0 , 0 }
118+ .use_msr_plat = true,
119+ .muldiv = { { 0 , 0 }, { 1 , 1 }, { 4 , 3 } },
120+ .mask = 0x07 ,
55121};
56122
123+ /*
124+ * Moorefield SDM MSR_FSB_FREQ frequencies simplified PLL model:
125+ * 0000: 100 * 5 / 6 = 83.3333 MHz
126+ * 0001: 100 * 1 / 1 = 100.0000 MHz
127+ * 0010: 100 * 4 / 3 = 133.3333 MHz
128+ * 0011: 100 * 1 / 1 = 100.0000 MHz
129+ */
57130static const struct freq_desc freq_desc_ann = {
58- 1 , { 83300 , 100000 , 133300 , 100000 , 0 , 0 , 0 , 0 }
131+ .use_msr_plat = true,
132+ .muldiv = { { 5 , 6 }, { 1 , 1 }, { 4 , 3 }, { 1 , 1 } },
133+ .mask = 0x0f ,
59134};
60135
136+ /* 24 MHz crystal? : 24 * 13 / 4 = 78 MHz */
61137static const struct freq_desc freq_desc_lgm = {
62- 1 , { 78000 , 78000 , 78000 , 78000 , 78000 , 78000 , 78000 , 78000 }
138+ .use_msr_plat = true,
139+ .freqs = { 78000 , 78000 , 78000 , 78000 , 78000 , 78000 , 78000 , 78000 },
140+ .mask = 0x0f ,
63141};
64142
65143static const struct x86_cpu_id tsc_msr_cpu_ids [] = {
@@ -81,17 +159,19 @@ static const struct x86_cpu_id tsc_msr_cpu_ids[] = {
81159 */
82160unsigned long cpu_khz_from_msr (void )
83161{
84- u32 lo , hi , ratio , freq ;
162+ u32 lo , hi , ratio , freq , tscref ;
85163 const struct freq_desc * freq_desc ;
86164 const struct x86_cpu_id * id ;
165+ const struct muldiv * md ;
87166 unsigned long res ;
167+ int index ;
88168
89169 id = x86_match_cpu (tsc_msr_cpu_ids );
90170 if (!id )
91171 return 0 ;
92172
93173 freq_desc = (struct freq_desc * )id -> driver_data ;
94- if (freq_desc -> msr_plat ) {
174+ if (freq_desc -> use_msr_plat ) {
95175 rdmsr (MSR_PLATFORM_INFO , lo , hi );
96176 ratio = (lo >> 8 ) & 0xff ;
97177 } else {
@@ -101,12 +181,28 @@ unsigned long cpu_khz_from_msr(void)
101181
102182 /* Get FSB FREQ ID */
103183 rdmsr (MSR_FSB_FREQ , lo , hi );
184+ index = lo & freq_desc -> mask ;
185+ md = & freq_desc -> muldiv [index ];
104186
105- /* Map CPU reference clock freq ID(0-7) to CPU reference clock freq(KHz) */
106- freq = freq_desc -> freqs [lo & 0x7 ];
187+ /*
188+ * Note this also catches cases where the index points to an unpopulated
189+ * part of muldiv, in that case the else will set freq and res to 0.
190+ */
191+ if (md -> divider ) {
192+ tscref = TSC_REFERENCE_KHZ * md -> multiplier ;
193+ freq = DIV_ROUND_CLOSEST (tscref , md -> divider );
194+ /*
195+ * Multiplying by ratio before the division has better
196+ * accuracy than just calculating freq * ratio.
197+ */
198+ res = DIV_ROUND_CLOSEST (tscref * ratio , md -> divider );
199+ } else {
200+ freq = freq_desc -> freqs [index ];
201+ res = freq * ratio ;
202+ }
107203
108- /* TSC frequency = maximum resolved freq * maximum resolved bus ratio */
109- res = freq * ratio ;
204+ if ( freq == 0 )
205+ pr_err ( "Error MSR_FSB_FREQ index %d is unknown\n" , index ) ;
110206
111207#ifdef CONFIG_X86_LOCAL_APIC
112208 lapic_timer_period = (freq * 1000 ) / HZ ;
0 commit comments