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 <architecture/i386/pio.h>
52 #include <i386/cpuid.h>
53 #include <i386/apic.h>
54 #include <i386/tsc.h>
55 #include <i386/hpet.h>
56 #include <i386/pmCPU.h>
57 #include <i386/cpu_topology.h>
58 #include <i386/cpu_threads.h>
59 #include <pexpert/device_tree.h>
60
61 /* Decimal powers: */
62 #define kilo (1000ULL)
63 #define Mega (kilo * kilo)
64 #define Giga (kilo * Mega)
65 #define Tera (kilo * Giga)
66 #define Peta (kilo * Tera)
67
68 vm_offset_t hpetArea = 0;
69 uint32_t hpetAreap = 0;
70 uint64_t hpetFemto = 0;
71 uint64_t hpetFreq = 0;
72 uint64_t hpetCvt = 0; /* (TAKE OUT LATER) */
73 uint64_t hpetCvtt2n = 0;
74 uint64_t hpetCvtn2t = 0;
75 uint64_t tsc2hpet = 0;
76 uint64_t hpet2tsc = 0;
77 uint64_t bus2hpet = 0;
78 uint64_t hpet2bus = 0;
79
80 vm_offset_t rcbaArea = 0;
81 uint32_t rcbaAreap = 0;
82
83 static int (*hpet_req)(uint32_t apicid, void *arg, hpetRequest_t *hpet) = NULL;
84 static void *hpet_arg = NULL;
85
86 #if DEBUG
87 #define DBG(x...) kprintf("DBG: " x)
88 #else
89 #define DBG(x...)
90 #endif
91
92 int
hpet_register_callback(int (* hpet_reqst)(uint32_t apicid,void * arg,hpetRequest_t * hpet),void * arg)93 hpet_register_callback(int (*hpet_reqst)(uint32_t apicid,
94 void *arg,
95 hpetRequest_t *hpet),
96 void *arg)
97 {
98 hpet_req = hpet_reqst;
99 hpet_arg = arg;
100 return 0;
101 }
102
103 /*
104 * This routine is called to obtain an HPET and have it assigned
105 * to a CPU. It returns 0 if successful and non-zero if one could
106 * not be assigned.
107 */
108 int
hpet_request(uint32_t cpu)109 hpet_request(uint32_t cpu)
110 {
111 hpetRequest_t hpetReq;
112 int rc;
113 x86_lcpu_t *lcpu;
114 x86_core_t *core;
115 x86_pkg_t *pkg;
116 boolean_t enabled;
117
118 if (hpet_req == NULL) {
119 return -1;
120 }
121
122 /*
123 * Deal with the case where the CPU # passed in is past the
124 * value specified in cpus=n in boot-args.
125 */
126 if (cpu >= real_ncpus) {
127 enabled = ml_set_interrupts_enabled(FALSE);
128 lcpu = cpu_to_lcpu(cpu);
129 if (lcpu != NULL) {
130 core = lcpu->core;
131 pkg = core->package;
132
133 if (lcpu->primary) {
134 pkg->flags |= X86PKG_FL_HAS_HPET;
135 }
136 }
137
138 ml_set_interrupts_enabled(enabled);
139 return 0;
140 }
141
142 rc = (*hpet_req)(ml_get_apicid(cpu), hpet_arg, &hpetReq);
143 if (rc != 0) {
144 return rc;
145 }
146
147 enabled = ml_set_interrupts_enabled(FALSE);
148 lcpu = cpu_to_lcpu(cpu);
149 core = lcpu->core;
150 pkg = core->package;
151
152 /*
153 * Compute the address of the HPET.
154 */
155 core->Hpet = (hpetTimer_t *)((uint8_t *)hpetArea + hpetReq.hpetOffset);
156 core->HpetVec = hpetReq.hpetVector;
157
158 /*
159 * Enable interrupts
160 */
161 core->Hpet->Config |= Tn_INT_ENB_CNF;
162
163 /*
164 * Save the configuration
165 */
166 core->HpetCfg = core->Hpet->Config;
167 core->HpetCmp = 0;
168
169 /*
170 * If the CPU is the "primary" for the package, then
171 * add the HPET to the package too.
172 */
173 if (lcpu->primary) {
174 pkg->Hpet = core->Hpet;
175 pkg->HpetCfg = core->HpetCfg;
176 pkg->HpetCmp = core->HpetCmp;
177 pkg->flags |= X86PKG_FL_HAS_HPET;
178 }
179
180 ml_set_interrupts_enabled(enabled);
181
182 return 0;
183 }
184
185 /*
186 * Map the RCBA area.
187 */
188 static void
map_rcbaArea(void)189 map_rcbaArea(void)
190 {
191 /*
192 * Get RCBA area physical address and map it
193 */
194 outl(cfgAdr, lpcCfg | (0xF0 & 0xFC));
195 rcbaAreap = inl(cfgDat | (0xF0 & 0x03));
196 rcbaArea = ml_io_map(rcbaAreap & -4096, PAGE_SIZE * 4);
197 kprintf("RCBA: vaddr = %lX, paddr = %08X\n", (unsigned long)rcbaArea, rcbaAreap);
198 }
199
200 /*
201 * Initialize the HPET
202 */
203 void
hpet_init(void)204 hpet_init(void)
205 {
206 unsigned int *xmod;
207
208 map_rcbaArea();
209
210 /*
211 * Is the HPET memory already enabled?
212 * If not, set address and enable.
213 */
214 xmod = (uint32_t *)(rcbaArea + 0x3404); /* Point to the HPTC */
215 uint32_t hptc = *xmod; /* Get HPET config */
216 DBG(" current RCBA.HPTC: %08X\n", *xmod);
217 if (!(hptc & hptcAE)) {
218 DBG("HPET memory is not enabled, "
219 "enabling and assigning to 0xFED00000 (hope that's ok)\n");
220 *xmod = (hptc & ~3) | hptcAE;
221 }
222
223 /*
224 * Get physical address of HPET and map it.
225 */
226 hpetAreap = hpetAddr | ((hptc & 3) << 12);
227 hpetArea = ml_io_map(hpetAreap & -4096, PAGE_SIZE * 4);
228 kprintf("HPET: vaddr = %lX, paddr = %08X\n", (unsigned long)hpetArea, hpetAreap);
229
230 /*
231 * Extract the HPET tick rate.
232 * The period of the HPET is reported in femtoseconds (10**-15s)
233 * and convert to frequency in hertz.
234 */
235 hpetFemto = (uint32_t)(((hpetReg_t *)hpetArea)->GCAP_ID >> 32);
236 hpetFreq = (1 * Peta) / hpetFemto;
237
238 /*
239 * The conversion factor is the number of nanoseconds per HPET tick
240 * with about 32 bits of fraction. The value is converted to a
241 * base-2 fixed point number. To convert from HPET to nanoseconds,
242 * multiply the value by the conversion factor using 96-bit arithmetic,
243 * then shift right 32 bits. If the value is known to be small,
244 * 64-bit arithmetic will work.
245 */
246
247 /*
248 * Begin conversion of base 10 femtoseconds to base 2, calculate:
249 * - HPET ticks to nanoseconds conversion in base 2 fraction (* 2**32)
250 * - nanoseconds to HPET ticks conversion
251 */
252 hpetCvtt2n = (uint64_t)hpetFemto << 32;
253 hpetCvtt2n = hpetCvtt2n / 1000000ULL;
254 hpetCvtn2t = 0xFFFFFFFFFFFFFFFFULL / hpetCvtt2n;
255 kprintf("HPET: Frequency = %6d.%04dMHz, "
256 "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n",
257 (uint32_t)(hpetFreq / Mega), (uint32_t)(hpetFreq % Mega),
258 (uint32_t)(hpetCvtt2n >> 32), (uint32_t)hpetCvtt2n,
259 (uint32_t)(hpetCvtn2t >> 32), (uint32_t)hpetCvtn2t);
260
261
262 /* (TAKE OUT LATER)
263 * Begin conversion of base 10 femtoseconds to base 2
264 * HPET ticks to nanoseconds in base 2 fraction (times 1048576)
265 */
266 hpetCvt = (uint64_t)hpetFemto << 20;
267 hpetCvt = hpetCvt / 1000000ULL;
268
269 /* Calculate conversion from TSC to HPET */
270 tsc2hpet = tmrCvt(tscFCvtt2n, hpetCvtn2t);
271 DBG(" CVT: TSC to HPET = %08X.%08X\n",
272 (uint32_t)(tsc2hpet >> 32), (uint32_t)tsc2hpet);
273
274 /* Calculate conversion from HPET to TSC */
275 hpet2tsc = tmrCvt(hpetCvtt2n, tscFCvtn2t);
276 DBG(" CVT: HPET to TSC = %08X.%08X\n",
277 (uint32_t)(hpet2tsc >> 32), (uint32_t)hpet2tsc);
278
279 /* Calculate conversion from BUS to HPET */
280 bus2hpet = tmrCvt(busFCvtt2n, hpetCvtn2t);
281 DBG(" CVT: BUS to HPET = %08X.%08X\n",
282 (uint32_t)(bus2hpet >> 32), (uint32_t)bus2hpet);
283
284 /* Calculate conversion from HPET to BUS */
285 hpet2bus = tmrCvt(hpetCvtt2n, busFCvtn2t);
286 DBG(" CVT: HPET to BUS = %08X.%08X\n",
287 (uint32_t)(hpet2bus >> 32), (uint32_t)hpet2bus);
288 }
289
290 /*
291 * This routine is used to get various information about the HPET
292 * without having to export gobs of globals. It fills in a data
293 * structure with the info.
294 */
295 void
hpet_get_info(hpetInfo_t * info)296 hpet_get_info(hpetInfo_t *info)
297 {
298 info->hpetCvtt2n = hpetCvtt2n;
299 info->hpetCvtn2t = hpetCvtn2t;
300 info->tsc2hpet = tsc2hpet;
301 info->hpet2tsc = hpet2tsc;
302 info->bus2hpet = bus2hpet;
303 info->hpet2bus = hpet2bus;
304 /*
305 * XXX
306 * We're repurposing the rcbaArea so we can use the HPET.
307 * Eventually we'll rename this correctly.
308 */
309 info->rcbaArea = hpetArea;
310 info->rcbaAreap = hpetAreap;
311 }
312
313
314 /*
315 * This routine is called by the HPET driver
316 * when it assigns an HPET timer to a processor.
317 *
318 * XXX with the new callback into the HPET driver,
319 * this routine will be deprecated.
320 */
321 void
ml_hpet_cfg(uint32_t cpu,uint32_t hpetVect)322 ml_hpet_cfg(uint32_t cpu, uint32_t hpetVect)
323 {
324 uint64_t *hpetVaddr;
325 hpetTimer_t *hpet;
326 x86_lcpu_t *lcpu;
327 x86_core_t *core;
328 x86_pkg_t *pkg;
329 boolean_t enabled;
330
331 if (cpu > 1) {
332 panic("ml_hpet_cfg: invalid cpu = %d", cpu);
333 }
334
335 lcpu = cpu_to_lcpu(cpu);
336 core = lcpu->core;
337 pkg = core->package;
338
339 /*
340 * Only deal with the primary CPU for the package.
341 */
342 if (!lcpu->primary) {
343 return;
344 }
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 */
387 int
HPETInterrupt(void)388 HPETInterrupt(void)
389 {
390 /* All we do here is to bump the count */
391 x86_package()->HpetInt++;
392
393 /*
394 * Let power management do it's thing.
395 */
396 pmHPETInterrupt();
397
398 /* Return and show that the 'rupt has been handled... */
399 return 1;
400 }
401
402
403 static hpetReg_t saved_hpet;
404
405 void
hpet_save(void)406 hpet_save(void)
407 {
408 hpetReg_t *from = (hpetReg_t *) hpetArea;
409 hpetReg_t *to = &saved_hpet;
410
411 to->GEN_CONF = from->GEN_CONF;
412 to->TIM0_CONF = from->TIM0_CONF;
413 to->TIM0_COMP = from->TIM0_COMP;
414 to->TIM1_CONF = from->TIM1_CONF;
415 to->TIM1_COMP = from->TIM1_COMP;
416 to->TIM2_CONF = from->TIM2_CONF;
417 to->TIM2_COMP = from->TIM2_COMP;
418 to->MAIN_CNT = from->MAIN_CNT;
419 }
420
421 void
hpet_restore(void)422 hpet_restore(void)
423 {
424 hpetReg_t *from = &saved_hpet;
425 hpetReg_t *to = (hpetReg_t *) hpetArea;
426
427 /*
428 * Is the HPET memory already enabled?
429 * If not, set address and enable.
430 */
431 uint32_t *hptcp = (uint32_t *)(rcbaArea + 0x3404);
432 uint32_t hptc = *hptcp;
433 if (!(hptc & hptcAE)) {
434 DBG("HPET memory is not enabled, "
435 "enabling and assigning to 0xFED00000 (hope that's ok)\n");
436 *hptcp = (hptc & ~3) | hptcAE;
437 }
438
439 to->GEN_CONF = from->GEN_CONF & ~1;
440
441 to->TIM0_CONF = from->TIM0_CONF;
442 to->TIM0_COMP = from->TIM0_COMP;
443 to->TIM1_CONF = from->TIM1_CONF;
444 to->TIM1_COMP = from->TIM1_COMP;
445 to->TIM2_CONF = from->TIM2_CONF;
446 to->TIM2_COMP = from->TIM2_COMP;
447 to->GINTR_STA = -1ULL;
448 to->MAIN_CNT = from->MAIN_CNT;
449
450 to->GEN_CONF = from->GEN_CONF;
451 }
452
453 /*
454 * Read the HPET timer
455 *
456 */
457 uint64_t
rdHPET(void)458 rdHPET(void)
459 {
460 hpetReg_t *hpetp = (hpetReg_t *) hpetArea;
461 volatile uint32_t *regp = (uint32_t *) &hpetp->MAIN_CNT;
462 uint32_t high;
463 uint32_t low;
464
465 do {
466 high = *(regp + 1);
467 low = *regp;
468 } while (high != *(regp + 1));
469
470 return (((uint64_t) high) << 32) | low;
471 }
472