NetBSD/sys/arch/i386/isa/intr.c

350 lines
8.3 KiB
C

/*-
* Copyright (c) 1993 Charles Hannum.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: intr.c,v 1.15 1993/12/05 11:20:09 mycroft Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/device.h>
#include <sys/syslog.h>
#include <machine/cpu.h>
#include <machine/pio.h>
#include <i386/isa/isa.h>
#include <i386/isa/isavar.h>
#include <i386/isa/icu.h>
#include <i386/isa/timerreg.h>
void isa_defaultirq __P((void));
void isa_flushintrs __P((void));
void isa_intrmaskwickedness __P((void));
int intrmask[NIRQ];
struct intrhand *intrhand[NIRQ];
int fastvec;
extern int ipending;
#define IDTVEC(name) __CONCAT(X,name)
/* default interrupt vector table entries */
extern *IDTVEC(intr)[];
/* fast interrupt vector table */
extern *IDTVEC(fast)[];
/* out of range default interrupt vector gate entry */
extern IDTVEC(wild);
/*
* Register a fast vector.
*/
void
intr_fasttrap(intr, ih)
int intr;
struct intrhand *ih;
{
int irqnum = ffs(intr) - 1;
#ifdef DIAGNOSTIC
if (intr == IRQUNK || intr == IRQNONE ||
(intr &~ (1 << irqnum)) != IRQNONE)
panic("intr_fasttrap: weird irq");
#endif
if (fastvec & intr)
panic("intr_fasttrap: irq is already fast vector");
if (intrhand[irqnum])
panic("intr_fasttrap: irq is already slow vector");
fastvec |= intr;
ih->ih_count = 0;
ih->ih_next = NULL;
intrhand[irqnum] = ih;
setidt(irqnum, IDTVEC(fast)[irqnum], SDT_SYS386IGT, SEL_KPL);
if (irqnum >= 8)
intr |= IRQ_SLAVE;
intr_enable(intr);
}
/*
* Register an interrupt handler.
*/
void
intr_establish(intr, ih, class)
int intr;
struct intrhand *ih;
enum devclass class;
{
int irqnum = ffs(intr) - 1;
register struct intrhand **p, *q;
#ifdef DIAGNOSTIC
if (intr == IRQUNK || intr == IRQNONE ||
(intr &~ (1 << irqnum)) != IRQNONE)
panic("intr_establish: weird irq");
#endif
if (fastvec & intr)
panic("intr_establish: irq is already fast vector");
switch (class) {
case DV_DULL:
break;
case DV_DISK:
case DV_TAPE:
biomask |= intr;
break;
case DV_IFNET:
netmask |= intr;
break;
case DV_TTY:
ttymask |= intr;
break;
case DV_CPU:
default:
panic("intr_establish: weird devclass");
}
ih->ih_count = 0;
ih->ih_next = NULL;
/*
* This is O(N^2), but we want to preserve the order, and N is
* always small.
*/
for (p = &intrhand[irqnum]; (q = *p) != NULL; p = &q->ih_next)
;
*p = ih;
if (irqnum >= 8)
intr |= IRQ_SLAVE;
intr_enable(intr);
}
/*
* Set up the masks for each interrupt handler based on the information
* recorded by intr_establish().
*/
void
isa_intrmaskwickedness()
{
int irq, mask;
for (irq = 0; irq < NIRQ; irq++) {
mask = (1 << irq) | astmask;
if (biomask & (1 << irq))
mask |= biomask;
if (ttymask & (1 << irq))
mask |= ttymask;
if (netmask & (1 << irq))
mask |= netmask;
intrmask[irq] = mask;
}
printf("biomask %x ttymask %x netmask %x\n",
biomask, ttymask, netmask);
biomask |= astmask;
ttymask |= astmask;
netmask |= astmask;
impmask = netmask | ttymask;
}
/*
* Fill in default interrupt table, and mask all interrupts.
*/
void
isa_defaultirq()
{
int i;
/* out of range vectors */
for (i = NRSVIDT; i < NIDT; i++)
setidt(i, &IDTVEC(wild), SDT_SYS386IGT, SEL_KPL);
/* icu vectors */
for (i = 0; i < ICU_LEN ; i++)
setidt(i + ICU_OFFSET, IDTVEC(intr)[i], SDT_SYS386IGT, SEL_KPL);
/* initialize 8259's */
outb(IO_ICU1, 0x11); /* reset; program device, four bytes */
outb(IO_ICU1+1, ICU_OFFSET); /* starting at this vector index */
outb(IO_ICU1+1, IRQ_SLAVE);
#ifdef AUTO_EOI_1
outb(IO_ICU1+1, 2 | 1); /* auto EOI, 8086 mode */
#else
outb(IO_ICU1+1, 1); /* 8086 mode */
#endif
outb(IO_ICU1+1, 0xff); /* mask everything */
outb(IO_ICU1, 0x68); /* special mask mode (if available) */
#ifdef REORDER_IRQ
outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */
#endif
outb(IO_ICU2, 0x11); /* reset; program device, four bytes */
outb(IO_ICU2+1, ICU_OFFSET+8); /* staring at this vector index */
outb(IO_ICU2+1, ffs(IRQ_SLAVE)-1);
#ifdef AUTO_EOI_2
outb(IO_ICU2+1, 2 | 1); /* auto EOI, 8086 mode */
#else
outb(IO_ICU2+1, 1); /* 8086 mode */
#endif
outb(IO_ICU2+1, 0xff); /* mask everything */
outb(IO_ICU1, 0x68); /* special mask mode (if available) */
/* enable interrupts, but all masked */
splhigh();
enable_intr();
}
/*
* Flush any pending interrupts.
*/
void
isa_flushintrs()
{
register int i;
/* clear any pending interrupts */
#ifndef SPECIAL_MASK_MODE
disable_intr();
intr_enable(ipending);
#endif
for (i = 0; i < 16; i++) {
outb(IO_ICU1, ICU_EOI);
outb(IO_ICU2, ICU_EOI);
}
ipending = 0;
#ifndef SPECIAL_MASK_MODE
enable_intr();
#endif
}
void
isa_intrstate()
{
register u_char a, b, c, d, e, f;
disable_intr();
outb(IO_ICU1, 0x0a);
a = inb(IO_ICU1);
outb(IO_ICU1, 0x0b);
b = inb(IO_ICU1);
c = inb(IO_ICU1 + 1);
outb(IO_ICU2, 0x0a);
d = inb(IO_ICU2);
outb(IO_ICU2, 0x0b);
e = inb(IO_ICU2);
f = inb(IO_ICU2 + 1);
enable_intr();
printf("irr1=%02x isr1=%02x imr1=%02x "
"irr2=%02x isr2=%02x imr2=%02x ipending=%08x\n",
a, b, c, d, e, f, ipending);
}
/*
* Determine what IRQ a device is using by trying to force it to generate an
* interrupt and seeing which IRQ line goes high. It is not safe to call
* this function after autoconfig.
*/
u_short
isa_discoverintr(force, aux)
void (*force)();
{
register int time = 1000000; /* wait up to 1 second */
isa_flushintrs();
/* attempt to force interrupt */
force(aux);
while (time > 0) {
register int irr;
disable_intr();
outb(IO_ICU1, 0x0a);
outb(IO_ICU2, 0x0a);
irr = inb(IO_ICU1) | (inb(IO_ICU2) << 8) | ipending;
enable_intr();
irr &= ~(IRQ_SLAVE | IRQ0);
if (irr)
return 1 << (ffs(irr) - 1);
delay(10000);
time -= 10000;
}
return IRQNONE;
}
/*
* Caught a stray interrupt, notify
*/
void
isa_strayintr(irq)
int irq;
{
extern u_long intrcnt_stray[],
intrcnt_wild;
/*
* Stray interrupts on irq 7 occur when an interrupt line is raised
* and then lowered before the CPU acknowledges it. This generally
* means either the device is screwed or something is cli'ing too
* long and it's timing out.
*
* -1 is passed by the generic handler for out of range exceptions,
* since we don't really want 208 little vectors. (It wouldn't be
* all that much code, but why bother?)
*/
if (irq == -1) {
intrcnt_wild++;
log(LOG_ERR, "wild interrupt\n");
} else {
#ifdef DIAGNOSTIC
isa_intrstate();
#endif
if (intrcnt_stray[irq]++ < 5)
log(LOG_ERR, "stray interrupt %d%s\n", irq,
intrcnt_stray[irq] == 5 ? "; stopped logging" : "");
}
}
/*
* Handle a NMI, possibly a machine check.
* return true to panic system, false to ignore.
*
* This implementation is hist[oe]rical.
*/
int
isa_nmi()
{
log(LOG_CRIT, "NMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70));
return 0;
}