15
15
#include <asm/param.h>
16
16
#include <asm/tsc.h>
17
17
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
+ };
19
41
20
42
/*
21
43
* If MSR_PERF_STAT[31] is set, the maximum resolved bus ratio can be
22
44
* read in MSR_PLATFORM_ID[12:8], otherwise in MSR_PERF_STAT[44:40].
23
45
* Unfortunately some Intel Atom SoCs aren't quite compliant to this,
24
46
* so we need manually differentiate SoC families. This is what the
25
- * field msr_plat does.
47
+ * field use_msr_plat does.
26
48
*/
27
49
struct 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
+ */
29
56
u32 freqs [MAX_NUM_FREQS ];
57
+ u32 mask ;
30
58
};
31
59
32
60
/*
@@ -35,31 +63,81 @@ struct freq_desc {
35
63
* by MSR based on SDM.
36
64
*/
37
65
static 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 ,
39
69
};
40
70
41
71
static 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 ,
43
75
};
44
76
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
+ */
45
85
static 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 ,
47
90
};
48
91
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
+ */
49
104
static 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 ,
51
110
};
52
111
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
+ */
53
117
static 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 ,
55
121
};
56
122
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
+ */
57
130
static 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 ,
59
134
};
60
135
136
+ /* 24 MHz crystal? : 24 * 13 / 4 = 78 MHz */
61
137
static 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 ,
63
141
};
64
142
65
143
static const struct x86_cpu_id tsc_msr_cpu_ids [] = {
@@ -81,17 +159,19 @@ static const struct x86_cpu_id tsc_msr_cpu_ids[] = {
81
159
*/
82
160
unsigned long cpu_khz_from_msr (void )
83
161
{
84
- u32 lo , hi , ratio , freq ;
162
+ u32 lo , hi , ratio , freq , tscref ;
85
163
const struct freq_desc * freq_desc ;
86
164
const struct x86_cpu_id * id ;
165
+ const struct muldiv * md ;
87
166
unsigned long res ;
167
+ int index ;
88
168
89
169
id = x86_match_cpu (tsc_msr_cpu_ids );
90
170
if (!id )
91
171
return 0 ;
92
172
93
173
freq_desc = (struct freq_desc * )id -> driver_data ;
94
- if (freq_desc -> msr_plat ) {
174
+ if (freq_desc -> use_msr_plat ) {
95
175
rdmsr (MSR_PLATFORM_INFO , lo , hi );
96
176
ratio = (lo >> 8 ) & 0xff ;
97
177
} else {
@@ -101,12 +181,28 @@ unsigned long cpu_khz_from_msr(void)
101
181
102
182
/* Get FSB FREQ ID */
103
183
rdmsr (MSR_FSB_FREQ , lo , hi );
184
+ index = lo & freq_desc -> mask ;
185
+ md = & freq_desc -> muldiv [index ];
104
186
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
+ }
107
203
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 ) ;
110
206
111
207
#ifdef CONFIG_X86_LOCAL_APIC
112
208
lapic_timer_period = (freq * 1000 ) / HZ ;
0 commit comments