]>
git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/hpet.c
46ddefa7d6630d3fb3a188ef48d8ea7a366f03b1
2 * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
24 #include <mach/vm_param.h>
25 #include <mach/vm_prot.h>
26 #include <mach/machine.h>
27 #include <mach/time_value.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>
38 #include <vm/vm_kern.h>
39 #include <i386/pmap.h>
40 #include <i386/misc_protos.h>
41 #include <i386/cpuid.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>
50 #include <i386/hpet.h>
51 #include <i386/pmCPU.h>
52 #include <pexpert/device_tree.h>
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>
65 #define kilo (1000ULL)
66 #define Mega (kilo * kilo)
67 #define Giga (kilo * Mega)
68 #define Tera (kilo * Giga)
69 #define Peta (kilo * Tera)
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;
83 uint32_t rcbaArea
= 0;
84 uint32_t rcbaAreap
= 0;
87 #define DBG(x...) kprintf("DBG: " x)
99 * Get RCBA area physical address and map it
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
);
108 * Initialize the HPET
118 * Is the HPET memory already enabled?
119 * If not, set address and enable.
121 xmod
= (uint32_t *)(rcbaArea
+ 0x3404); /* Point to the HPTC */
122 uint32_t hptc
= *xmod
; /* Get HPET config */
123 DBG(" current RCBA.HPTC: %08X\n", *xmod
);
124 if(!(hptc
& hptcAE
)) {
125 DBG("HPET memory is not enabled, "
126 "enabling and assigning to 0xFED00000 (hope that's ok)\n");
127 *xmod
= (hptc
& ~3) | hptcAE
;
131 * Get physical address of HPET and map it.
133 hpetAreap
= hpetAddr
| ((hptc
& 3) << 12);
134 hpetArea
= io_map_spec(hpetAreap
& -4096, PAGE_SIZE
* 4, VM_WIMG_IO
);
135 kprintf("HPET: vaddr = %08X, paddr = %08X\n", hpetArea
, hpetAreap
);
138 * Extract the HPET tick rate.
139 * The period of the HPET is reported in femtoseconds (10**-15s)
140 * and convert to frequency in hertz.
142 hpetFemto
= (uint32_t)(((hpetReg_t
*)hpetArea
)->GCAP_ID
>> 32);
143 hpetFreq
= (1 * Peta
) / hpetFemto
;
146 * The conversion factor is the number of nanoseconds per HPET tick
147 * with about 32 bits of fraction. The value is converted to a
148 * base-2 fixed point number. To convert from HPET to nanoseconds,
149 * multiply the value by the conversion factor using 96-bit arithmetic,
150 * then shift right 32 bits. If the value is known to be small,
151 * 64-bit arithmetic will work.
155 * Begin conversion of base 10 femtoseconds to base 2, calculate:
156 * - HPET ticks to nanoseconds conversion in base 2 fraction (* 2**32)
157 * - nanoseconds to HPET ticks conversion
159 hpetCvtt2n
= (uint64_t)hpetFemto
<< 32;
160 hpetCvtt2n
= hpetCvtt2n
/ 1000000ULL;
161 hpetCvtn2t
= 0xFFFFFFFFFFFFFFFFULL
/ hpetCvtt2n
;
162 kprintf("HPET: Frequency = %6d.%04dMHz, "
163 "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n",
164 (uint32_t)(hpetFreq
/ Mega
), (uint32_t)(hpetFreq
% Mega
),
165 (uint32_t)(hpetCvtt2n
>> 32), (uint32_t)hpetCvtt2n
,
166 (uint32_t)(hpetCvtn2t
>> 32), (uint32_t)hpetCvtn2t
);
170 * Begin conversion of base 10 femtoseconds to base 2
171 * HPET ticks to nanoseconds in base 2 fraction (times 1048576)
173 hpetCvt
= (uint64_t)hpetFemto
<< 20;
174 hpetCvt
= hpetCvt
/ 1000000ULL;
176 /* Calculate conversion from TSC to HPET */
177 tsc2hpet
= tmrCvt(tscFCvtt2n
, hpetCvtn2t
);
178 DBG(" CVT: TSC to HPET = %08X.%08X\n",
179 (uint32_t)(tsc2hpet
>> 32), (uint32_t)tsc2hpet
);
181 /* Calculate conversion from HPET to TSC */
182 hpet2tsc
= tmrCvt(hpetCvtt2n
, tscFCvtn2t
);
183 DBG(" CVT: HPET to TSC = %08X.%08X\n",
184 (uint32_t)(hpet2tsc
>> 32), (uint32_t)hpet2tsc
);
186 /* Calculate conversion from BUS to HPET */
187 bus2hpet
= tmrCvt(busFCvtt2n
, hpetCvtn2t
);
188 DBG(" CVT: BUS to HPET = %08X.%08X\n",
189 (uint32_t)(bus2hpet
>> 32), (uint32_t)bus2hpet
);
191 /* Calculate conversion from HPET to BUS */
192 hpet2bus
= tmrCvt(hpetCvtt2n
, busFCvtn2t
);
193 DBG(" CVT: HPET to BUS = %08X.%08X\n",
194 (uint32_t)(hpet2bus
>> 32), (uint32_t)hpet2bus
);
196 /* Make sure the counter is off in the HPET configuration flags */
197 uint64_t hpetcon
= ((hpetReg_t
*)hpetArea
)->GEN_CONF
;
198 hpetcon
= hpetcon
& ~1;
199 ((hpetReg_t
*)hpetArea
)->GEN_CONF
= hpetcon
;
202 * Convert current TSC to HPET value,
203 * set it, and start it ticking.
205 uint64_t currtsc
= rdtsc64();
206 uint64_t tscInHPET
= tmrCvt(currtsc
, tsc2hpet
);
207 ((hpetReg_t
*)hpetArea
)->MAIN_CNT
= tscInHPET
;
208 hpetcon
= hpetcon
| 1;
209 ((hpetReg_t
*)hpetArea
)->GEN_CONF
= hpetcon
;
210 kprintf("HPET started: TSC = %08X.%08X, HPET = %08X.%08X\n",
211 (uint32_t)(currtsc
>> 32), (uint32_t)currtsc
,
212 (uint32_t)(tscInHPET
>> 32), (uint32_t)tscInHPET
);
215 db_display_hpet((hpetReg_t
*)hpetArea
); /* (BRINGUP) */
220 * This routine is used to get various information about the HPET
221 * without having to export gobs of globals. It fills in a data
222 * structure with the info.
225 hpet_get_info(hpetInfo_t
*info
)
227 info
->hpetCvtt2n
= hpetCvtt2n
;
228 info
->hpetCvtn2t
= hpetCvtn2t
;
229 info
->tsc2hpet
= tsc2hpet
;
230 info
->hpet2tsc
= hpet2tsc
;
231 info
->bus2hpet
= bus2hpet
;
232 info
->hpet2bus
= hpet2bus
;
233 info
->rcbaArea
= rcbaArea
;
234 info
->rcbaAreap
= rcbaAreap
;
239 * This routine is called by the HPET driver
240 * when it assigns an HPET timer to a processor
244 ml_hpet_cfg(uint32_t cpu
, uint32_t hpetVect
)
250 panic("ml_hpet_cfg: invalid cpu = %d\n", cpu
);
253 /* Calculate address of the HPET for this processor */
254 hpetVaddr
= (uint64_t *)(((uint32_t)&(((hpetReg_t
*)hpetArea
)->TIM1_CONF
)) + (cpu
<< 5));
256 DBG("ml_hpet_cfg: HPET for cpu %d at %08X, vector = %d\n",
257 cpu
, hpetVaddr
, hpetVect
);
259 /* Save the address and vector of the HPET for this processor */
260 cpu_data_ptr
[cpu
]->cpu_pmHpet
= (uint64_t *)hpetVaddr
;
261 cpu_data_ptr
[cpu
]->cpu_pmHpetVec
= hpetVect
;
263 /* Enable the interruptions now that we have a vector */
264 hpetcnf
= *hpetVaddr
;
265 hpetcnf
= hpetcnf
| Tn_INT_ENB_CNF
;
266 *hpetVaddr
= hpetcnf
;
268 /* Save the configuration */
269 cpu_data_ptr
[cpu
]->cpu_pmStats
.pmHpetCfg
= hpetcnf
;
270 cpu_data_ptr
[cpu
]->cpu_pmStats
.pmHpetCmp
= 0;
272 /* See if nap policy has changed now */
273 machine_nap_policy();
278 * This is the HPET interrupt handler.
280 * We really don't want to be here, but so far, I haven't figured out
281 * a way to cancel the interrupt. Hopefully, some day we will figure out
282 * how to do that or switch all timers to the HPET.
288 /* All we do here is to bump the count */
289 current_cpu_datap()->cpu_pmStats
.pmHPETRupt
++;
291 /* Return and show that the 'rupt has been handled... */
296 static hpetReg_t saved_hpet
;
298 void hpet_save( void )
300 hpetReg_t
*from
= (hpetReg_t
*) hpetArea
;
301 hpetReg_t
*to
= &saved_hpet
;
303 to
->GEN_CONF
= from
->GEN_CONF
;
304 to
->TIM0_CONF
= from
->TIM0_CONF
;
305 to
->TIM0_COMP
= from
->TIM0_COMP
;
306 to
->TIM1_CONF
= from
->TIM1_CONF
;
307 to
->TIM1_COMP
= from
->TIM1_COMP
;
308 to
->TIM2_CONF
= from
->TIM2_CONF
;
309 to
->TIM2_COMP
= from
->TIM2_COMP
;
310 to
->MAIN_CNT
= from
->MAIN_CNT
;
313 void hpet_restore( void )
315 hpetReg_t
*from
= &saved_hpet
;
316 hpetReg_t
*to
= (hpetReg_t
*) hpetArea
;
319 * Is the HPET memory already enabled?
320 * If not, set address and enable.
322 uint32_t *hptcp
= (uint32_t *)(rcbaArea
+ 0x3404);
323 uint32_t hptc
= *hptcp
;
324 if(!(hptc
& hptcAE
)) {
325 DBG("HPET memory is not enabled, "
326 "enabling and assigning to 0xFED00000 (hope that's ok)\n");
327 *hptcp
= (hptc
& ~3) | hptcAE
;
330 to
->GEN_CONF
= from
->GEN_CONF
& ~1;
332 to
->TIM0_CONF
= from
->TIM0_CONF
;
333 to
->TIM0_COMP
= from
->TIM0_COMP
;
334 to
->TIM1_CONF
= from
->TIM1_CONF
;
335 to
->TIM1_COMP
= from
->TIM1_COMP
;
336 to
->TIM2_CONF
= from
->TIM2_CONF
;
337 to
->TIM2_COMP
= from
->TIM2_COMP
;
338 to
->GINTR_STA
= -1ULL;
339 to
->MAIN_CNT
= from
->MAIN_CNT
;
341 to
->GEN_CONF
= from
->GEN_CONF
;
345 * Read the HPET timer
351 hpetReg_t
*hpetp
= (hpetReg_t
*) hpetArea
;
352 volatile uint32_t *regp
= (uint32_t *) &hpetp
->MAIN_CNT
;
359 } while (high
!= *(regp
+ 1));
361 return (((uint64_t) high
) << 32) | low
;
366 #define HI32(x) ((uint32_t)(((x) >> 32) & 0xFFFFFFFF))
367 #define LO32(x) ((uint32_t)((x) & 0xFFFFFFFF))
370 * Displays HPET memory mapped area
374 db_hpet(__unused db_expr_t addr
, __unused
int have_addr
, __unused db_expr_t count
, __unused
char *modif
)
377 db_display_hpet((hpetReg_t
*) hpetArea
); /* Dump out the HPET
383 db_display_hpet(hpetReg_t
* hpt
)
388 cmain
= hpt
->MAIN_CNT
; /* Get the main timer */
390 /* General capabilities */
391 db_printf(" GCAP_ID = %08X.%08X\n",
392 HI32(hpt
->GCAP_ID
), LO32(hpt
->GCAP_ID
));
393 /* General configuration */
394 db_printf(" GEN_CONF = %08X.%08X\n",
395 HI32(hpt
->GEN_CONF
), LO32(hpt
->GEN_CONF
));
396 /* General Interrupt status */
397 db_printf("GINTR_STA = %08X.%08X\n",
398 HI32(hpt
->GINTR_STA
), LO32(hpt
->GINTR_STA
));
400 db_printf(" MAIN_CNT = %08X.%08X\n",
401 HI32(cmain
), LO32(cmain
));
402 /* Timer 0 config and cap */
403 db_printf("TIM0_CONF = %08X.%08X\n",
404 HI32(hpt
->TIM0_CONF
), LO32(hpt
->TIM0_CONF
));
405 /* Timer 0 comparator */
406 db_printf("TIM0_COMP = %08X.%08X\n",
407 HI32(hpt
->TIM0_COMP
), LO32(hpt
->TIM0_COMP
));
408 /* Timer 1 config and cap */
409 db_printf("TIM0_CONF = %08X.%08X\n",
410 HI32(hpt
->TIM1_CONF
), LO32(hpt
->TIM1_CONF
));
411 /* Timer 1 comparator */
412 db_printf("TIM1_COMP = %08X.%08X\n",
413 HI32(hpt
->TIM1_COMP
), LO32(hpt
->TIM1_COMP
));
414 /* Timer 2 config and cap */
415 db_printf("TIM2_CONF = %08X.%08X\n",
416 HI32(hpt
->TIM2_CONF
), LO32(hpt
->TIM2_CONF
));
417 /* Timer 2 comparator */
418 db_printf("TIM2_COMP = %08X.%08X\n",
419 HI32(hpt
->TIM2_COMP
), LO32(hpt
->TIM2_COMP
));
421 db_printf("\nHPET Frequency = %d.%05dMHz\n",
422 (uint32_t) (hpetFreq
/ 1000000), (uint32_t) (hpetFreq
% 1000000));