]>
Commit | Line | Data |
---|---|---|
0c530ab8 | 1 | /* |
2d21ac55 | 2 | * Copyright (c) 2005-2007 Apple Inc. All rights reserved. |
0c530ab8 | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0c530ab8 | 5 | * |
2d21ac55 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
0c530ab8 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
0c530ab8 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
0c530ab8 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
0c530ab8 A |
27 | */ |
28 | /* | |
29 | * @OSF_COPYRIGHT@ | |
30 | */ | |
31 | ||
32 | /* | |
33 | * File: i386/tsc.c | |
34 | * Purpose: Initializes the TSC and the various conversion | |
35 | * factors needed by other parts of the system. | |
36 | */ | |
37 | ||
0c530ab8 A |
38 | |
39 | #include <mach/mach_types.h> | |
40 | ||
41 | #include <kern/cpu_data.h> | |
42 | #include <kern/cpu_number.h> | |
43 | #include <kern/clock.h> | |
44 | #include <kern/host_notify.h> | |
45 | #include <kern/macro_help.h> | |
46 | #include <kern/misc_protos.h> | |
47 | #include <kern/spl.h> | |
48 | #include <kern/assert.h> | |
49 | #include <mach/vm_prot.h> | |
50 | #include <vm/pmap.h> | |
51 | #include <vm/vm_kern.h> /* for kernel_map */ | |
0c530ab8 | 52 | #include <architecture/i386/pio.h> |
0c530ab8 | 53 | #include <i386/machine_cpu.h> |
2d21ac55 | 54 | #include <i386/cpuid.h> |
b0d623f7 | 55 | #include <i386/mp.h> |
0c530ab8 | 56 | #include <i386/machine_routines.h> |
b0d623f7 A |
57 | #include <i386/proc_reg.h> |
58 | #include <i386/tsc.h> | |
59 | #include <i386/misc_protos.h> | |
0c530ab8 A |
60 | #include <pexpert/pexpert.h> |
61 | #include <machine/limits.h> | |
62 | #include <machine/commpage.h> | |
63 | #include <sys/kdebug.h> | |
64 | #include <pexpert/device_tree.h> | |
0c530ab8 A |
65 | |
66 | uint64_t busFCvtt2n = 0; | |
67 | uint64_t busFCvtn2t = 0; | |
68 | uint64_t tscFreq = 0; | |
69 | uint64_t tscFCvtt2n = 0; | |
70 | uint64_t tscFCvtn2t = 0; | |
71 | uint64_t tscGranularity = 0; | |
72 | uint64_t bus2tsc = 0; | |
2d21ac55 | 73 | uint64_t busFreq = 0; |
593a1d5f A |
74 | uint32_t flex_ratio = 0; |
75 | uint32_t flex_ratio_min = 0; | |
76 | uint32_t flex_ratio_max = 0; | |
77 | ||
15129b1c | 78 | uint64_t tsc_at_boot = 0; |
2d21ac55 A |
79 | |
80 | #define bit(n) (1ULL << (n)) | |
81 | #define bitmask(h,l) ((bit(h)|(bit(h)-1)) & ~(bit(l)-1)) | |
82 | #define bitfield(x,h,l) (((x) & bitmask(h,l)) >> l) | |
0c530ab8 A |
83 | |
84 | /* Decimal powers: */ | |
85 | #define kilo (1000ULL) | |
86 | #define Mega (kilo * kilo) | |
87 | #define Giga (kilo * Mega) | |
88 | #define Tera (kilo * Giga) | |
89 | #define Peta (kilo * Tera) | |
90 | ||
2d21ac55 A |
91 | #define CPU_FAMILY_PENTIUM_M (0x6) |
92 | ||
0c530ab8 | 93 | /* |
3e170ce0 | 94 | * This routine extracts a frequency property in Hz from the device tree. |
15129b1c | 95 | * Also reads any initial TSC value at boot from the device tree. |
0c530ab8 A |
96 | */ |
97 | static uint64_t | |
3e170ce0 | 98 | EFI_get_frequency(const char *prop) |
0c530ab8 A |
99 | { |
100 | uint64_t frequency = 0; | |
101 | DTEntry entry; | |
102 | void *value; | |
2d21ac55 | 103 | unsigned int size; |
0c530ab8 A |
104 | |
105 | if (DTLookupEntry(0, "/efi/platform", &entry) != kSuccess) { | |
3e170ce0 | 106 | kprintf("EFI_get_frequency: didn't find /efi/platform\n"); |
0c530ab8 A |
107 | return 0; |
108 | } | |
3e170ce0 A |
109 | if (DTGetProperty(entry,prop,&value,&size) != kSuccess) { |
110 | kprintf("EFI_get_frequency: property %s not found\n", prop); | |
0c530ab8 A |
111 | return 0; |
112 | } | |
113 | if (size == sizeof(uint64_t)) { | |
114 | frequency = *(uint64_t *) value; | |
3e170ce0 A |
115 | kprintf("EFI_get_frequency: read %s value: %llu\n", |
116 | prop, frequency); | |
0c530ab8 | 117 | } |
15129b1c A |
118 | |
119 | /* | |
120 | * While we're here, see if EFI published an initial TSC value. | |
121 | */ | |
3e170ce0 | 122 | if (DTGetProperty(entry,"InitialTSC",&value,&size) == kSuccess) { |
15129b1c A |
123 | if (size == sizeof(uint64_t)) { |
124 | tsc_at_boot = *(uint64_t *) value; | |
3e170ce0 A |
125 | kprintf("EFI_get_frequency: read InitialTSC: %llu\n", |
126 | tsc_at_boot); | |
15129b1c A |
127 | } |
128 | } | |
129 | ||
0c530ab8 A |
130 | return frequency; |
131 | } | |
132 | ||
133 | /* | |
134 | * Initialize the various conversion factors needed by code referencing | |
135 | * the TSC. | |
136 | */ | |
137 | void | |
138 | tsc_init(void) | |
139 | { | |
2d21ac55 A |
140 | boolean_t N_by_2_bus_ratio = FALSE; |
141 | ||
316670eb A |
142 | if (cpuid_vmm_present()) { |
143 | kprintf("VMM vendor %u TSC frequency %u KHz bus frequency %u KHz\n", | |
144 | cpuid_vmm_info()->cpuid_vmm_family, | |
145 | cpuid_vmm_info()->cpuid_vmm_tsc_frequency, | |
146 | cpuid_vmm_info()->cpuid_vmm_bus_frequency); | |
147 | ||
148 | if (cpuid_vmm_info()->cpuid_vmm_tsc_frequency && | |
149 | cpuid_vmm_info()->cpuid_vmm_bus_frequency) { | |
150 | ||
151 | busFreq = (uint64_t)cpuid_vmm_info()->cpuid_vmm_bus_frequency * kilo; | |
152 | busFCvtt2n = ((1 * Giga) << 32) / busFreq; | |
153 | busFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / busFCvtt2n; | |
154 | ||
155 | tscFreq = (uint64_t)cpuid_vmm_info()->cpuid_vmm_tsc_frequency * kilo; | |
156 | tscFCvtt2n = ((1 * Giga) << 32) / tscFreq; | |
157 | tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; | |
158 | ||
159 | tscGranularity = tscFreq / busFreq; | |
160 | ||
161 | bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t); | |
162 | ||
163 | return; | |
164 | } | |
165 | } | |
166 | ||
7e4a7d39 | 167 | switch (cpuid_cpufamily()) { |
5ba3f43e | 168 | case CPUFAMILY_INTEL_KABYLAKE: |
2dced7af A |
169 | case CPUFAMILY_INTEL_SKYLAKE: { |
170 | /* | |
171 | * SkyLake and later has an Always Running Timer (ART) providing | |
172 | * the reference frequency. CPUID leaf 0x15 determines the | |
173 | * rationship between this and the TSC frequency expressed as | |
174 | * - multiplier (numerator, N), and | |
175 | * - divisor (denominator, M). | |
176 | * So that TSC = ART * N / M. | |
177 | */ | |
178 | cpuid_tsc_leaf_t *tsc_leafp = &cpuid_info()->cpuid_tsc_leaf; | |
179 | uint64_t N = (uint64_t) tsc_leafp->numerator; | |
180 | uint64_t M = (uint64_t) tsc_leafp->denominator; | |
181 | uint64_t refFreq; | |
182 | ||
183 | refFreq = EFI_get_frequency("ARTFrequency"); | |
184 | if (refFreq == 0) | |
185 | refFreq = BASE_ART_CLOCK_SOURCE; | |
186 | ||
187 | assert(N != 0); | |
188 | assert(M != 1); | |
189 | tscFreq = refFreq * N / M; | |
190 | busFreq = tscFreq; /* bus is APIC frequency */ | |
191 | ||
192 | kprintf(" ART: Frequency = %6d.%06dMHz, N/M = %lld/%llu\n", | |
193 | (uint32_t)(refFreq / Mega), | |
194 | (uint32_t)(refFreq % Mega), | |
195 | N, M); | |
196 | ||
197 | break; | |
198 | } | |
fe8ab488 | 199 | default: { |
c910b4d9 A |
200 | uint64_t msr_flex_ratio; |
201 | uint64_t msr_platform_info; | |
202 | ||
203 | /* See if FLEX_RATIO is being used */ | |
204 | msr_flex_ratio = rdmsr64(MSR_FLEX_RATIO); | |
205 | msr_platform_info = rdmsr64(MSR_PLATFORM_INFO); | |
206 | flex_ratio_min = (uint32_t)bitfield(msr_platform_info, 47, 40); | |
207 | flex_ratio_max = (uint32_t)bitfield(msr_platform_info, 15, 8); | |
208 | /* No BIOS-programed flex ratio. Use hardware max as default */ | |
209 | tscGranularity = flex_ratio_max; | |
210 | if (msr_flex_ratio & bit(16)) { | |
211 | /* Flex Enabled: Use this MSR if less than max */ | |
212 | flex_ratio = (uint32_t)bitfield(msr_flex_ratio, 15, 8); | |
213 | if (flex_ratio < flex_ratio_max) | |
214 | tscGranularity = flex_ratio; | |
215 | } | |
216 | ||
3e170ce0 | 217 | busFreq = EFI_get_frequency("FSBFrequency"); |
c910b4d9 A |
218 | /* If EFI isn't configured correctly, use a constant |
219 | * value. See 6036811. | |
220 | */ | |
221 | if (busFreq == 0) | |
b0d623f7 | 222 | busFreq = BASE_NHM_CLOCK_SOURCE; |
c910b4d9 | 223 | |
c910b4d9 A |
224 | break; |
225 | } | |
fe8ab488 | 226 | case CPUFAMILY_INTEL_PENRYN: { |
593a1d5f A |
227 | uint64_t prfsts; |
228 | ||
229 | prfsts = rdmsr64(IA32_PERF_STS); | |
230 | tscGranularity = (uint32_t)bitfield(prfsts, 44, 40); | |
231 | N_by_2_bus_ratio = (prfsts & bit(46)) != 0; | |
3e170ce0 A |
232 | |
233 | busFreq = EFI_get_frequency("FSBFrequency"); | |
c910b4d9 | 234 | } |
593a1d5f A |
235 | } |
236 | ||
2d21ac55 A |
237 | if (busFreq != 0) { |
238 | busFCvtt2n = ((1 * Giga) << 32) / busFreq; | |
239 | busFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / busFCvtt2n; | |
2d21ac55 | 240 | } else { |
593a1d5f | 241 | panic("tsc_init: EFI not supported!\n"); |
2d21ac55 A |
242 | } |
243 | ||
316670eb | 244 | kprintf(" BUS: Frequency = %6d.%06dMHz, " |
bd504ef0 | 245 | "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n", |
593a1d5f A |
246 | (uint32_t)(busFreq / Mega), |
247 | (uint32_t)(busFreq % Mega), | |
248 | (uint32_t)(busFCvtt2n >> 32), (uint32_t)busFCvtt2n, | |
316670eb | 249 | (uint32_t)(busFCvtn2t >> 32), (uint32_t)busFCvtn2t); |
2d21ac55 | 250 | |
3e170ce0 A |
251 | if (tscFreq == busFreq) { |
252 | bus2tsc = 1; | |
316670eb | 253 | tscGranularity = 1; |
3e170ce0 A |
254 | tscFCvtn2t = busFCvtn2t; |
255 | tscFCvtt2n = busFCvtt2n; | |
256 | } else { | |
257 | /* | |
258 | * Get the TSC increment. The TSC is incremented by this | |
259 | * on every bus tick. Calculate the TSC conversion factors | |
260 | * to and from nano-seconds. | |
261 | * The tsc granularity is also called the "bus ratio". | |
262 | * If the N/2 bit is set this indicates the bus ration is | |
263 | * 0.5 more than this - i.e. that the true bus ratio | |
264 | * is (2*tscGranularity + 1)/2. | |
265 | */ | |
266 | if (N_by_2_bus_ratio) | |
267 | tscFCvtt2n = busFCvtt2n * 2 / (1 + 2*tscGranularity); | |
268 | else | |
269 | tscFCvtt2n = busFCvtt2n / tscGranularity; | |
316670eb | 270 | |
3e170ce0 A |
271 | tscFreq = ((1 * Giga) << 32) / tscFCvtt2n; |
272 | tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; | |
2d21ac55 | 273 | |
3e170ce0 A |
274 | /* |
275 | * Calculate conversion from BUS to TSC | |
276 | */ | |
277 | bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t); | |
278 | } | |
2d21ac55 | 279 | |
316670eb | 280 | kprintf(" TSC: Frequency = %6d.%06dMHz, " |
bd504ef0 | 281 | "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, gran = %lld%s\n", |
593a1d5f A |
282 | (uint32_t)(tscFreq / Mega), |
283 | (uint32_t)(tscFreq % Mega), | |
284 | (uint32_t)(tscFCvtt2n >> 32), (uint32_t)tscFCvtt2n, | |
285 | (uint32_t)(tscFCvtn2t >> 32), (uint32_t)tscFCvtn2t, | |
286 | tscGranularity, N_by_2_bus_ratio ? " (N/2)" : ""); | |
0c530ab8 A |
287 | } |
288 | ||
289 | void | |
290 | tsc_get_info(tscInfo_t *info) | |
291 | { | |
2d21ac55 A |
292 | info->busFCvtt2n = busFCvtt2n; |
293 | info->busFCvtn2t = busFCvtn2t; | |
294 | info->tscFreq = tscFreq; | |
295 | info->tscFCvtt2n = tscFCvtt2n; | |
296 | info->tscFCvtn2t = tscFCvtn2t; | |
297 | info->tscGranularity = tscGranularity; | |
298 | info->bus2tsc = bus2tsc; | |
299 | info->busFreq = busFreq; | |
593a1d5f A |
300 | info->flex_ratio = flex_ratio; |
301 | info->flex_ratio_min = flex_ratio_min; | |
302 | info->flex_ratio_max = flex_ratio_max; | |
0c530ab8 | 303 | } |