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>
34 #include <machine/globaldata.h>
35 #include <machine/smp.h>
36 #include <machine/cputypes.h>
37 #include <machine/md_var.h>
38 #include <machine/pmap.h>
39 #include <machine_base/apic/lapic.h>
40 #include <machine_base/apic/ioapic.h>
41 #include <machine_base/apic/ioapic_abi.h>
42 #include <machine_base/icu/icu_var.h>
43 #include <machine/segments.h>
44 #include <sys/thread2.h>
46 #include <machine/intr_machdep.h>
48 #define IOAPIC_COUNT_MAX 16
49 #define IOAPIC_ID_MASK (IOAPIC_COUNT_MAX - 1)
52 extern pt_entry_t *SMPpt;
61 TAILQ_ENTRY(ioapic_info) io_link;
63 TAILQ_HEAD(ioapic_info_list, ioapic_info);
65 struct ioapic_intsrc {
67 enum intr_trigger int_trig;
68 enum intr_polarity int_pola;
72 struct ioapic_info_list ioc_list;
73 struct ioapic_intsrc ioc_intsrc[16]; /* XXX magic number */
76 static void ioapic_setup(const struct ioapic_info *);
77 static int ioapic_alloc_apic_id(int);
78 static void ioapic_set_apic_id(const struct ioapic_info *);
79 static void ioapic_gsi_setup(int);
80 static const struct ioapic_info *
81 ioapic_gsi_search(int);
82 static void ioapic_pin_prog(void *, int, int,
83 enum intr_trigger, enum intr_polarity, uint32_t);
85 static struct ioapic_conf ioapic_conf;
87 static TAILQ_HEAD(, ioapic_enumerator) ioapic_enumerators =
88 TAILQ_HEAD_INITIALIZER(ioapic_enumerators);
90 int ioapic_enable = 1; /* I/O APIC is enabled by default */
95 struct ioapic_info *info;
96 int start_apic_id = 0;
97 struct ioapic_enumerator *e;
101 TAILQ_INIT(&ioapic_conf.ioc_list);
102 /* XXX magic number */
103 for (i = 0; i < 16; ++i)
104 ioapic_conf.ioc_intsrc[i].int_gsi = -1;
107 TUNABLE_INT_FETCH("hw.ioapic_probe", &probe);
109 kprintf("IOAPIC: warning I/O APIC will not be probed\n");
113 TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
114 error = e->ioapic_probe(e);
119 kprintf("IOAPIC: can't find I/O APIC\n");
129 * Switch to I/O APIC MachIntrABI and reconfigure
130 * the default IDT entries.
132 MachIntrABI = MachIntrABI_IOAPIC;
133 MachIntrABI.setdefault();
135 e->ioapic_enumerate(e);
141 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
144 if (i > IOAPIC_COUNT_MAX) /* XXX magic number */
145 panic("ioapic_config: more than 16 I/O APIC\n");
150 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
153 apic_id = ioapic_alloc_apic_id(start_apic_id);
154 if (apic_id == NAPICID) {
155 kprintf("IOAPIC: can't alloc APIC ID for "
156 "%dth I/O APIC\n", info->io_idx);
159 info->io_apic_id = apic_id;
161 start_apic_id = apic_id + 1;
165 * xAPIC allows I/O APIC's APIC ID to be same
166 * as the LAPIC's APIC ID
168 kprintf("IOAPIC: use xAPIC model to alloc APIC ID "
171 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
172 info->io_apic_id = info->io_idx;
176 * Warning about any GSI holes
178 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
179 const struct ioapic_info *prev_info;
181 prev_info = TAILQ_PREV(info, ioapic_info_list, io_link);
182 if (prev_info != NULL) {
183 if (info->io_gsi_base !=
184 prev_info->io_gsi_base + prev_info->io_npin) {
185 kprintf("IOAPIC: warning gsi hole "
187 prev_info->io_gsi_base +
189 info->io_gsi_base - 1);
195 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
196 kprintf("IOAPIC: idx %d, apic id %d, "
197 "gsi base %d, npin %d\n",
208 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
210 ioapic_abi_fixup_irqmap();
214 MachIntrABI.cleanup();
222 ioapic_enumerator_register(struct ioapic_enumerator *ne)
224 struct ioapic_enumerator *e;
226 TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
227 if (e->ioapic_prio < ne->ioapic_prio) {
228 TAILQ_INSERT_BEFORE(e, ne, ioapic_link);
232 TAILQ_INSERT_TAIL(&ioapic_enumerators, ne, ioapic_link);
236 ioapic_add(void *addr, int gsi_base, int npin)
238 struct ioapic_info *info, *ninfo;
241 gsi_end = gsi_base + npin - 1;
242 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
243 if ((gsi_base >= info->io_gsi_base &&
244 gsi_base < info->io_gsi_base + info->io_npin) ||
245 (gsi_end >= info->io_gsi_base &&
246 gsi_end < info->io_gsi_base + info->io_npin)) {
247 panic("ioapic_add: overlapped gsi, base %d npin %d, "
248 "hit base %d, npin %d\n", gsi_base, npin,
249 info->io_gsi_base, info->io_npin);
251 if (info->io_addr == addr)
252 panic("ioapic_add: duplicated addr %p\n", addr);
255 ninfo = kmalloc(sizeof(*ninfo), M_DEVBUF, M_WAITOK | M_ZERO);
256 ninfo->io_addr = addr;
257 ninfo->io_npin = npin;
258 ninfo->io_gsi_base = gsi_base;
259 ninfo->io_apic_id = -1;
262 * Create IOAPIC list in ascending order of GSI base
264 TAILQ_FOREACH_REVERSE(info, &ioapic_conf.ioc_list,
265 ioapic_info_list, io_link) {
266 if (ninfo->io_gsi_base > info->io_gsi_base) {
267 TAILQ_INSERT_AFTER(&ioapic_conf.ioc_list,
268 info, ninfo, io_link);
273 TAILQ_INSERT_HEAD(&ioapic_conf.ioc_list, ninfo, io_link);
277 ioapic_intsrc(int irq, int gsi, enum intr_trigger trig, enum intr_polarity pola)
279 struct ioapic_intsrc *int_src;
282 int_src = &ioapic_conf.ioc_intsrc[irq];
285 /* Don't allow mixed mode */
286 kprintf("IOAPIC: warning intsrc irq %d -> gsi 0\n", irq);
290 if (int_src->int_gsi != -1) {
291 if (int_src->int_gsi != gsi) {
292 kprintf("IOAPIC: warning intsrc irq %d, gsi "
293 "%d -> %d\n", irq, int_src->int_gsi, gsi);
295 if (int_src->int_trig != trig) {
296 kprintf("IOAPIC: warning intsrc irq %d, trig "
298 intr_str_trigger(int_src->int_trig),
299 intr_str_trigger(trig));
301 if (int_src->int_pola != pola) {
302 kprintf("IOAPIC: warning intsrc irq %d, pola "
304 intr_str_polarity(int_src->int_pola),
305 intr_str_polarity(pola));
308 int_src->int_gsi = gsi;
309 int_src->int_trig = trig;
310 int_src->int_pola = pola;
314 ioapic_set_apic_id(const struct ioapic_info *info)
319 id = ioapic_read(info->io_addr, IOAPIC_ID);
322 id |= (info->io_apic_id << 24);
324 ioapic_write(info->io_addr, IOAPIC_ID, id);
329 id = ioapic_read(info->io_addr, IOAPIC_ID);
330 apic_id = (id & APIC_ID_MASK) >> 24;
333 * I/O APIC ID is a 4bits field
335 if ((apic_id & IOAPIC_ID_MASK) !=
336 (info->io_apic_id & IOAPIC_ID_MASK)) {
337 panic("ioapic_set_apic_id: can't set apic id to %d, "
338 "currently set to %d\n", info->io_apic_id, apic_id);
343 ioapic_gsi_setup(int gsi)
345 enum intr_trigger trig;
346 enum intr_polarity pola;
352 ioapic_extpin_setup(ioapic_gsi_ioaddr(gsi),
353 ioapic_gsi_pin(gsi), 0);
358 trig = 0; /* silence older gcc's */
359 pola = 0; /* silence older gcc's */
361 for (irq = 0; irq < 16; ++irq) {
362 const struct ioapic_intsrc *int_src =
363 &ioapic_conf.ioc_intsrc[irq];
365 if (gsi == int_src->int_gsi) {
366 trig = int_src->int_trig;
367 pola = int_src->int_pola;
374 trig = INTR_TRIGGER_EDGE;
375 pola = INTR_POLARITY_HIGH;
377 trig = INTR_TRIGGER_LEVEL;
378 pola = INTR_POLARITY_LOW;
383 ioapic_abi_set_irqmap(irq, gsi, trig, pola);
387 ioapic_gsi_ioaddr(int gsi)
389 const struct ioapic_info *info;
391 info = ioapic_gsi_search(gsi);
392 return info->io_addr;
396 ioapic_gsi_pin(int gsi)
398 const struct ioapic_info *info;
400 info = ioapic_gsi_search(gsi);
401 return gsi - info->io_gsi_base;
404 static const struct ioapic_info *
405 ioapic_gsi_search(int gsi)
407 const struct ioapic_info *info;
409 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
410 if (gsi >= info->io_gsi_base &&
411 gsi < info->io_gsi_base + info->io_npin)
414 panic("ioapic_gsi_search: no I/O APIC\n");
418 ioapic_gsi(int idx, int pin)
420 const struct ioapic_info *info;
422 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
423 if (info->io_idx == idx)
428 if (pin >= info->io_npin)
430 return info->io_gsi_base + pin;
434 ioapic_extpin_setup(void *addr, int pin, int vec)
436 ioapic_pin_prog(addr, pin, vec,
437 INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT);
441 ioapic_extpin_gsi(void)
447 ioapic_pin_setup(void *addr, int pin, int vec,
448 enum intr_trigger trig, enum intr_polarity pola)
451 * Always clear an I/O APIC pin before [re]programming it. This is
452 * particularly important if the pin is set up for a level interrupt
453 * as the IOART_REM_IRR bit might be set. When we reprogram the
454 * vector any EOI from pending ints on this pin could be lost and
455 * IRR might never get reset.
457 * To fix this problem, clear the vector and make sure it is
458 * programmed as an edge interrupt. This should theoretically
459 * clear IRR so we can later, safely program it as a level
462 ioapic_pin_prog(addr, pin, vec, INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH,
464 ioapic_pin_prog(addr, pin, vec, trig, pola, IOART_DELFIXED);
468 ioapic_pin_prog(void *addr, int pin, int vec,
469 enum intr_trigger trig, enum intr_polarity pola, uint32_t del_mode)
471 uint32_t flags, target;
474 KKASSERT(del_mode == IOART_DELEXINT || del_mode == IOART_DELFIXED);
476 select = IOAPIC_REDTBL0 + (2 * pin);
478 flags = ioapic_read(addr, select) & IOART_RESV;
479 flags |= IOART_INTMSET | IOART_DESTPHY;
484 * We only support limited I/O APIC mixed mode,
485 * so even for ExtINT, we still use "fixed"
488 flags |= IOART_DELFIXED;
491 if (del_mode == IOART_DELEXINT) {
492 KKASSERT(trig == INTR_TRIGGER_CONFORM &&
493 pola == INTR_POLARITY_CONFORM);
494 flags |= IOART_TRGREDG | IOART_INTAHI;
497 case INTR_TRIGGER_EDGE:
498 flags |= IOART_TRGREDG;
501 case INTR_TRIGGER_LEVEL:
502 flags |= IOART_TRGRLVL;
505 case INTR_TRIGGER_CONFORM:
506 panic("ioapic_pin_prog: trig conform is not "
510 case INTR_POLARITY_HIGH:
511 flags |= IOART_INTAHI;
514 case INTR_POLARITY_LOW:
515 flags |= IOART_INTALO;
518 case INTR_POLARITY_CONFORM:
519 panic("ioapic_pin_prog: pola conform is not "
524 target = ioapic_read(addr, select + 1) & IOART_HI_DEST_RESV;
525 target |= (CPUID_TO_APICID(0) << IOART_HI_DEST_SHIFT) &
528 ioapic_write(addr, select, flags | vec);
529 ioapic_write(addr, select + 1, target);
533 ioapic_setup(const struct ioapic_info *info)
537 ioapic_set_apic_id(info);
539 for (i = 0; i < info->io_npin; ++i)
540 ioapic_gsi_setup(info->io_gsi_base + i);
544 ioapic_alloc_apic_id(int start)
547 const struct ioapic_info *info;
548 int apic_id, apic_id16;
550 apic_id = lapic_unused_apic_id(start);
551 if (apic_id == NAPICID) {
552 kprintf("IOAPIC: can't find unused APIC ID\n");
555 apic_id16 = apic_id & IOAPIC_ID_MASK;
558 * Check against other I/O APIC's APIC ID's lower 4bits.
560 * The new APIC ID will have to be different from others
561 * in the lower 4bits, no matter whether xAPIC is used
564 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
565 if (info->io_apic_id == -1) {
569 if ((info->io_apic_id & IOAPIC_ID_MASK) == apic_id16)
575 kprintf("IOAPIC: APIC ID %d has same lower 4bits as "
576 "%dth I/O APIC, keep searching...\n",
577 apic_id, info->io_idx);
581 panic("ioapic_unused_apic_id: never reached\n");
585 ioapic_map(vm_paddr_t pa)
587 KKASSERT(pa < 0x100000000LL);
588 return pmap_mapdev_uncacheable(pa, PAGE_SIZE);
592 ioapic_sysinit(void *dummy __unused)
599 KASSERT(lapic_enable, ("I/O APIC is enabled, but LAPIC is disabled\n"));
600 error = ioapic_config();
603 icu_reinit_noioapic();
604 lapic_fixup_noioapic();
607 SYSINIT(ioapic, SI_BOOT2_IOAPIC, SI_ORDER_FIRST, ioapic_sysinit, NULL)