FreeBSD/Linux Kernel Cross Reference
sys/i386/i386/mpapic.c
1 /*
2 * Copyright (c) 1996, by Steve Passe
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. The name of the developer may NOT be used to endorse or promote products
11 * derived from this software without specific prior written permission.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 * $FreeBSD$
26 */
27
28 #include "opt_smp.h"
29
30 #include <sys/types.h>
31 #include <sys/systm.h>
32
33 #include <machine/smptests.h> /** TEST_TEST1 */
34 #include <machine/smp.h>
35 #include <machine/mpapic.h>
36 #include <machine/segments.h>
37
38 #include <i386/isa/intr_machdep.h> /* Xspuriousint() */
39
40 /* EISA Edge/Level trigger control registers */
41 #define ELCR0 0x4d0 /* eisa irq 0-7 */
42 #define ELCR1 0x4d1 /* eisa irq 8-15 */
43
44 /*
45 * pointers to pmapped apic hardware.
46 */
47
48 #if defined(APIC_IO)
49 volatile ioapic_t *ioapic[NAPIC];
50 #endif /* APIC_IO */
51
52 /*
53 * Enable APIC, configure interrupts.
54 */
55 void
56 apic_initialize(void)
57 {
58 u_int temp;
59
60 /* setup LVT1 as ExtINT */
61 temp = lapic.lvt_lint0;
62 temp &= ~(APIC_LVT_M | APIC_LVT_TM | APIC_LVT_IIPP | APIC_LVT_DM);
63 if (cpuid == 0)
64 temp |= 0x00000700; /* process ExtInts */
65 else
66 temp |= 0x00010700; /* mask ExtInts */
67 lapic.lvt_lint0 = temp;
68
69 /* setup LVT2 as NMI, masked till later... */
70 temp = lapic.lvt_lint1;
71 temp &= ~(APIC_LVT_M | APIC_LVT_TM | APIC_LVT_IIPP | APIC_LVT_DM);
72 temp |= 0x00010400; /* masked, edge trigger, active hi */
73
74 lapic.lvt_lint1 = temp;
75
76 /* set the Task Priority Register as needed */
77 temp = lapic.tpr;
78 temp &= ~APIC_TPR_PRIO; /* clear priority field */
79 temp |= LOPRIO_LEVEL; /* allow INT arbitration */
80
81 lapic.tpr = temp;
82
83 /* enable the local APIC */
84 temp = lapic.svr;
85 temp |= APIC_SVR_SWEN; /* software enable APIC */
86 temp &= ~APIC_SVR_FOCUS; /* enable 'focus processor' */
87
88 /* set the 'spurious INT' vector */
89 if ((XSPURIOUSINT_OFFSET & APIC_SVR_VEC_FIX) != APIC_SVR_VEC_FIX)
90 panic("bad XSPURIOUSINT_OFFSET: 0x%08x", XSPURIOUSINT_OFFSET);
91 temp &= ~APIC_SVR_VEC_PROG; /* clear (programmable) vector field */
92 temp |= (XSPURIOUSINT_OFFSET & APIC_SVR_VEC_PROG);
93
94 #if defined(TEST_TEST1)
95 if (cpuid == GUARD_CPU) {
96 temp &= ~APIC_SVR_SWEN; /* software DISABLE APIC */
97 }
98 #endif /** TEST_TEST1 */
99
100 lapic.svr = temp;
101
102 if (bootverbose)
103 apic_dump("apic_initialize()");
104 }
105
106
107 /*
108 * dump contents of local APIC registers
109 */
110 void
111 apic_dump(char* str)
112 {
113 printf("SMP: CPU%d %s:\n", cpuid, str);
114 printf(" lint0: 0x%08x lint1: 0x%08x TPR: 0x%08x SVR: 0x%08x\n",
115 lapic.lvt_lint0, lapic.lvt_lint1, lapic.tpr, lapic.svr);
116 }
117
118
119 #if defined(APIC_IO)
120
121 /*
122 * IO APIC code,
123 */
124
125 #define IOAPIC_ISA_INTS 16
126 #define REDIRCNT_IOAPIC(A) \
127 ((int)((io_apic_versions[(A)] & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1)
128
129 static int trigger __P((int apic, int pin, u_int32_t * flags));
130 static void polarity __P((int apic, int pin, u_int32_t * flags, int level));
131
132 #define DEFAULT_FLAGS \
133 ((u_int32_t) \
134 (IOART_INTMSET | \
135 IOART_DESTPHY | \
136 IOART_DELLOPRI))
137
138 #define DEFAULT_ISA_FLAGS \
139 ((u_int32_t) \
140 (IOART_INTMSET | \
141 IOART_TRGREDG | \
142 IOART_INTAHI | \
143 IOART_DESTPHY | \
144 IOART_DELLOPRI))
145
146 /*
147 * Setup the IO APIC.
148 */
149 extern int apic_pin_trigger; /* 'opaque' */
150 int
151 io_apic_setup(int apic)
152 {
153 int maxpin;
154 u_char select; /* the select register is 8 bits */
155 u_int32_t flags; /* the window register is 32 bits */
156 u_int32_t target; /* the window register is 32 bits */
157 u_int32_t vector; /* the window register is 32 bits */
158 int pin, level;
159
160 target = IOART_DEST;
161
162 if (apic == 0)
163 apic_pin_trigger = 0; /* default to edge-triggered */
164
165 maxpin = REDIRCNT_IOAPIC(apic); /* pins in APIC */
166 printf("Programming %d pins in IOAPIC #%d\n", maxpin, apic);
167
168 for (pin = 0; pin < maxpin; ++pin) {
169 int bus, bustype, irq;
170
171 /* we only deal with vectored INTs here */
172 if (apic_int_type(apic, pin) != 0)
173 continue;
174
175 irq = apic_irq(apic, pin);
176 if (irq == 0xff)
177 continue;
178
179 /* determine the bus type for this pin */
180 bus = apic_src_bus_id(apic, pin);
181 if (bus == -1)
182 continue;
183 bustype = apic_bus_type(bus);
184
185 /* the "ISA" type INTerrupts */
186 if ((bustype == ISA) &&
187 (pin < IOAPIC_ISA_INTS) &&
188 (irq == pin) &&
189 (apic_polarity(apic, pin) == 0x1) &&
190 (apic_trigger(apic, pin) == 0x3)) {
191 flags = DEFAULT_ISA_FLAGS;
192 }
193
194 /* PCI or other bus */
195 else {
196 flags = DEFAULT_FLAGS;
197 level = trigger(apic, pin, &flags);
198 if (level == 1)
199 apic_pin_trigger |= (1 << irq);
200 polarity(apic, pin, &flags, level);
201 }
202
203 /* program the appropriate registers */
204 if (apic != 0 || pin != irq)
205 printf("IOAPIC #%d intpint %d -> irq %d\n",
206 apic, pin, irq);
207 select = pin * 2 + IOAPIC_REDTBL0; /* register */
208 vector = NRSVIDT + irq; /* IDT vec */
209 io_apic_write(apic, select, flags | vector);
210 io_apic_write(apic, select + 1, target);
211 }
212
213 /* return GOOD status */
214 return 0;
215 }
216 #undef DEFAULT_ISA_FLAGS
217 #undef DEFAULT_FLAGS
218
219
220 #define DEFAULT_EXTINT_FLAGS \
221 ((u_int32_t) \
222 (IOART_INTMSET | \
223 IOART_TRGREDG | \
224 IOART_INTAHI | \
225 IOART_DESTPHY | \
226 IOART_DELLOPRI))
227
228 /*
229 * Setup the source of External INTerrupts.
230 */
231 int
232 ext_int_setup(int apic, int intr)
233 {
234 u_char select; /* the select register is 8 bits */
235 u_int32_t flags; /* the window register is 32 bits */
236 u_int32_t target; /* the window register is 32 bits */
237 u_int32_t vector; /* the window register is 32 bits */
238
239 if (apic_int_type(apic, intr) != 3)
240 return -1;
241
242 target = IOART_DEST;
243 select = IOAPIC_REDTBL0 + (2 * intr);
244 vector = NRSVIDT + intr;
245 flags = DEFAULT_EXTINT_FLAGS;
246
247 io_apic_write(apic, select, flags | vector);
248 io_apic_write(apic, select + 1, target);
249
250 return 0;
251 }
252 #undef DEFAULT_EXTINT_FLAGS
253
254
255 /*
256 * Set the trigger level for an IO APIC pin.
257 */
258 static int
259 trigger(int apic, int pin, u_int32_t * flags)
260 {
261 int id;
262 int eirq;
263 int level;
264 static int intcontrol = -1;
265
266 switch (apic_trigger(apic, pin)) {
267
268 case 0x00:
269 break;
270
271 case 0x01:
272 *flags &= ~IOART_TRGRLVL; /* *flags |= IOART_TRGREDG */
273 return 0;
274
275 case 0x03:
276 *flags |= IOART_TRGRLVL;
277 return 1;
278
279 case -1:
280 default:
281 goto bad;
282 }
283
284 if ((id = apic_src_bus_id(apic, pin)) == -1)
285 goto bad;
286
287 switch (apic_bus_type(id)) {
288 case ISA:
289 *flags &= ~IOART_TRGRLVL; /* *flags |= IOART_TRGREDG; */
290 return 0;
291
292 case EISA:
293 eirq = apic_src_bus_irq(apic, pin);
294
295 if (eirq < 0 || eirq > 15) {
296 printf("EISA IRQ %d?!?!\n", eirq);
297 goto bad;
298 }
299
300 if (intcontrol == -1) {
301 intcontrol = inb(ELCR1) << 8;
302 intcontrol |= inb(ELCR0);
303 printf("EISA INTCONTROL = %08x\n", intcontrol);
304 }
305
306 /* Use ELCR settings to determine level or edge mode */
307 level = (intcontrol >> eirq) & 1;
308
309 /*
310 * Note that on older Neptune chipset based systems, any
311 * pci interrupts often show up here and in the ELCR as well
312 * as level sensitive interrupts attributed to the EISA bus.
313 */
314
315 if (level)
316 *flags |= IOART_TRGRLVL;
317 else
318 *flags &= ~IOART_TRGRLVL;
319
320 return level;
321
322 case PCI:
323 *flags |= IOART_TRGRLVL;
324 return 1;
325
326 case -1:
327 default:
328 goto bad;
329 }
330
331 bad:
332 panic("bad APIC IO INT flags");
333 }
334
335
336 /*
337 * Set the polarity value for an IO APIC pin.
338 */
339 static void
340 polarity(int apic, int pin, u_int32_t * flags, int level)
341 {
342 int id;
343
344 switch (apic_polarity(apic, pin)) {
345
346 case 0x00:
347 break;
348
349 case 0x01:
350 *flags &= ~IOART_INTALO; /* *flags |= IOART_INTAHI */
351 return;
352
353 case 0x03:
354 *flags |= IOART_INTALO;
355 return;
356
357 case -1:
358 default:
359 goto bad;
360 }
361
362 if ((id = apic_src_bus_id(apic, pin)) == -1)
363 goto bad;
364
365 switch (apic_bus_type(id)) {
366 case ISA:
367 *flags &= ~IOART_INTALO; /* *flags |= IOART_INTAHI */
368 return;
369
370 case EISA:
371 /* polarity converter always gives active high */
372 *flags &= ~IOART_INTALO;
373 return;
374
375 case PCI:
376 *flags |= IOART_INTALO;
377 return;
378
379 case -1:
380 default:
381 goto bad;
382 }
383
384 bad:
385 panic("bad APIC IO INT flags");
386 }
387
388
389 /*
390 * Print contents of apic_imen.
391 */
392 extern u_int apic_imen; /* keep apic_imen 'opaque' */
393 void
394 imen_dump(void)
395 {
396 int x;
397
398 printf("SMP: enabled INTs: ");
399 for (x = 0; x < 24; ++x)
400 if ((apic_imen & (1 << x)) == 0)
401 printf("%d, ", x);
402 printf("apic_imen: 0x%08x\n", apic_imen);
403 }
404
405
406 /*
407 * Inter Processor Interrupt functions.
408 */
409
410
411 /*
412 * Send APIC IPI 'vector' to 'destType' via 'deliveryMode'.
413 *
414 * destType is 1 of: APIC_DEST_SELF, APIC_DEST_ALLISELF, APIC_DEST_ALLESELF
415 * vector is any valid SYSTEM INT vector
416 * delivery_mode is 1 of: APIC_DELMODE_FIXED, APIC_DELMODE_LOWPRIO
417 */
418 #define DETECT_DEADLOCK
419 int
420 apic_ipi(int dest_type, int vector, int delivery_mode)
421 {
422 u_long icr_lo;
423
424 #if defined(DETECT_DEADLOCK)
425 #define MAX_SPIN1 10000000
426 #define MAX_SPIN2 1000
427 int x;
428
429 /* "lazy delivery", ie we only barf if they stack up on us... */
430 for (x = MAX_SPIN1; x; --x) {
431 if ((lapic.icr_lo & APIC_DELSTAT_MASK) == 0)
432 break;
433 }
434 if (x == 0)
435 panic("apic_ipi was stuck");
436 #endif /* DETECT_DEADLOCK */
437
438 /* build IRC_LOW */
439 icr_lo = (lapic.icr_lo & APIC_RESV2_MASK)
440 | dest_type | delivery_mode | vector;
441
442 /* write APIC ICR */
443 lapic.icr_lo = icr_lo;
444
445 /* wait for pending status end */
446 #if defined(DETECT_DEADLOCK)
447 for (x = MAX_SPIN2; x; --x) {
448 if ((lapic.icr_lo & APIC_DELSTAT_MASK) == 0)
449 break;
450 }
451 #ifdef needsattention
452 /*
453 * XXX FIXME:
454 * The above loop waits for the message to actually be delivered.
455 * It breaks out after an arbitrary timout on the theory that it eventually
456 * will be delivered and we will catch a real failure on the next entry to
457 * this function, which would panic().
458 * We could skip this wait entirely, EXCEPT it probably protects us from
459 * other "less robust" routines that assume the message was delivered and
460 * acted upon when this function returns. TLB shootdowns are one such
461 * "less robust" function.
462 */
463 if (x == 0)
464 printf("apic_ipi might be stuck\n");
465 #endif
466 #undef MAX_SPIN2
467 #undef MAX_SPIN1
468 #else
469 while (lapic.icr_lo & APIC_DELSTAT_MASK)
470 /* spin */ ;
471 #endif /* DETECT_DEADLOCK */
472
473 /** XXX FIXME: return result */
474 return 0;
475 }
476
477 static int
478 apic_ipi_singledest(int cpu, int vector, int delivery_mode)
479 {
480 u_long icr_lo;
481 u_long icr_hi;
482 u_long eflags;
483
484 #if defined(DETECT_DEADLOCK)
485 #define MAX_SPIN1 10000000
486 #define MAX_SPIN2 1000
487 int x;
488
489 /* "lazy delivery", ie we only barf if they stack up on us... */
490 for (x = MAX_SPIN1; x; --x) {
491 if ((lapic.icr_lo & APIC_DELSTAT_MASK) == 0)
492 break;
493 }
494 if (x == 0)
495 panic("apic_ipi was stuck");
496 #endif /* DETECT_DEADLOCK */
497
498 eflags = read_eflags();
499 __asm __volatile("cli" : : : "memory");
500 icr_hi = lapic.icr_hi & ~APIC_ID_MASK;
501 icr_hi |= (CPU_TO_ID(cpu) << 24);
502 lapic.icr_hi = icr_hi;
503
504 /* build IRC_LOW */
505 icr_lo = (lapic.icr_lo & APIC_RESV2_MASK)
506 | APIC_DEST_DESTFLD | delivery_mode | vector;
507
508 /* write APIC ICR */
509 lapic.icr_lo = icr_lo;
510 write_eflags(eflags);
511
512 /* wait for pending status end */
513 #if defined(DETECT_DEADLOCK)
514 for (x = MAX_SPIN2; x; --x) {
515 if ((lapic.icr_lo & APIC_DELSTAT_MASK) == 0)
516 break;
517 }
518 #ifdef needsattention
519 /*
520 * XXX FIXME:
521 * The above loop waits for the message to actually be delivered.
522 * It breaks out after an arbitrary timout on the theory that it eventually
523 * will be delivered and we will catch a real failure on the next entry to
524 * this function, which would panic().
525 * We could skip this wait entirely, EXCEPT it probably protects us from
526 * other "less robust" routines that assume the message was delivered and
527 * acted upon when this function returns. TLB shootdowns are one such
528 * "less robust" function.
529 */
530 if (x == 0)
531 printf("apic_ipi might be stuck\n");
532 #endif
533 #undef MAX_SPIN2
534 #undef MAX_SPIN1
535 #else
536 while (lapic.icr_lo & APIC_DELSTAT_MASK)
537 /* spin */ ;
538 #endif /* DETECT_DEADLOCK */
539
540 /** XXX FIXME: return result */
541 return 0;
542 }
543
544
545 /*
546 * Send APIC IPI 'vector' to 'target's via 'delivery_mode'.
547 *
548 * target contains a bitfield with a bit set for selected APICs.
549 * vector is any valid SYSTEM INT vector
550 * delivery_mode is 1 of: APIC_DELMODE_FIXED, APIC_DELMODE_LOWPRIO
551 */
552 int
553 selected_apic_ipi(u_int target, int vector, int delivery_mode)
554 {
555 int x;
556 int status;
557
558 if (target & ~0x7fff)
559 return -1; /* only 15 targets allowed */
560
561 for (status = 0, x = 0; x <= 14; ++x)
562 if (target & (1 << x)) {
563
564 /* send the IPI */
565 if (apic_ipi_singledest(x, vector,
566 delivery_mode) == -1)
567 status |= (1 << x);
568 }
569 return status;
570 }
571
572
573 #if defined(READY)
574 /*
575 * Send an IPI INTerrupt containing 'vector' to CPU 'target'
576 * NOTE: target is a LOGICAL APIC ID
577 */
578 int
579 selected_proc_ipi(int target, int vector)
580 {
581 u_long icr_lo;
582 u_long icr_hi;
583
584 /* write the destination field for the target AP */
585 icr_hi = (lapic.icr_hi & ~APIC_ID_MASK) |
586 (cpu_num_to_apic_id[target] << 24);
587 lapic.icr_hi = icr_hi;
588
589 /* write command */
590 icr_lo = (lapic.icr_lo & APIC_RESV2_MASK) |
591 APIC_DEST_DESTFLD | APIC_DELMODE_FIXED | vector;
592 lapic.icr_lo = icr_lo;
593
594 /* wait for pending status end */
595 while (lapic.icr_lo & APIC_DELSTAT_MASK)
596 /* spin */ ;
597
598 return 0; /** XXX FIXME: return result */
599 }
600 #endif /* READY */
601
602 #endif /* APIC_IO */
603
604
605 /*
606 * Timer code, in development...
607 * - suggested by rgrimes@gndrsh.aac.dev.com
608 */
609
610 /** XXX FIXME: temp hack till we can determin bus clock */
611 #ifndef BUS_CLOCK
612 #define BUS_CLOCK 66000000
613 #define bus_clock() 66000000
614 #endif
615
616 #if defined(READY)
617 int acquire_apic_timer __P((void));
618 int release_apic_timer __P((void));
619
620 /*
621 * Acquire the APIC timer for exclusive use.
622 */
623 int
624 acquire_apic_timer(void)
625 {
626 #if 1
627 return 0;
628 #else
629 /** XXX FIXME: make this really do something */
630 panic("APIC timer in use when attempting to aquire");
631 #endif
632 }
633
634
635 /*
636 * Return the APIC timer.
637 */
638 int
639 release_apic_timer(void)
640 {
641 #if 1
642 return 0;
643 #else
644 /** XXX FIXME: make this really do something */
645 panic("APIC timer was already released");
646 #endif
647 }
648 #endif /* READY */
649
650
651 /*
652 * Load a 'downcount time' in uSeconds.
653 */
654 void
655 set_apic_timer(int value)
656 {
657 u_long lvtt;
658 long ticks_per_microsec;
659
660 /*
661 * Calculate divisor and count from value:
662 *
663 * timeBase == CPU bus clock divisor == [1,2,4,8,16,32,64,128]
664 * value == time in uS
665 */
666 lapic.dcr_timer = APIC_TDCR_1;
667 ticks_per_microsec = bus_clock() / 1000000;
668
669 /* configure timer as one-shot */
670 lvtt = lapic.lvt_timer;
671 lvtt &= ~(APIC_LVTT_VECTOR | APIC_LVTT_DS | APIC_LVTT_M | APIC_LVTT_TM);
672 lvtt |= APIC_LVTT_M; /* no INT, one-shot */
673 lapic.lvt_timer = lvtt;
674
675 /* */
676 lapic.icr_timer = value * ticks_per_microsec;
677 }
678
679
680 /*
681 * Read remaining time in timer.
682 */
683 int
684 read_apic_timer(void)
685 {
686 #if 0
687 /** XXX FIXME: we need to return the actual remaining time,
688 * for now we just return the remaining count.
689 */
690 #else
691 return lapic.ccr_timer;
692 #endif
693 }
694
695
696 /*
697 * Spin-style delay, set delay time in uS, spin till it drains.
698 */
699 void
700 u_sleep(int count)
701 {
702 set_apic_timer(count);
703 while (read_apic_timer())
704 /* spin */ ;
705 }
Cache object: afcec94e3f678e65278a2678c56af4ee
|