nmi.c revision 46454
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 *	$Id: intr_machdep.c,v 1.20 1999/04/23 21:01:19 peter Exp $
38 */
39/*
40 * This file contains an aggregated module marked:
41 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
42 * All rights reserved.
43 * See the notice for details.
44 */
45
46#include "opt_auto_eoi.h"
47
48#include <sys/param.h>
49#ifndef SMP
50#include <machine/lock.h>
51#endif
52#include <sys/systm.h>
53#include <sys/syslog.h>
54#include <sys/malloc.h>
55#include <sys/errno.h>
56#include <sys/interrupt.h>
57#include <machine/ipl.h>
58#include <machine/md_var.h>
59#include <machine/segments.h>
60#include <sys/bus.h>
61
62#if defined(APIC_IO)
63#include <machine/smp.h>
64#include <machine/smptests.h>			/** FAST_HI */
65#endif /* APIC_IO */
66#include <i386/isa/isa_device.h>
67#ifdef PC98
68#include <pc98/pc98/pc98.h>
69#include <pc98/pc98/pc98_machdep.h>
70#include <pc98/pc98/epsonio.h>
71#else
72#include <i386/isa/isa.h>
73#endif
74#include <i386/isa/icu.h>
75
76#include <isa/isavar.h>
77#include <i386/isa/intr_machdep.h>
78#include <sys/interrupt.h>
79#ifdef APIC_IO
80#include <machine/clock.h>
81#endif
82
83/* XXX should be in suitable include files */
84#ifdef PC98
85#define	ICU_IMR_OFFSET		2		/* IO_ICU{1,2} + 2 */
86#define	ICU_SLAVEID			7
87#else
88#define	ICU_IMR_OFFSET		1		/* IO_ICU{1,2} + 1 */
89#define	ICU_SLAVEID			2
90#endif
91
92#ifdef APIC_IO
93/*
94 * This is to accommodate "mixed-mode" programming for
95 * motherboards that don't connect the 8254 to the IO APIC.
96 */
97#define	AUTO_EOI_1	1
98#endif
99
100#define	NR_INTRNAMES	(1 + ICU_LEN + 2 * ICU_LEN)
101
102u_long	*intr_countp[ICU_LEN];
103inthand2_t *intr_handler[ICU_LEN];
104u_int	intr_mask[ICU_LEN];
105static u_int*	intr_mptr[ICU_LEN];
106void	*intr_unit[ICU_LEN];
107
108static inthand_t *fastintr[ICU_LEN] = {
109	&IDTVEC(fastintr0), &IDTVEC(fastintr1),
110	&IDTVEC(fastintr2), &IDTVEC(fastintr3),
111	&IDTVEC(fastintr4), &IDTVEC(fastintr5),
112	&IDTVEC(fastintr6), &IDTVEC(fastintr7),
113	&IDTVEC(fastintr8), &IDTVEC(fastintr9),
114	&IDTVEC(fastintr10), &IDTVEC(fastintr11),
115	&IDTVEC(fastintr12), &IDTVEC(fastintr13),
116	&IDTVEC(fastintr14), &IDTVEC(fastintr15)
117#if defined(APIC_IO)
118	, &IDTVEC(fastintr16), &IDTVEC(fastintr17),
119	&IDTVEC(fastintr18), &IDTVEC(fastintr19),
120	&IDTVEC(fastintr20), &IDTVEC(fastintr21),
121	&IDTVEC(fastintr22), &IDTVEC(fastintr23)
122#endif /* APIC_IO */
123};
124
125static inthand_t *slowintr[ICU_LEN] = {
126	&IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
127	&IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
128	&IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
129	&IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15)
130#if defined(APIC_IO)
131	, &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19),
132	&IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23)
133#endif /* APIC_IO */
134};
135
136static inthand2_t isa_strayintr;
137
138#ifdef PC98
139#define NMI_PARITY 0x04
140#define NMI_EPARITY 0x02
141#else
142#define NMI_PARITY (1 << 7)
143#define NMI_IOCHAN (1 << 6)
144#define ENMI_WATCHDOG (1 << 7)
145#define ENMI_BUSTIMER (1 << 6)
146#define ENMI_IOSTATUS (1 << 5)
147#endif
148
149/*
150 * Handle a NMI, possibly a machine check.
151 * return true to panic system, false to ignore.
152 */
153int
154isa_nmi(cd)
155	int cd;
156{
157#ifdef PC98
158 	int port = inb(0x33);
159	if (epson_machine_id == 0x20)
160		epson_outb(0xc16, epson_inb(0xc16) | 0x1);
161	if (port & NMI_PARITY) {
162		panic("BASE RAM parity error, likely hardware failure.");
163	} else if (port & NMI_EPARITY) {
164		panic("EXTENDED RAM parity error, likely hardware failure.");
165	} else {
166		printf("\nNMI Resume ??\n");
167		return(0);
168	}
169#else /* IBM-PC */
170	int isa_port = inb(0x61);
171	int eisa_port = inb(0x461);
172
173	if (isa_port & NMI_PARITY)
174		panic("RAM parity error, likely hardware failure.");
175
176	if (isa_port & NMI_IOCHAN)
177		panic("I/O channel check, likely hardware failure.");
178
179	/*
180	 * On a real EISA machine, this will never happen.  However it can
181	 * happen on ISA machines which implement XT style floating point
182	 * error handling (very rare).  Save them from a meaningless panic.
183	 */
184	if (eisa_port == 0xff)
185		return(0);
186
187	if (eisa_port & ENMI_WATCHDOG)
188		panic("EISA watchdog timer expired, likely hardware failure.");
189
190	if (eisa_port & ENMI_BUSTIMER)
191		panic("EISA bus timeout, likely hardware failure.");
192
193	if (eisa_port & ENMI_IOSTATUS)
194		panic("EISA I/O port status error.");
195
196	printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port);
197	return(0);
198#endif
199}
200
201/*
202 * Fill in default interrupt table (in case of spuruious interrupt
203 * during configuration of kernel, setup interrupt control unit
204 */
205void
206isa_defaultirq()
207{
208	int i;
209
210	/* icu vectors */
211	for (i = 0; i < ICU_LEN; i++)
212		icu_unset(i, (inthand2_t *)NULL);
213
214	/* initialize 8259's */
215	outb(IO_ICU1, 0x11);		/* reset; program device, four bytes */
216
217	outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT);	/* starting at this vector index */
218	outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE);		/* slave on line 7 */
219#ifdef PC98
220#ifdef AUTO_EOI_1
221	outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f);		/* (master) auto EOI, 8086 mode */
222#else
223	outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d);		/* (master) 8086 mode */
224#endif
225#else /* IBM-PC */
226#ifdef AUTO_EOI_1
227	outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1);		/* auto EOI, 8086 mode */
228#else
229	outb(IO_ICU1+ICU_IMR_OFFSET, 1);		/* 8086 mode */
230#endif
231#endif /* PC98 */
232	outb(IO_ICU1+ICU_IMR_OFFSET, 0xff);		/* leave interrupts masked */
233	outb(IO_ICU1, 0x0a);		/* default to IRR on read */
234#ifndef PC98
235	outb(IO_ICU1, 0xc0 | (3 - 1));	/* pri order 3-7, 0-2 (com2 first) */
236#endif /* !PC98 */
237
238	outb(IO_ICU2, 0x11);		/* reset; program device, four bytes */
239	outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */
240	outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID);         /* my slave id is 7 */
241#ifdef PC98
242	outb(IO_ICU2+ICU_IMR_OFFSET,9);              /* 8086 mode */
243#else /* IBM-PC */
244#ifdef AUTO_EOI_2
245	outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1);		/* auto EOI, 8086 mode */
246#else
247	outb(IO_ICU2+ICU_IMR_OFFSET,1);		/* 8086 mode */
248#endif
249#endif /* PC98 */
250	outb(IO_ICU2+ICU_IMR_OFFSET, 0xff);          /* leave interrupts masked */
251	outb(IO_ICU2, 0x0a);		/* default to IRR on read */
252}
253
254/*
255 * Caught a stray interrupt, notify
256 */
257static void
258isa_strayintr(vcookiep)
259	void *vcookiep;
260{
261	int intr = (void **)vcookiep - &intr_unit[0];
262
263	/* DON'T BOTHER FOR NOW! */
264	/* for some reason, we get bursts of intr #7, even if not enabled! */
265	/*
266	 * Well the reason you got bursts of intr #7 is because someone
267	 * raised an interrupt line and dropped it before the 8259 could
268	 * prioritize it.  This is documented in the intel data book.  This
269	 * means you have BAD hardware!  I have changed this so that only
270	 * the first 5 get logged, then it quits logging them, and puts
271	 * out a special message. rgrimes 3/25/1993
272	 */
273	/*
274	 * XXX TODO print a different message for #7 if it is for a
275	 * glitch.  Glitches can be distinguished from real #7's by
276	 * testing that the in-service bit is _not_ set.  The test
277	 * must be done before sending an EOI so it can't be done if
278	 * we are using AUTO_EOI_1.
279	 */
280	if (intrcnt[1 + intr] <= 5)
281		log(LOG_ERR, "stray irq %d\n", intr);
282	if (intrcnt[1 + intr] == 5)
283		log(LOG_CRIT,
284		    "too many stray irq %d's; not logging any more\n", intr);
285}
286
287/*
288 * Return a bitmap of the current interrupt requests.  This is 8259-specific
289 * and is only suitable for use at probe time.
290 */
291intrmask_t
292isa_irq_pending()
293{
294	u_char irr1;
295	u_char irr2;
296
297	irr1 = inb(IO_ICU1);
298	irr2 = inb(IO_ICU2);
299	return ((irr2 << 8) | irr1);
300}
301
302int
303update_intr_masks(void)
304{
305	int intr, n=0;
306	u_int mask,*maskptr;
307
308	for (intr=0; intr < ICU_LEN; intr ++) {
309#if defined(APIC_IO)
310		/* no 8259 SLAVE to ignore */
311#else
312		if (intr==ICU_SLAVEID) continue;	/* ignore 8259 SLAVE output */
313#endif /* APIC_IO */
314		maskptr = intr_mptr[intr];
315		if (!maskptr)
316			continue;
317		*maskptr |= 1 << intr;
318		mask = *maskptr;
319		if (mask != intr_mask[intr]) {
320#if 0
321			printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n",
322				intr, intr_mask[intr], mask, maskptr);
323#endif
324			intr_mask[intr]=mask;
325			n++;
326		}
327
328	}
329	return (n);
330}
331
332static void
333update_intrname(int intr, char *name)
334{
335	char buf[32];
336	char *cp;
337	int name_index, off, strayintr;
338
339	/*
340	 * Initialise strings for bitbucket and stray interrupt counters.
341	 * These have statically allocated indices 0 and 1 through ICU_LEN.
342	 */
343	if (intrnames[0] == '\0') {
344		off = sprintf(intrnames, "???") + 1;
345		for (strayintr = 0; strayintr < ICU_LEN; strayintr++)
346			off += sprintf(intrnames + off, "stray irq%d",
347			    strayintr) + 1;
348	}
349
350	if (name == NULL)
351		name = "???";
352	if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf))
353		goto use_bitbucket;
354
355	/*
356	 * Search for `buf' in `intrnames'.  In the usual case when it is
357	 * not found, append it to the end if there is enough space (the \0
358	 * terminator for the previous string, if any, becomes a separator).
359	 */
360	for (cp = intrnames, name_index = 0;
361	    cp != eintrnames && name_index < NR_INTRNAMES;
362	    cp += strlen(cp) + 1, name_index++) {
363		if (*cp == '\0') {
364			if (strlen(buf) >= eintrnames - cp)
365				break;
366			strcpy(cp, buf);
367			goto found;
368		}
369		if (strcmp(cp, buf) == 0)
370			goto found;
371	}
372
373use_bitbucket:
374	printf("update_intrname: counting %s irq%d as %s\n", name, intr,
375	    intrnames);
376	name_index = 0;
377found:
378	intr_countp[intr] = &intrcnt[name_index];
379}
380
381int
382icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
383{
384#ifdef FAST_HI
385	int		select;		/* the select register is 8 bits */
386	int		vector;
387	u_int32_t	value;		/* the window register is 32 bits */
388#endif /* FAST_HI */
389	u_long	ef;
390	u_int	mask = (maskptr ? *maskptr : 0);
391
392#if defined(APIC_IO)
393	if ((u_int)intr >= ICU_LEN)	/* no 8259 SLAVE to ignore */
394#else
395	if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID)
396#endif /* APIC_IO */
397	if (intr_handler[intr] != isa_strayintr)
398		return (EBUSY);
399
400	ef = read_eflags();
401	disable_intr();
402	intr_handler[intr] = handler;
403	intr_mptr[intr] = maskptr;
404	intr_mask[intr] = mask | (1 << intr);
405	intr_unit[intr] = arg;
406#ifdef FAST_HI
407	if (flags & INTR_FAST) {
408		vector = TPR_FAST_INTS + intr;
409		setidt(vector, fastintr[intr],
410		       SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
411	}
412	else {
413		vector = TPR_SLOW_INTS + intr;
414#ifdef APIC_INTR_REORDER
415#ifdef APIC_INTR_HIGHPRI_CLOCK
416		/* XXX: Hack (kludge?) for more accurate clock. */
417		if (intr == apic_8254_intr || intr == 8) {
418			vector = TPR_FAST_INTS + intr;
419		}
420#endif
421#endif
422		setidt(vector, slowintr[intr],
423		       SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
424	}
425#ifdef APIC_INTR_REORDER
426	set_lapic_isrloc(intr, vector);
427#endif
428	/*
429	 * Reprogram the vector in the IO APIC.
430	 */
431	if (int_to_apicintpin[intr].ioapic >= 0) {
432		select = int_to_apicintpin[intr].redirindex;
433		value = io_apic_read(int_to_apicintpin[intr].ioapic,
434				     select) & ~IOART_INTVEC;
435		io_apic_write(int_to_apicintpin[intr].ioapic,
436			      select, value | vector);
437	}
438#else
439	setidt(ICU_OFFSET + intr,
440	       flags & INTR_FAST ? fastintr[intr] : slowintr[intr],
441	       SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
442#endif /* FAST_HI */
443	INTREN(1 << intr);
444	MPINTR_UNLOCK();
445	write_eflags(ef);
446	return (0);
447}
448
449void
450register_imask(dvp, mask)
451	struct isa_device *dvp;
452	u_int	mask;
453{
454	if (dvp->id_alive && dvp->id_irq) {
455		int	intr;
456
457		intr = ffs(dvp->id_irq) - 1;
458		intr_mask[intr] = mask | (1 <<intr);
459	}
460	(void) update_intr_masks();
461}
462
463int
464icu_unset(intr, handler)
465	int	intr;
466	inthand2_t *handler;
467{
468	u_long	ef;
469
470	if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr])
471		return (EINVAL);
472
473	INTRDIS(1 << intr);
474	ef = read_eflags();
475	disable_intr();
476	intr_countp[intr] = &intrcnt[1 + intr];
477	intr_handler[intr] = isa_strayintr;
478	intr_mptr[intr] = NULL;
479	intr_mask[intr] = HWI_MASK | SWI_MASK;
480	intr_unit[intr] = &intr_unit[intr];
481#ifdef FAST_HI_XXX
482	/* XXX how do I re-create dvp here? */
483	setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr,
484	    slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
485#else /* FAST_HI */
486#ifdef APIC_INTR_REORDER
487	set_lapic_isrloc(intr, ICU_OFFSET + intr);
488#endif
489	setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL,
490	    GSEL(GCODE_SEL, SEL_KPL));
491#endif /* FAST_HI */
492	MPINTR_UNLOCK();
493	write_eflags(ef);
494	return (0);
495}
496
497/* The following notice applies beyond this point in the file */
498
499/*
500 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
501 * All rights reserved.
502 *
503 * Redistribution and use in source and binary forms, with or without
504 * modification, are permitted provided that the following conditions
505 * are met:
506 * 1. Redistributions of source code must retain the above copyright
507 *    notice unmodified, this list of conditions, and the following
508 *    disclaimer.
509 * 2. Redistributions in binary form must reproduce the above copyright
510 *    notice, this list of conditions and the following disclaimer in the
511 *    documentation and/or other materials provided with the distribution.
512 *
513 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
514 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
515 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
516 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
517 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
518 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
519 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
520 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
521 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
522 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
523 *
524 * $Id: intr_machdep.c,v 1.20 1999/04/23 21:01:19 peter Exp $
525 *
526 */
527
528typedef struct intrec {
529	intrmask_t	mask;
530	inthand2_t	*handler;
531	void		*argument;
532	struct intrec	*next;
533	char		*name;
534	int		intr;
535	intrmask_t	*maskptr;
536	int		flags;
537} intrec;
538
539static intrec *intreclist_head[ICU_LEN];
540
541typedef struct isarec {
542	int		id_unit;
543	ointhand2_t	*id_handler;
544} isarec;
545
546static isarec *isareclist[ICU_LEN];
547
548/*
549 * The interrupt multiplexer calls each of the handlers in turn,
550 * and applies the associated interrupt mask to "cpl", which is
551 * defined as a ".long" in /sys/i386/isa/ipl.s
552 */
553
554static void
555intr_mux(void *arg)
556{
557	intrec *p = arg;
558	int oldspl;
559
560	while (p != NULL) {
561		oldspl = splq(p->mask);
562		p->handler(p->argument);
563		splx(oldspl);
564		p = p->next;
565	}
566}
567
568static void
569isa_intr_wrap(void *cookie)
570{
571	isarec *irec = (isarec *)cookie;
572
573	irec->id_handler(irec->id_unit);
574}
575
576static intrec*
577find_idesc(unsigned *maskptr, int irq)
578{
579	intrec *p = intreclist_head[irq];
580
581	while (p && p->maskptr != maskptr)
582		p = p->next;
583
584	return (p);
585}
586
587static intrec**
588find_pred(intrec *idesc, int irq)
589{
590	intrec **pp = &intreclist_head[irq];
591	intrec *p = *pp;
592
593	while (p != idesc) {
594		if (p == NULL)
595			return (NULL);
596		pp = &p->next;
597		p = *pp;
598	}
599	return (pp);
600}
601
602/*
603 * Both the low level handler and the shared interrupt multiplexer
604 * block out further interrupts as set in the handlers "mask", while
605 * the handler is running. In fact *maskptr should be used for this
606 * purpose, but since this requires one more pointer dereference on
607 * each interrupt, we rather bother update "mask" whenever *maskptr
608 * changes. The function "update_masks" should be called **after**
609 * all manipulation of the linked list of interrupt handlers hung
610 * off of intrdec_head[irq] is complete, since the chain of handlers
611 * will both determine the *maskptr values and the instances of mask
612 * that are fixed. This function should be called with the irq for
613 * which a new handler has been add blocked, since the masks may not
614 * yet know about the use of this irq for a device of a certain class.
615 */
616
617static void
618update_mux_masks(void)
619{
620	int irq;
621	for (irq = 0; irq < ICU_LEN; irq++) {
622		intrec *idesc = intreclist_head[irq];
623		while (idesc != NULL) {
624			if (idesc->maskptr != NULL) {
625				/* our copy of *maskptr may be stale, refresh */
626				idesc->mask = *idesc->maskptr;
627			}
628			idesc = idesc->next;
629		}
630	}
631}
632
633static void
634update_masks(intrmask_t *maskptr, int irq)
635{
636	intrmask_t mask = 1 << irq;
637
638	if (maskptr == NULL)
639		return;
640
641	if (find_idesc(maskptr, irq) == NULL) {
642		/* no reference to this maskptr was found in this irq's chain */
643		if ((*maskptr & mask) == 0)
644			return;
645		/* the irq was included in the classes mask, remove it */
646		INTRUNMASK(*maskptr, mask);
647	} else {
648		/* a reference to this maskptr was found in this irq's chain */
649		if ((*maskptr & mask) != 0)
650			return;
651		/* put the irq into the classes mask */
652		INTRMASK(*maskptr, mask);
653	}
654	/* we need to update all values in the intr_mask[irq] array */
655	update_intr_masks();
656	/* update mask in chains of the interrupt multiplex handler as well */
657	update_mux_masks();
658}
659
660/*
661 * Add interrupt handler to linked list hung off of intreclist_head[irq]
662 * and install shared interrupt multiplex handler, if necessary
663 */
664
665static int
666add_intrdesc(intrec *idesc)
667{
668	int irq = idesc->intr;
669
670	intrec *head = intreclist_head[irq];
671
672	if (head == NULL) {
673		/* first handler for this irq, just install it */
674		if (icu_setup(irq, idesc->handler, idesc->argument,
675			      idesc->maskptr, idesc->flags) != 0)
676			return (-1);
677
678		update_intrname(irq, idesc->name);
679		/* keep reference */
680		intreclist_head[irq] = idesc;
681	} else {
682		if ((idesc->flags & INTR_EXCL) != 0
683		    || (head->flags & INTR_EXCL) != 0) {
684			/*
685			 * can't append new handler, if either list head or
686			 * new handler do not allow interrupts to be shared
687			 */
688			if (bootverbose)
689				printf("\tdevice combination doesn't support "
690				       "shared irq%d\n", irq);
691			return (-1);
692		}
693		if (head->next == NULL) {
694			/*
695			 * second handler for this irq, replace device driver's
696			 * handler by shared interrupt multiplexer function
697			 */
698			icu_unset(irq, head->handler);
699			if (icu_setup(irq, intr_mux, head, 0, 0) != 0)
700				return (-1);
701			if (bootverbose)
702				printf("\tusing shared irq%d.\n", irq);
703			update_intrname(irq, "mux");
704		}
705		/* just append to the end of the chain */
706		while (head->next != NULL)
707			head = head->next;
708		head->next = idesc;
709	}
710	update_masks(idesc->maskptr, irq);
711	return (0);
712}
713
714/*
715 * Create and activate an interrupt handler descriptor data structure.
716 *
717 * The dev_instance pointer is required for resource management, and will
718 * only be passed through to resource_claim().
719 *
720 * There will be functions that derive a driver and unit name from a
721 * dev_instance variable, and those functions will be used to maintain the
722 * interrupt counter label array referenced by systat and vmstat to report
723 * device interrupt rates (->update_intrlabels).
724 *
725 * Add the interrupt handler descriptor data structure created by an
726 * earlier call of create_intr() to the linked list for its irq and
727 * adjust the interrupt masks if necessary.
728 */
729
730intrec *
731inthand_add(const char *name, int irq, inthand2_t handler, void *arg,
732	     intrmask_t *maskptr, int flags)
733{
734	intrec *idesc;
735	int errcode = -1;
736	intrmask_t oldspl;
737
738	if (ICU_LEN > 8 * sizeof *maskptr) {
739		printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n",
740		       ICU_LEN, 8 * sizeof *maskptr);
741		return (NULL);
742	}
743	if ((unsigned)irq >= ICU_LEN) {
744		printf("create_intr: requested irq%d too high, limit is %d\n",
745		       irq, ICU_LEN -1);
746		return (NULL);
747	}
748
749	idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK);
750	if (idesc == NULL)
751		return NULL;
752	bzero(idesc, sizeof *idesc);
753
754	if (name == NULL)
755		name = "???";
756	idesc->name     = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK);
757	if (idesc->name == NULL) {
758		free(idesc, M_DEVBUF);
759		return NULL;
760	}
761	strcpy(idesc->name, name);
762
763	idesc->handler  = handler;
764	idesc->argument = arg;
765	idesc->maskptr  = maskptr;
766	idesc->intr     = irq;
767	idesc->flags    = flags;
768
769	/* block this irq */
770	oldspl = splq(1 << irq);
771
772	/* add irq to class selected by maskptr */
773	errcode = add_intrdesc(idesc);
774	splx(oldspl);
775
776	if (errcode != 0) {
777		if (bootverbose)
778			printf("\tintr_connect(irq%d) failed, result=%d\n",
779			       irq, errcode);
780		free(idesc->name, M_DEVBUF);
781		free(idesc, M_DEVBUF);
782		idesc = NULL;
783	}
784
785	return (idesc);
786}
787
788/*
789 * Deactivate and remove the interrupt handler descriptor data connected
790 * created by an earlier call of intr_connect() from the linked list and
791 * adjust theinterrupt masks if necessary.
792 *
793 * Return the memory held by the interrupt handler descriptor data structure
794 * to the system. Make sure, the handler is not actively used anymore, before.
795 */
796
797int
798inthand_remove(intrec *idesc)
799{
800	intrec **hook, *head;
801	int irq;
802	int errcode = 0;
803	intrmask_t oldspl;
804
805	if (idesc == NULL)
806		return (-1);
807
808	irq = idesc->intr;
809
810	/* find pointer that keeps the reference to this interrupt descriptor */
811	hook = find_pred(idesc, irq);
812	if (hook == NULL)
813		return (-1);
814
815	/* make copy of original list head, the line after may overwrite it */
816	head = intreclist_head[irq];
817
818	/* unlink: make predecessor point to idesc->next instead of to idesc */
819	*hook = idesc->next;
820
821	/* now check whether the element we removed was the list head */
822	if (idesc == head) {
823
824		oldspl = splq(1 << irq);
825
826		/* we want to remove the list head, which was known to intr_mux */
827		icu_unset(irq, intr_mux);
828
829		/* check whether the new list head is the only element on list */
830		head = intreclist_head[irq];
831		if (head != NULL) {
832			if (head->next != NULL) {
833				/* install the multiplex handler with new list head as argument */
834				errcode = icu_setup(irq, intr_mux, head, 0, 0);
835				if (errcode == 0)
836					update_intrname(irq, NULL);
837			} else {
838				/* install the one remaining handler for this irq */
839				errcode = icu_setup(irq, head->handler,
840						    head->argument,
841						    head->maskptr, head->flags);
842				if (errcode == 0)
843					update_intrname(irq, head->name);
844			}
845		}
846		splx(oldspl);
847	}
848	update_masks(idesc->maskptr, irq);
849
850	free(idesc, M_DEVBUF);
851	return (0);
852}
853
854/*
855 * Emulate the register_intr() call previously defined as low level function.
856 * That function (now icu_setup()) may no longer be directly called, since
857 * a conflict between an ISA and PCI interrupt might go by unnocticed, else.
858 */
859
860int
861register_intr(int intr, int device_id, u_int flags,
862	      ointhand2_t handler, u_int *maskptr, int unit)
863{
864	intrec *idesc;
865	isarec *irec;
866
867	irec = malloc(sizeof *irec, M_DEVBUF, M_WAITOK);
868	if (irec == NULL)
869		return NULL;
870	bzero(irec, sizeof *irec);
871	irec->id_unit = unit;
872	irec->id_handler = handler;
873
874	flags |= INTR_EXCL;
875	idesc = inthand_add("old", intr, isa_intr_wrap, irec, maskptr, flags);
876	if (idesc == NULL) {
877		free(irec, M_DEVBUF);
878		return -1;
879	}
880	isareclist[intr] = irec;
881	return 0;
882}
883
884/*
885 * Emulate the old unregister_intr() low level function.
886 * Make sure there is just one interrupt, that it was
887 * registered as non-shared, and that the handlers match.
888 */
889
890int
891unregister_intr(int intr, ointhand2_t handler)
892{
893	intrec *p = intreclist_head[intr];
894
895	if (p != NULL && (p->flags & INTR_EXCL) != 0 &&
896	    p->handler == isa_intr_wrap && isareclist[intr] != NULL &&
897	    isareclist[intr]->id_handler == handler) {
898		free(isareclist[intr], M_DEVBUF);
899		isareclist[intr] = NULL;
900		return (inthand_remove(p));
901	}
902	return (EINVAL);
903}
904