]>
git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/hpet.c
2 * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 #include <mach/vm_param.h>
31 #include <mach/vm_prot.h>
32 #include <mach/machine.h>
33 #include <mach/time_value.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>
44 #include <vm/vm_kern.h>
45 #include <i386/cpuid.h>
46 #include <i386/machine_cpu.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>
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>
63 #define kilo (1000ULL)
64 #define Mega (kilo * kilo)
65 #define Giga (kilo * Mega)
66 #define Tera (kilo * Giga)
67 #define Peta (kilo * Tera)
69 vm_offset_t hpetArea
= 0;
70 uint32_t hpetAreap
= 0;
71 uint64_t hpetFemto
= 0;
72 uint64_t hpetFreq
= 0;
73 uint64_t hpetCvt
= 0; /* (TAKE OUT LATER) */
74 uint64_t hpetCvtt2n
= 0;
75 uint64_t hpetCvtn2t
= 0;
76 uint64_t tsc2hpet
= 0;
77 uint64_t hpet2tsc
= 0;
78 uint64_t bus2hpet
= 0;
79 uint64_t hpet2bus
= 0;
81 vm_offset_t rcbaArea
= 0;
82 uint32_t rcbaAreap
= 0;
84 static int (*hpet_req
)(uint32_t apicid
, void *arg
, hpetRequest_t
*hpet
) = NULL
;
85 static void *hpet_arg
= NULL
;
88 #define DBG(x...) kprintf("DBG: " x)
94 hpet_register_callback(int (*hpet_reqst
)(uint32_t apicid
,
99 hpet_req
= hpet_reqst
;
105 * This routine is called to obtain an HPET and have it assigned
106 * to a CPU. It returns 0 if successful and non-zero if one could
110 hpet_request(uint32_t cpu
)
112 hpetRequest_t hpetReq
;
119 if (hpet_req
== NULL
) {
124 * Deal with the case where the CPU # passed in is past the
125 * value specified in cpus=n in boot-args.
127 if (cpu
>= real_ncpus
) {
128 enabled
= ml_set_interrupts_enabled(FALSE
);
129 lcpu
= cpu_to_lcpu(cpu
);
135 pkg
->flags
|= X86PKG_FL_HAS_HPET
;
139 ml_set_interrupts_enabled(enabled
);
143 rc
= (*hpet_req
)(ml_get_apicid(cpu
), hpet_arg
, &hpetReq
);
148 enabled
= ml_set_interrupts_enabled(FALSE
);
149 lcpu
= cpu_to_lcpu(cpu
);
154 * Compute the address of the HPET.
156 core
->Hpet
= (hpetTimer_t
*)((uint8_t *)hpetArea
+ hpetReq
.hpetOffset
);
157 core
->HpetVec
= hpetReq
.hpetVector
;
162 core
->Hpet
->Config
|= Tn_INT_ENB_CNF
;
165 * Save the configuration
167 core
->HpetCfg
= core
->Hpet
->Config
;
171 * If the CPU is the "primary" for the package, then
172 * add the HPET to the package too.
175 pkg
->Hpet
= core
->Hpet
;
176 pkg
->HpetCfg
= core
->HpetCfg
;
177 pkg
->HpetCmp
= core
->HpetCmp
;
178 pkg
->flags
|= X86PKG_FL_HAS_HPET
;
181 ml_set_interrupts_enabled(enabled
);
193 * Get RCBA area physical address and map it
195 outl(cfgAdr
, lpcCfg
| (0xF0 & 0xFC));
196 rcbaAreap
= inl(cfgDat
| (0xF0 & 0x03));
197 rcbaArea
= io_map_spec(rcbaAreap
& -4096, PAGE_SIZE
* 4, VM_WIMG_IO
);
198 kprintf("RCBA: vaddr = %lX, paddr = %08X\n", (unsigned long)rcbaArea
, rcbaAreap
);
202 * Initialize the HPET
212 * Is the HPET memory already enabled?
213 * If not, set address and enable.
215 xmod
= (uint32_t *)(rcbaArea
+ 0x3404); /* Point to the HPTC */
216 uint32_t hptc
= *xmod
; /* Get HPET config */
217 DBG(" current RCBA.HPTC: %08X\n", *xmod
);
218 if(!(hptc
& hptcAE
)) {
219 DBG("HPET memory is not enabled, "
220 "enabling and assigning to 0xFED00000 (hope that's ok)\n");
221 *xmod
= (hptc
& ~3) | hptcAE
;
225 * Get physical address of HPET and map it.
227 hpetAreap
= hpetAddr
| ((hptc
& 3) << 12);
228 hpetArea
= io_map_spec(hpetAreap
& -4096, PAGE_SIZE
* 4, VM_WIMG_IO
);
229 kprintf("HPET: vaddr = %lX, paddr = %08X\n", (unsigned long)hpetArea
, hpetAreap
);
232 * Extract the HPET tick rate.
233 * The period of the HPET is reported in femtoseconds (10**-15s)
234 * and convert to frequency in hertz.
236 hpetFemto
= (uint32_t)(((hpetReg_t
*)hpetArea
)->GCAP_ID
>> 32);
237 hpetFreq
= (1 * Peta
) / hpetFemto
;
240 * The conversion factor is the number of nanoseconds per HPET tick
241 * with about 32 bits of fraction. The value is converted to a
242 * base-2 fixed point number. To convert from HPET to nanoseconds,
243 * multiply the value by the conversion factor using 96-bit arithmetic,
244 * then shift right 32 bits. If the value is known to be small,
245 * 64-bit arithmetic will work.
249 * Begin conversion of base 10 femtoseconds to base 2, calculate:
250 * - HPET ticks to nanoseconds conversion in base 2 fraction (* 2**32)
251 * - nanoseconds to HPET ticks conversion
253 hpetCvtt2n
= (uint64_t)hpetFemto
<< 32;
254 hpetCvtt2n
= hpetCvtt2n
/ 1000000ULL;
255 hpetCvtn2t
= 0xFFFFFFFFFFFFFFFFULL
/ hpetCvtt2n
;
256 kprintf("HPET: Frequency = %6d.%04dMHz, "
257 "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n",
258 (uint32_t)(hpetFreq
/ Mega
), (uint32_t)(hpetFreq
% Mega
),
259 (uint32_t)(hpetCvtt2n
>> 32), (uint32_t)hpetCvtt2n
,
260 (uint32_t)(hpetCvtn2t
>> 32), (uint32_t)hpetCvtn2t
);
264 * Begin conversion of base 10 femtoseconds to base 2
265 * HPET ticks to nanoseconds in base 2 fraction (times 1048576)
267 hpetCvt
= (uint64_t)hpetFemto
<< 20;
268 hpetCvt
= hpetCvt
/ 1000000ULL;
270 /* Calculate conversion from TSC to HPET */
271 tsc2hpet
= tmrCvt(tscFCvtt2n
, hpetCvtn2t
);
272 DBG(" CVT: TSC to HPET = %08X.%08X\n",
273 (uint32_t)(tsc2hpet
>> 32), (uint32_t)tsc2hpet
);
275 /* Calculate conversion from HPET to TSC */
276 hpet2tsc
= tmrCvt(hpetCvtt2n
, tscFCvtn2t
);
277 DBG(" CVT: HPET to TSC = %08X.%08X\n",
278 (uint32_t)(hpet2tsc
>> 32), (uint32_t)hpet2tsc
);
280 /* Calculate conversion from BUS to HPET */
281 bus2hpet
= tmrCvt(busFCvtt2n
, hpetCvtn2t
);
282 DBG(" CVT: BUS to HPET = %08X.%08X\n",
283 (uint32_t)(bus2hpet
>> 32), (uint32_t)bus2hpet
);
285 /* Calculate conversion from HPET to BUS */
286 hpet2bus
= tmrCvt(hpetCvtt2n
, busFCvtn2t
);
287 DBG(" CVT: HPET to BUS = %08X.%08X\n",
288 (uint32_t)(hpet2bus
>> 32), (uint32_t)hpet2bus
);
292 * This routine is used to get various information about the HPET
293 * without having to export gobs of globals. It fills in a data
294 * structure with the info.
297 hpet_get_info(hpetInfo_t
*info
)
299 info
->hpetCvtt2n
= hpetCvtt2n
;
300 info
->hpetCvtn2t
= hpetCvtn2t
;
301 info
->tsc2hpet
= tsc2hpet
;
302 info
->hpet2tsc
= hpet2tsc
;
303 info
->bus2hpet
= bus2hpet
;
304 info
->hpet2bus
= hpet2bus
;
307 * We're repurposing the rcbaArea so we can use the HPET.
308 * Eventually we'll rename this correctly.
310 info
->rcbaArea
= hpetArea
;
311 info
->rcbaAreap
= hpetAreap
;
316 * This routine is called by the HPET driver
317 * when it assigns an HPET timer to a processor.
319 * XXX with the new callback into the HPET driver,
320 * this routine will be deprecated.
323 ml_hpet_cfg(uint32_t cpu
, uint32_t hpetVect
)
333 panic("ml_hpet_cfg: invalid cpu = %d\n", cpu
);
336 lcpu
= cpu_to_lcpu(cpu
);
341 * Only deal with the primary CPU for the package.
346 enabled
= ml_set_interrupts_enabled(FALSE
);
348 /* Calculate address of the HPET for this processor */
349 hpetVaddr
= (uint64_t *)(((uintptr_t)&(((hpetReg_t
*)hpetArea
)->TIM1_CONF
)) + (cpu
<< 5));
350 hpet
= (hpetTimer_t
*)hpetVaddr
;
352 DBG("ml_hpet_cfg: HPET for cpu %d at %p, vector = %d\n",
353 cpu
, hpetVaddr
, hpetVect
);
355 /* Save the address and vector of the HPET for this processor */
357 core
->HpetVec
= hpetVect
;
362 core
->Hpet
->Config
|= Tn_INT_ENB_CNF
;
364 /* Save the configuration */
365 core
->HpetCfg
= core
->Hpet
->Config
;
369 * We're only doing this for the primary CPU, so go
370 * ahead and add the HPET to the package too.
372 pkg
->Hpet
= core
->Hpet
;
373 pkg
->HpetVec
= core
->HpetVec
;
374 pkg
->HpetCfg
= core
->HpetCfg
;
375 pkg
->HpetCmp
= core
->HpetCmp
;
376 pkg
->flags
|= X86PKG_FL_HAS_HPET
;
378 ml_set_interrupts_enabled(enabled
);
382 * This is the HPET interrupt handler.
384 * It just hands off to the power management code so that the
385 * appropriate things get done there.
391 /* All we do here is to bump the count */
392 x86_package()->HpetInt
++;
395 * Let power management do it's thing.
399 /* Return and show that the 'rupt has been handled... */
404 static hpetReg_t saved_hpet
;
409 hpetReg_t
*from
= (hpetReg_t
*) hpetArea
;
410 hpetReg_t
*to
= &saved_hpet
;
412 to
->GEN_CONF
= from
->GEN_CONF
;
413 to
->TIM0_CONF
= from
->TIM0_CONF
;
414 to
->TIM0_COMP
= from
->TIM0_COMP
;
415 to
->TIM1_CONF
= from
->TIM1_CONF
;
416 to
->TIM1_COMP
= from
->TIM1_COMP
;
417 to
->TIM2_CONF
= from
->TIM2_CONF
;
418 to
->TIM2_COMP
= from
->TIM2_COMP
;
419 to
->MAIN_CNT
= from
->MAIN_CNT
;
425 hpetReg_t
*from
= &saved_hpet
;
426 hpetReg_t
*to
= (hpetReg_t
*) hpetArea
;
429 * Is the HPET memory already enabled?
430 * If not, set address and enable.
432 uint32_t *hptcp
= (uint32_t *)(rcbaArea
+ 0x3404);
433 uint32_t hptc
= *hptcp
;
434 if(!(hptc
& hptcAE
)) {
435 DBG("HPET memory is not enabled, "
436 "enabling and assigning to 0xFED00000 (hope that's ok)\n");
437 *hptcp
= (hptc
& ~3) | hptcAE
;
440 to
->GEN_CONF
= from
->GEN_CONF
& ~1;
442 to
->TIM0_CONF
= from
->TIM0_CONF
;
443 to
->TIM0_COMP
= from
->TIM0_COMP
;
444 to
->TIM1_CONF
= from
->TIM1_CONF
;
445 to
->TIM1_COMP
= from
->TIM1_COMP
;
446 to
->TIM2_CONF
= from
->TIM2_CONF
;
447 to
->TIM2_COMP
= from
->TIM2_COMP
;
448 to
->GINTR_STA
= -1ULL;
449 to
->MAIN_CNT
= from
->MAIN_CNT
;
451 to
->GEN_CONF
= from
->GEN_CONF
;
455 * Read the HPET timer
461 hpetReg_t
*hpetp
= (hpetReg_t
*) hpetArea
;
462 volatile uint32_t *regp
= (uint32_t *) &hpetp
->MAIN_CNT
;
469 } while (high
!= *(regp
+ 1));
471 return (((uint64_t) high
) << 32) | low
;