Remove all remaining SPL code. Replace the mtd_cpl field in the machine
[dragonfly.git] / sys / i386 / isa / intr_machdep.c
... / ...
CommitLineData
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * William Jolitz.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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.
23 *
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
34 * SUCH DAMAGE.
35 *
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/i386/isa/Attic/intr_machdep.c,v 1.31 2005/06/16 21:12:47 dillon Exp $
39 */
40/*
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.
45 */
46
47#include "use_isa.h"
48#include "opt_auto_eoi.h"
49
50#include <sys/param.h>
51#ifndef SMP
52#include <machine/lock.h>
53#endif
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>
62#include <sys/bus.h>
63#include <machine/globaldata.h>
64#include <sys/proc.h>
65#include <sys/thread2.h>
66
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>
71
72#if NISA > 0
73#include <bus/isa/isavar.h>
74#endif
75#include <i386/isa/intr_machdep.h>
76#include <bus/isa/isavar.h>
77#include <sys/interrupt.h>
78#ifdef APIC_IO
79#include <machine/clock.h>
80#endif
81#include <machine/cpu.h>
82
83/* XXX should be in suitable include files */
84#define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */
85#define ICU_SLAVEID 2
86
87#ifdef APIC_IO
88/*
89 * This is to accommodate "mixed-mode" programming for
90 * motherboards that don't connect the 8254 to the IO APIC.
91 */
92#define AUTO_EOI_1 1
93#endif
94
95#define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN)
96
97static inthand2_t isa_strayintr;
98#if defined(FAST_HI) && defined(APIC_IO)
99static inthand2_t isa_wrongintr;
100#endif
101static void init_i8259(void);
102
103void *intr_unit[ICU_LEN*2];
104u_long *intr_countp[ICU_LEN*2];
105inthand2_t *intr_handler[ICU_LEN*2] = {
106 isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr,
107 isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr,
108 isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr,
109 isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr,
110 isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr,
111 isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr,
112 isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr,
113 isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr,
114};
115
116static struct md_intr_info {
117 int irq;
118 int mihandler_installed;
119} intr_info[ICU_LEN*2];
120
121static inthand_t *fastintr[ICU_LEN] = {
122 &IDTVEC(fastintr0), &IDTVEC(fastintr1),
123 &IDTVEC(fastintr2), &IDTVEC(fastintr3),
124 &IDTVEC(fastintr4), &IDTVEC(fastintr5),
125 &IDTVEC(fastintr6), &IDTVEC(fastintr7),
126 &IDTVEC(fastintr8), &IDTVEC(fastintr9),
127 &IDTVEC(fastintr10), &IDTVEC(fastintr11),
128 &IDTVEC(fastintr12), &IDTVEC(fastintr13),
129 &IDTVEC(fastintr14), &IDTVEC(fastintr15),
130#if defined(APIC_IO)
131 &IDTVEC(fastintr16), &IDTVEC(fastintr17),
132 &IDTVEC(fastintr18), &IDTVEC(fastintr19),
133 &IDTVEC(fastintr20), &IDTVEC(fastintr21),
134 &IDTVEC(fastintr22), &IDTVEC(fastintr23),
135#endif /* APIC_IO */
136};
137
138unpendhand_t *fastunpend[ICU_LEN] = {
139 IDTVEC(fastunpend0), IDTVEC(fastunpend1),
140 IDTVEC(fastunpend2), IDTVEC(fastunpend3),
141 IDTVEC(fastunpend4), IDTVEC(fastunpend5),
142 IDTVEC(fastunpend6), IDTVEC(fastunpend7),
143 IDTVEC(fastunpend8), IDTVEC(fastunpend9),
144 IDTVEC(fastunpend10), IDTVEC(fastunpend11),
145 IDTVEC(fastunpend12), IDTVEC(fastunpend13),
146 IDTVEC(fastunpend14), IDTVEC(fastunpend15),
147#if defined(APIC_IO)
148 IDTVEC(fastunpend16), IDTVEC(fastunpend17),
149 IDTVEC(fastunpend18), IDTVEC(fastunpend19),
150 IDTVEC(fastunpend20), IDTVEC(fastunpend21),
151 IDTVEC(fastunpend22), IDTVEC(fastunpend23),
152#endif
153};
154
155static inthand_t *slowintr[ICU_LEN] = {
156 &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
157 &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
158 &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
159 &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15),
160#if defined(APIC_IO)
161 &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19),
162 &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23),
163#endif /* APIC_IO */
164};
165
166#define NMI_PARITY (1 << 7)
167#define NMI_IOCHAN (1 << 6)
168#define ENMI_WATCHDOG (1 << 7)
169#define ENMI_BUSTIMER (1 << 6)
170#define ENMI_IOSTATUS (1 << 5)
171
172/*
173 * Handle a NMI, possibly a machine check.
174 * return true to panic system, false to ignore.
175 */
176int
177isa_nmi(cd)
178 int cd;
179{
180 int retval = 0;
181 int isa_port = inb(0x61);
182 int eisa_port = inb(0x461);
183
184 log(LOG_CRIT, "NMI ISA %x, EISA %x\n", isa_port, eisa_port);
185
186 if (isa_port & NMI_PARITY) {
187 log(LOG_CRIT, "RAM parity error, likely hardware failure.");
188 retval = 1;
189 }
190
191 if (isa_port & NMI_IOCHAN) {
192 log(LOG_CRIT, "I/O channel check, likely hardware failure.");
193 retval = 1;
194 }
195
196 /*
197 * On a real EISA machine, this will never happen. However it can
198 * happen on ISA machines which implement XT style floating point
199 * error handling (very rare). Save them from a meaningless panic.
200 */
201 if (eisa_port == 0xff)
202 return(retval);
203
204 if (eisa_port & ENMI_WATCHDOG) {
205 log(LOG_CRIT, "EISA watchdog timer expired, likely hardware failure.");
206 retval = 1;
207 }
208
209 if (eisa_port & ENMI_BUSTIMER) {
210 log(LOG_CRIT, "EISA bus timeout, likely hardware failure.");
211 retval = 1;
212 }
213
214 if (eisa_port & ENMI_IOSTATUS) {
215 log(LOG_CRIT, "EISA I/O port status error.");
216 retval = 1;
217 }
218 return(retval);
219}
220
221/*
222 * ICU reinitialize when ICU configuration has lost.
223 */
224void
225icu_reinit()
226{
227 int i;
228
229 init_i8259();
230 for(i=0;i<ICU_LEN;i++)
231 if(intr_handler[i] != isa_strayintr)
232 INTREN(1<<i);
233}
234
235
236/*
237 * Fill in default interrupt table (in case of spurious interrupt
238 * during configuration of kernel, setup interrupt control unit
239 */
240void
241isa_defaultirq()
242{
243 int i;
244
245 /* icu vectors */
246 for (i = 0; i < ICU_LEN; i++)
247 icu_unset(i, isa_strayintr);
248 init_i8259();
249}
250
251static void
252init_i8259(void)
253{
254
255 /* initialize 8259's */
256 outb(IO_ICU1, 0x11); /* reset; program device, four bytes */
257 outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */
258 outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */
259#ifdef AUTO_EOI_1
260 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */
261#else
262 outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */
263#endif
264 outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */
265 outb(IO_ICU1, 0x0a); /* default to IRR on read */
266 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */
267 outb(IO_ICU2, 0x11); /* reset; program device, four bytes */
268 outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */
269 outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */
270#ifdef AUTO_EOI_2
271 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */
272#else
273 outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */
274#endif
275 outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */
276 outb(IO_ICU2, 0x0a); /* default to IRR on read */
277}
278
279/*
280 * Caught a stray interrupt, notify
281 */
282static void
283isa_strayintr(void *vcookiep)
284{
285 int intr = (void **)vcookiep - &intr_unit[0];
286
287 /* DON'T BOTHER FOR NOW! */
288 /* for some reason, we get bursts of intr #7, even if not enabled! */
289 /*
290 * Well the reason you got bursts of intr #7 is because someone
291 * raised an interrupt line and dropped it before the 8259 could
292 * prioritize it. This is documented in the intel data book. This
293 * means you have BAD hardware! I have changed this so that only
294 * the first 5 get logged, then it quits logging them, and puts
295 * out a special message. rgrimes 3/25/1993
296 */
297 /*
298 * XXX TODO print a different message for #7 if it is for a
299 * glitch. Glitches can be distinguished from real #7's by
300 * testing that the in-service bit is _not_ set. The test
301 * must be done before sending an EOI so it can't be done if
302 * we are using AUTO_EOI_1.
303 */
304 if (intrcnt[1 + intr] <= 5)
305 log(LOG_ERR, "stray irq %d\n", intr);
306 if (intrcnt[1 + intr] == 5)
307 log(LOG_CRIT,
308 "too many stray irq %d's; not logging any more\n", intr);
309}
310
311#if defined(FAST_HI) && defined(APIC_IO)
312
313/*
314 * This occurs if we mis-programmed the APIC and its vector is still
315 * pointing to the slow vector even when we thought we reprogrammed it
316 * to the high vector. This can occur when interrupts are improperly
317 * routed by the APIC. The unit data is opaque so we have to try to
318 * find it in the unit array.
319 */
320static void
321isa_wrongintr(void *vcookiep)
322{
323 int intr;
324
325 for (intr = 0; intr < ICU_LEN*2; ++intr) {
326 if (intr_unit[intr] == vcookiep)
327 break;
328 }
329 if (intr == ICU_LEN*2) {
330 log(LOG_ERR, "stray unknown irq (APIC misprogrammed)\n");
331 } else if (intrcnt[1 + intr] <= 5) {
332 log(LOG_ERR, "stray irq ~%d (APIC misprogrammed)\n", intr);
333 } else if (intrcnt[1 + intr] == 6) {
334 log(LOG_CRIT,
335 "too many stray irq ~%d's; not logging any more\n", intr);
336 }
337}
338
339#endif
340
341#if NISA > 0
342/*
343 * Return a bitmap of the current interrupt requests. This is 8259-specific
344 * and is only suitable for use at probe time.
345 */
346intrmask_t
347isa_irq_pending(void)
348{
349 u_char irr1;
350 u_char irr2;
351
352 irr1 = inb(IO_ICU1);
353 irr2 = inb(IO_ICU2);
354 return ((irr2 << 8) | irr1);
355}
356#endif
357
358static void
359update_intrname(int intr, char *name)
360{
361 char buf[32];
362 char *cp;
363 int name_index, off, strayintr;
364
365 /*
366 * Initialise strings for bitbucket and stray interrupt counters.
367 * These have statically allocated indices 0 and 1 through ICU_LEN.
368 */
369 if (intrnames[0] == '\0') {
370 off = sprintf(intrnames, "???") + 1;
371 for (strayintr = 0; strayintr < ICU_LEN; strayintr++)
372 off += sprintf(intrnames + off, "stray irq%d",
373 strayintr) + 1;
374 }
375
376 if (name == NULL)
377 name = "???";
378 if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf))
379 goto use_bitbucket;
380
381 /*
382 * Search for `buf' in `intrnames'. In the usual case when it is
383 * not found, append it to the end if there is enough space (the \0
384 * terminator for the previous string, if any, becomes a separator).
385 */
386 for (cp = intrnames, name_index = 0;
387 cp != eintrnames && name_index < NR_INTRNAMES;
388 cp += strlen(cp) + 1, name_index++) {
389 if (*cp == '\0') {
390 if (strlen(buf) >= eintrnames - cp)
391 break;
392 strcpy(cp, buf);
393 goto found;
394 }
395 if (strcmp(cp, buf) == 0)
396 goto found;
397 }
398
399use_bitbucket:
400 printf("update_intrname: counting %s irq%d as %s\n", name, intr,
401 intrnames);
402 name_index = 0;
403found:
404 intr_countp[intr] = &intrcnt[name_index];
405}
406
407/*
408 * NOTE! intr_handler[] is only used for FAST interrupts, the *vector.s
409 * code ignores it for normal interrupts.
410 */
411int
412icu_setup(int intr, inthand2_t *handler, void *arg, int flags)
413{
414#if defined(FAST_HI) && defined(APIC_IO)
415 int select; /* the select register is 8 bits */
416 int vector;
417 u_int32_t value; /* the window register is 32 bits */
418#endif /* FAST_HI */
419 u_long ef;
420
421#if defined(APIC_IO)
422 if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */
423#else
424 if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID)
425#endif /* APIC_IO */
426 return (EINVAL);
427 if (intr_handler[intr] != isa_strayintr)
428 return (EBUSY);
429
430 ef = read_eflags();
431 cpu_disable_intr(); /* YYY */
432 intr_handler[intr] = handler;
433 intr_unit[intr] = arg;
434#if 0
435 /* YYY fast ints supported and mp protected but ... */
436 flags &= ~INTR_FAST;
437#endif
438#if defined(FAST_HI) && defined(APIC_IO)
439 if (flags & INTR_FAST) {
440 /*
441 * Install a spurious interrupt in the low space in case
442 * the IO apic is not properly reprogrammed.
443 */
444 vector = TPR_SLOW_INTS + intr;
445 setidt(vector, isa_wrongintr,
446 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
447 vector = TPR_FAST_INTS + intr;
448 setidt(vector, fastintr[intr],
449 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
450 } else {
451 vector = TPR_SLOW_INTS + intr;
452#ifdef APIC_INTR_REORDER
453#ifdef APIC_INTR_HIGHPRI_CLOCK
454 /* XXX: Hack (kludge?) for more accurate clock. */
455 if (intr == apic_8254_intr || intr == 8) {
456 vector = TPR_FAST_INTS + intr;
457 }
458#endif
459#endif
460 setidt(vector, slowintr[intr],
461 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
462 }
463#ifdef APIC_INTR_REORDER
464 set_lapic_isrloc(intr, vector);
465#endif
466 /*
467 * Reprogram the vector in the IO APIC.
468 *
469 * XXX EOI/mask a pending (stray) interrupt on the old vector?
470 */
471 if (int_to_apicintpin[intr].ioapic >= 0) {
472 select = int_to_apicintpin[intr].redirindex;
473 value = io_apic_read(int_to_apicintpin[intr].ioapic,
474 select) & ~IOART_INTVEC;
475 io_apic_write(int_to_apicintpin[intr].ioapic,
476 select, value | vector);
477 }
478#else
479 setidt(ICU_OFFSET + intr,
480 flags & INTR_FAST ? fastintr[intr] : slowintr[intr],
481 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
482#endif /* FAST_HI && APIC_IO */
483 INTREN(1 << intr);
484 write_eflags(ef);
485 return (0);
486}
487
488int
489icu_unset(intr, handler)
490 int intr;
491 inthand2_t *handler;
492{
493 u_long ef;
494
495 if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr]) {
496 printf("icu_unset: invalid handler %d %p/%p\n", intr, handler,
497 (((u_int)intr >= ICU_LEN) ? (void *)-1 : intr_handler[intr]));
498 return (EINVAL);
499 }
500
501 INTRDIS(1 << intr);
502 ef = read_eflags();
503 cpu_disable_intr(); /* YYY */
504 intr_countp[intr] = &intrcnt[1 + intr];
505 intr_handler[intr] = isa_strayintr;
506 intr_unit[intr] = &intr_unit[intr];
507#ifdef FAST_HI_XXX
508 /* XXX how do I re-create dvp here? */
509 setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr,
510 slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
511#else /* FAST_HI */
512#ifdef APIC_INTR_REORDER
513 set_lapic_isrloc(intr, ICU_OFFSET + intr);
514#endif
515 setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL,
516 GSEL(GCODE_SEL, SEL_KPL));
517#endif /* FAST_HI */
518 write_eflags(ef);
519 return (0);
520}
521
522
523/* The following notice applies beyond this point in the file */
524
525/*
526 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
527 * All rights reserved.
528 *
529 * Redistribution and use in source and binary forms, with or without
530 * modification, are permitted provided that the following conditions
531 * are met:
532 * 1. Redistributions of source code must retain the above copyright
533 * notice unmodified, this list of conditions, and the following
534 * disclaimer.
535 * 2. Redistributions in binary form must reproduce the above copyright
536 * notice, this list of conditions and the following disclaimer in the
537 * documentation and/or other materials provided with the distribution.
538 *
539 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
540 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
541 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
542 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
543 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
544 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
545 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
546 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
547 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
548 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
549 *
550 * $FreeBSD: src/sys/i386/isa/intr_machdep.c,v 1.29.2.5 2001/10/14 06:54:27 luigi Exp $
551 *
552 */
553
554typedef struct intrec {
555 inthand2_t *handler;
556 void *argument;
557 struct intrec *next;
558 char *name;
559 int intr;
560 int flags;
561 lwkt_serialize_t serializer;
562 volatile int in_handler;
563} intrec;
564
565static intrec *intreclist_head[ICU_LEN];
566
567/*
568 * The interrupt multiplexer calls each of the handlers in turn. A handler
569 * is called only if we can successfully obtain the interlock, meaning
570 * (1) we aren't recursed and (2) the handler has not been disabled via
571 * inthand_disabled().
572 *
573 * XXX the IPL is currently raised as necessary for the handler. However,
574 * IPLs are not MP safe so the IPL code will be removed when the device
575 * drivers, BIO, and VM no longer depend on it.
576 */
577static void
578intr_mux(void *arg)
579{
580 intrec **pp;
581 intrec *p;
582
583 for (pp = arg; (p = *pp) != NULL; pp = &p->next) {
584 if (p->serializer) {
585 /*
586 * New handler dispatch method. Only the serializer
587 * is used to interlock access. Note that this
588 * API includes a handler disablement feature.
589 */
590 lwkt_serialize_handler_call(p->serializer,
591 p->handler, p->argument);
592 } else {
593 /*
594 * Old handlers may expect multiple interrupt
595 * sources to be masked. We must use a critical
596 * section.
597 */
598 crit_enter();
599 p->handler(p->argument);
600 crit_exit();
601 }
602 }
603}
604
605/*
606 * Add an interrupt handler to the linked list hung off of intreclist_head[irq]
607 * and install a shared interrupt multiplex handler. Install an interrupt
608 * thread for each interrupt (though FAST interrupts will not use it).
609 * The preemption procedure checks the CPL. lwkt_preempt() will check
610 * relative thread priorities for us as long as we properly pass through
611 * critpri.
612 *
613 * The interrupt thread has already been put on the run queue, so if we cannot
614 * preempt we should force a reschedule.
615 *
616 * This preemption check routine is currently empty, but will be used in the
617 * future to pre-check interrupts for preemptability to avoid the
618 * inefficiencies of having to instantly block. We used to do a CPL check
619 * here (otherwise the interrupt thread could preempt even when it wasn't
620 * supposed to), but with CPLs gone we no longer have to do this.
621 */
622static void
623cpu_intr_preempt(struct thread *td, int critpri)
624{
625 lwkt_preempt(td, critpri);
626}
627
628static int
629add_intrdesc(intrec *idesc)
630{
631 int irq = idesc->intr;
632 intrec *head;
633 intrec **headp;
634
635 /*
636 * There are two ways to enter intr_mux(). (1) via the scheduled
637 * interrupt thread or (2) directly. The thread mechanism is the
638 * normal mechanism used by SLOW interrupts, while the direct method
639 * is used by FAST interrupts.
640 *
641 * We need to create an interrupt thread if none exists.
642 */
643 if (intr_info[irq].mihandler_installed == 0) {
644 struct thread *td;
645
646 intr_info[irq].mihandler_installed = 1;
647 intr_info[irq].irq = irq;
648 td = register_int(irq, intr_mux, &intreclist_head[irq], idesc->name);
649 td->td_info.intdata = &intr_info[irq];
650 td->td_preemptable = cpu_intr_preempt;
651 printf("installed MI handler for int %d\n", irq);
652 }
653
654 headp = &intreclist_head[irq];
655 head = *headp;
656
657 /*
658 * Check exclusion
659 */
660 if (head) {
661 if ((idesc->flags & INTR_EXCL) || (head->flags & INTR_EXCL)) {
662 printf("\tdevice combination doesn't support "
663 "shared irq%d\n", irq);
664 return (-1);
665 }
666 if ((idesc->flags & INTR_FAST) || (head->flags & INTR_FAST)) {
667 printf("\tdevice combination doesn't support "
668 "multiple FAST interrupts on IRQ%d\n", irq);
669 }
670 }
671
672 /*
673 * Always install intr_mux as the hard handler so it can deal with
674 * individual enablement on handlers.
675 */
676 if (head == NULL) {
677 if (icu_setup(irq, idesc->handler, idesc->argument, idesc->flags) != 0)
678 return (-1);
679 update_intrname(irq, idesc->name);
680 } else if (head->next == NULL) {
681 icu_unset(irq, head->handler);
682 if (icu_setup(irq, intr_mux, &intreclist_head[irq], 0) != 0)
683 return (-1);
684 if (bootverbose && head->next == NULL)
685 printf("\tusing shared irq%d.\n", irq);
686 update_intrname(irq, "mux");
687 }
688
689 /*
690 * Append to the end of the chain.
691 */
692 while (*headp != NULL)
693 headp = &(*headp)->next;
694 *headp = idesc;
695
696 return (0);
697}
698
699/*
700 * Create and activate an interrupt handler descriptor data structure.
701 *
702 * The dev_instance pointer is required for resource management, and will
703 * only be passed through to resource_claim().
704 *
705 * There will be functions that derive a driver and unit name from a
706 * dev_instance variable, and those functions will be used to maintain the
707 * interrupt counter label array referenced by systat and vmstat to report
708 * device interrupt rates (->update_intrlabels).
709 *
710 * Add the interrupt handler descriptor data structure created by an
711 * earlier call of create_intr() to the linked list for its irq.
712 *
713 * WARNING: This is an internal function and not to be used by device
714 * drivers. It is subject to change without notice.
715 */
716
717intrec *
718inthand_add(const char *name, int irq, inthand2_t handler, void *arg,
719 int flags, lwkt_serialize_t serializer)
720{
721 intrec *idesc;
722 int errcode = -1;
723
724 if ((unsigned)irq >= ICU_LEN) {
725 printf("create_intr: requested irq%d too high, limit is %d\n",
726 irq, ICU_LEN -1);
727 return (NULL);
728 }
729
730 idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK | M_ZERO);
731 if (idesc == NULL)
732 return NULL;
733
734 if (name == NULL)
735 name = "???";
736 idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK);
737 if (idesc->name == NULL) {
738 free(idesc, M_DEVBUF);
739 return NULL;
740 }
741 strcpy(idesc->name, name);
742
743 idesc->handler = handler;
744 idesc->argument = arg;
745 idesc->intr = irq;
746 idesc->flags = flags;
747 idesc->serializer = serializer;
748
749 crit_enter();
750 errcode = add_intrdesc(idesc);
751 crit_exit();
752
753 if (errcode != 0) {
754 if (bootverbose)
755 printf("\tintr_connect(irq%d) failed, result=%d\n",
756 irq, errcode);
757 free(idesc->name, M_DEVBUF);
758 free(idesc, M_DEVBUF);
759 idesc = NULL;
760 }
761
762 return (idesc);
763}
764
765/*
766 * Deactivate and remove the interrupt handler descriptor data connected
767 * created by an earlier call of intr_connect() from the linked list.
768 *
769 * Return the memory held by the interrupt handler descriptor data structure
770 * to the system. Make sure, the handler is not actively used anymore, before.
771 */
772int
773inthand_remove(intrec *idesc)
774{
775 intrec **hook, *head;
776 int irq;
777
778 if (idesc == NULL)
779 return (-1);
780
781 irq = idesc->intr;
782 crit_enter();
783
784 /*
785 * Find and remove the interrupt descriptor.
786 */
787 hook = &intreclist_head[irq];
788 while (*hook != idesc) {
789 if (*hook == NULL) {
790 crit_exit();
791 return(-1);
792 }
793 hook = &(*hook)->next;
794 }
795 *hook = idesc->next;
796
797 /*
798 * If the list is now empty, revert the hard vector to the spurious
799 * interrupt.
800 */
801 head = intreclist_head[irq];
802 if (head == NULL) {
803 /*
804 * No more interrupts on this irq
805 */
806 icu_unset(irq, idesc->handler);
807 update_intrname(irq, NULL);
808 } else if (head->next) {
809 /*
810 * This irq is still shared (has at least two handlers)
811 * (the name should already be set to "mux").
812 */
813 } else {
814 /*
815 * This irq is no longer shared
816 */
817 icu_unset(irq, intr_mux);
818 icu_setup(irq, head->handler, head->argument, head->flags);
819 update_intrname(irq, head->name);
820 }
821 crit_exit();
822 free(idesc, M_DEVBUF);
823
824 return (0);
825}
826
827/*
828 * ithread_done()
829 *
830 * This function is called by an interrupt thread when it has completed
831 * processing a loop. We re-enable interrupts and interlock with
832 * ipending.
833 *
834 * See kern/kern_intr.c for more information.
835 */
836void
837ithread_done(int irq)
838{
839 struct mdglobaldata *gd = mdcpu;
840 int mask = 1 << irq;
841 thread_t td;
842
843 td = gd->mi.gd_curthread;
844
845 KKASSERT(td->td_pri >= TDPRI_CRIT);
846 lwkt_deschedule_self(td);
847 INTREN(mask);
848 if (gd->gd_ipending & mask) {
849 atomic_clear_int_nonlocked(&gd->gd_ipending, mask);
850 INTRDIS(mask);
851 lwkt_schedule_self(td);
852 } else {
853 lwkt_switch();
854 }
855}
856
857#ifdef SMP
858/*
859 * forward_fast_remote()
860 *
861 * This function is called from the receiving end of an IPIQ when a
862 * remote cpu wishes to forward a fast interrupt to us. All we have to
863 * do is set the interrupt pending and let the IPI's doreti deal with it.
864 */
865void
866forward_fastint_remote(void *arg)
867{
868 int irq = (int)arg;
869 struct mdglobaldata *gd = mdcpu;
870
871 atomic_set_int_nonlocked(&gd->gd_fpending, 1 << irq);
872 atomic_set_int_nonlocked(&gd->mi.gd_reqflags, RQF_INTPEND);
873}
874
875#endif