]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/hpet.c
xnu-792.13.8.tar.gz
[apple/xnu.git] / osfmk / i386 / hpet.c
1 /*
2 * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30
31 #include <string.h>
32 #include <mach/vm_param.h>
33 #include <mach/vm_prot.h>
34 #include <mach/machine.h>
35 #include <mach/time_value.h>
36 #include <kern/spl.h>
37 #include <kern/assert.h>
38 #include <kern/debug.h>
39 #include <kern/misc_protos.h>
40 #include <kern/startup.h>
41 #include <kern/clock.h>
42 #include <kern/cpu_data.h>
43 #include <kern/processor.h>
44 #include <vm/vm_page.h>
45 #include <vm/pmap.h>
46 #include <vm/vm_kern.h>
47 #include <i386/pmap.h>
48 #include <i386/misc_protos.h>
49 #include <i386/cpuid.h>
50 #include <i386/mp.h>
51 #include <i386/machine_cpu.h>
52 #include <i386/machine_routines.h>
53 #include <i386/io_map_entries.h>
54 #include <architecture/i386/pio.h>
55 #include <i386/cpuid.h>
56 #include <i386/apic.h>
57 #include <i386/tsc.h>
58 #include <i386/hpet.h>
59 #include <i386/pmCPU.h>
60 #include <pexpert/device_tree.h>
61 #if MACH_KDB
62 #include <ddb/db_aout.h>
63 #include <ddb/db_access.h>
64 #include <ddb/db_sym.h>
65 #include <ddb/db_variables.h>
66 #include <ddb/db_command.h>
67 #include <ddb/db_output.h>
68 #include <ddb/db_expr.h>
69 #endif /* MACH_KDB */
70 #include <ddb/tr.h>
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 uint32_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 uint32_t rcbaArea = 0;
92 uint32_t rcbaAreap = 0;
93
94 #if DEBUG
95 #define DBG(x...) kprintf("DBG: " x)
96 #else
97 #define DBG(x...)
98 #endif
99
100 /*
101 * Map the RCBA area.
102 */
103 static void
104 map_rcbaArea(void)
105 {
106 /*
107 * Get RCBA area physical address and map it
108 */
109 outl(cfgAdr, lpcCfg | (0xF0 & 0xFC));
110 rcbaAreap = inl(cfgDat | (0xF0 & 0x03));
111 rcbaArea = io_map_spec(rcbaAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO);
112 kprintf("RCBA: vaddr = %08X, paddr = %08X\n", rcbaArea, rcbaAreap);
113 }
114
115 /*
116 * Initialize the HPET
117 */
118 void
119 hpet_init(void)
120 {
121 unsigned int *xmod;
122
123 map_rcbaArea();
124
125 /*
126 * Is the HPET memory already enabled?
127 * If not, set address and enable.
128 */
129 xmod = (uint32_t *)(rcbaArea + 0x3404); /* Point to the HPTC */
130 uint32_t hptc = *xmod; /* Get HPET config */
131 DBG(" current RCBA.HPTC: %08X\n", *xmod);
132 if(!(hptc & hptcAE)) {
133 DBG("HPET memory is not enabled, "
134 "enabling and assigning to 0xFED00000 (hope that's ok)\n");
135 *xmod = (hptc & ~3) | hptcAE;
136 }
137
138 /*
139 * Get physical address of HPET and map it.
140 */
141 hpetAreap = hpetAddr | ((hptc & 3) << 12);
142 hpetArea = io_map_spec(hpetAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO);
143 kprintf("HPET: vaddr = %08X, paddr = %08X\n", hpetArea, hpetAreap);
144
145 /*
146 * Extract the HPET tick rate.
147 * The period of the HPET is reported in femtoseconds (10**-15s)
148 * and convert to frequency in hertz.
149 */
150 hpetFemto = (uint32_t)(((hpetReg_t *)hpetArea)->GCAP_ID >> 32);
151 hpetFreq = (1 * Peta) / hpetFemto;
152
153 /*
154 * The conversion factor is the number of nanoseconds per HPET tick
155 * with about 32 bits of fraction. The value is converted to a
156 * base-2 fixed point number. To convert from HPET to nanoseconds,
157 * multiply the value by the conversion factor using 96-bit arithmetic,
158 * then shift right 32 bits. If the value is known to be small,
159 * 64-bit arithmetic will work.
160 */
161
162 /*
163 * Begin conversion of base 10 femtoseconds to base 2, calculate:
164 * - HPET ticks to nanoseconds conversion in base 2 fraction (* 2**32)
165 * - nanoseconds to HPET ticks conversion
166 */
167 hpetCvtt2n = (uint64_t)hpetFemto << 32;
168 hpetCvtt2n = hpetCvtt2n / 1000000ULL;
169 hpetCvtn2t = 0xFFFFFFFFFFFFFFFFULL / hpetCvtt2n;
170 kprintf("HPET: Frequency = %6d.%04dMHz, "
171 "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n",
172 (uint32_t)(hpetFreq / Mega), (uint32_t)(hpetFreq % Mega),
173 (uint32_t)(hpetCvtt2n >> 32), (uint32_t)hpetCvtt2n,
174 (uint32_t)(hpetCvtn2t >> 32), (uint32_t)hpetCvtn2t);
175
176
177 /* (TAKE OUT LATER)
178 * Begin conversion of base 10 femtoseconds to base 2
179 * HPET ticks to nanoseconds in base 2 fraction (times 1048576)
180 */
181 hpetCvt = (uint64_t)hpetFemto << 20;
182 hpetCvt = hpetCvt / 1000000ULL;
183
184 /* Calculate conversion from TSC to HPET */
185 tsc2hpet = tmrCvt(tscFCvtt2n, hpetCvtn2t);
186 DBG(" CVT: TSC to HPET = %08X.%08X\n",
187 (uint32_t)(tsc2hpet >> 32), (uint32_t)tsc2hpet);
188
189 /* Calculate conversion from HPET to TSC */
190 hpet2tsc = tmrCvt(hpetCvtt2n, tscFCvtn2t);
191 DBG(" CVT: HPET to TSC = %08X.%08X\n",
192 (uint32_t)(hpet2tsc >> 32), (uint32_t)hpet2tsc);
193
194 /* Calculate conversion from BUS to HPET */
195 bus2hpet = tmrCvt(busFCvtt2n, hpetCvtn2t);
196 DBG(" CVT: BUS to HPET = %08X.%08X\n",
197 (uint32_t)(bus2hpet >> 32), (uint32_t)bus2hpet);
198
199 /* Calculate conversion from HPET to BUS */
200 hpet2bus = tmrCvt(hpetCvtt2n, busFCvtn2t);
201 DBG(" CVT: HPET to BUS = %08X.%08X\n",
202 (uint32_t)(hpet2bus >> 32), (uint32_t)hpet2bus);
203
204 /* Make sure the counter is off in the HPET configuration flags */
205 uint64_t hpetcon = ((hpetReg_t *)hpetArea)->GEN_CONF;
206 hpetcon = hpetcon & ~1;
207 ((hpetReg_t *)hpetArea)->GEN_CONF = hpetcon;
208
209 /*
210 * Convert current TSC to HPET value,
211 * set it, and start it ticking.
212 */
213 uint64_t currtsc = rdtsc64();
214 uint64_t tscInHPET = tmrCvt(currtsc, tsc2hpet);
215 ((hpetReg_t *)hpetArea)->MAIN_CNT = tscInHPET;
216 hpetcon = hpetcon | 1;
217 ((hpetReg_t *)hpetArea)->GEN_CONF = hpetcon;
218 kprintf("HPET started: TSC = %08X.%08X, HPET = %08X.%08X\n",
219 (uint32_t)(currtsc >> 32), (uint32_t)currtsc,
220 (uint32_t)(tscInHPET >> 32), (uint32_t)tscInHPET);
221
222 #if MACH_KDB
223 db_display_hpet((hpetReg_t *)hpetArea); /* (BRINGUP) */
224 #endif
225 }
226
227 /*
228 * This routine is used to get various information about the HPET
229 * without having to export gobs of globals. It fills in a data
230 * structure with the info.
231 */
232 void
233 hpet_get_info(hpetInfo_t *info)
234 {
235 info->hpetCvtt2n = hpetCvtt2n;
236 info->hpetCvtn2t = hpetCvtn2t;
237 info->tsc2hpet = tsc2hpet;
238 info->hpet2tsc = hpet2tsc;
239 info->bus2hpet = bus2hpet;
240 info->hpet2bus = hpet2bus;
241 info->rcbaArea = rcbaArea;
242 info->rcbaAreap = rcbaAreap;
243 }
244
245
246 /*
247 * This routine is called by the HPET driver
248 * when it assigns an HPET timer to a processor
249 */
250
251 void
252 ml_hpet_cfg(uint32_t cpu, uint32_t hpetVect)
253 {
254 uint64_t *hpetVaddr;
255 uint64_t hpetcnf;
256
257 if(cpu > 1) {
258 panic("ml_hpet_cfg: invalid cpu = %d\n", cpu);
259 }
260
261 /* Calculate address of the HPET for this processor */
262 hpetVaddr = (uint64_t *)(((uint32_t)&(((hpetReg_t *)hpetArea)->TIM1_CONF)) + (cpu << 5));
263
264 DBG("ml_hpet_cfg: HPET for cpu %d at %08X, vector = %d\n",
265 cpu, hpetVaddr, hpetVect);
266
267 /* Save the address and vector of the HPET for this processor */
268 cpu_data_ptr[cpu]->cpu_pmHpet = (uint64_t *)hpetVaddr;
269 cpu_data_ptr[cpu]->cpu_pmHpetVec = hpetVect;
270
271 /* Enable the interruptions now that we have a vector */
272 hpetcnf = *hpetVaddr;
273 hpetcnf = hpetcnf | Tn_INT_ENB_CNF;
274 *hpetVaddr = hpetcnf;
275
276 /* Save the configuration */
277 cpu_data_ptr[cpu]->cpu_pmStats.pmHpetCfg = hpetcnf;
278 cpu_data_ptr[cpu]->cpu_pmStats.pmHpetCmp = 0;
279
280 /* See if nap policy has changed now */
281 machine_nap_policy();
282
283 }
284
285 /*
286 * This is the HPET interrupt handler.
287 *
288 * We really don't want to be here, but so far, I haven't figured out
289 * a way to cancel the interrupt. Hopefully, some day we will figure out
290 * how to do that or switch all timers to the HPET.
291 */
292 int
293 HPETInterrupt(void)
294 {
295
296 /* All we do here is to bump the count */
297 current_cpu_datap()->cpu_pmStats.pmHPETRupt++;
298
299 /* Return and show that the 'rupt has been handled... */
300 return 1;
301 }
302
303
304 static hpetReg_t saved_hpet;
305
306 void hpet_save( void )
307 {
308 hpetReg_t *from = (hpetReg_t *) hpetArea;
309 hpetReg_t *to = &saved_hpet;
310
311 to->GEN_CONF = from->GEN_CONF;
312 to->TIM0_CONF = from->TIM0_CONF;
313 to->TIM0_COMP = from->TIM0_COMP;
314 to->TIM1_CONF = from->TIM1_CONF;
315 to->TIM1_COMP = from->TIM1_COMP;
316 to->TIM2_CONF = from->TIM2_CONF;
317 to->TIM2_COMP = from->TIM2_COMP;
318 to->MAIN_CNT = from->MAIN_CNT;
319 }
320
321 void hpet_restore( void )
322 {
323 hpetReg_t *from = &saved_hpet;
324 hpetReg_t *to = (hpetReg_t *) hpetArea;
325
326 /*
327 * Is the HPET memory already enabled?
328 * If not, set address and enable.
329 */
330 uint32_t *hptcp = (uint32_t *)(rcbaArea + 0x3404);
331 uint32_t hptc = *hptcp;
332 if(!(hptc & hptcAE)) {
333 DBG("HPET memory is not enabled, "
334 "enabling and assigning to 0xFED00000 (hope that's ok)\n");
335 *hptcp = (hptc & ~3) | hptcAE;
336 }
337
338 to->GEN_CONF = from->GEN_CONF & ~1;
339
340 to->TIM0_CONF = from->TIM0_CONF;
341 to->TIM0_COMP = from->TIM0_COMP;
342 to->TIM1_CONF = from->TIM1_CONF;
343 to->TIM1_COMP = from->TIM1_COMP;
344 to->TIM2_CONF = from->TIM2_CONF;
345 to->TIM2_COMP = from->TIM2_COMP;
346 to->GINTR_STA = -1ULL;
347 to->MAIN_CNT = from->MAIN_CNT;
348
349 to->GEN_CONF = from->GEN_CONF;
350 }
351
352 /*
353 * Read the HPET timer
354 *
355 */
356 uint64_t
357 rdHPET(void)
358 {
359 hpetReg_t *hpetp = (hpetReg_t *) hpetArea;
360 volatile uint32_t *regp = (uint32_t *) &hpetp->MAIN_CNT;
361 uint32_t high;
362 uint32_t low;
363
364 do {
365 high = *(regp + 1);
366 low = *regp;
367 } while (high != *(regp + 1));
368
369 return (((uint64_t) high) << 32) | low;
370 }
371
372 #if MACH_KDB
373
374 #define HI32(x) ((uint32_t)(((x) >> 32) & 0xFFFFFFFF))
375 #define LO32(x) ((uint32_t)((x) & 0xFFFFFFFF))
376
377 /*
378 * Displays HPET memory mapped area
379 * hp
380 */
381 void
382 db_hpet(__unused db_expr_t addr, __unused int have_addr, __unused db_expr_t count, __unused char *modif)
383 {
384
385 db_display_hpet((hpetReg_t *) hpetArea); /* Dump out the HPET
386 * stuff */
387 return;
388 }
389
390 void
391 db_display_hpet(hpetReg_t * hpt)
392 {
393
394 uint64_t cmain;
395
396 cmain = hpt->MAIN_CNT; /* Get the main timer */
397
398 /* General capabilities */
399 db_printf(" GCAP_ID = %08X.%08X\n",
400 HI32(hpt->GCAP_ID), LO32(hpt->GCAP_ID));
401 /* General configuration */
402 db_printf(" GEN_CONF = %08X.%08X\n",
403 HI32(hpt->GEN_CONF), LO32(hpt->GEN_CONF));
404 /* General Interrupt status */
405 db_printf("GINTR_STA = %08X.%08X\n",
406 HI32(hpt->GINTR_STA), LO32(hpt->GINTR_STA));
407 /* Main counter */
408 db_printf(" MAIN_CNT = %08X.%08X\n",
409 HI32(cmain), LO32(cmain));
410 /* Timer 0 config and cap */
411 db_printf("TIM0_CONF = %08X.%08X\n",
412 HI32(hpt->TIM0_CONF), LO32(hpt->TIM0_CONF));
413 /* Timer 0 comparator */
414 db_printf("TIM0_COMP = %08X.%08X\n",
415 HI32(hpt->TIM0_COMP), LO32(hpt->TIM0_COMP));
416 /* Timer 1 config and cap */
417 db_printf("TIM0_CONF = %08X.%08X\n",
418 HI32(hpt->TIM1_CONF), LO32(hpt->TIM1_CONF));
419 /* Timer 1 comparator */
420 db_printf("TIM1_COMP = %08X.%08X\n",
421 HI32(hpt->TIM1_COMP), LO32(hpt->TIM1_COMP));
422 /* Timer 2 config and cap */
423 db_printf("TIM2_CONF = %08X.%08X\n",
424 HI32(hpt->TIM2_CONF), LO32(hpt->TIM2_CONF));
425 /* Timer 2 comparator */
426 db_printf("TIM2_COMP = %08X.%08X\n",
427 HI32(hpt->TIM2_COMP), LO32(hpt->TIM2_COMP));
428
429 db_printf("\nHPET Frequency = %d.%05dMHz\n",
430 (uint32_t) (hpetFreq / 1000000), (uint32_t) (hpetFreq % 1000000));
431
432 return;
433
434 }
435
436 #endif