]>
Commit | Line | Data |
---|---|---|
b0d623f7 A |
1 | /* |
2 | * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
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. | |
14 | * | |
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 | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
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. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | #include <string.h> | |
30 | #include <mach/vm_param.h> | |
31 | #include <mach/vm_prot.h> | |
32 | #include <mach/machine.h> | |
33 | #include <mach/time_value.h> | |
34 | #include <kern/spl.h> | |
35 | #include <kern/assert.h> | |
36 | #include <kern/debug.h> | |
37 | #include <kern/misc_protos.h> | |
38 | #include <kern/startup.h> | |
39 | #include <kern/clock.h> | |
40 | #include <kern/cpu_data.h> | |
41 | #include <kern/processor.h> | |
42 | #include <vm/vm_page.h> | |
43 | #include <vm/pmap.h> | |
44 | #include <vm/vm_kern.h> | |
45 | #include <i386/cpuid.h> | |
46 | #include <i386/machine_cpu.h> | |
47 | #include <i386/mp.h> | |
48 | #include <i386/machine_routines.h> | |
49 | #include <i386/pmap.h> | |
50 | #include <i386/misc_protos.h> | |
51 | #include <i386/io_map_entries.h> | |
52 | #include <architecture/i386/pio.h> | |
53 | #include <i386/cpuid.h> | |
54 | #include <i386/apic.h> | |
55 | #include <i386/tsc.h> | |
56 | #include <i386/hpet.h> | |
57 | #include <i386/pmCPU.h> | |
58 | #include <i386/cpu_topology.h> | |
59 | #include <i386/cpu_threads.h> | |
60 | #include <pexpert/device_tree.h> | |
61 | #if MACH_KDB | |
62 | #include <machine/db_machdep.h> | |
63 | #include <ddb/db_aout.h> | |
64 | #include <ddb/db_access.h> | |
65 | #include <ddb/db_sym.h> | |
66 | #include <ddb/db_variables.h> | |
67 | #include <ddb/db_command.h> | |
68 | #include <ddb/db_output.h> | |
69 | #include <ddb/db_expr.h> | |
70 | #endif /* MACH_KDB */ | |
71 | ||
72 | /* Decimal powers: */ | |
73 | #define kilo (1000ULL) | |
74 | #define Mega (kilo * kilo) | |
75 | #define Giga (kilo * Mega) | |
76 | #define Tera (kilo * Giga) | |
77 | #define Peta (kilo * Tera) | |
78 | ||
79 | vm_offset_t hpetArea = 0; | |
80 | uint32_t hpetAreap = 0; | |
81 | uint64_t hpetFemto = 0; | |
82 | uint64_t hpetFreq = 0; | |
83 | uint64_t hpetCvt = 0; /* (TAKE OUT LATER) */ | |
84 | uint64_t hpetCvtt2n = 0; | |
85 | uint64_t hpetCvtn2t = 0; | |
86 | uint64_t tsc2hpet = 0; | |
87 | uint64_t hpet2tsc = 0; | |
88 | uint64_t bus2hpet = 0; | |
89 | uint64_t hpet2bus = 0; | |
90 | ||
91 | vm_offset_t rcbaArea = 0; | |
92 | uint32_t rcbaAreap = 0; | |
93 | ||
94 | static int (*hpet_req)(uint32_t apicid, void *arg, hpetRequest_t *hpet) = NULL; | |
95 | static void *hpet_arg = NULL; | |
96 | ||
97 | #if DEBUG | |
98 | #define DBG(x...) kprintf("DBG: " x) | |
99 | #else | |
100 | #define DBG(x...) | |
101 | #endif | |
102 | ||
103 | int | |
104 | hpet_register_callback(int (*hpet_reqst)(uint32_t apicid, | |
105 | void *arg, | |
106 | hpetRequest_t *hpet), | |
107 | void *arg) | |
108 | { | |
109 | hpet_req = hpet_reqst; | |
110 | hpet_arg = arg; | |
111 | return(0); | |
112 | } | |
113 | ||
114 | /* | |
115 | * This routine is called to obtain an HPET and have it assigned | |
116 | * to a CPU. It returns 0 if successful and non-zero if one could | |
117 | * not be assigned. | |
118 | */ | |
119 | int | |
120 | hpet_request(uint32_t cpu) | |
121 | { | |
122 | hpetRequest_t hpetReq; | |
123 | int rc; | |
124 | x86_lcpu_t *lcpu; | |
125 | x86_core_t *core; | |
126 | x86_pkg_t *pkg; | |
127 | boolean_t enabled; | |
128 | ||
129 | if (hpet_req == NULL) { | |
130 | return(-1); | |
131 | } | |
132 | ||
133 | /* | |
134 | * Deal with the case where the CPU # passed in is past the | |
135 | * value specified in cpus=n in boot-args. | |
136 | */ | |
137 | if (cpu >= real_ncpus) { | |
138 | enabled = ml_set_interrupts_enabled(FALSE); | |
139 | lcpu = cpu_to_lcpu(cpu); | |
140 | if (lcpu != NULL) { | |
141 | core = lcpu->core; | |
142 | pkg = core->package; | |
143 | ||
144 | if (lcpu->primary) { | |
145 | pkg->flags |= X86PKG_FL_HAS_HPET; | |
146 | } | |
147 | } | |
148 | ||
149 | ml_set_interrupts_enabled(enabled); | |
150 | return(0); | |
151 | } | |
152 | ||
153 | rc = (*hpet_req)(ml_get_apicid(cpu), hpet_arg, &hpetReq); | |
154 | if (rc != 0) { | |
155 | return(rc); | |
156 | } | |
157 | ||
158 | enabled = ml_set_interrupts_enabled(FALSE); | |
159 | lcpu = cpu_to_lcpu(cpu); | |
160 | core = lcpu->core; | |
161 | pkg = core->package; | |
162 | ||
163 | /* | |
164 | * Compute the address of the HPET. | |
165 | */ | |
166 | core->Hpet = (hpetTimer_t *)((uint8_t *)hpetArea + hpetReq.hpetOffset); | |
167 | core->HpetVec = hpetReq.hpetVector; | |
168 | ||
169 | /* | |
170 | * Enable interrupts | |
171 | */ | |
172 | core->Hpet->Config |= Tn_INT_ENB_CNF; | |
173 | ||
174 | /* | |
175 | * Save the configuration | |
176 | */ | |
177 | core->HpetCfg = core->Hpet->Config; | |
178 | core->HpetCmp = 0; | |
179 | ||
180 | /* | |
181 | * If the CPU is the "primary" for the package, then | |
182 | * add the HPET to the package too. | |
183 | */ | |
184 | if (lcpu->primary) { | |
185 | pkg->Hpet = core->Hpet; | |
186 | pkg->HpetCfg = core->HpetCfg; | |
187 | pkg->HpetCmp = core->HpetCmp; | |
188 | pkg->flags |= X86PKG_FL_HAS_HPET; | |
189 | } | |
190 | ||
191 | ml_set_interrupts_enabled(enabled); | |
192 | ||
193 | return(0); | |
194 | } | |
195 | ||
196 | /* | |
197 | * Map the RCBA area. | |
198 | */ | |
199 | static void | |
200 | map_rcbaArea(void) | |
201 | { | |
202 | /* | |
203 | * Get RCBA area physical address and map it | |
204 | */ | |
205 | outl(cfgAdr, lpcCfg | (0xF0 & 0xFC)); | |
206 | rcbaAreap = inl(cfgDat | (0xF0 & 0x03)); | |
207 | rcbaArea = io_map_spec(rcbaAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO); | |
208 | kprintf("RCBA: vaddr = %lX, paddr = %08X\n", (unsigned long)rcbaArea, rcbaAreap); | |
209 | } | |
210 | ||
211 | /* | |
212 | * Initialize the HPET | |
213 | */ | |
214 | void | |
215 | hpet_init(void) | |
216 | { | |
217 | unsigned int *xmod; | |
218 | ||
219 | map_rcbaArea(); | |
220 | ||
221 | /* | |
222 | * Is the HPET memory already enabled? | |
223 | * If not, set address and enable. | |
224 | */ | |
225 | xmod = (uint32_t *)(rcbaArea + 0x3404); /* Point to the HPTC */ | |
226 | uint32_t hptc = *xmod; /* Get HPET config */ | |
227 | DBG(" current RCBA.HPTC: %08X\n", *xmod); | |
228 | if(!(hptc & hptcAE)) { | |
229 | DBG("HPET memory is not enabled, " | |
230 | "enabling and assigning to 0xFED00000 (hope that's ok)\n"); | |
231 | *xmod = (hptc & ~3) | hptcAE; | |
232 | } | |
233 | ||
234 | /* | |
235 | * Get physical address of HPET and map it. | |
236 | */ | |
237 | hpetAreap = hpetAddr | ((hptc & 3) << 12); | |
238 | hpetArea = io_map_spec(hpetAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO); | |
239 | kprintf("HPET: vaddr = %lX, paddr = %08X\n", (unsigned long)hpetArea, hpetAreap); | |
240 | ||
241 | /* | |
242 | * Extract the HPET tick rate. | |
243 | * The period of the HPET is reported in femtoseconds (10**-15s) | |
244 | * and convert to frequency in hertz. | |
245 | */ | |
246 | hpetFemto = (uint32_t)(((hpetReg_t *)hpetArea)->GCAP_ID >> 32); | |
247 | hpetFreq = (1 * Peta) / hpetFemto; | |
248 | ||
249 | /* | |
250 | * The conversion factor is the number of nanoseconds per HPET tick | |
251 | * with about 32 bits of fraction. The value is converted to a | |
252 | * base-2 fixed point number. To convert from HPET to nanoseconds, | |
253 | * multiply the value by the conversion factor using 96-bit arithmetic, | |
254 | * then shift right 32 bits. If the value is known to be small, | |
255 | * 64-bit arithmetic will work. | |
256 | */ | |
257 | ||
258 | /* | |
259 | * Begin conversion of base 10 femtoseconds to base 2, calculate: | |
260 | * - HPET ticks to nanoseconds conversion in base 2 fraction (* 2**32) | |
261 | * - nanoseconds to HPET ticks conversion | |
262 | */ | |
263 | hpetCvtt2n = (uint64_t)hpetFemto << 32; | |
264 | hpetCvtt2n = hpetCvtt2n / 1000000ULL; | |
265 | hpetCvtn2t = 0xFFFFFFFFFFFFFFFFULL / hpetCvtt2n; | |
266 | kprintf("HPET: Frequency = %6d.%04dMHz, " | |
267 | "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n", | |
268 | (uint32_t)(hpetFreq / Mega), (uint32_t)(hpetFreq % Mega), | |
269 | (uint32_t)(hpetCvtt2n >> 32), (uint32_t)hpetCvtt2n, | |
270 | (uint32_t)(hpetCvtn2t >> 32), (uint32_t)hpetCvtn2t); | |
271 | ||
272 | ||
273 | /* (TAKE OUT LATER) | |
274 | * Begin conversion of base 10 femtoseconds to base 2 | |
275 | * HPET ticks to nanoseconds in base 2 fraction (times 1048576) | |
276 | */ | |
277 | hpetCvt = (uint64_t)hpetFemto << 20; | |
278 | hpetCvt = hpetCvt / 1000000ULL; | |
279 | ||
280 | /* Calculate conversion from TSC to HPET */ | |
281 | tsc2hpet = tmrCvt(tscFCvtt2n, hpetCvtn2t); | |
282 | DBG(" CVT: TSC to HPET = %08X.%08X\n", | |
283 | (uint32_t)(tsc2hpet >> 32), (uint32_t)tsc2hpet); | |
284 | ||
285 | /* Calculate conversion from HPET to TSC */ | |
286 | hpet2tsc = tmrCvt(hpetCvtt2n, tscFCvtn2t); | |
287 | DBG(" CVT: HPET to TSC = %08X.%08X\n", | |
288 | (uint32_t)(hpet2tsc >> 32), (uint32_t)hpet2tsc); | |
289 | ||
290 | /* Calculate conversion from BUS to HPET */ | |
291 | bus2hpet = tmrCvt(busFCvtt2n, hpetCvtn2t); | |
292 | DBG(" CVT: BUS to HPET = %08X.%08X\n", | |
293 | (uint32_t)(bus2hpet >> 32), (uint32_t)bus2hpet); | |
294 | ||
295 | /* Calculate conversion from HPET to BUS */ | |
296 | hpet2bus = tmrCvt(hpetCvtt2n, busFCvtn2t); | |
297 | DBG(" CVT: HPET to BUS = %08X.%08X\n", | |
298 | (uint32_t)(hpet2bus >> 32), (uint32_t)hpet2bus); | |
299 | ||
300 | #if MACH_KDB | |
301 | db_display_hpet((hpetReg_t *)hpetArea); /* (BRINGUP) */ | |
302 | #endif | |
303 | } | |
304 | ||
305 | /* | |
306 | * This routine is used to get various information about the HPET | |
307 | * without having to export gobs of globals. It fills in a data | |
308 | * structure with the info. | |
309 | */ | |
310 | void | |
311 | hpet_get_info(hpetInfo_t *info) | |
312 | { | |
313 | info->hpetCvtt2n = hpetCvtt2n; | |
314 | info->hpetCvtn2t = hpetCvtn2t; | |
315 | info->tsc2hpet = tsc2hpet; | |
316 | info->hpet2tsc = hpet2tsc; | |
317 | info->bus2hpet = bus2hpet; | |
318 | info->hpet2bus = hpet2bus; | |
319 | /* | |
320 | * XXX | |
321 | * We're repurposing the rcbaArea so we can use the HPET. | |
322 | * Eventually we'll rename this correctly. | |
323 | */ | |
324 | info->rcbaArea = hpetArea; | |
325 | info->rcbaAreap = hpetAreap; | |
326 | } | |
327 | ||
328 | ||
329 | /* | |
330 | * This routine is called by the HPET driver | |
331 | * when it assigns an HPET timer to a processor. | |
332 | * | |
333 | * XXX with the new callback into the HPET driver, | |
334 | * this routine will be deprecated. | |
335 | */ | |
336 | void | |
337 | ml_hpet_cfg(uint32_t cpu, uint32_t hpetVect) | |
338 | { | |
339 | uint64_t *hpetVaddr; | |
340 | hpetTimer_t *hpet; | |
341 | x86_lcpu_t *lcpu; | |
342 | x86_core_t *core; | |
343 | x86_pkg_t *pkg; | |
344 | boolean_t enabled; | |
345 | ||
346 | if(cpu > 1) { | |
347 | panic("ml_hpet_cfg: invalid cpu = %d\n", cpu); | |
348 | } | |
349 | ||
350 | lcpu = cpu_to_lcpu(cpu); | |
351 | core = lcpu->core; | |
352 | pkg = core->package; | |
353 | ||
354 | /* | |
355 | * Only deal with the primary CPU for the package. | |
356 | */ | |
357 | if (!lcpu->primary) | |
358 | return; | |
359 | ||
360 | enabled = ml_set_interrupts_enabled(FALSE); | |
361 | ||
362 | /* Calculate address of the HPET for this processor */ | |
363 | hpetVaddr = (uint64_t *)(((uintptr_t)&(((hpetReg_t *)hpetArea)->TIM1_CONF)) + (cpu << 5)); | |
364 | hpet = (hpetTimer_t *)hpetVaddr; | |
365 | ||
366 | DBG("ml_hpet_cfg: HPET for cpu %d at %p, vector = %d\n", | |
367 | cpu, hpetVaddr, hpetVect); | |
368 | ||
369 | /* Save the address and vector of the HPET for this processor */ | |
370 | core->Hpet = hpet; | |
371 | core->HpetVec = hpetVect; | |
372 | ||
373 | /* | |
374 | * Enable interrupts | |
375 | */ | |
376 | core->Hpet->Config |= Tn_INT_ENB_CNF; | |
377 | ||
378 | /* Save the configuration */ | |
379 | core->HpetCfg = core->Hpet->Config; | |
380 | core->HpetCmp = 0; | |
381 | ||
382 | /* | |
383 | * We're only doing this for the primary CPU, so go | |
384 | * ahead and add the HPET to the package too. | |
385 | */ | |
386 | pkg->Hpet = core->Hpet; | |
387 | pkg->HpetVec = core->HpetVec; | |
388 | pkg->HpetCfg = core->HpetCfg; | |
389 | pkg->HpetCmp = core->HpetCmp; | |
390 | pkg->flags |= X86PKG_FL_HAS_HPET; | |
391 | ||
392 | ml_set_interrupts_enabled(enabled); | |
393 | } | |
394 | ||
395 | /* | |
396 | * This is the HPET interrupt handler. | |
397 | * | |
398 | * It just hands off to the power management code so that the | |
399 | * appropriate things get done there. | |
400 | */ | |
401 | int | |
402 | HPETInterrupt(void) | |
403 | { | |
404 | ||
405 | /* All we do here is to bump the count */ | |
406 | x86_package()->HpetInt++; | |
407 | ||
408 | /* | |
409 | * Let power management do it's thing. | |
410 | */ | |
411 | pmHPETInterrupt(); | |
412 | ||
413 | /* Return and show that the 'rupt has been handled... */ | |
414 | return 1; | |
415 | } | |
416 | ||
417 | ||
418 | static hpetReg_t saved_hpet; | |
419 | ||
420 | void | |
421 | hpet_save(void) | |
422 | { | |
423 | hpetReg_t *from = (hpetReg_t *) hpetArea; | |
424 | hpetReg_t *to = &saved_hpet; | |
425 | ||
426 | to->GEN_CONF = from->GEN_CONF; | |
427 | to->TIM0_CONF = from->TIM0_CONF; | |
428 | to->TIM0_COMP = from->TIM0_COMP; | |
429 | to->TIM1_CONF = from->TIM1_CONF; | |
430 | to->TIM1_COMP = from->TIM1_COMP; | |
431 | to->TIM2_CONF = from->TIM2_CONF; | |
432 | to->TIM2_COMP = from->TIM2_COMP; | |
433 | to->MAIN_CNT = from->MAIN_CNT; | |
434 | } | |
435 | ||
436 | void | |
437 | hpet_restore(void) | |
438 | { | |
439 | hpetReg_t *from = &saved_hpet; | |
440 | hpetReg_t *to = (hpetReg_t *) hpetArea; | |
441 | ||
442 | /* | |
443 | * Is the HPET memory already enabled? | |
444 | * If not, set address and enable. | |
445 | */ | |
446 | uint32_t *hptcp = (uint32_t *)(rcbaArea + 0x3404); | |
447 | uint32_t hptc = *hptcp; | |
448 | if(!(hptc & hptcAE)) { | |
449 | DBG("HPET memory is not enabled, " | |
450 | "enabling and assigning to 0xFED00000 (hope that's ok)\n"); | |
451 | *hptcp = (hptc & ~3) | hptcAE; | |
452 | } | |
453 | ||
454 | to->GEN_CONF = from->GEN_CONF & ~1; | |
455 | ||
456 | to->TIM0_CONF = from->TIM0_CONF; | |
457 | to->TIM0_COMP = from->TIM0_COMP; | |
458 | to->TIM1_CONF = from->TIM1_CONF; | |
459 | to->TIM1_COMP = from->TIM1_COMP; | |
460 | to->TIM2_CONF = from->TIM2_CONF; | |
461 | to->TIM2_COMP = from->TIM2_COMP; | |
462 | to->GINTR_STA = -1ULL; | |
463 | to->MAIN_CNT = from->MAIN_CNT; | |
464 | ||
465 | to->GEN_CONF = from->GEN_CONF; | |
466 | } | |
467 | ||
468 | /* | |
469 | * Read the HPET timer | |
470 | * | |
471 | */ | |
472 | uint64_t | |
473 | rdHPET(void) | |
474 | { | |
475 | hpetReg_t *hpetp = (hpetReg_t *) hpetArea; | |
476 | volatile uint32_t *regp = (uint32_t *) &hpetp->MAIN_CNT; | |
477 | uint32_t high; | |
478 | uint32_t low; | |
479 | ||
480 | do { | |
481 | high = *(regp + 1); | |
482 | low = *regp; | |
483 | } while (high != *(regp + 1)); | |
484 | ||
485 | return (((uint64_t) high) << 32) | low; | |
486 | } | |
487 | ||
488 | #if MACH_KDB | |
489 | ||
490 | #define HI32(x) ((uint32_t)(((x) >> 32) & 0xFFFFFFFF)) | |
491 | #define LO32(x) ((uint32_t)((x) & 0xFFFFFFFF)) | |
492 | ||
493 | /* | |
494 | * Displays HPET memory mapped area | |
495 | * hp | |
496 | */ | |
497 | void | |
498 | db_hpet(__unused db_expr_t addr, __unused int have_addr, __unused db_expr_t count, __unused char *modif) | |
499 | { | |
500 | ||
501 | db_display_hpet((hpetReg_t *) hpetArea); /* Dump out the HPET | |
502 | * stuff */ | |
503 | return; | |
504 | } | |
505 | ||
506 | void | |
507 | db_display_hpet(hpetReg_t *hpt) | |
508 | { | |
509 | uint64_t cmain; | |
510 | ||
511 | cmain = hpt->MAIN_CNT; /* Get the main timer */ | |
512 | ||
513 | /* General capabilities */ | |
514 | db_printf(" GCAP_ID = %08X.%08X\n", | |
515 | HI32(hpt->GCAP_ID), LO32(hpt->GCAP_ID)); | |
516 | /* General configuration */ | |
517 | db_printf(" GEN_CONF = %08X.%08X\n", | |
518 | HI32(hpt->GEN_CONF), LO32(hpt->GEN_CONF)); | |
519 | /* General Interrupt status */ | |
520 | db_printf("GINTR_STA = %08X.%08X\n", | |
521 | HI32(hpt->GINTR_STA), LO32(hpt->GINTR_STA)); | |
522 | /* Main counter */ | |
523 | db_printf(" MAIN_CNT = %08X.%08X\n", | |
524 | HI32(cmain), LO32(cmain)); | |
525 | /* Timer 0 config and cap */ | |
526 | db_printf("TIM0_CONF = %08X.%08X\n", | |
527 | HI32(hpt->TIM0_CONF), LO32(hpt->TIM0_CONF)); | |
528 | /* Timer 0 comparator */ | |
529 | db_printf("TIM0_COMP = %08X.%08X\n", | |
530 | HI32(hpt->TIM0_COMP), LO32(hpt->TIM0_COMP)); | |
531 | /* Timer 1 config and cap */ | |
532 | db_printf("TIM0_CONF = %08X.%08X\n", | |
533 | HI32(hpt->TIM1_CONF), LO32(hpt->TIM1_CONF)); | |
534 | /* Timer 1 comparator */ | |
535 | db_printf("TIM1_COMP = %08X.%08X\n", | |
536 | HI32(hpt->TIM1_COMP), LO32(hpt->TIM1_COMP)); | |
537 | /* Timer 2 config and cap */ | |
538 | db_printf("TIM2_CONF = %08X.%08X\n", | |
539 | HI32(hpt->TIM2_CONF), LO32(hpt->TIM2_CONF)); | |
540 | /* Timer 2 comparator */ | |
541 | db_printf("TIM2_COMP = %08X.%08X\n", | |
542 | HI32(hpt->TIM2_COMP), LO32(hpt->TIM2_COMP)); | |
543 | ||
544 | db_printf("\nHPET Frequency = %d.%05dMHz\n", | |
545 | (uint32_t) (hpetFreq / 1000000), (uint32_t) (hpetFreq % 1000000)); | |
546 | } | |
547 | #endif |