]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | ||
23 | #include <string.h> | |
24 | #include <mach/vm_param.h> | |
25 | #include <mach/vm_prot.h> | |
26 | #include <mach/machine.h> | |
27 | #include <mach/time_value.h> | |
28 | #include <kern/spl.h> | |
29 | #include <kern/assert.h> | |
30 | #include <kern/debug.h> | |
31 | #include <kern/misc_protos.h> | |
32 | #include <kern/startup.h> | |
33 | #include <kern/clock.h> | |
34 | #include <kern/cpu_data.h> | |
35 | #include <kern/processor.h> | |
36 | #include <vm/vm_page.h> | |
37 | #include <vm/pmap.h> | |
38 | #include <vm/vm_kern.h> | |
39 | #include <i386/pmap.h> | |
40 | #include <i386/misc_protos.h> | |
41 | #include <i386/cpuid.h> | |
42 | #include <i386/mp.h> | |
43 | #include <i386/machine_cpu.h> | |
44 | #include <i386/machine_routines.h> | |
45 | #include <i386/io_map_entries.h> | |
46 | #include <architecture/i386/pio.h> | |
47 | #include <i386/cpuid.h> | |
48 | #include <i386/apic.h> | |
49 | #include <i386/tsc.h> | |
50 | #include <i386/hpet.h> | |
51 | #include <i386/pmCPU.h> | |
52 | #include <pexpert/device_tree.h> | |
53 | #if MACH_KDB | |
54 | #include <ddb/db_aout.h> | |
55 | #include <ddb/db_access.h> | |
56 | #include <ddb/db_sym.h> | |
57 | #include <ddb/db_variables.h> | |
58 | #include <ddb/db_command.h> | |
59 | #include <ddb/db_output.h> | |
60 | #include <ddb/db_expr.h> | |
61 | #endif /* MACH_KDB */ | |
62 | #include <ddb/tr.h> | |
63 | ||
64 | /* Decimal powers: */ | |
65 | #define kilo (1000ULL) | |
66 | #define Mega (kilo * kilo) | |
67 | #define Giga (kilo * Mega) | |
68 | #define Tera (kilo * Giga) | |
69 | #define Peta (kilo * Tera) | |
70 | ||
71 | uint32_t hpetArea = 0; | |
72 | uint32_t hpetAreap = 0; | |
73 | uint64_t hpetFemto = 0; | |
74 | uint64_t hpetFreq = 0; | |
75 | uint64_t hpetCvt = 0; /* (TAKE OUT LATER) */ | |
76 | uint64_t hpetCvtt2n = 0; | |
77 | uint64_t hpetCvtn2t = 0; | |
78 | uint64_t tsc2hpet = 0; | |
79 | uint64_t hpet2tsc = 0; | |
80 | uint64_t bus2hpet = 0; | |
81 | uint64_t hpet2bus = 0; | |
82 | ||
83 | uint32_t rcbaArea = 0; | |
84 | uint32_t rcbaAreap = 0; | |
85 | ||
86 | #if DEBUG | |
87 | #define DBG(x...) kprintf("DBG: " x) | |
88 | #else | |
89 | #define DBG(x...) | |
90 | #endif | |
91 | ||
92 | /* | |
93 | * Map the RCBA area. | |
94 | */ | |
95 | static void | |
96 | map_rcbaArea(void) | |
97 | { | |
98 | /* | |
99 | * Get RCBA area physical address and map it | |
100 | */ | |
101 | outl(cfgAdr, lpcCfg | (0xF0 & 0xFC)); | |
102 | rcbaAreap = inl(cfgDat | (0xF0 & 0x03)); | |
103 | rcbaArea = io_map_spec(rcbaAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO); | |
104 | kprintf("RCBA: vaddr = %08X, paddr = %08X\n", rcbaArea, rcbaAreap); | |
105 | } | |
106 | ||
107 | /* | |
108 | * Initialize the HPET | |
109 | */ | |
110 | void | |
111 | hpet_init(void) | |
112 | { | |
113 | unsigned int *xmod; | |
114 | uint64_t now; | |
115 | uint64_t initialHPET; | |
116 | ||
117 | map_rcbaArea(); | |
118 | ||
119 | /* | |
120 | * Is the HPET memory already enabled? | |
121 | * If not, set address and enable. | |
122 | */ | |
123 | xmod = (uint32_t *)(rcbaArea + 0x3404); /* Point to the HPTC */ | |
124 | uint32_t hptc = *xmod; /* Get HPET config */ | |
125 | DBG(" current RCBA.HPTC: %08X\n", *xmod); | |
126 | if(!(hptc & hptcAE)) { | |
127 | DBG("HPET memory is not enabled, " | |
128 | "enabling and assigning to 0xFED00000 (hope that's ok)\n"); | |
129 | *xmod = (hptc & ~3) | hptcAE; | |
130 | } | |
131 | ||
132 | /* | |
133 | * Get physical address of HPET and map it. | |
134 | */ | |
135 | hpetAreap = hpetAddr | ((hptc & 3) << 12); | |
136 | hpetArea = io_map_spec(hpetAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO); | |
137 | kprintf("HPET: vaddr = %08X, paddr = %08X\n", hpetArea, hpetAreap); | |
138 | ||
139 | /* | |
140 | * Extract the HPET tick rate. | |
141 | * The period of the HPET is reported in femtoseconds (10**-15s) | |
142 | * and convert to frequency in hertz. | |
143 | */ | |
144 | hpetFemto = (uint32_t)(((hpetReg_t *)hpetArea)->GCAP_ID >> 32); | |
145 | hpetFreq = (1 * Peta) / hpetFemto; | |
146 | ||
147 | /* | |
148 | * The conversion factor is the number of nanoseconds per HPET tick | |
149 | * with about 32 bits of fraction. The value is converted to a | |
150 | * base-2 fixed point number. To convert from HPET to nanoseconds, | |
151 | * multiply the value by the conversion factor using 96-bit arithmetic, | |
152 | * then shift right 32 bits. If the value is known to be small, | |
153 | * 64-bit arithmetic will work. | |
154 | */ | |
155 | ||
156 | /* | |
157 | * Begin conversion of base 10 femtoseconds to base 2, calculate: | |
158 | * - HPET ticks to nanoseconds conversion in base 2 fraction (* 2**32) | |
159 | * - nanoseconds to HPET ticks conversion | |
160 | */ | |
161 | hpetCvtt2n = (uint64_t)hpetFemto << 32; | |
162 | hpetCvtt2n = hpetCvtt2n / 1000000ULL; | |
163 | hpetCvtn2t = 0xFFFFFFFFFFFFFFFFULL / hpetCvtt2n; | |
164 | kprintf("HPET: Frequency = %6d.%04dMHz, " | |
165 | "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n", | |
166 | (uint32_t)(hpetFreq / Mega), (uint32_t)(hpetFreq % Mega), | |
167 | (uint32_t)(hpetCvtt2n >> 32), (uint32_t)hpetCvtt2n, | |
168 | (uint32_t)(hpetCvtn2t >> 32), (uint32_t)hpetCvtn2t); | |
169 | ||
170 | ||
171 | /* (TAKE OUT LATER) | |
172 | * Begin conversion of base 10 femtoseconds to base 2 | |
173 | * HPET ticks to nanoseconds in base 2 fraction (times 1048576) | |
174 | */ | |
175 | hpetCvt = (uint64_t)hpetFemto << 20; | |
176 | hpetCvt = hpetCvt / 1000000ULL; | |
177 | ||
178 | /* Calculate conversion from TSC to HPET */ | |
179 | tsc2hpet = tmrCvt(tscFCvtt2n, hpetCvtn2t); | |
180 | DBG(" CVT: TSC to HPET = %08X.%08X\n", | |
181 | (uint32_t)(tsc2hpet >> 32), (uint32_t)tsc2hpet); | |
182 | ||
183 | /* Calculate conversion from HPET to TSC */ | |
184 | hpet2tsc = tmrCvt(hpetCvtt2n, tscFCvtn2t); | |
185 | DBG(" CVT: HPET to TSC = %08X.%08X\n", | |
186 | (uint32_t)(hpet2tsc >> 32), (uint32_t)hpet2tsc); | |
187 | ||
188 | /* Calculate conversion from BUS to HPET */ | |
189 | bus2hpet = tmrCvt(busFCvtt2n, hpetCvtn2t); | |
190 | DBG(" CVT: BUS to HPET = %08X.%08X\n", | |
191 | (uint32_t)(bus2hpet >> 32), (uint32_t)bus2hpet); | |
192 | ||
193 | /* Calculate conversion from HPET to BUS */ | |
194 | hpet2bus = tmrCvt(hpetCvtt2n, busFCvtn2t); | |
195 | DBG(" CVT: HPET to BUS = %08X.%08X\n", | |
196 | (uint32_t)(hpet2bus >> 32), (uint32_t)hpet2bus); | |
197 | ||
198 | /* Make sure the counter is off in the HPET configuration flags */ | |
199 | uint64_t hpetcon = ((hpetReg_t *)hpetArea)->GEN_CONF; | |
200 | hpetcon = hpetcon & ~1; | |
201 | ((hpetReg_t *)hpetArea)->GEN_CONF = hpetcon; | |
202 | ||
203 | /* | |
204 | * Convert current TSC to HPET value, | |
205 | * set it, and start it ticking. | |
206 | */ | |
207 | now = mach_absolute_time(); | |
208 | initialHPET = tmrCvt(now, hpetCvtn2t); | |
209 | ((hpetReg_t *)hpetArea)->MAIN_CNT = initialHPET; | |
210 | hpetcon = hpetcon | 1; | |
211 | ((hpetReg_t *)hpetArea)->GEN_CONF = hpetcon; | |
212 | kprintf("HPET started: now = %08X.%08X, HPET = %08X.%08X\n", | |
213 | (uint32_t)(now >> 32), (uint32_t)now, | |
214 | (uint32_t)(initialHPET >> 32), (uint32_t)initialHPET); | |
215 | ||
216 | #if MACH_KDB | |
217 | db_display_hpet((hpetReg_t *)hpetArea); /* (BRINGUP) */ | |
218 | #endif | |
219 | } | |
220 | ||
221 | /* | |
222 | * This routine is used to get various information about the HPET | |
223 | * without having to export gobs of globals. It fills in a data | |
224 | * structure with the info. | |
225 | */ | |
226 | void | |
227 | hpet_get_info(hpetInfo_t *info) | |
228 | { | |
229 | info->hpetCvtt2n = hpetCvtt2n; | |
230 | info->hpetCvtn2t = hpetCvtn2t; | |
231 | info->tsc2hpet = tsc2hpet; | |
232 | info->hpet2tsc = hpet2tsc; | |
233 | info->bus2hpet = bus2hpet; | |
234 | info->hpet2bus = hpet2bus; | |
235 | info->rcbaArea = rcbaArea; | |
236 | info->rcbaAreap = rcbaAreap; | |
237 | } | |
238 | ||
239 | ||
240 | /* | |
241 | * This routine is called by the HPET driver | |
242 | * when it assigns an HPET timer to a processor | |
243 | */ | |
244 | ||
245 | void | |
246 | ml_hpet_cfg(uint32_t cpu, uint32_t hpetVect) | |
247 | { | |
248 | uint64_t *hpetVaddr; | |
249 | uint64_t hpetcnf; | |
250 | ||
251 | if(cpu > 1) { | |
252 | panic("ml_hpet_cfg: invalid cpu = %d\n", cpu); | |
253 | } | |
254 | ||
255 | /* Calculate address of the HPET for this processor */ | |
256 | hpetVaddr = (uint64_t *)(((uint32_t)&(((hpetReg_t *)hpetArea)->TIM1_CONF)) + (cpu << 5)); | |
257 | ||
258 | DBG("ml_hpet_cfg: HPET for cpu %d at %08X, vector = %d\n", | |
259 | cpu, hpetVaddr, hpetVect); | |
260 | ||
261 | /* Save the address and vector of the HPET for this processor */ | |
262 | cpu_data_ptr[cpu]->cpu_pmHpet = (uint64_t *)hpetVaddr; | |
263 | cpu_data_ptr[cpu]->cpu_pmHpetVec = hpetVect; | |
264 | ||
265 | /* Enable the interruptions now that we have a vector */ | |
266 | hpetcnf = *hpetVaddr; | |
267 | hpetcnf = hpetcnf | Tn_INT_ENB_CNF; | |
268 | *hpetVaddr = hpetcnf; | |
269 | ||
270 | /* Save the configuration */ | |
271 | cpu_data_ptr[cpu]->cpu_pmStats.pmHpetCfg = hpetcnf; | |
272 | cpu_data_ptr[cpu]->cpu_pmStats.pmHpetCmp = 0; | |
273 | ||
274 | /* See if nap policy has changed now */ | |
275 | machine_nap_policy(); | |
276 | ||
277 | } | |
278 | ||
279 | /* | |
280 | * This is the HPET interrupt handler. | |
281 | * | |
282 | * We really don't want to be here, but so far, I haven't figured out | |
283 | * a way to cancel the interrupt. Hopefully, some day we will figure out | |
284 | * how to do that or switch all timers to the HPET. | |
285 | */ | |
286 | int | |
287 | HPETInterrupt(void) | |
288 | { | |
289 | ||
290 | /* All we do here is to bump the count */ | |
291 | current_cpu_datap()->cpu_pmStats.pmHPETRupt++; | |
292 | ||
293 | /* Return and show that the 'rupt has been handled... */ | |
294 | return 1; | |
295 | } | |
296 | ||
297 | ||
298 | static hpetReg_t saved_hpet; | |
299 | ||
300 | void hpet_save( void ) | |
301 | { | |
302 | hpetReg_t *from = (hpetReg_t *) hpetArea; | |
303 | hpetReg_t *to = &saved_hpet; | |
304 | ||
305 | to->GEN_CONF = from->GEN_CONF; | |
306 | to->TIM0_CONF = from->TIM0_CONF; | |
307 | to->TIM0_COMP = from->TIM0_COMP; | |
308 | to->TIM1_CONF = from->TIM1_CONF; | |
309 | to->TIM1_COMP = from->TIM1_COMP; | |
310 | to->TIM2_CONF = from->TIM2_CONF; | |
311 | to->TIM2_COMP = from->TIM2_COMP; | |
312 | to->MAIN_CNT = from->MAIN_CNT; | |
313 | } | |
314 | ||
315 | void hpet_restore( void ) | |
316 | { | |
317 | hpetReg_t *from = &saved_hpet; | |
318 | hpetReg_t *to = (hpetReg_t *) hpetArea; | |
319 | ||
320 | /* | |
321 | * Is the HPET memory already enabled? | |
322 | * If not, set address and enable. | |
323 | */ | |
324 | uint32_t *hptcp = (uint32_t *)(rcbaArea + 0x3404); | |
325 | uint32_t hptc = *hptcp; | |
326 | if(!(hptc & hptcAE)) { | |
327 | DBG("HPET memory is not enabled, " | |
328 | "enabling and assigning to 0xFED00000 (hope that's ok)\n"); | |
329 | *hptcp = (hptc & ~3) | hptcAE; | |
330 | } | |
331 | ||
332 | to->GEN_CONF = from->GEN_CONF & ~1; | |
333 | ||
334 | to->TIM0_CONF = from->TIM0_CONF; | |
335 | to->TIM0_COMP = from->TIM0_COMP; | |
336 | to->TIM1_CONF = from->TIM1_CONF; | |
337 | to->TIM1_COMP = from->TIM1_COMP; | |
338 | to->TIM2_CONF = from->TIM2_CONF; | |
339 | to->TIM2_COMP = from->TIM2_COMP; | |
340 | to->GINTR_STA = -1ULL; | |
341 | to->MAIN_CNT = tmrCvt(mach_absolute_time(), hpetCvtn2t); | |
342 | ||
343 | to->GEN_CONF = from->GEN_CONF; | |
344 | } | |
345 | ||
346 | /* | |
347 | * Read the HPET timer | |
348 | * | |
349 | */ | |
350 | uint64_t | |
351 | rdHPET(void) | |
352 | { | |
353 | hpetReg_t *hpetp = (hpetReg_t *) hpetArea; | |
354 | volatile uint32_t *regp = (uint32_t *) &hpetp->MAIN_CNT; | |
355 | uint32_t high; | |
356 | uint32_t low; | |
357 | ||
358 | do { | |
359 | high = *(regp + 1); | |
360 | low = *regp; | |
361 | } while (high != *(regp + 1)); | |
362 | ||
363 | return (((uint64_t) high) << 32) | low; | |
364 | } | |
365 | ||
366 | #if MACH_KDB | |
367 | ||
368 | #define HI32(x) ((uint32_t)(((x) >> 32) & 0xFFFFFFFF)) | |
369 | #define LO32(x) ((uint32_t)((x) & 0xFFFFFFFF)) | |
370 | ||
371 | /* | |
372 | * Displays HPET memory mapped area | |
373 | * hp | |
374 | */ | |
375 | void | |
376 | db_hpet(__unused db_expr_t addr, __unused int have_addr, __unused db_expr_t count, __unused char *modif) | |
377 | { | |
378 | ||
379 | db_display_hpet((hpetReg_t *) hpetArea); /* Dump out the HPET | |
380 | * stuff */ | |
381 | return; | |
382 | } | |
383 | ||
384 | void | |
385 | db_display_hpet(hpetReg_t * hpt) | |
386 | { | |
387 | ||
388 | uint64_t cmain; | |
389 | ||
390 | cmain = hpt->MAIN_CNT; /* Get the main timer */ | |
391 | ||
392 | /* General capabilities */ | |
393 | db_printf(" GCAP_ID = %08X.%08X\n", | |
394 | HI32(hpt->GCAP_ID), LO32(hpt->GCAP_ID)); | |
395 | /* General configuration */ | |
396 | db_printf(" GEN_CONF = %08X.%08X\n", | |
397 | HI32(hpt->GEN_CONF), LO32(hpt->GEN_CONF)); | |
398 | /* General Interrupt status */ | |
399 | db_printf("GINTR_STA = %08X.%08X\n", | |
400 | HI32(hpt->GINTR_STA), LO32(hpt->GINTR_STA)); | |
401 | /* Main counter */ | |
402 | db_printf(" MAIN_CNT = %08X.%08X\n", | |
403 | HI32(cmain), LO32(cmain)); | |
404 | /* Timer 0 config and cap */ | |
405 | db_printf("TIM0_CONF = %08X.%08X\n", | |
406 | HI32(hpt->TIM0_CONF), LO32(hpt->TIM0_CONF)); | |
407 | /* Timer 0 comparator */ | |
408 | db_printf("TIM0_COMP = %08X.%08X\n", | |
409 | HI32(hpt->TIM0_COMP), LO32(hpt->TIM0_COMP)); | |
410 | /* Timer 1 config and cap */ | |
411 | db_printf("TIM0_CONF = %08X.%08X\n", | |
412 | HI32(hpt->TIM1_CONF), LO32(hpt->TIM1_CONF)); | |
413 | /* Timer 1 comparator */ | |
414 | db_printf("TIM1_COMP = %08X.%08X\n", | |
415 | HI32(hpt->TIM1_COMP), LO32(hpt->TIM1_COMP)); | |
416 | /* Timer 2 config and cap */ | |
417 | db_printf("TIM2_CONF = %08X.%08X\n", | |
418 | HI32(hpt->TIM2_CONF), LO32(hpt->TIM2_CONF)); | |
419 | /* Timer 2 comparator */ | |
420 | db_printf("TIM2_COMP = %08X.%08X\n", | |
421 | HI32(hpt->TIM2_COMP), LO32(hpt->TIM2_COMP)); | |
422 | ||
423 | db_printf("\nHPET Frequency = %d.%05dMHz\n", | |
424 | (uint32_t) (hpetFreq / 1000000), (uint32_t) (hpetFreq % 1000000)); | |
425 | ||
426 | return; | |
427 | ||
428 | } | |
429 | ||
430 | #endif |