2 * Copyright (c) 1996, by Steve Passe
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
25 * $FreeBSD: src/sys/i386/i386/mpapic.c,v 1.37.2.7 2003/01/25 02:31:47 peter Exp $
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
32 #include <sys/machintr.h>
33 #include <sys/thread2.h>
35 #include <machine/pmap.h>
36 #include <machine_base/isa/isa_intr.h>
37 #include <machine_base/icu/icu_var.h>
38 #include <machine_base/apic/lapic.h>
39 #include <machine_base/apic/ioapic.h>
40 #include <machine_base/apic/ioapic_abi.h>
41 #include <machine_base/apic/apicvar.h>
43 #define IOAPIC_COUNT_MAX 16
44 #define IOAPIC_ID_MASK (IOAPIC_COUNT_MAX - 1)
53 TAILQ_ENTRY(ioapic_info
) io_link
;
55 TAILQ_HEAD(ioapic_info_list
, ioapic_info
);
57 struct ioapic_intsrc
{
59 enum intr_trigger int_trig
;
60 enum intr_polarity int_pola
;
64 struct ioapic_info_list ioc_list
;
65 struct ioapic_intsrc ioc_intsrc
[ISA_IRQ_CNT
];
68 static int ioapic_config(void);
69 static void ioapic_setup(const struct ioapic_info
*);
70 static int ioapic_alloc_apic_id(int);
71 static void ioapic_set_apic_id(const struct ioapic_info
*);
72 static void ioapic_gsi_setup(int);
73 static const struct ioapic_info
*
74 ioapic_gsi_search(int);
75 static void ioapic_pin_prog(void *, int, int,
76 enum intr_trigger
, enum intr_polarity
, uint32_t);
78 static struct ioapic_conf ioapic_conf
;
80 static TAILQ_HEAD(, ioapic_enumerator
) ioapic_enumerators
=
81 TAILQ_HEAD_INITIALIZER(ioapic_enumerators
);
83 int ioapic_enable
= 1; /* I/O APIC is enabled by default */
88 struct ioapic_info
*info
;
89 int start_apic_id
= 0;
90 struct ioapic_enumerator
*e
;
94 TAILQ_INIT(&ioapic_conf
.ioc_list
);
95 for (i
= 0; i
< ISA_IRQ_CNT
; ++i
)
96 ioapic_conf
.ioc_intsrc
[i
].int_gsi
= -1;
99 TUNABLE_INT_FETCH("hw.ioapic_probe", &probe
);
101 kprintf("IOAPIC: warning I/O APIC will not be probed\n");
105 TAILQ_FOREACH(e
, &ioapic_enumerators
, ioapic_link
) {
106 error
= e
->ioapic_probe(e
);
111 kprintf("IOAPIC: can't find I/O APIC\n");
121 * Switch to I/O APIC MachIntrABI and reconfigure
122 * the default IDT entries.
124 MachIntrABI
= MachIntrABI_IOAPIC
;
125 MachIntrABI
.setdefault();
127 e
->ioapic_enumerate(e
);
133 TAILQ_FOREACH(info
, &ioapic_conf
.ioc_list
, io_link
)
136 if (i
> IOAPIC_COUNT_MAX
)
137 panic("ioapic_config: more than 16 I/O APIC\n");
142 TAILQ_FOREACH(info
, &ioapic_conf
.ioc_list
, io_link
) {
145 apic_id
= ioapic_alloc_apic_id(start_apic_id
);
146 if (apic_id
== NAPICID
) {
147 kprintf("IOAPIC: can't alloc APIC ID for "
148 "%dth I/O APIC\n", info
->io_idx
);
151 info
->io_apic_id
= apic_id
;
153 start_apic_id
= apic_id
+ 1;
157 * xAPIC allows I/O APIC's APIC ID to be same
158 * as the LAPIC's APIC ID
160 kprintf("IOAPIC: use xAPIC model to alloc APIC ID "
163 TAILQ_FOREACH(info
, &ioapic_conf
.ioc_list
, io_link
)
164 info
->io_apic_id
= info
->io_idx
;
168 * Warning about any GSI holes
170 TAILQ_FOREACH(info
, &ioapic_conf
.ioc_list
, io_link
) {
171 const struct ioapic_info
*prev_info
;
173 prev_info
= TAILQ_PREV(info
, ioapic_info_list
, io_link
);
174 if (prev_info
!= NULL
) {
175 if (info
->io_gsi_base
!=
176 prev_info
->io_gsi_base
+ prev_info
->io_npin
) {
177 kprintf("IOAPIC: warning gsi hole "
179 prev_info
->io_gsi_base
+
181 info
->io_gsi_base
- 1);
187 TAILQ_FOREACH(info
, &ioapic_conf
.ioc_list
, io_link
) {
188 kprintf("IOAPIC: idx %d, apic id %d, "
189 "gsi base %d, npin %d\n",
200 TAILQ_FOREACH(info
, &ioapic_conf
.ioc_list
, io_link
)
202 ioapic_abi_fixup_irqmap();
206 MachIntrABI
.cleanup();
214 ioapic_enumerator_register(struct ioapic_enumerator
*ne
)
216 struct ioapic_enumerator
*e
;
218 TAILQ_FOREACH(e
, &ioapic_enumerators
, ioapic_link
) {
219 if (e
->ioapic_prio
< ne
->ioapic_prio
) {
220 TAILQ_INSERT_BEFORE(e
, ne
, ioapic_link
);
224 TAILQ_INSERT_TAIL(&ioapic_enumerators
, ne
, ioapic_link
);
228 ioapic_add(void *addr
, int gsi_base
, int npin
)
230 struct ioapic_info
*info
, *ninfo
;
233 gsi_end
= gsi_base
+ npin
- 1;
234 TAILQ_FOREACH(info
, &ioapic_conf
.ioc_list
, io_link
) {
235 if ((gsi_base
>= info
->io_gsi_base
&&
236 gsi_base
< info
->io_gsi_base
+ info
->io_npin
) ||
237 (gsi_end
>= info
->io_gsi_base
&&
238 gsi_end
< info
->io_gsi_base
+ info
->io_npin
)) {
239 panic("ioapic_add: overlapped gsi, base %d npin %d, "
240 "hit base %d, npin %d\n", gsi_base
, npin
,
241 info
->io_gsi_base
, info
->io_npin
);
243 if (info
->io_addr
== addr
)
244 panic("ioapic_add: duplicated addr %p\n", addr
);
247 ninfo
= kmalloc(sizeof(*ninfo
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
248 ninfo
->io_addr
= addr
;
249 ninfo
->io_npin
= npin
;
250 ninfo
->io_gsi_base
= gsi_base
;
251 ninfo
->io_apic_id
= -1;
254 * Create IOAPIC list in ascending order of GSI base
256 TAILQ_FOREACH_REVERSE(info
, &ioapic_conf
.ioc_list
,
257 ioapic_info_list
, io_link
) {
258 if (ninfo
->io_gsi_base
> info
->io_gsi_base
) {
259 TAILQ_INSERT_AFTER(&ioapic_conf
.ioc_list
,
260 info
, ninfo
, io_link
);
265 TAILQ_INSERT_HEAD(&ioapic_conf
.ioc_list
, ninfo
, io_link
);
269 ioapic_intsrc(int irq
, int gsi
, enum intr_trigger trig
, enum intr_polarity pola
)
271 struct ioapic_intsrc
*int_src
;
273 KKASSERT(irq
< ISA_IRQ_CNT
);
274 int_src
= &ioapic_conf
.ioc_intsrc
[irq
];
277 /* Don't allow mixed mode */
278 kprintf("IOAPIC: warning intsrc irq %d -> gsi 0\n", irq
);
282 if (int_src
->int_gsi
!= -1) {
283 if (int_src
->int_gsi
!= gsi
) {
284 kprintf("IOAPIC: warning intsrc irq %d, gsi "
285 "%d -> %d\n", irq
, int_src
->int_gsi
, gsi
);
287 if (int_src
->int_trig
!= trig
) {
288 kprintf("IOAPIC: warning intsrc irq %d, trig "
290 intr_str_trigger(int_src
->int_trig
),
291 intr_str_trigger(trig
));
293 if (int_src
->int_pola
!= pola
) {
294 kprintf("IOAPIC: warning intsrc irq %d, pola "
296 intr_str_polarity(int_src
->int_pola
),
297 intr_str_polarity(pola
));
300 int_src
->int_gsi
= gsi
;
301 int_src
->int_trig
= trig
;
302 int_src
->int_pola
= pola
;
306 ioapic_set_apic_id(const struct ioapic_info
*info
)
311 id
= ioapic_read(info
->io_addr
, IOAPIC_ID
);
314 id
|= (info
->io_apic_id
<< 24);
316 ioapic_write(info
->io_addr
, IOAPIC_ID
, id
);
321 id
= ioapic_read(info
->io_addr
, IOAPIC_ID
);
322 apic_id
= (id
& APIC_ID_MASK
) >> 24;
325 * I/O APIC ID is a 4bits field
327 if ((apic_id
& IOAPIC_ID_MASK
) !=
328 (info
->io_apic_id
& IOAPIC_ID_MASK
)) {
329 panic("ioapic_set_apic_id: can't set apic id to %d, "
330 "currently set to %d\n", info
->io_apic_id
, apic_id
);
335 ioapic_gsi_setup(int gsi
)
337 enum intr_trigger trig
;
338 enum intr_polarity pola
;
344 ioapic_extpin_setup(ioapic_gsi_ioaddr(gsi
),
345 ioapic_gsi_pin(gsi
), 0);
350 trig
= 0; /* silence older gcc's */
351 pola
= 0; /* silence older gcc's */
353 for (irq
= 0; irq
< ISA_IRQ_CNT
; ++irq
) {
354 const struct ioapic_intsrc
*int_src
=
355 &ioapic_conf
.ioc_intsrc
[irq
];
357 if (gsi
== int_src
->int_gsi
) {
358 trig
= int_src
->int_trig
;
359 pola
= int_src
->int_pola
;
364 if (irq
== ISA_IRQ_CNT
) {
366 * No explicit IRQ to GSI mapping;
367 * use the default 1:1 mapping
370 if (irq
< ISA_IRQ_CNT
) {
371 if (ioapic_conf
.ioc_intsrc
[irq
].int_gsi
>= 0) {
373 * This IRQ is mapped to different GSI,
374 * don't do the default configuration.
375 * The configuration of the target GSI
376 * will finally setup this IRQ.
378 * This GSI is not used, disable it.
380 ioapic_pin_setup(ioapic_gsi_ioaddr(gsi
),
381 ioapic_gsi_pin(gsi
), 0,
382 INTR_TRIGGER_EDGE
, INTR_POLARITY_HIGH
);
385 trig
= INTR_TRIGGER_EDGE
;
386 pola
= INTR_POLARITY_HIGH
;
388 trig
= INTR_TRIGGER_LEVEL
;
389 pola
= INTR_POLARITY_LOW
;
393 ioapic_abi_set_irqmap(irq
, gsi
, trig
, pola
);
397 ioapic_gsi_ioaddr(int gsi
)
399 const struct ioapic_info
*info
;
401 info
= ioapic_gsi_search(gsi
);
402 return info
->io_addr
;
406 ioapic_gsi_pin(int gsi
)
408 const struct ioapic_info
*info
;
410 info
= ioapic_gsi_search(gsi
);
411 return gsi
- info
->io_gsi_base
;
414 static const struct ioapic_info
*
415 ioapic_gsi_search(int gsi
)
417 const struct ioapic_info
*info
;
419 TAILQ_FOREACH(info
, &ioapic_conf
.ioc_list
, io_link
) {
420 if (gsi
>= info
->io_gsi_base
&&
421 gsi
< info
->io_gsi_base
+ info
->io_npin
)
424 panic("ioapic_gsi_search: no I/O APIC\n");
428 ioapic_gsi(int idx
, int pin
)
430 const struct ioapic_info
*info
;
432 TAILQ_FOREACH(info
, &ioapic_conf
.ioc_list
, io_link
) {
433 if (info
->io_idx
== idx
)
438 if (pin
>= info
->io_npin
)
440 return info
->io_gsi_base
+ pin
;
444 ioapic_extpin_setup(void *addr
, int pin
, int vec
)
446 ioapic_pin_prog(addr
, pin
, vec
,
447 INTR_TRIGGER_CONFORM
, INTR_POLARITY_CONFORM
, IOART_DELEXINT
);
451 ioapic_extpin_gsi(void)
457 ioapic_pin_setup(void *addr
, int pin
, int vec
,
458 enum intr_trigger trig
, enum intr_polarity pola
)
461 * Always clear an I/O APIC pin before [re]programming it. This is
462 * particularly important if the pin is set up for a level interrupt
463 * as the IOART_REM_IRR bit might be set. When we reprogram the
464 * vector any EOI from pending ints on this pin could be lost and
465 * IRR might never get reset.
467 * To fix this problem, clear the vector and make sure it is
468 * programmed as an edge interrupt. This should theoretically
469 * clear IRR so we can later, safely program it as a level
472 ioapic_pin_prog(addr
, pin
, vec
, INTR_TRIGGER_EDGE
, INTR_POLARITY_HIGH
,
474 ioapic_pin_prog(addr
, pin
, vec
, trig
, pola
, IOART_DELFIXED
);
478 ioapic_pin_prog(void *addr
, int pin
, int vec
,
479 enum intr_trigger trig
, enum intr_polarity pola
, uint32_t del_mode
)
481 uint32_t flags
, target
;
484 KKASSERT(del_mode
== IOART_DELEXINT
|| del_mode
== IOART_DELFIXED
);
486 select
= IOAPIC_REDTBL0
+ (2 * pin
);
488 flags
= ioapic_read(addr
, select
) & IOART_RESV
;
489 flags
|= IOART_INTMSET
| IOART_DESTPHY
;
494 * We only support limited I/O APIC mixed mode,
495 * so even for ExtINT, we still use "fixed"
498 flags
|= IOART_DELFIXED
;
501 if (del_mode
== IOART_DELEXINT
) {
502 KKASSERT(trig
== INTR_TRIGGER_CONFORM
&&
503 pola
== INTR_POLARITY_CONFORM
);
504 flags
|= IOART_TRGREDG
| IOART_INTAHI
;
507 case INTR_TRIGGER_EDGE
:
508 flags
|= IOART_TRGREDG
;
511 case INTR_TRIGGER_LEVEL
:
512 flags
|= IOART_TRGRLVL
;
515 case INTR_TRIGGER_CONFORM
:
516 panic("ioapic_pin_prog: trig conform is not "
520 case INTR_POLARITY_HIGH
:
521 flags
|= IOART_INTAHI
;
524 case INTR_POLARITY_LOW
:
525 flags
|= IOART_INTALO
;
528 case INTR_POLARITY_CONFORM
:
529 panic("ioapic_pin_prog: pola conform is not "
534 target
= ioapic_read(addr
, select
+ 1) & IOART_HI_DEST_RESV
;
535 target
|= (CPUID_TO_APICID(0) << IOART_HI_DEST_SHIFT
) &
538 ioapic_write(addr
, select
, flags
| vec
);
539 ioapic_write(addr
, select
+ 1, target
);
543 ioapic_setup(const struct ioapic_info
*info
)
547 ioapic_set_apic_id(info
);
549 for (i
= 0; i
< info
->io_npin
; ++i
)
550 ioapic_gsi_setup(info
->io_gsi_base
+ i
);
554 ioapic_alloc_apic_id(int start
)
557 const struct ioapic_info
*info
;
558 int apic_id
, apic_id16
;
560 apic_id
= lapic_unused_apic_id(start
);
561 if (apic_id
== NAPICID
) {
562 kprintf("IOAPIC: can't find unused APIC ID\n");
565 apic_id16
= apic_id
& IOAPIC_ID_MASK
;
568 * Check against other I/O APIC's APIC ID's lower 4bits.
570 * The new APIC ID will have to be different from others
571 * in the lower 4bits, no matter whether xAPIC is used
574 TAILQ_FOREACH(info
, &ioapic_conf
.ioc_list
, io_link
) {
575 if (info
->io_apic_id
== -1) {
579 if ((info
->io_apic_id
& IOAPIC_ID_MASK
) == apic_id16
)
585 kprintf("IOAPIC: APIC ID %d has same lower 4bits as "
586 "%dth I/O APIC, keep searching...\n",
587 apic_id
, info
->io_idx
);
591 panic("ioapic_unused_apic_id: never reached\n");
595 ioapic_map(vm_paddr_t pa
)
597 KKASSERT(pa
< 0x100000000LL
);
598 return pmap_mapdev_uncacheable(pa
, PAGE_SIZE
);
602 ioapic_sysinit(void *dummy __unused
)
609 KASSERT(lapic_enable
, ("I/O APIC is enabled, but LAPIC is disabled\n"));
610 error
= ioapic_config();
613 icu_reinit_noioapic();
614 lapic_fixup_noioapic();
617 SYSINIT(ioapic
, SI_BOOT2_IOAPIC
, SI_ORDER_FIRST
, ioapic_sysinit
, NULL
)