]>
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 | ||
38 | #include <platforms.h> | |
39 | #include <mach_kdb.h> | |
40 | ||
41 | #include <mach/mach_types.h> | |
42 | ||
43 | #include <kern/cpu_data.h> | |
44 | #include <kern/cpu_number.h> | |
45 | #include <kern/clock.h> | |
46 | #include <kern/host_notify.h> | |
47 | #include <kern/macro_help.h> | |
48 | #include <kern/misc_protos.h> | |
49 | #include <kern/spl.h> | |
50 | #include <kern/assert.h> | |
51 | #include <mach/vm_prot.h> | |
52 | #include <vm/pmap.h> | |
53 | #include <vm/vm_kern.h> /* for kernel_map */ | |
54 | #include <i386/ipl.h> | |
0c530ab8 A |
55 | #include <architecture/i386/pio.h> |
56 | #include <i386/misc_protos.h> | |
57 | #include <i386/proc_reg.h> | |
58 | #include <i386/machine_cpu.h> | |
59 | #include <i386/mp.h> | |
0c530ab8 | 60 | #include <i386/cpu_data.h> |
2d21ac55 | 61 | #include <i386/cpuid.h> |
0c530ab8 A |
62 | #include <i386/machine_routines.h> |
63 | #include <pexpert/pexpert.h> | |
64 | #include <machine/limits.h> | |
65 | #include <machine/commpage.h> | |
66 | #include <sys/kdebug.h> | |
67 | #include <pexpert/device_tree.h> | |
68 | #include <i386/tsc.h> | |
69 | ||
70 | uint64_t busFCvtt2n = 0; | |
71 | uint64_t busFCvtn2t = 0; | |
72 | uint64_t tscFreq = 0; | |
73 | uint64_t tscFCvtt2n = 0; | |
74 | uint64_t tscFCvtn2t = 0; | |
75 | uint64_t tscGranularity = 0; | |
76 | uint64_t bus2tsc = 0; | |
2d21ac55 | 77 | uint64_t busFreq = 0; |
593a1d5f A |
78 | uint32_t flex_ratio = 0; |
79 | uint32_t flex_ratio_min = 0; | |
80 | uint32_t flex_ratio_max = 0; | |
81 | ||
2d21ac55 A |
82 | |
83 | #define bit(n) (1ULL << (n)) | |
84 | #define bitmask(h,l) ((bit(h)|(bit(h)-1)) & ~(bit(l)-1)) | |
85 | #define bitfield(x,h,l) (((x) & bitmask(h,l)) >> l) | |
0c530ab8 A |
86 | |
87 | /* Decimal powers: */ | |
88 | #define kilo (1000ULL) | |
89 | #define Mega (kilo * kilo) | |
90 | #define Giga (kilo * Mega) | |
91 | #define Tera (kilo * Giga) | |
92 | #define Peta (kilo * Tera) | |
93 | ||
2d21ac55 A |
94 | #define CPU_FAMILY_PENTIUM_M (0x6) |
95 | ||
0c530ab8 A |
96 | static const char FSB_Frequency_prop[] = "FSBFrequency"; |
97 | /* | |
593a1d5f | 98 | * This routine extracts the bus frequency in Hz from the device tree. |
0c530ab8 A |
99 | */ |
100 | static uint64_t | |
101 | EFI_FSB_frequency(void) | |
102 | { | |
103 | uint64_t frequency = 0; | |
104 | DTEntry entry; | |
105 | void *value; | |
2d21ac55 | 106 | unsigned int size; |
0c530ab8 A |
107 | |
108 | if (DTLookupEntry(0, "/efi/platform", &entry) != kSuccess) { | |
109 | kprintf("EFI_FSB_frequency: didn't find /efi/platform\n"); | |
110 | return 0; | |
111 | } | |
112 | if (DTGetProperty(entry,FSB_Frequency_prop,&value,&size) != kSuccess) { | |
2d21ac55 A |
113 | kprintf("EFI_FSB_frequency: property %s not found\n", |
114 | FSB_Frequency_prop); | |
0c530ab8 A |
115 | return 0; |
116 | } | |
117 | if (size == sizeof(uint64_t)) { | |
118 | frequency = *(uint64_t *) value; | |
119 | kprintf("EFI_FSB_frequency: read %s value: %llu\n", | |
120 | FSB_Frequency_prop, frequency); | |
121 | if (!(90*Mega < frequency && frequency < 10*Giga)) { | |
122 | kprintf("EFI_FSB_frequency: value out of range\n"); | |
123 | frequency = 0; | |
124 | } | |
125 | } else { | |
126 | kprintf("EFI_FSB_frequency: unexpected size %d\n", size); | |
127 | } | |
128 | return frequency; | |
129 | } | |
130 | ||
131 | /* | |
132 | * Initialize the various conversion factors needed by code referencing | |
133 | * the TSC. | |
134 | */ | |
135 | void | |
136 | tsc_init(void) | |
137 | { | |
2d21ac55 A |
138 | uint64_t busFCvtInt = 0; |
139 | boolean_t N_by_2_bus_ratio = FALSE; | |
140 | ||
141 | /* | |
593a1d5f | 142 | * Get the FSB frequency and conversion factors from EFI. |
2d21ac55 A |
143 | */ |
144 | busFreq = EFI_FSB_frequency(); | |
593a1d5f A |
145 | |
146 | if (cpuid_info()->cpuid_family != CPU_FAMILY_PENTIUM_M) { | |
147 | panic("tsc_init: unknown CPU family: 0x%X\n", | |
148 | cpuid_info()->cpuid_family); | |
149 | } | |
150 | ||
c910b4d9 A |
151 | switch (cpuid_info()->cpuid_model) { |
152 | case CPUID_MODEL_NEHALEM: { | |
153 | uint64_t cpu_mhz; | |
154 | uint64_t msr_flex_ratio; | |
155 | uint64_t msr_platform_info; | |
156 | ||
157 | /* See if FLEX_RATIO is being used */ | |
158 | msr_flex_ratio = rdmsr64(MSR_FLEX_RATIO); | |
159 | msr_platform_info = rdmsr64(MSR_PLATFORM_INFO); | |
160 | flex_ratio_min = (uint32_t)bitfield(msr_platform_info, 47, 40); | |
161 | flex_ratio_max = (uint32_t)bitfield(msr_platform_info, 15, 8); | |
162 | /* No BIOS-programed flex ratio. Use hardware max as default */ | |
163 | tscGranularity = flex_ratio_max; | |
164 | if (msr_flex_ratio & bit(16)) { | |
165 | /* Flex Enabled: Use this MSR if less than max */ | |
166 | flex_ratio = (uint32_t)bitfield(msr_flex_ratio, 15, 8); | |
167 | if (flex_ratio < flex_ratio_max) | |
168 | tscGranularity = flex_ratio; | |
169 | } | |
170 | ||
171 | /* If EFI isn't configured correctly, use a constant | |
172 | * value. See 6036811. | |
173 | */ | |
174 | if (busFreq == 0) | |
175 | busFreq = BASE_NHM_CLOCK_SOURCE; | |
176 | ||
177 | cpu_mhz = tscGranularity * BASE_NHM_CLOCK_SOURCE; | |
178 | ||
179 | kprintf("[NHM] Maximum Non-Turbo Ratio = [%d]\n", | |
180 | (uint32_t)tscGranularity); | |
181 | kprintf("[NHM] CPU: Frequency = %6d.%04dMhz\n", | |
182 | (uint32_t)(cpu_mhz / Mega), (uint32_t)(cpu_mhz % Mega)); | |
183 | break; | |
184 | } | |
185 | default: { | |
593a1d5f A |
186 | uint64_t prfsts; |
187 | ||
188 | prfsts = rdmsr64(IA32_PERF_STS); | |
189 | tscGranularity = (uint32_t)bitfield(prfsts, 44, 40); | |
190 | N_by_2_bus_ratio = (prfsts & bit(46)) != 0; | |
c910b4d9 | 191 | } |
593a1d5f A |
192 | } |
193 | ||
2d21ac55 A |
194 | if (busFreq != 0) { |
195 | busFCvtt2n = ((1 * Giga) << 32) / busFreq; | |
196 | busFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / busFCvtt2n; | |
197 | busFCvtInt = tmrCvt(1 * Peta, 0xFFFFFFFFFFFFFFFFULL / busFreq); | |
198 | } else { | |
593a1d5f | 199 | panic("tsc_init: EFI not supported!\n"); |
2d21ac55 A |
200 | } |
201 | ||
202 | kprintf(" BUS: Frequency = %6d.%04dMHz, " | |
593a1d5f A |
203 | "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, " |
204 | "cvtInt = %08X.%08X\n", | |
205 | (uint32_t)(busFreq / Mega), | |
206 | (uint32_t)(busFreq % Mega), | |
207 | (uint32_t)(busFCvtt2n >> 32), (uint32_t)busFCvtt2n, | |
208 | (uint32_t)(busFCvtn2t >> 32), (uint32_t)busFCvtn2t, | |
209 | (uint32_t)(busFCvtInt >> 32), (uint32_t)busFCvtInt); | |
2d21ac55 A |
210 | |
211 | /* | |
212 | * Get the TSC increment. The TSC is incremented by this | |
213 | * on every bus tick. Calculate the TSC conversion factors | |
214 | * to and from nano-seconds. | |
4a3eedf9 A |
215 | * The tsc granularity is also called the "bus ratio". If the N/2 bit |
216 | * is set this indicates the bus ration is 0.5 more than this - i.e. | |
217 | * that the true bus ratio is (2*tscGranularity + 1)/2. | |
2d21ac55 | 218 | */ |
2d21ac55 | 219 | if (N_by_2_bus_ratio) |
4a3eedf9 | 220 | tscFCvtt2n = busFCvtt2n * 2 / (1 + 2*tscGranularity); |
2d21ac55 | 221 | else |
4a3eedf9 | 222 | tscFCvtt2n = busFCvtt2n / tscGranularity; |
2d21ac55 A |
223 | |
224 | tscFreq = ((1 * Giga) << 32) / tscFCvtt2n; | |
225 | tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; | |
226 | ||
227 | kprintf(" TSC: Frequency = %6d.%04dMHz, " | |
593a1d5f A |
228 | "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, gran = %lld%s\n", |
229 | (uint32_t)(tscFreq / Mega), | |
230 | (uint32_t)(tscFreq % Mega), | |
231 | (uint32_t)(tscFCvtt2n >> 32), (uint32_t)tscFCvtt2n, | |
232 | (uint32_t)(tscFCvtn2t >> 32), (uint32_t)tscFCvtn2t, | |
233 | tscGranularity, N_by_2_bus_ratio ? " (N/2)" : ""); | |
2d21ac55 A |
234 | |
235 | /* | |
236 | * Calculate conversion from BUS to TSC | |
237 | */ | |
238 | bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t); | |
0c530ab8 A |
239 | } |
240 | ||
241 | void | |
242 | tsc_get_info(tscInfo_t *info) | |
243 | { | |
2d21ac55 A |
244 | info->busFCvtt2n = busFCvtt2n; |
245 | info->busFCvtn2t = busFCvtn2t; | |
246 | info->tscFreq = tscFreq; | |
247 | info->tscFCvtt2n = tscFCvtt2n; | |
248 | info->tscFCvtn2t = tscFCvtn2t; | |
249 | info->tscGranularity = tscGranularity; | |
250 | info->bus2tsc = bus2tsc; | |
251 | info->busFreq = busFreq; | |
593a1d5f A |
252 | info->flex_ratio = flex_ratio; |
253 | info->flex_ratio_min = flex_ratio_min; | |
254 | info->flex_ratio_max = flex_ratio_max; | |
0c530ab8 | 255 | } |