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