2 * Copyright (c) 1991 The Regents of the University of California.
5 * This code is derived from software contributed to Berkeley by
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * from: @(#)isa.c 7.2 (Berkeley) 5/13/91
37 * $FreeBSD: src/sys/i386/isa/intr_machdep.c,v 1.29.2.5 2001/10/14 06:54:27 luigi Exp $
38 * $DragonFly: src/sys/platform/pc32/isa/intr_machdep.c,v 1.33 2005/10/13 00:02:47 dillon Exp $
41 * This file contains an aggregated module marked:
42 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
43 * All rights reserved.
44 * See the notice for details.
48 #include "opt_auto_eoi.h"
50 #include <sys/param.h>
52 #include <machine/lock.h>
54 #include <sys/systm.h>
55 #include <sys/syslog.h>
56 #include <sys/malloc.h>
57 #include <sys/errno.h>
58 #include <sys/interrupt.h>
59 #include <machine/ipl.h>
60 #include <machine/md_var.h>
61 #include <machine/segments.h>
63 #include <machine/globaldata.h>
65 #include <sys/thread2.h>
67 #include <machine/smptests.h> /** FAST_HI */
68 #include <machine/smp.h>
69 #include <bus/isa/i386/isa.h>
70 #include <i386/isa/icu.h>
73 #include <bus/isa/isavar.h>
75 #include <i386/isa/intr_machdep.h>
76 #include <bus/isa/isavar.h>
77 #include <sys/interrupt.h>
79 #include <machine/clock.h>
81 #include <machine/cpu.h>
83 /* XXX should be in suitable include files */
84 #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */
89 * This is to accommodate "mixed-mode" programming for
90 * motherboards that don't connect the 8254 to the IO APIC.
95 #define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN)
97 static void init_i8259(void);
98 static int icu_unset(int intr);
99 static int icu_setup(int intr, int flags);
101 static inthand_t *fastintr[ICU_LEN] = {
102 &IDTVEC(fastintr0), &IDTVEC(fastintr1),
103 &IDTVEC(fastintr2), &IDTVEC(fastintr3),
104 &IDTVEC(fastintr4), &IDTVEC(fastintr5),
105 &IDTVEC(fastintr6), &IDTVEC(fastintr7),
106 &IDTVEC(fastintr8), &IDTVEC(fastintr9),
107 &IDTVEC(fastintr10), &IDTVEC(fastintr11),
108 &IDTVEC(fastintr12), &IDTVEC(fastintr13),
109 &IDTVEC(fastintr14), &IDTVEC(fastintr15),
111 &IDTVEC(fastintr16), &IDTVEC(fastintr17),
112 &IDTVEC(fastintr18), &IDTVEC(fastintr19),
113 &IDTVEC(fastintr20), &IDTVEC(fastintr21),
114 &IDTVEC(fastintr22), &IDTVEC(fastintr23),
118 unpendhand_t *fastunpend[ICU_LEN] = {
119 IDTVEC(fastunpend0), IDTVEC(fastunpend1),
120 IDTVEC(fastunpend2), IDTVEC(fastunpend3),
121 IDTVEC(fastunpend4), IDTVEC(fastunpend5),
122 IDTVEC(fastunpend6), IDTVEC(fastunpend7),
123 IDTVEC(fastunpend8), IDTVEC(fastunpend9),
124 IDTVEC(fastunpend10), IDTVEC(fastunpend11),
125 IDTVEC(fastunpend12), IDTVEC(fastunpend13),
126 IDTVEC(fastunpend14), IDTVEC(fastunpend15),
128 IDTVEC(fastunpend16), IDTVEC(fastunpend17),
129 IDTVEC(fastunpend18), IDTVEC(fastunpend19),
130 IDTVEC(fastunpend20), IDTVEC(fastunpend21),
131 IDTVEC(fastunpend22), IDTVEC(fastunpend23),
135 static inthand_t *slowintr[ICU_LEN] = {
136 &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
137 &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
138 &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
139 &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15),
141 &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19),
142 &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23),
148 static inthand_t *wrongintr[ICU_LEN] = {
149 &IDTVEC(wrongintr0), &IDTVEC(wrongintr1), &IDTVEC(wrongintr2),
150 &IDTVEC(wrongintr3), &IDTVEC(wrongintr4), &IDTVEC(wrongintr5),
151 &IDTVEC(wrongintr6), &IDTVEC(wrongintr7), &IDTVEC(wrongintr8),
152 &IDTVEC(wrongintr9), &IDTVEC(wrongintr10), &IDTVEC(wrongintr11),
153 &IDTVEC(wrongintr12), &IDTVEC(wrongintr13), &IDTVEC(wrongintr14),
154 &IDTVEC(wrongintr15),
155 &IDTVEC(wrongintr16), &IDTVEC(wrongintr17), &IDTVEC(wrongintr18),
156 &IDTVEC(wrongintr19), &IDTVEC(wrongintr20), &IDTVEC(wrongintr21),
157 &IDTVEC(wrongintr22), &IDTVEC(wrongintr23)
162 #define NMI_PARITY (1 << 7)
163 #define NMI_IOCHAN (1 << 6)
164 #define ENMI_WATCHDOG (1 << 7)
165 #define ENMI_BUSTIMER (1 << 6)
166 #define ENMI_IOSTATUS (1 << 5)
169 * Handle a NMI, possibly a machine check.
170 * return true to panic system, false to ignore.
177 int isa_port = inb(0x61);
178 int eisa_port = inb(0x461);
180 log(LOG_CRIT, "NMI ISA %x, EISA %x\n", isa_port, eisa_port);
182 if (isa_port & NMI_PARITY) {
183 log(LOG_CRIT, "RAM parity error, likely hardware failure.");
187 if (isa_port & NMI_IOCHAN) {
188 log(LOG_CRIT, "I/O channel check, likely hardware failure.");
193 * On a real EISA machine, this will never happen. However it can
194 * happen on ISA machines which implement XT style floating point
195 * error handling (very rare). Save them from a meaningless panic.
197 if (eisa_port == 0xff)
200 if (eisa_port & ENMI_WATCHDOG) {
201 log(LOG_CRIT, "EISA watchdog timer expired, likely hardware failure.");
205 if (eisa_port & ENMI_BUSTIMER) {
206 log(LOG_CRIT, "EISA bus timeout, likely hardware failure.");
210 if (eisa_port & ENMI_IOSTATUS) {
211 log(LOG_CRIT, "EISA I/O port status error.");
218 * ICU reinitialize when ICU configuration has lost.
226 for (i = 0; i < ICU_LEN; ++i) {
227 if (count_registered_ints(i))
233 * Fill in default interrupt table (in case of spurious interrupt
234 * during configuration of kernel, setup interrupt control unit
242 for (i = 0; i < ICU_LEN; i++)
251 /* initialize 8259's */
252 outb(IO_ICU1, 0x11); /* reset; program device, four bytes */
253 outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */
254 outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */
256 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */
258 outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */
260 outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */
261 outb(IO_ICU1, 0x0a); /* default to IRR on read */
262 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */
263 outb(IO_ICU2, 0x11); /* reset; program device, four bytes */
264 outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */
265 outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */
267 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */
269 outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */
271 outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */
272 outb(IO_ICU2, 0x0a); /* default to IRR on read */
277 * Return a bitmap of the current interrupt requests. This is 8259-specific
278 * and is only suitable for use at probe time.
281 isa_irq_pending(void)
288 return ((irr2 << 8) | irr1);
294 icu_setup(int intr, int flags)
296 #if defined(FAST_HI) && defined(APIC_IO)
297 int select; /* the select register is 8 bits */
299 u_int32_t value; /* the window register is 32 bits */
304 if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */
306 if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID)
311 cpu_disable_intr(); /* YYY */
312 #if defined(FAST_HI) && defined(APIC_IO)
313 if (flags & INTR_FAST) {
315 * Install a spurious interrupt in the low space in case
316 * the IO apic is not properly reprogrammed.
318 vector = TPR_SLOW_INTS + intr;
319 setidt(vector, wrongintr[intr],
320 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
321 vector = TPR_FAST_INTS + intr;
322 setidt(vector, fastintr[intr],
323 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
325 vector = TPR_SLOW_INTS + intr;
326 #ifdef APIC_INTR_REORDER
327 #ifdef APIC_INTR_HIGHPRI_CLOCK
328 /* XXX: Hack (kludge?) for more accurate clock. */
329 if (intr == apic_8254_intr || intr == 8) {
330 vector = TPR_FAST_INTS + intr;
334 setidt(vector, slowintr[intr],
335 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
337 #ifdef APIC_INTR_REORDER
338 set_lapic_isrloc(intr, vector);
341 * Reprogram the vector in the IO APIC.
343 * XXX EOI/mask a pending (stray) interrupt on the old vector?
345 if (int_to_apicintpin[intr].ioapic >= 0) {
346 select = int_to_apicintpin[intr].redirindex;
347 value = io_apic_read(int_to_apicintpin[intr].ioapic,
348 select) & ~IOART_INTVEC;
349 io_apic_write(int_to_apicintpin[intr].ioapic,
350 select, value | vector);
353 setidt(ICU_OFFSET + intr,
354 flags & INTR_FAST ? fastintr[intr] : slowintr[intr],
355 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
356 #endif /* FAST_HI && APIC_IO */
367 KKASSERT((u_int)intr < ICU_LEN);
370 cpu_disable_intr(); /* YYY */
372 /* XXX how do I re-create dvp here? */
373 setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr,
374 slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
376 #ifdef APIC_INTR_REORDER
377 set_lapic_isrloc(intr, ICU_OFFSET + intr);
379 setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL,
380 GSEL(GCODE_SEL, SEL_KPL));
387 /* The following notice applies beyond this point in the file */
390 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
391 * All rights reserved.
393 * Redistribution and use in source and binary forms, with or without
394 * modification, are permitted provided that the following conditions
396 * 1. Redistributions of source code must retain the above copyright
397 * notice unmodified, this list of conditions, and the following
399 * 2. Redistributions in binary form must reproduce the above copyright
400 * notice, this list of conditions and the following disclaimer in the
401 * documentation and/or other materials provided with the distribution.
403 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
404 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
405 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
406 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
407 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
408 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
409 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
410 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
411 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
412 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
414 * $FreeBSD: src/sys/i386/isa/intr_machdep.c,v 1.29.2.5 2001/10/14 06:54:27 luigi Exp $
419 * Create and activate an interrupt handler descriptor data structure.
421 * The dev_instance pointer is required for resource management, and will
422 * only be passed through to resource_claim().
424 * There will be functions that derive a driver and unit name from a
425 * dev_instance variable, and those functions will be used to maintain the
426 * interrupt counter label array referenced by systat and vmstat to report
427 * device interrupt rates (->update_intrlabels).
429 * Add the interrupt handler descriptor data structure created by an
430 * earlier call of create_intr() to the linked list for its irq.
432 * WARNING: This is an internal function and not to be used by device
433 * drivers. It is subject to change without notice.
437 inthand_add(const char *name, int irq, inthand2_t handler, void *arg,
438 int flags, lwkt_serialize_t serializer)
443 if ((unsigned)irq >= ICU_LEN) {
444 printf("create_intr: requested irq%d too high, limit is %d\n",
449 * Register the interrupt, then setup the ICU
451 id = register_int(irq, handler, arg, name, serializer, flags);
454 printf("Unable to install handler for %s\n", name);
455 printf("\tdevice combination not supported on irq %d\n", irq);
460 if (count_registered_ints(irq) == 1) {
461 if (icu_setup(irq, flags))
471 printf("\tinthand_add(irq%d) failed, result=%d\n",
481 * Deactivate and remove the interrupt handler descriptor data connected
482 * created by an earlier call of intr_connect() from the linked list.
484 * Return the memory held by the interrupt handler descriptor data structure
485 * to the system. Make sure, the handler is not actively used anymore, before.
488 inthand_remove(void *id)
496 irq = get_registered_intr(id);
497 if (unregister_int(id) == 0) {
507 * This function is called by an interrupt thread when it has completed
508 * processing a loop. We re-enable interrupts and interlock with
511 * See kern/kern_intr.c for more information.
514 ithread_done(int irq)
516 struct mdglobaldata *gd = mdcpu;
520 td = gd->mi.gd_curthread;
522 KKASSERT(td->td_pri >= TDPRI_CRIT);
523 lwkt_deschedule_self(td);
525 if (gd->gd_ipending & mask) {
526 atomic_clear_int_nonlocked(&gd->gd_ipending, mask);
528 lwkt_schedule_self(td);
536 * forward_fast_remote()
538 * This function is called from the receiving end of an IPIQ when a
539 * remote cpu wishes to forward a fast interrupt to us. All we have to
540 * do is set the interrupt pending and let the IPI's doreti deal with it.
543 forward_fastint_remote(void *arg)
546 struct mdglobaldata *gd = mdcpu;
548 atomic_set_int_nonlocked(&gd->gd_fpending, 1 << irq);
549 atomic_set_int_nonlocked(&gd->mi.gd_reqflags, RQF_INTPEND);