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