]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/hpet.c
xnu-4570.71.2.tar.gz
[apple/xnu.git] / osfmk / i386 / hpet.c
CommitLineData
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>
b0d623f7
A
61
62/* Decimal powers: */
63#define kilo (1000ULL)
64#define Mega (kilo * kilo)
65#define Giga (kilo * Mega)
66#define Tera (kilo * Giga)
67#define Peta (kilo * Tera)
68
69vm_offset_t hpetArea = 0;
70uint32_t hpetAreap = 0;
71uint64_t hpetFemto = 0;
72uint64_t hpetFreq = 0;
73uint64_t hpetCvt = 0; /* (TAKE OUT LATER) */
74uint64_t hpetCvtt2n = 0;
75uint64_t hpetCvtn2t = 0;
76uint64_t tsc2hpet = 0;
77uint64_t hpet2tsc = 0;
78uint64_t bus2hpet = 0;
79uint64_t hpet2bus = 0;
80
81vm_offset_t rcbaArea = 0;
82uint32_t rcbaAreap = 0;
83
84static int (*hpet_req)(uint32_t apicid, void *arg, hpetRequest_t *hpet) = NULL;
85static void *hpet_arg = NULL;
86
87#if DEBUG
88#define DBG(x...) kprintf("DBG: " x)
89#else
90#define DBG(x...)
91#endif
92
93int
94hpet_register_callback(int (*hpet_reqst)(uint32_t apicid,
95 void *arg,
96 hpetRequest_t *hpet),
97 void *arg)
98{
99 hpet_req = hpet_reqst;
100 hpet_arg = arg;
101 return(0);
102}
103
104/*
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
107 * not be assigned.
108 */
109int
110hpet_request(uint32_t cpu)
111{
112 hpetRequest_t hpetReq;
113 int rc;
114 x86_lcpu_t *lcpu;
115 x86_core_t *core;
116 x86_pkg_t *pkg;
117 boolean_t enabled;
118
119 if (hpet_req == NULL) {
120 return(-1);
121 }
122
123 /*
124 * Deal with the case where the CPU # passed in is past the
125 * value specified in cpus=n in boot-args.
126 */
127 if (cpu >= real_ncpus) {
128 enabled = ml_set_interrupts_enabled(FALSE);
129 lcpu = cpu_to_lcpu(cpu);
130 if (lcpu != NULL) {
131 core = lcpu->core;
132 pkg = core->package;
133
134 if (lcpu->primary) {
135 pkg->flags |= X86PKG_FL_HAS_HPET;
136 }
137 }
138
139 ml_set_interrupts_enabled(enabled);
140 return(0);
141 }
142
143 rc = (*hpet_req)(ml_get_apicid(cpu), hpet_arg, &hpetReq);
144 if (rc != 0) {
145 return(rc);
146 }
147
148 enabled = ml_set_interrupts_enabled(FALSE);
149 lcpu = cpu_to_lcpu(cpu);
150 core = lcpu->core;
151 pkg = core->package;
152
153 /*
154 * Compute the address of the HPET.
155 */
156 core->Hpet = (hpetTimer_t *)((uint8_t *)hpetArea + hpetReq.hpetOffset);
157 core->HpetVec = hpetReq.hpetVector;
158
159 /*
160 * Enable interrupts
161 */
162 core->Hpet->Config |= Tn_INT_ENB_CNF;
163
164 /*
165 * Save the configuration
166 */
167 core->HpetCfg = core->Hpet->Config;
168 core->HpetCmp = 0;
169
170 /*
171 * If the CPU is the "primary" for the package, then
172 * add the HPET to the package too.
173 */
174 if (lcpu->primary) {
175 pkg->Hpet = core->Hpet;
176 pkg->HpetCfg = core->HpetCfg;
177 pkg->HpetCmp = core->HpetCmp;
178 pkg->flags |= X86PKG_FL_HAS_HPET;
179 }
180
181 ml_set_interrupts_enabled(enabled);
182
183 return(0);
184}
185
186/*
187 * Map the RCBA area.
188 */
189static void
190map_rcbaArea(void)
191{
192 /*
193 * Get RCBA area physical address and map it
194 */
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);
199}
200
201/*
202 * Initialize the HPET
203 */
204void
205hpet_init(void)
206{
207 unsigned int *xmod;
208
209 map_rcbaArea();
210
211 /*
212 * Is the HPET memory already enabled?
213 * If not, set address and enable.
214 */
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;
222 }
223
224 /*
225 * Get physical address of HPET and map it.
226 */
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);
230
231 /*
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.
235 */
236 hpetFemto = (uint32_t)(((hpetReg_t *)hpetArea)->GCAP_ID >> 32);
237 hpetFreq = (1 * Peta) / hpetFemto;
238
239 /*
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.
246 */
247
248 /*
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
252 */
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);
261
262
263 /* (TAKE OUT LATER)
264 * Begin conversion of base 10 femtoseconds to base 2
265 * HPET ticks to nanoseconds in base 2 fraction (times 1048576)
266 */
267 hpetCvt = (uint64_t)hpetFemto << 20;
268 hpetCvt = hpetCvt / 1000000ULL;
269
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);
274
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);
279
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);
284
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);
b0d623f7
A
289}
290
291/*
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.
295 */
296void
297hpet_get_info(hpetInfo_t *info)
298{
299 info->hpetCvtt2n = hpetCvtt2n;
300 info->hpetCvtn2t = hpetCvtn2t;
301 info->tsc2hpet = tsc2hpet;
302 info->hpet2tsc = hpet2tsc;
303 info->bus2hpet = bus2hpet;
304 info->hpet2bus = hpet2bus;
305 /*
306 * XXX
307 * We're repurposing the rcbaArea so we can use the HPET.
308 * Eventually we'll rename this correctly.
309 */
310 info->rcbaArea = hpetArea;
311 info->rcbaAreap = hpetAreap;
312}
313
314
315/*
316 * This routine is called by the HPET driver
317 * when it assigns an HPET timer to a processor.
318 *
319 * XXX with the new callback into the HPET driver,
320 * this routine will be deprecated.
321 */
322void
323ml_hpet_cfg(uint32_t cpu, uint32_t hpetVect)
324{
325 uint64_t *hpetVaddr;
326 hpetTimer_t *hpet;
327 x86_lcpu_t *lcpu;
328 x86_core_t *core;
329 x86_pkg_t *pkg;
330 boolean_t enabled;
331
332 if(cpu > 1) {
333 panic("ml_hpet_cfg: invalid cpu = %d\n", cpu);
334 }
335
336 lcpu = cpu_to_lcpu(cpu);
337 core = lcpu->core;
338 pkg = core->package;
339
340 /*
341 * Only deal with the primary CPU for the package.
342 */
343 if (!lcpu->primary)
344 return;
345
346 enabled = ml_set_interrupts_enabled(FALSE);
347
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;
351
352 DBG("ml_hpet_cfg: HPET for cpu %d at %p, vector = %d\n",
353 cpu, hpetVaddr, hpetVect);
354
355 /* Save the address and vector of the HPET for this processor */
356 core->Hpet = hpet;
357 core->HpetVec = hpetVect;
358
359 /*
360 * Enable interrupts
361 */
362 core->Hpet->Config |= Tn_INT_ENB_CNF;
363
364 /* Save the configuration */
365 core->HpetCfg = core->Hpet->Config;
366 core->HpetCmp = 0;
367
368 /*
369 * We're only doing this for the primary CPU, so go
370 * ahead and add the HPET to the package too.
371 */
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;
377
378 ml_set_interrupts_enabled(enabled);
379}
380
381/*
382 * This is the HPET interrupt handler.
383 *
384 * It just hands off to the power management code so that the
385 * appropriate things get done there.
386 */
387int
388HPETInterrupt(void)
389{
390
391 /* All we do here is to bump the count */
392 x86_package()->HpetInt++;
393
394 /*
395 * Let power management do it's thing.
396 */
397 pmHPETInterrupt();
398
399 /* Return and show that the 'rupt has been handled... */
400 return 1;
401}
402
403
404static hpetReg_t saved_hpet;
405
406void
407hpet_save(void)
408{
409 hpetReg_t *from = (hpetReg_t *) hpetArea;
410 hpetReg_t *to = &saved_hpet;
411
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;
420}
421
422void
423hpet_restore(void)
424{
425 hpetReg_t *from = &saved_hpet;
426 hpetReg_t *to = (hpetReg_t *) hpetArea;
427
428 /*
429 * Is the HPET memory already enabled?
430 * If not, set address and enable.
431 */
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;
438 }
439
440 to->GEN_CONF = from->GEN_CONF & ~1;
441
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;
450
451 to->GEN_CONF = from->GEN_CONF;
452}
453
454/*
455 * Read the HPET timer
456 *
457 */
458uint64_t
459rdHPET(void)
460{
461 hpetReg_t *hpetp = (hpetReg_t *) hpetArea;
462 volatile uint32_t *regp = (uint32_t *) &hpetp->MAIN_CNT;
463 uint32_t high;
464 uint32_t low;
465
466 do {
467 high = *(regp + 1);
468 low = *regp;
469 } while (high != *(regp + 1));
470
471 return (((uint64_t) high) << 32) | low;
472}