ioapic_abi: More consistent function name w/ legacy interrupt
[dragonfly.git] / sys / platform / pc32 / apic / ioapic.c
CommitLineData
11bae9b8
SZ
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: src/sys/i386/i386/mpapic.c,v 1.37.2.7 2003/01/25 02:31:47 peter Exp $
26 */
27
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/kernel.h>
31#include <sys/bus.h>
32#include <sys/machintr.h>
8c8874f9
SZ
33#include <sys/thread2.h>
34
11bae9b8 35#include <machine/pmap.h>
58587c23 36#include <machine_base/isa/isa_intr.h>
8c8874f9 37#include <machine_base/icu/icu_var.h>
3340ac41 38#include <machine_base/apic/lapic.h>
4298586a 39#include <machine_base/apic/ioapic.h>
11bae9b8 40#include <machine_base/apic/ioapic_abi.h>
bfcc9e9b 41#include <machine_base/apic/apicvar.h>
11bae9b8
SZ
42
43#define IOAPIC_COUNT_MAX 16
44#define IOAPIC_ID_MASK (IOAPIC_COUNT_MAX - 1)
45
11bae9b8
SZ
46struct ioapic_info {
47 int io_idx;
48 int io_apic_id;
49 void *io_addr;
50 int io_npin;
51 int io_gsi_base;
52
53 TAILQ_ENTRY(ioapic_info) io_link;
54};
55TAILQ_HEAD(ioapic_info_list, ioapic_info);
56
57struct ioapic_intsrc {
58 int int_gsi;
59 enum intr_trigger int_trig;
60 enum intr_polarity int_pola;
61};
62
63struct ioapic_conf {
64 struct ioapic_info_list ioc_list;
58587c23 65 struct ioapic_intsrc ioc_intsrc[ISA_IRQ_CNT];
11bae9b8
SZ
66};
67
438ae23b 68static int ioapic_config(void);
11bae9b8
SZ
69static void ioapic_setup(const struct ioapic_info *);
70static int ioapic_alloc_apic_id(int);
71static void ioapic_set_apic_id(const struct ioapic_info *);
72static void ioapic_gsi_setup(int);
73static const struct ioapic_info *
74 ioapic_gsi_search(int);
75static void ioapic_pin_prog(void *, int, int,
9dba15ae 76 enum intr_trigger, enum intr_polarity, uint32_t, int);
11bae9b8
SZ
77
78static struct ioapic_conf ioapic_conf;
79
80static TAILQ_HEAD(, ioapic_enumerator) ioapic_enumerators =
81 TAILQ_HEAD_INITIALIZER(ioapic_enumerators);
82
ed4d621d
SZ
83int ioapic_enable = 1; /* I/O APIC is enabled by default */
84
438ae23b 85static int
11bae9b8
SZ
86ioapic_config(void)
87{
88 struct ioapic_info *info;
89 int start_apic_id = 0;
90 struct ioapic_enumerator *e;
0bccf4f5 91 int error, i, probe;
11bae9b8
SZ
92 u_long ef = 0;
93
94 TAILQ_INIT(&ioapic_conf.ioc_list);
58587c23 95 for (i = 0; i < ISA_IRQ_CNT; ++i)
11bae9b8
SZ
96 ioapic_conf.ioc_intsrc[i].int_gsi = -1;
97
0bccf4f5
SZ
98 probe = 1;
99 TUNABLE_INT_FETCH("hw.ioapic_probe", &probe);
100 if (!probe) {
101 kprintf("IOAPIC: warning I/O APIC will not be probed\n");
102 return ENXIO;
103 }
104
11bae9b8
SZ
105 TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
106 error = e->ioapic_probe(e);
107 if (!error)
108 break;
109 }
110 if (e == NULL) {
0bccf4f5
SZ
111 kprintf("IOAPIC: can't find I/O APIC\n");
112 return ENXIO;
11bae9b8
SZ
113 }
114
115 crit_enter();
116
117 ef = read_eflags();
118 cpu_disable_intr();
119
120 /*
121 * Switch to I/O APIC MachIntrABI and reconfigure
122 * the default IDT entries.
123 */
124 MachIntrABI = MachIntrABI_IOAPIC;
125 MachIntrABI.setdefault();
126
127 e->ioapic_enumerate(e);
128
129 /*
130 * Setup index
131 */
132 i = 0;
133 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
134 info->io_idx = i++;
135
58587c23 136 if (i > IOAPIC_COUNT_MAX)
11bae9b8
SZ
137 panic("ioapic_config: more than 16 I/O APIC\n");
138
139 /*
140 * Setup APIC ID
141 */
142 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
143 int apic_id;
144
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);
149 break;
150 }
151 info->io_apic_id = apic_id;
152
153 start_apic_id = apic_id + 1;
154 }
155 if (info != NULL) {
156 /*
157 * xAPIC allows I/O APIC's APIC ID to be same
158 * as the LAPIC's APIC ID
159 */
160 kprintf("IOAPIC: use xAPIC model to alloc APIC ID "
161 "for I/O APIC\n");
162
163 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
164 info->io_apic_id = info->io_idx;
165 }
166
167 /*
168 * Warning about any GSI holes
169 */
170 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
171 const struct ioapic_info *prev_info;
172
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 "
178 "[%d, %d]\n",
179 prev_info->io_gsi_base +
180 prev_info->io_npin,
181 info->io_gsi_base - 1);
182 }
183 }
184 }
185
186 if (bootverbose) {
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",
190 info->io_idx,
191 info->io_apic_id,
192 info->io_gsi_base,
193 info->io_npin);
194 }
195 }
196
197 /*
198 * Setup all I/O APIC
199 */
200 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
201 ioapic_setup(info);
027bbbfe 202 ioapic_fixup_legacy_irqmaps();
11bae9b8
SZ
203
204 write_eflags(ef);
205
206 MachIntrABI.cleanup();
207
208 crit_exit();
0bccf4f5
SZ
209
210 return 0;
11bae9b8
SZ
211}
212
213void
214ioapic_enumerator_register(struct ioapic_enumerator *ne)
215{
216 struct ioapic_enumerator *e;
217
218 TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
219 if (e->ioapic_prio < ne->ioapic_prio) {
220 TAILQ_INSERT_BEFORE(e, ne, ioapic_link);
221 return;
222 }
223 }
224 TAILQ_INSERT_TAIL(&ioapic_enumerators, ne, ioapic_link);
225}
226
227void
228ioapic_add(void *addr, int gsi_base, int npin)
229{
230 struct ioapic_info *info, *ninfo;
231 int gsi_end;
232
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);
242 }
243 if (info->io_addr == addr)
244 panic("ioapic_add: duplicated addr %p\n", addr);
245 }
246
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;
252
253 /*
254 * Create IOAPIC list in ascending order of GSI base
255 */
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);
261 break;
262 }
263 }
264 if (info == NULL)
265 TAILQ_INSERT_HEAD(&ioapic_conf.ioc_list, ninfo, io_link);
266}
267
268void
269ioapic_intsrc(int irq, int gsi, enum intr_trigger trig, enum intr_polarity pola)
270{
271 struct ioapic_intsrc *int_src;
272
58587c23 273 KKASSERT(irq < ISA_IRQ_CNT);
11bae9b8
SZ
274 int_src = &ioapic_conf.ioc_intsrc[irq];
275
276 if (gsi == 0) {
277 /* Don't allow mixed mode */
278 kprintf("IOAPIC: warning intsrc irq %d -> gsi 0\n", irq);
279 return;
280 }
281
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);
286 }
287 if (int_src->int_trig != trig) {
288 kprintf("IOAPIC: warning intsrc irq %d, trig "
289 "%s -> %s\n", irq,
290 intr_str_trigger(int_src->int_trig),
291 intr_str_trigger(trig));
292 }
293 if (int_src->int_pola != pola) {
294 kprintf("IOAPIC: warning intsrc irq %d, pola "
295 "%s -> %s\n", irq,
296 intr_str_polarity(int_src->int_pola),
297 intr_str_polarity(pola));
298 }
299 }
300 int_src->int_gsi = gsi;
301 int_src->int_trig = trig;
302 int_src->int_pola = pola;
303}
304
305static void
306ioapic_set_apic_id(const struct ioapic_info *info)
307{
308 uint32_t id;
309 int apic_id;
310
311 id = ioapic_read(info->io_addr, IOAPIC_ID);
312
313 id &= ~APIC_ID_MASK;
314 id |= (info->io_apic_id << 24);
315
316 ioapic_write(info->io_addr, IOAPIC_ID, id);
317
318 /*
319 * Re-read && test
320 */
321 id = ioapic_read(info->io_addr, IOAPIC_ID);
322 apic_id = (id & APIC_ID_MASK) >> 24;
323
324 /*
325 * I/O APIC ID is a 4bits field
326 */
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);
331 }
332}
333
334static void
335ioapic_gsi_setup(int gsi)
336{
337 enum intr_trigger trig;
338 enum intr_polarity pola;
339 int irq;
340
341 if (gsi == 0) {
342 /* ExtINT */
343 imen_lock();
344 ioapic_extpin_setup(ioapic_gsi_ioaddr(gsi),
345 ioapic_gsi_pin(gsi), 0);
346 imen_unlock();
347 return;
348 }
349
350 trig = 0; /* silence older gcc's */
351 pola = 0; /* silence older gcc's */
352
58587c23 353 for (irq = 0; irq < ISA_IRQ_CNT; ++irq) {
11bae9b8
SZ
354 const struct ioapic_intsrc *int_src =
355 &ioapic_conf.ioc_intsrc[irq];
356
357 if (gsi == int_src->int_gsi) {
358 trig = int_src->int_trig;
359 pola = int_src->int_pola;
360 break;
361 }
362 }
363
58587c23 364 if (irq == ISA_IRQ_CNT) {
41f4f287
SZ
365 /*
366 * No explicit IRQ to GSI mapping;
367 * use the default 1:1 mapping
368 */
369 irq = gsi;
58587c23 370 if (irq < ISA_IRQ_CNT) {
41f4f287
SZ
371 if (ioapic_conf.ioc_intsrc[irq].int_gsi >= 0) {
372 /*
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.
289debb8
SZ
377 *
378 * This GSI is not used, disable it.
41f4f287 379 */
3c161c1d 380 imen_lock();
289debb8
SZ
381 ioapic_pin_setup(ioapic_gsi_ioaddr(gsi),
382 ioapic_gsi_pin(gsi), 0,
9dba15ae 383 INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH, 0);
3c161c1d 384 imen_unlock();
41f4f287
SZ
385 return;
386 }
11bae9b8
SZ
387 trig = INTR_TRIGGER_EDGE;
388 pola = INTR_POLARITY_HIGH;
389 } else {
390 trig = INTR_TRIGGER_LEVEL;
391 pola = INTR_POLARITY_LOW;
392 }
11bae9b8
SZ
393 }
394
027bbbfe 395 ioapic_set_legacy_irqmap(irq, gsi, trig, pola);
11bae9b8
SZ
396}
397
398void *
399ioapic_gsi_ioaddr(int gsi)
400{
401 const struct ioapic_info *info;
402
403 info = ioapic_gsi_search(gsi);
404 return info->io_addr;
405}
406
407int
408ioapic_gsi_pin(int gsi)
409{
410 const struct ioapic_info *info;
411
412 info = ioapic_gsi_search(gsi);
413 return gsi - info->io_gsi_base;
414}
415
416static const struct ioapic_info *
417ioapic_gsi_search(int gsi)
418{
419 const struct ioapic_info *info;
420
421 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
422 if (gsi >= info->io_gsi_base &&
423 gsi < info->io_gsi_base + info->io_npin)
424 return info;
425 }
426 panic("ioapic_gsi_search: no I/O APIC\n");
427}
428
429int
430ioapic_gsi(int idx, int pin)
431{
432 const struct ioapic_info *info;
433
434 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
435 if (info->io_idx == idx)
436 break;
437 }
438 if (info == NULL)
439 return -1;
440 if (pin >= info->io_npin)
441 return -1;
442 return info->io_gsi_base + pin;
443}
444
445void
446ioapic_extpin_setup(void *addr, int pin, int vec)
447{
448 ioapic_pin_prog(addr, pin, vec,
9dba15ae 449 INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT, 0);
11bae9b8
SZ
450}
451
452int
453ioapic_extpin_gsi(void)
454{
455 return 0;
456}
457
458void
459ioapic_pin_setup(void *addr, int pin, int vec,
9dba15ae 460 enum intr_trigger trig, enum intr_polarity pola, int cpuid)
11bae9b8
SZ
461{
462 /*
463 * Always clear an I/O APIC pin before [re]programming it. This is
464 * particularly important if the pin is set up for a level interrupt
465 * as the IOART_REM_IRR bit might be set. When we reprogram the
466 * vector any EOI from pending ints on this pin could be lost and
467 * IRR might never get reset.
468 *
469 * To fix this problem, clear the vector and make sure it is
470 * programmed as an edge interrupt. This should theoretically
471 * clear IRR so we can later, safely program it as a level
472 * interrupt.
473 */
474 ioapic_pin_prog(addr, pin, vec, INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH,
9dba15ae
SZ
475 IOART_DELFIXED, cpuid);
476 ioapic_pin_prog(addr, pin, vec, trig, pola, IOART_DELFIXED, cpuid);
11bae9b8
SZ
477}
478
479static void
480ioapic_pin_prog(void *addr, int pin, int vec,
9dba15ae
SZ
481 enum intr_trigger trig, enum intr_polarity pola,
482 uint32_t del_mode, int cpuid)
11bae9b8
SZ
483{
484 uint32_t flags, target;
485 int select;
486
487 KKASSERT(del_mode == IOART_DELEXINT || del_mode == IOART_DELFIXED);
488
489 select = IOAPIC_REDTBL0 + (2 * pin);
490
491 flags = ioapic_read(addr, select) & IOART_RESV;
492 flags |= IOART_INTMSET | IOART_DESTPHY;
493#ifdef foo
494 flags |= del_mode;
495#else
496 /*
497 * We only support limited I/O APIC mixed mode,
498 * so even for ExtINT, we still use "fixed"
499 * delivery mode.
500 */
501 flags |= IOART_DELFIXED;
502#endif
503
504 if (del_mode == IOART_DELEXINT) {
505 KKASSERT(trig == INTR_TRIGGER_CONFORM &&
506 pola == INTR_POLARITY_CONFORM);
507 flags |= IOART_TRGREDG | IOART_INTAHI;
508 } else {
509 switch (trig) {
510 case INTR_TRIGGER_EDGE:
511 flags |= IOART_TRGREDG;
512 break;
513
514 case INTR_TRIGGER_LEVEL:
515 flags |= IOART_TRGRLVL;
516 break;
517
518 case INTR_TRIGGER_CONFORM:
519 panic("ioapic_pin_prog: trig conform is not "
520 "supported\n");
521 }
522 switch (pola) {
523 case INTR_POLARITY_HIGH:
524 flags |= IOART_INTAHI;
525 break;
526
527 case INTR_POLARITY_LOW:
528 flags |= IOART_INTALO;
529 break;
530
531 case INTR_POLARITY_CONFORM:
532 panic("ioapic_pin_prog: pola conform is not "
533 "supported\n");
534 }
535 }
536
537 target = ioapic_read(addr, select + 1) & IOART_HI_DEST_RESV;
9dba15ae 538 target |= (CPUID_TO_APICID(cpuid) << IOART_HI_DEST_SHIFT) &
11bae9b8
SZ
539 IOART_HI_DEST_MASK;
540
541 ioapic_write(addr, select, flags | vec);
542 ioapic_write(addr, select + 1, target);
543}
544
545static void
546ioapic_setup(const struct ioapic_info *info)
547{
548 int i;
549
550 ioapic_set_apic_id(info);
551
552 for (i = 0; i < info->io_npin; ++i)
553 ioapic_gsi_setup(info->io_gsi_base + i);
554}
555
556static int
557ioapic_alloc_apic_id(int start)
558{
559 for (;;) {
560 const struct ioapic_info *info;
561 int apic_id, apic_id16;
562
563 apic_id = lapic_unused_apic_id(start);
564 if (apic_id == NAPICID) {
565 kprintf("IOAPIC: can't find unused APIC ID\n");
566 return apic_id;
567 }
568 apic_id16 = apic_id & IOAPIC_ID_MASK;
569
570 /*
571 * Check against other I/O APIC's APIC ID's lower 4bits.
572 *
573 * The new APIC ID will have to be different from others
574 * in the lower 4bits, no matter whether xAPIC is used
575 * or not.
576 */
577 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
578 if (info->io_apic_id == -1) {
579 info = NULL;
580 break;
581 }
582 if ((info->io_apic_id & IOAPIC_ID_MASK) == apic_id16)
583 break;
584 }
585 if (info == NULL)
586 return apic_id;
587
588 kprintf("IOAPIC: APIC ID %d has same lower 4bits as "
589 "%dth I/O APIC, keep searching...\n",
590 apic_id, info->io_idx);
591
592 start = apic_id + 1;
593 }
594 panic("ioapic_unused_apic_id: never reached\n");
595}
05c90f00
SZ
596
597void *
598ioapic_map(vm_paddr_t pa)
599{
600 KKASSERT(pa < 0x100000000LL);
601 return pmap_mapdev_uncacheable(pa, PAGE_SIZE);
602}
d631ab59
SZ
603
604static void
605ioapic_sysinit(void *dummy __unused)
606{
607 int error;
608
609 if (!ioapic_enable)
610 return;
611
612 KASSERT(lapic_enable, ("I/O APIC is enabled, but LAPIC is disabled\n"));
613 error = ioapic_config();
614 if (error) {
615 ioapic_enable = 0;
616 icu_reinit_noioapic();
617 lapic_fixup_noioapic();
618 }
619}
620SYSINIT(ioapic, SI_BOOT2_IOAPIC, SI_ORDER_FIRST, ioapic_sysinit, NULL)