nmi.c revision 37050
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.11 1998/05/31 10:53:52 bde Exp $
38 */
39
40#include "opt_auto_eoi.h"
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/syslog.h>
45#include <machine/ipl.h>
46#include <machine/md_var.h>
47#include <machine/segments.h>
48#if defined(APIC_IO)
49#include <machine/smp.h>
50#include <machine/smptests.h>			/** FAST_HI */
51#endif /* APIC_IO */
52#include <i386/isa/isa_device.h>
53#ifdef PC98
54#include <pc98/pc98/pc98.h>
55#include <pc98/pc98/pc98_machdep.h>
56#include <pc98/pc98/epsonio.h>
57#else
58#include <i386/isa/isa.h>
59#endif
60#include <i386/isa/icu.h>
61#include "vector.h"
62
63#include <i386/isa/intr_machdep.h>
64#include <sys/interrupt.h>
65#ifdef APIC_IO
66#include <machine/clock.h>
67#endif
68
69/* XXX should be in suitable include files */
70#ifdef PC98
71#define	ICU_IMR_OFFSET		2		/* IO_ICU{1,2} + 2 */
72#define	ICU_SLAVEID			7
73#else
74#define	ICU_IMR_OFFSET		1		/* IO_ICU{1,2} + 1 */
75#define	ICU_SLAVEID			2
76#endif
77
78#ifdef APIC_IO
79/*
80 * This is to accommodate "mixed-mode" programming for
81 * motherboards that don't connect the 8254 to the IO APIC.
82 */
83#define AUTO_EOI_1
84#endif
85
86u_long	*intr_countp[ICU_LEN];
87inthand2_t *intr_handler[ICU_LEN];
88u_int	intr_mask[ICU_LEN];
89static u_int*	intr_mptr[ICU_LEN];
90void	*intr_unit[ICU_LEN];
91
92static inthand_t *fastintr[ICU_LEN] = {
93	&IDTVEC(fastintr0), &IDTVEC(fastintr1),
94	&IDTVEC(fastintr2), &IDTVEC(fastintr3),
95	&IDTVEC(fastintr4), &IDTVEC(fastintr5),
96	&IDTVEC(fastintr6), &IDTVEC(fastintr7),
97	&IDTVEC(fastintr8), &IDTVEC(fastintr9),
98	&IDTVEC(fastintr10), &IDTVEC(fastintr11),
99	&IDTVEC(fastintr12), &IDTVEC(fastintr13),
100	&IDTVEC(fastintr14), &IDTVEC(fastintr15)
101#if defined(APIC_IO)
102	, &IDTVEC(fastintr16), &IDTVEC(fastintr17),
103	&IDTVEC(fastintr18), &IDTVEC(fastintr19),
104	&IDTVEC(fastintr20), &IDTVEC(fastintr21),
105	&IDTVEC(fastintr22), &IDTVEC(fastintr23)
106#endif /* APIC_IO */
107};
108
109static inthand_t *slowintr[ICU_LEN] = {
110	&IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
111	&IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
112	&IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
113	&IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15)
114#if defined(APIC_IO)
115	, &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19),
116	&IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23)
117#endif /* APIC_IO */
118};
119
120static ointhand2_t isa_strayintr;
121
122#ifdef PC98
123#define NMI_PARITY 0x04
124#define NMI_EPARITY 0x02
125#else
126#define NMI_PARITY (1 << 7)
127#define NMI_IOCHAN (1 << 6)
128#define ENMI_WATCHDOG (1 << 7)
129#define ENMI_BUSTIMER (1 << 6)
130#define ENMI_IOSTATUS (1 << 5)
131#endif
132
133/*
134 * Handle a NMI, possibly a machine check.
135 * return true to panic system, false to ignore.
136 */
137int
138isa_nmi(cd)
139	int cd;
140{
141#ifdef PC98
142 	int port = inb(0x33);
143	if (epson_machine_id == 0x20)
144		epson_outb(0xc16, epson_inb(0xc16) | 0x1);
145	if (port & NMI_PARITY) {
146		panic("BASE RAM parity error, likely hardware failure.");
147	} else if (port & NMI_EPARITY) {
148		panic("EXTENDED RAM parity error, likely hardware failure.");
149	} else {
150		printf("\nNMI Resume ??\n");
151		return(0);
152	}
153#else /* IBM-PC */
154	int isa_port = inb(0x61);
155	int eisa_port = inb(0x461);
156
157	if (isa_port & NMI_PARITY)
158		panic("RAM parity error, likely hardware failure.");
159
160	if (isa_port & NMI_IOCHAN)
161		panic("I/O channel check, likely hardware failure.");
162
163	/*
164	 * On a real EISA machine, this will never happen.  However it can
165	 * happen on ISA machines which implement XT style floating point
166	 * error handling (very rare).  Save them from a meaningless panic.
167	 */
168	if (eisa_port == 0xff)
169		return(0);
170
171	if (eisa_port & ENMI_WATCHDOG)
172		panic("EISA watchdog timer expired, likely hardware failure.");
173
174	if (eisa_port & ENMI_BUSTIMER)
175		panic("EISA bus timeout, likely hardware failure.");
176
177	if (eisa_port & ENMI_IOSTATUS)
178		panic("EISA I/O port status error.");
179
180	printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port);
181	return(0);
182#endif
183}
184
185/*
186 * Fill in default interrupt table (in case of spuruious interrupt
187 * during configuration of kernel, setup interrupt control unit
188 */
189void
190isa_defaultirq()
191{
192	int i;
193
194	/* icu vectors */
195	for (i = 0; i < ICU_LEN; i++)
196		icu_unset(i, (inthand2_t *)NULL);
197
198	/* initialize 8259's */
199	outb(IO_ICU1, 0x11);		/* reset; program device, four bytes */
200
201	outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT);	/* starting at this vector index */
202	outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE);		/* slave on line 7 */
203#ifdef PC98
204#ifdef AUTO_EOI_1
205	outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f);		/* (master) auto EOI, 8086 mode */
206#else
207	outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d);		/* (master) 8086 mode */
208#endif
209#else /* IBM-PC */
210#ifdef AUTO_EOI_1
211	outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1);		/* auto EOI, 8086 mode */
212#else
213	outb(IO_ICU1+ICU_IMR_OFFSET, 1);		/* 8086 mode */
214#endif
215#endif /* PC98 */
216	outb(IO_ICU1+ICU_IMR_OFFSET, 0xff);		/* leave interrupts masked */
217	outb(IO_ICU1, 0x0a);		/* default to IRR on read */
218#ifndef PC98
219	outb(IO_ICU1, 0xc0 | (3 - 1));	/* pri order 3-7, 0-2 (com2 first) */
220#endif /* !PC98 */
221
222	outb(IO_ICU2, 0x11);		/* reset; program device, four bytes */
223	outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */
224	outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID);         /* my slave id is 7 */
225#ifdef PC98
226	outb(IO_ICU2+ICU_IMR_OFFSET,9);              /* 8086 mode */
227#else /* IBM-PC */
228#ifdef AUTO_EOI_2
229	outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1);		/* auto EOI, 8086 mode */
230#else
231	outb(IO_ICU2+ICU_IMR_OFFSET,1);		/* 8086 mode */
232#endif
233#endif /* PC98 */
234	outb(IO_ICU2+ICU_IMR_OFFSET, 0xff);          /* leave interrupts masked */
235	outb(IO_ICU2, 0x0a);		/* default to IRR on read */
236}
237
238/*
239 * Caught a stray interrupt, notify
240 */
241static void
242isa_strayintr(d)
243	int d;
244{
245
246	/* DON'T BOTHER FOR NOW! */
247	/* for some reason, we get bursts of intr #7, even if not enabled! */
248	/*
249	 * Well the reason you got bursts of intr #7 is because someone
250	 * raised an interrupt line and dropped it before the 8259 could
251	 * prioritize it.  This is documented in the intel data book.  This
252	 * means you have BAD hardware!  I have changed this so that only
253	 * the first 5 get logged, then it quits logging them, and puts
254	 * out a special message. rgrimes 3/25/1993
255	 */
256	/*
257	 * XXX TODO print a different message for #7 if it is for a
258	 * glitch.  Glitches can be distinguished from real #7's by
259	 * testing that the in-service bit is _not_ set.  The test
260	 * must be done before sending an EOI so it can't be done if
261	 * we are using AUTO_EOI_1.
262	 */
263	if (intrcnt[NR_DEVICES + d] <= 5)
264		log(LOG_ERR, "stray irq %d\n", d);
265	if (intrcnt[NR_DEVICES + d] == 5)
266		log(LOG_CRIT,
267		    "too many stray irq %d's; not logging any more\n", d);
268}
269
270/*
271 * Return a bitmap of the current interrupt requests.  This is 8259-specific
272 * and is only suitable for use at probe time.
273 */
274intrmask_t
275isa_irq_pending()
276{
277	u_char irr1;
278	u_char irr2;
279
280	irr1 = inb(IO_ICU1);
281	irr2 = inb(IO_ICU2);
282	return ((irr2 << 8) | irr1);
283}
284
285int
286update_intr_masks(void)
287{
288	int intr, n=0;
289	u_int mask,*maskptr;
290
291	for (intr=0; intr < ICU_LEN; intr ++) {
292#if defined(APIC_IO)
293		/* no 8259 SLAVE to ignore */
294#else
295		if (intr==ICU_SLAVEID) continue;	/* ignore 8259 SLAVE output */
296#endif /* APIC_IO */
297		maskptr = intr_mptr[intr];
298		if (!maskptr) continue;
299		*maskptr |= 1 << intr;
300		mask = *maskptr;
301		if (mask != intr_mask[intr]) {
302#if 0
303			printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n",
304				intr, intr_mask[intr], mask, maskptr);
305#endif
306			intr_mask[intr]=mask;
307			n++;
308		}
309
310	}
311	return (n);
312}
313
314/*
315 * The find_device_id function is only required because of the way the
316 * device names are currently stored for reporting in systat or vmstat.
317 * In fact, those programs should be modified to use the sysctl interface
318 * to obtain a list of driver names by traversing intreclist_head[irq].
319 */
320static int
321find_device_id(int irq)
322{
323	char buf[16];
324	char *cp;
325	int free_id, id;
326
327	sprintf(buf, "pci irq%d", irq);
328	cp = intrnames;
329	/* default to 0, which corresponds to clk0 */
330	free_id = 0;
331
332	for (id = 0; id < NR_DEVICES; id++) {
333		if (strcmp(cp, buf) == 0)
334			return (id);
335		if (free_id == 0 && strcmp(cp, "pci irqnn") == 0)
336			free_id = id;
337		while (*cp++ != '\0');
338	}
339#if 0
340	if (free_id == 0) {
341		/*
342		 * All pci irq counters are in use, perhaps because config
343		 * is old so there aren't any. Abuse the clk0 counter.
344		 */
345		printf("\tcounting shared irq%d as clk0 irq\n", irq);
346	}
347#endif
348	return (free_id);
349}
350
351void
352update_intrname(int intr, int device_id)
353{
354	char	*cp;
355	int	id;
356
357	if (device_id == -1)
358		device_id = find_device_id(intr);
359
360	if ((u_int)device_id >= NR_DEVICES)
361		return;
362
363	intr_countp[intr] = &intrcnt[device_id];
364
365	for (cp = intrnames, id = 0; id <= device_id; id++)
366		while (*cp++ != '\0')
367			;
368	if (cp > eintrnames)
369		return;
370	if (intr < 10) {
371		cp[-3] = intr + '0';
372		cp[-2] = ' ';
373	} else if (intr < 20) {
374		cp[-3] = '1';
375		cp[-2] = intr - 10 + '0';
376	} else {
377		cp[-3] = '2';
378		cp[-2] = intr - 20 + '0';
379	}
380}
381
382
383int
384icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
385{
386#ifdef FAST_HI
387	int		select;		/* the select register is 8 bits */
388	int		vector;
389	u_int32_t	value;		/* the window register is 32 bits */
390#endif /* FAST_HI */
391	u_long	ef;
392	u_int	mask = (maskptr ? *maskptr : 0);
393
394#if defined(APIC_IO)
395	if ((u_int)intr >= ICU_LEN)	/* no 8259 SLAVE to ignore */
396#else
397	if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID)
398#endif /* APIC_IO */
399	if (intr_handler[intr] != isa_strayintr)
400		return (EBUSY);
401
402	ef = read_eflags();
403	disable_intr();
404	intr_handler[intr] = handler;
405	intr_mptr[intr] = maskptr;
406	intr_mask[intr] = mask | (1 << intr);
407	intr_unit[intr] = arg;
408#ifdef FAST_HI
409	if (flags & INTR_FAST) {
410		vector = TPR_FAST_INTS + intr;
411		setidt(vector, fastintr[intr],
412		       SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
413	}
414	else {
415		vector = TPR_SLOW_INTS + intr;
416#ifdef APIC_INTR_REORDER
417#ifdef APIC_INTR_HIGHPRI_CLOCK
418		/* XXX: Hack (kludge?) for more accurate clock. */
419		if (intr == apic_8254_intr || intr == 8) {
420			vector = TPR_FAST_INTS + intr;
421		}
422#endif
423#endif
424		setidt(vector, slowintr[intr],
425		       SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
426	}
427#ifdef APIC_INTR_REORDER
428	set_lapic_isrloc(intr, vector);
429#endif
430	/*
431	 * XXX MULTIPLE_IOAPICSXXX
432	 * Reprogram the vector in the IO APIC.
433	 */
434	select = (intr * 2) + IOAPIC_REDTBL0;
435	value = io_apic_read(0, select) & ~IOART_INTVEC;
436	io_apic_write(0, select, value | vector);
437#else
438	setidt(ICU_OFFSET + intr,
439	       flags & INTR_FAST ? fastintr[intr] : slowintr[intr],
440	       SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
441#endif /* FAST_HI */
442	INTREN(1 << intr);
443	MPINTR_UNLOCK();
444	write_eflags(ef);
445	return (0);
446}
447
448void
449register_imask(dvp, mask)
450	struct isa_device *dvp;
451	u_int	mask;
452{
453	if (dvp->id_alive && dvp->id_irq) {
454		int	intr;
455
456		intr = ffs(dvp->id_irq) - 1;
457		intr_mask[intr] = mask | (1 <<intr);
458	}
459	(void) update_intr_masks();
460}
461
462int
463icu_unset(intr, handler)
464	int	intr;
465	inthand2_t *handler;
466{
467	u_long	ef;
468
469	if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr])
470		return (EINVAL);
471
472	INTRDIS(1 << intr);
473	ef = read_eflags();
474	disable_intr();
475	intr_countp[intr] = &intrcnt[NR_DEVICES + intr];
476	intr_handler[intr] = isa_strayintr;
477	intr_mptr[intr] = NULL;
478	intr_mask[intr] = HWI_MASK | SWI_MASK;
479	intr_unit[intr] = intr;
480#ifdef FAST_HI_XXX
481	/* XXX how do I re-create dvp here? */
482	setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr,
483	    slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
484#else /* FAST_HI */
485#ifdef APIC_INTR_REORDER
486	set_lapic_isrloc(intr, ICU_OFFSET + intr);
487#endif
488	setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL,
489	    GSEL(GCODE_SEL, SEL_KPL));
490#endif /* FAST_HI */
491	MPINTR_UNLOCK();
492	write_eflags(ef);
493	return (0);
494}
495