Annotation of sys/arch/i386/i386/lapic.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: lapic.c,v 1.17 2007/08/01 13:18:18 martin Exp $ */
2: /* $NetBSD: lapic.c,v 1.1.2.8 2000/02/23 06:10:50 sommerfeld Exp $ */
3:
4: /*-
5: * Copyright (c) 2000 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by RedBack Networks Inc.
10: *
11: * Author: Bill Sommerfeld
12: *
13: * Redistribution and use in source and binary forms, with or without
14: * modification, are permitted provided that the following conditions
15: * are met:
16: * 1. Redistributions of source code must retain the above copyright
17: * notice, this list of conditions and the following disclaimer.
18: * 2. Redistributions in binary form must reproduce the above copyright
19: * notice, this list of conditions and the following disclaimer in the
20: * documentation and/or other materials provided with the distribution.
21: * 3. All advertising materials mentioning features or use of this software
22: * must display the following acknowledgement:
23: * This product includes software developed by the NetBSD
24: * Foundation, Inc. and its contributors.
25: * 4. Neither the name of The NetBSD Foundation nor the names of its
26: * contributors may be used to endorse or promote products derived
27: * from this software without specific prior written permission.
28: *
29: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
30: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
33: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39: * POSSIBILITY OF SUCH DAMAGE.
40: */
41:
42: #include <sys/param.h>
43: #include <sys/proc.h>
44: #include <sys/user.h>
45: #include <sys/systm.h>
46: #include <sys/device.h>
47: #include <sys/timetc.h>
48:
49: #include <uvm/uvm_extern.h>
50:
51: #include <machine/cpu.h>
52: #include <machine/cpufunc.h>
53: #include <machine/cpuvar.h>
54: #include <machine/pmap.h>
55: #include <machine/vmparam.h>
56: #include <machine/mpbiosvar.h>
57: #include <machine/pcb.h>
58: #include <machine/specialreg.h>
59: #include <machine/segments.h>
60:
61: #include <machine/apicvar.h>
62: #include <machine/i82489reg.h>
63: #include <machine/i82489var.h>
64: #include <machine/pctr.h>
65:
66: #include <dev/ic/i8253reg.h>
67:
68: struct evcount clk_count;
69: struct evcount ipi_count;
70:
71: void lapic_delay(int);
72: void lapic_microtime(struct timeval *);
73: static __inline u_int32_t lapic_gettick(void);
74: void lapic_clockintr(void *);
75: void lapic_initclocks(void);
76: void lapic_map(paddr_t);
77:
78: void
79: lapic_map(lapic_base)
80: paddr_t lapic_base;
81: {
82: int s;
83: pt_entry_t *pte;
84: vaddr_t va = (vaddr_t)&local_apic;
85:
86: disable_intr();
87: s = lapic_tpr;
88:
89: /*
90: * Map local apic. If we have a local apic, it's safe to assume
91: * we're on a 486 or better and can use invlpg and non-cacheable PTE's
92: *
93: * Whap the PTE "by hand" rather than calling pmap_kenter_pa because
94: * the latter will attempt to invoke TLB shootdown code just as we
95: * might have changed the value of cpu_number()..
96: */
97:
98: pte = kvtopte(va);
99: *pte = lapic_base | PG_RW | PG_V | PG_N;
100: invlpg(va);
101:
102: #ifdef MULTIPROCESSOR
103: cpu_init_first(); /* catch up to changed cpu_number() */
104: #endif
105:
106: lapic_tpr = s;
107: enable_intr();
108: }
109:
110: /*
111: * enable local apic
112: */
113: void
114: lapic_enable()
115: {
116: i82489_writereg(LAPIC_SVR, LAPIC_SVR_ENABLE | LAPIC_SPURIOUS_VECTOR);
117: }
118:
119: void
120: lapic_set_softvectors()
121: {
122: idt_vec_set(LAPIC_SOFTCLOCK_VECTOR, Xintrsoftclock);
123: idt_vec_set(LAPIC_SOFTNET_VECTOR, Xintrsoftnet);
124: idt_vec_set(LAPIC_SOFTTTY_VECTOR, Xintrsofttty);
125: idt_vec_set(LAPIC_SOFTAST_VECTOR, Xintrsoftast);
126: }
127:
128: void
129: lapic_set_lvt()
130: {
131: struct cpu_info *ci = curcpu();
132: int i;
133: struct mp_intr_map *mpi;
134:
135: #ifdef MULTIPROCESSOR
136: if (mp_verbose) {
137: apic_format_redir(ci->ci_dev.dv_xname, "prelint", 0, 0,
138: i82489_readreg(LAPIC_LVINT0));
139: apic_format_redir(ci->ci_dev.dv_xname, "prelint", 1, 0,
140: i82489_readreg(LAPIC_LVINT1));
141: }
142: #endif
143:
144: for (i = 0; i < mp_nintrs; i++) {
145: mpi = &mp_intrs[i];
146: if (mpi->ioapic == NULL && (mpi->cpu_id == MPS_ALL_APICS
147: || mpi->cpu_id == ci->ci_apicid)) {
148: #ifdef DIAGNOSTIC
149: if (mpi->ioapic_pin > 1)
150: panic("lapic_set_lvt: bad pin value %d",
151: mpi->ioapic_pin);
152: #endif
153: if (mpi->ioapic_pin == 0)
154: i82489_writereg(LAPIC_LVINT0, mpi->redir);
155: else
156: i82489_writereg(LAPIC_LVINT1, mpi->redir);
157: }
158: }
159:
160: #ifdef MULTIPROCESSOR
161: if (mp_verbose) {
162: apic_format_redir(ci->ci_dev.dv_xname, "timer", 0, 0,
163: i82489_readreg(LAPIC_LVTT));
164: apic_format_redir(ci->ci_dev.dv_xname, "pcint", 0, 0,
165: i82489_readreg(LAPIC_PCINT));
166: apic_format_redir(ci->ci_dev.dv_xname, "lint", 0, 0,
167: i82489_readreg(LAPIC_LVINT0));
168: apic_format_redir(ci->ci_dev.dv_xname, "lint", 1, 0,
169: i82489_readreg(LAPIC_LVINT1));
170: apic_format_redir(ci->ci_dev.dv_xname, "err", 0, 0,
171: i82489_readreg(LAPIC_LVERR));
172: }
173: #endif
174: }
175:
176: /*
177: * Initialize fixed idt vectors for use by local apic.
178: */
179: void
180: lapic_boot_init(paddr_t lapic_base)
181: {
182: static int clk_irq = 0;
183: static int ipi_irq = 0;
184:
185: lapic_map(lapic_base);
186:
187: #ifdef MULTIPROCESSOR
188: idt_vec_set(LAPIC_IPI_VECTOR, Xintripi);
189: idt_vec_set(LAPIC_IPI_AST, Xintripi_ast);
190: idt_vec_set(LAPIC_IPI_INVLTLB, Xintripi_invltlb);
191: idt_vec_set(LAPIC_IPI_INVLPG, Xintripi_invlpg);
192: idt_vec_set(LAPIC_IPI_INVLRANGE, Xintripi_invlrange);
193: #endif
194: idt_vec_set(LAPIC_SPURIOUS_VECTOR, Xintrspurious);
195: idt_vec_set(LAPIC_TIMER_VECTOR, Xintrltimer);
196:
197: evcount_attach(&clk_count, "clock", (void *)&clk_irq, &evcount_intr);
198: evcount_attach(&ipi_count, "ipi", (void *)&ipi_irq, &evcount_intr);
199: }
200:
201: static __inline u_int32_t
202: lapic_gettick()
203: {
204: return (i82489_readreg(LAPIC_CCR_TIMER));
205: }
206:
207: #include <sys/kernel.h> /* for hz */
208:
209: u_int32_t lapic_tval;
210:
211: /*
212: * this gets us up to a 4GHz busclock....
213: */
214: u_int32_t lapic_per_second;
215: u_int32_t lapic_frac_usec_per_cycle;
216: u_int64_t lapic_frac_cycle_per_usec;
217: u_int32_t lapic_delaytab[26];
218: u_int64_t scaled_pentium_mhz;
219:
220: void
221: lapic_clockintr(arg)
222: void *arg;
223: {
224: struct cpu_info *ci = curcpu();
225: struct clockframe *frame = arg;
226:
227: if (CPU_IS_PRIMARY(ci)) {
228: ci->ci_tscbase = rdtsc();
229: i386_broadcast_ipi(I386_IPI_MICROSET);
230: }
231: hardclock(frame);
232:
233: clk_count.ec_count++;
234: }
235:
236: void
237: lapic_initclocks()
238: {
239: /*
240: * Start local apic countdown timer running, in repeated mode.
241: *
242: * Mask the clock interrupt and set mode,
243: * then set divisor,
244: * then unmask and set the vector.
245: */
246: i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_LVTT_M);
247: i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
248: i82489_writereg(LAPIC_ICR_TIMER, lapic_tval);
249: i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_TIMER_VECTOR);
250: }
251:
252: extern int gettick(void); /* XXX put in header file */
253: extern void (*initclock_func)(void); /* XXX put in header file */
254:
255: /*
256: * Calibrate the local apic count-down timer (which is running at
257: * bus-clock speed) vs. the i8254 counter/timer (which is running at
258: * a fixed rate).
259: *
260: * The Intel MP spec says: "An MP operating system may use the IRQ8
261: * real-time clock as a reference to determine the actual APIC timer clock
262: * speed."
263: *
264: * We're actually using the IRQ0 timer. Hmm.
265: */
266: void
267: lapic_calibrate_timer(struct cpu_info *ci)
268: {
269: unsigned int starttick, tick1, tick2, endtick;
270: unsigned int startapic, apic1, apic2, endapic;
271: u_int64_t dtick, dapic, tmp;
272: int i;
273: char tbuf[9];
274:
275: if (mp_verbose)
276: printf("%s: calibrating local timer\n", ci->ci_dev.dv_xname);
277:
278: /*
279: * Configure timer to one-shot, interrupt masked,
280: * large positive number.
281: */
282: i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_M);
283: i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
284: i82489_writereg(LAPIC_ICR_TIMER, 0x80000000);
285:
286: starttick = gettick();
287: startapic = lapic_gettick();
288:
289: DELAY(2); /* using "old" delay here.. */
290:
291: for (i=0; i<hz; i++) {
292: do {
293: tick1 = gettick();
294: apic1 = lapic_gettick();
295: } while (tick1 < starttick);
296:
297: do {
298: tick2 = gettick();
299: apic2 = lapic_gettick();
300: } while (tick2 > starttick);
301: }
302:
303: endtick = gettick();
304: endapic = lapic_gettick();
305:
306: dtick = hz * TIMER_DIV(hz) + (starttick-endtick);
307: dapic = startapic-endapic;
308:
309: /*
310: * there are TIMER_FREQ ticks per second.
311: * in dtick ticks, there are dapic bus clocks.
312: */
313: tmp = (TIMER_FREQ * dapic) / dtick;
314:
315: lapic_per_second = tmp;
316:
317: #if 0
318: humanize_number(tbuf, sizeof(tbuf), tmp, "Hz", 1000);
319: #else /* XXX: from NetBSD sources... sigh. */
320: {
321: /* prefixes are: (none), Kilo, Mega, Giga, Tera, Peta, Exa */
322: static const char prefixes[] = " KMGTPE";
323:
324: int i;
325: u_int64_t max;
326: size_t suffixlen;
327:
328: if (tbuf == NULL)
329: goto out;
330: if (sizeof(tbuf) > 0)
331: tbuf[0] = '\0';
332: suffixlen = sizeof "Hz" - 1;
333: /* check if enough room for `x y' + suffix + `\0' */
334: if (sizeof(tbuf) < 4 + suffixlen)
335: goto out;
336:
337: max = 1;
338: for (i = 0; i < sizeof(tbuf) - suffixlen - 3; i++)
339: max *= 10;
340: for (i = 0; tmp >= max && i < sizeof(prefixes); i++)
341: tmp /= 1000;
342:
343: snprintf(tbuf, sizeof(tbuf), "%qu%s%c%s",
344: (unsigned long long)tmp, i == 0 ? "" : " ", prefixes[i],
345: "Hz");
346: out:
347: ;
348: }
349: #endif
350:
351: printf("%s: apic clock running at %s\n", ci->ci_dev.dv_xname, tbuf);
352:
353: if (lapic_per_second != 0) {
354: /*
355: * reprogram the apic timer to run in periodic mode.
356: * XXX need to program timer on other cpu's, too.
357: */
358: lapic_tval = (lapic_per_second * 2) / hz;
359: lapic_tval = (lapic_tval / 2) + (lapic_tval & 0x1);
360:
361: i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM | LAPIC_LVTT_M |
362: LAPIC_TIMER_VECTOR);
363: i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
364: i82489_writereg(LAPIC_ICR_TIMER, lapic_tval);
365:
366: /*
367: * Compute fixed-point ratios between cycles and
368: * microseconds to avoid having to do any division
369: * in lapic_delay and lapic_microtime.
370: */
371:
372: tmp = (1000000 * (u_int64_t)1 << 32) / lapic_per_second;
373: lapic_frac_usec_per_cycle = tmp;
374:
375: tmp = (lapic_per_second * (u_int64_t)1 << 32) / 1000000;
376:
377: lapic_frac_cycle_per_usec = tmp;
378:
379: scaled_pentium_mhz = (1ULL << 32) / cpuspeed;
380:
381: /*
382: * Compute delay in cycles for likely short delays in usec.
383: */
384: for (i = 0; i < 26; i++)
385: lapic_delaytab[i] = (lapic_frac_cycle_per_usec * i) >>
386: 32;
387:
388: /*
389: * Now that the timer's calibrated, use the apic timer routines
390: * for all our timing needs..
391: */
392: delay_func = lapic_delay;
393: initclock_func = lapic_initclocks;
394: }
395: }
396:
397: /*
398: * delay for N usec.
399: */
400:
401: void
402: lapic_delay(int usec)
403: {
404: int32_t tick, otick;
405: int64_t deltat; /* XXX may want to be 64bit */
406:
407: otick = lapic_gettick();
408:
409: if (usec <= 0)
410: return;
411: if (usec <= 25)
412: deltat = lapic_delaytab[usec];
413: else
414: deltat = (lapic_frac_cycle_per_usec * usec) >> 32;
415:
416: while (deltat > 0) {
417: tick = lapic_gettick();
418: if (tick > otick)
419: deltat -= lapic_tval - (tick - otick);
420: else
421: deltat -= otick - tick;
422: otick = tick;
423: }
424: }
425:
426: #define LAPIC_TICK_THRESH 200
427:
428: /*
429: * An IPI handler to record current timer value
430: */
431: void
432: i386_ipi_microset(struct cpu_info *ci)
433: {
434: ci->ci_tscbase = rdtsc();
435: }
436:
437: #if 0
438: /*
439: * XXX need to make work correctly on other than cpu 0.
440: */
441: void
442: lapic_microtime(tv)
443: struct timeval *tv;
444: {
445: struct cpu_info *ci = curcpu();
446: struct timeval now;
447: u_int64_t tmp;
448:
449: disable_intr();
450: now = time;
451: tmp = rdtsc() - ci->ci_tscbase;
452: enable_intr();
453:
454: now.tv_usec += (tmp * scaled_pentium_mhz) >> 32;
455:
456: while (now.tv_usec >= 1000000) {
457: now.tv_sec += 1;
458: now.tv_usec -= 1000000;
459: }
460:
461: *tv = now;
462: }
463: #endif
464:
465: /*
466: * XXX the following belong mostly or partly elsewhere..
467: */
468:
469: int
470: i386_ipi_init(target)
471: int target;
472: {
473: unsigned j;
474:
475: if ((target & LAPIC_DEST_MASK) == 0)
476: i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
477:
478: i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
479: LAPIC_DLMODE_INIT | LAPIC_LVL_ASSERT );
480:
481: for (j = 100000; j > 0; j--) {
482: __asm __volatile("pause": : :"memory");
483: if ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) == 0)
484: break;
485: }
486:
487: delay(10000);
488:
489: i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
490: LAPIC_DLMODE_INIT | LAPIC_LVL_TRIG | LAPIC_LVL_DEASSERT);
491:
492: for (j = 100000; j > 0; j--) {
493: __asm __volatile("pause": : :"memory");
494: if ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) == 0)
495: break;
496: }
497:
498: return (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY)?EBUSY:0;
499: }
500:
501: int
502: i386_ipi(vec,target,dl)
503: int vec,target,dl;
504: {
505: unsigned j;
506:
507: if ((target & LAPIC_DEST_MASK) == 0)
508: i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
509:
510: i82489_writereg(LAPIC_ICRLO,
511: (target & LAPIC_DEST_MASK) | vec | dl | LAPIC_LVL_ASSERT);
512:
513: for (j = 100000;
514: j > 0 && (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY);
515: j--)
516: SPINLOCK_SPIN_HOOK;
517:
518: return (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) ? EBUSY : 0;
519: }
CVSweb