summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2005-08-14 10:58:37 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2005-08-14 10:58:37 +0000
commitaa90e1774f220cac512a167a80241a994474f8e9 (patch)
treeb0b1d0116d130ebc9fdfdad4d8fd5003440348f1 /sys/arch
parent7e00142ff0e7ff8241ff33647552b669d0dbbe08 (diff)
Add more flexibility to the fast trap handlers mechanism:
- add the ability to deregister a fast trap handler. - when registering a fast trap, provide an optional callback which will be invoked if we try to register a regular trap handler later; the callback will be responsible to replace the fast trap handler with a regular trap handler (and is allowed to fail). Alter audioamd(4) to take advantage of this, so that it can share its interrupt with stp(4) on SPARCclassic machines. Problem found the hard way and fix tested by Jason George; discussed and ok deraadt@
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/sparc/dev/amd7930.c68
-rw-r--r--sys/arch/sparc/dev/fd.c10
-rw-r--r--sys/arch/sparc/include/cpu.h5
-rw-r--r--sys/arch/sparc/sparc/intr.c92
4 files changed, 145 insertions, 30 deletions
diff --git a/sys/arch/sparc/dev/amd7930.c b/sys/arch/sparc/dev/amd7930.c
index de17fca17c5..fabc158aedc 100644
--- a/sys/arch/sparc/dev/amd7930.c
+++ b/sys/arch/sparc/dev/amd7930.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: amd7930.c,v 1.29 2005/07/09 22:23:13 miod Exp $ */
+/* $OpenBSD: amd7930.c,v 1.30 2005/08/14 10:58:33 miod Exp $ */
/* $NetBSD: amd7930.c,v 1.37 1998/03/30 14:23:40 pk Exp $ */
/*
@@ -57,6 +57,11 @@ int amd7930debug = 0;
#endif
/*
+ * Define AUDIO_C_HANDLER to force using non-fast trap routines.
+ */
+/* #define AUDIO_C_HANDLER */
+
+/*
* Software state, per AMD79C30 audio chip.
*/
struct amd7930_softc {
@@ -84,8 +89,6 @@ struct amd7930_softc {
};
/* interrupt interfaces */
-#ifdef AUDIO_C_HANDLER
-int amd7930hwintr(void *);
#if defined(SUN4M)
#define AUDIO_SET_SWINTR do { \
if (CPU_ISSUN4M) \
@@ -96,14 +99,17 @@ int amd7930hwintr(void *);
#else
#define AUDIO_SET_SWINTR ienab_bis(IE_L4)
#endif /* defined(SUN4M) */
-#else
+
+#ifndef AUDIO_C_HANDLER
struct auio *auiop;
#endif /* AUDIO_C_HANDLER */
+int amd7930hwintr(void *);
int amd7930swintr(void *);
/* forward declarations */
void audio_setmap(volatile struct amd7930 *, struct mapreg *);
static void init_amd(volatile struct amd7930 *);
+int amd7930_shareintr(void *);
/* autoconfiguration driver */
void amd7930attach(struct device *, struct device *, void *);
@@ -294,17 +300,30 @@ amd7930attach(parent, self, args)
init_amd(amd);
+ /*
+ * Register interrupt handlers. We'll prefer a fast trap (unless
+ * AUDIO_C_HANDLER is defined), with a sharing callback so that we
+ * can revert into a regular trap vector if necessary.
+ */
#ifndef AUDIO_C_HANDLER
- auiop = &sc->sc_au;
sc->sc_hwih.ih_vec = pri;
- evcount_attach(&sc->sc_hwih.ih_count, sc->sc_dev.dv_xname,
- &sc->sc_hwih.ih_vec, &evcount_intr);
- intr_fasttrap(pri, amd7930_trap);
+ if (intr_fasttrap(pri, amd7930_trap, amd7930_shareintr, sc) == 0) {
+ auiop = &sc->sc_au;
+ evcount_attach(&sc->sc_hwih.ih_count, sc->sc_dev.dv_xname,
+ &sc->sc_hwih.ih_vec, &evcount_intr);
+ } else {
+#ifdef AUDIO_DEBUG
+ printf("%s: unable to register fast trap handler\n",
+ self->dv_xname);
+#endif
#else
- sc->sc_hwih.ih_fun = amd7930hwintr;
- sc->sc_hwih.ih_arg = &sc->sc_au;
- intr_establish(pri, &sc->sc_hwih, IPL_AUHARD, sc->sc_dev.dv_xname);
+ {
#endif
+ sc->sc_hwih.ih_fun = amd7930hwintr;
+ sc->sc_hwih.ih_arg = &sc->sc_au;
+ intr_establish(pri, &sc->sc_hwih, IPL_AUHARD,
+ sc->sc_dev.dv_xname);
+ }
sc->sc_swih.ih_fun = amd7930swintr;
sc->sc_swih.ih_arg = sc;
intr_establish(IPL_AUSOFT, &sc->sc_swih, IPL_AUSOFT,
@@ -767,7 +786,6 @@ amd7930_query_devinfo(addr, dip)
return (0);
}
-#ifdef AUDIO_C_HANDLER
int
amd7930hwintr(au0)
void *au0;
@@ -811,7 +829,6 @@ amd7930hwintr(au0)
return (-1);
}
-#endif /* AUDIO_C_HANDLER */
int
amd7930swintr(sc0)
@@ -842,3 +859,28 @@ amd7930swintr(sc0)
splx(s);
return (ret);
}
+
+#ifndef AUDIO_C_HANDLER
+int
+amd7930_shareintr(void *arg)
+{
+ struct amd7930_softc *sc = arg;
+
+ /*
+ * We are invoked at splhigh(), so there is no need to prevent the chip
+ * from interrupting while we are messing with the handlers. We
+ * however need to properly untie the event counter from the chain,
+ * since it will be reused immediately by intr_establish()...
+ */
+
+ intr_fastuntrap(sc->sc_hwih.ih_vec);
+ evcount_detach(&sc->sc_hwih.ih_count);
+
+ sc->sc_hwih.ih_fun = amd7930hwintr;
+ sc->sc_hwih.ih_arg = &sc->sc_au;
+ intr_establish(sc->sc_hwih.ih_vec, &sc->sc_hwih, IPL_AUHARD,
+ sc->sc_dev.dv_xname);
+
+ return (0);
+}
+#endif
diff --git a/sys/arch/sparc/dev/fd.c b/sys/arch/sparc/dev/fd.c
index bfbff8f15fa..2ad2d5fa5a1 100644
--- a/sys/arch/sparc/dev/fd.c
+++ b/sys/arch/sparc/dev/fd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: fd.c,v 1.41 2005/03/23 17:10:22 miod Exp $ */
+/* $OpenBSD: fd.c,v 1.42 2005/08/14 10:58:33 miod Exp $ */
/* $NetBSD: fd.c,v 1.51 1997/05/24 20:16:19 pk Exp $ */
/*-
@@ -442,6 +442,7 @@ fdcattach(parent, self, aux)
TAILQ_INIT(&fdc->sc_drives);
pri = ca->ca_ra.ra_intr[0].int_pri;
+ printf(" pri %d, softpri %d: ", pri, IPL_FDSOFT);
#ifdef FDC_C_HANDLER
fdc->sc_hih.ih_fun = (void *)fdc_c_hwintr;
fdc->sc_hih.ih_arg = fdc;
@@ -449,9 +450,12 @@ fdcattach(parent, self, aux)
#else
fdciop = &fdc->sc_io;
fdc->sc_hih.ih_vec = pri;
+ if (intr_fasttrap(pri, fdchwintr, NULL, NULL) != 0) {
+ printf("unable to register fast trap handler\n");
+ return;
+ }
evcount_attach(&fdc->sc_hih.ih_count, self->dv_xname,
&fdc->sc_hih.ih_vec, &evcount_intr);
- intr_fasttrap(pri, fdchwintr);
#endif
fdc->sc_sih.ih_fun = (void *)fdcswintr;
fdc->sc_sih.ih_arg = fdc;
@@ -500,7 +504,7 @@ fdcattach(parent, self, aux)
printf(" CFGLOCK: unexpected response");
}
- printf(" pri %d, softpri %d: chip 8207%c\n", pri, IPL_FDSOFT, code);
+ printf("chip 8207%c\n", code);
/*
* Controller and drives are represented by one and the same
diff --git a/sys/arch/sparc/include/cpu.h b/sys/arch/sparc/include/cpu.h
index 37ae52d7a88..6fa40683229 100644
--- a/sys/arch/sparc/include/cpu.h
+++ b/sys/arch/sparc/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.24 2005/03/23 17:14:45 miod Exp $ */
+/* $OpenBSD: cpu.h,v 1.25 2005/08/14 10:58:36 miod Exp $ */
/* $NetBSD: cpu.h,v 1.24 1997/03/15 22:25:15 pk Exp $ */
/*
@@ -173,7 +173,8 @@ void vmeintr_establish(int vec, int level, struct intrhand *, int, const char *)
* interrupt vectors (vectors that are not shared and are handled in the
* trap window). Such functions must be written in assembly.
*/
-void intr_fasttrap(int level, void (*vec)(void));
+int intr_fasttrap(int, void (*)(void), int (*)(void *), void *);
+void intr_fastuntrap(int);
/* auxreg.c */
void led_blink(void *);
diff --git a/sys/arch/sparc/sparc/intr.c b/sys/arch/sparc/sparc/intr.c
index e544aa33353..5b15030efb6 100644
--- a/sys/arch/sparc/sparc/intr.c
+++ b/sys/arch/sparc/sparc/intr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: intr.c,v 1.27 2004/09/29 07:35:14 miod Exp $ */
+/* $OpenBSD: intr.c,v 1.28 2005/08/14 10:58:36 miod Exp $ */
/* $NetBSD: intr.c,v 1.20 1997/07/29 09:42:03 fair Exp $ */
/*
@@ -244,6 +244,11 @@ struct intrhand *intrhand[15] = {
};
static int fastvec; /* marks fast vectors (see below) */
+static struct {
+ int (*cb)(void *);
+ void *data;
+} fastvec_share[15];
+
#ifdef DIAGNOSTIC
extern int sparc_interrupt4m[];
extern int sparc_interrupt44c[];
@@ -251,7 +256,7 @@ extern int sparc_interrupt44c[];
/*
* Attach an interrupt handler to the vector chain for the given level.
- * This is not possible if it has been taken away as a fast vector.
+ * This may not be possible if it has been taken away as a fast vector.
*/
void
intr_establish(level, ih, ipl_block, name)
@@ -294,9 +299,19 @@ intr_establish(level, ih, ipl_block, name)
evcount_attach(&ih->ih_count, name, &ih->ih_vec, &evcount_intr);
s = splhigh();
- if (fastvec & (1 << level))
- panic("intr_establish: level %d interrupt tied to fast vector",
- level);
+
+ /*
+ * Check if this interrupt is already being handled by a fast trap.
+ * If so, attempt to change it back to a regular (thus) shareable
+ * trap.
+ */
+ if (fastvec & (1 << level)) {
+ if (fastvec_share[level].cb == NULL ||
+ (*fastvec_share[level].cb)(fastvec_share[level].data) != 0)
+ panic("intr_establish: level %d interrupt tied to fast vector",
+ level);
+ }
+
#ifdef DIAGNOSTIC
/* double check for legal hardware interrupt */
if ((level != 1 && level != 4 && level != 6) || CPU_ISSUN4M ) {
@@ -329,11 +344,13 @@ intr_establish(level, ih, ipl_block, name)
/*
* Like intr_establish, but wires a fast trap vector. Only one such fast
* trap is legal for any interrupt, and it must be a hardware interrupt.
+ * In case some other device wants to share the interrupt, we also register
+ * a callback which will be able to revert this and register a slower, but
+ * shareable trap vector if necessary (for example, to share int 13 between
+ * audioamd and stp).
*/
-void
-intr_fasttrap(level, vec)
- int level;
- void (*vec)(void);
+int
+intr_fasttrap(int level, void (*vec)(void), int (*share)(void *), void *cbdata)
{
struct trapvec *tv;
u_long hi22, lo10;
@@ -349,9 +366,16 @@ intr_fasttrap(level, vec)
hi22 = ((u_long)vec) >> 10;
lo10 = ((u_long)vec) & 0x3ff;
s = splhigh();
- if ((fastvec & (1 << level)) != 0 || intrhand[level] != NULL)
- panic("intr_fasttrap: already handling level %d interrupts",
- level);
+
+ /*
+ * If this interrupt is already being handled, fail; the caller will
+ * either panic or try to register a slow (shareable) trap.
+ */
+ if ((fastvec & (1 << level)) != 0 || intrhand[level] != NULL) {
+ splx(s);
+ return (EBUSY);
+ }
+
#ifdef DIAGNOSTIC
displ = (CPU_ISSUN4M)
? &sparc_interrupt4m[0] - &tv->tv_instr[1]
@@ -371,12 +395,56 @@ intr_fasttrap(level, vec)
instr[1] = I_JMPLri(I_G0, I_L3, lo10); /* jmpl %l3+%lo(vec),%g0 */
instr[2] = I_RDPSR(I_L0); /* mov %psr, %l0 */
+ fastvec_share[level].cb = share;
+ fastvec_share[level].data = cbdata;
+
tvp = (char *)tv->tv_instr;
instrp = (char *)instr;
for (i = 0; i < sizeof(int) * 3; i++, instrp++, tvp++)
pmap_writetext(tvp, *instrp);
fastvec |= 1 << level;
splx(s);
+
+ return (0);
+}
+
+void
+intr_fastuntrap(int level)
+{
+ struct trapvec *tv;
+ int i, s;
+ int displ;
+ int instr[3];
+ char *instrp;
+ char *tvp;
+
+ tv = &trapbase[T_L1INT - 1 + level];
+
+ /* restore to `mov level,%l3; ba _sparc_interrupt; rdpsr %l0' */
+ displ = (CPU_ISSUN4M)
+ ? &sparc_interrupt4m[0] - &tv->tv_instr[1]
+ : &sparc_interrupt44c[0] - &tv->tv_instr[1];
+ instr[0] = I_MOVi(I_L3, level);
+ instr[1] = I_BA(0, displ);
+ instr[2] = I_RDPSR(I_L0);
+
+ s = splhigh();
+
+#ifdef DIAGNOSTIC
+ if ((fastvec & (1 << level)) == 0) {
+ splx(s);
+ return;
+ }
+#endif
+
+ tvp = (char *)tv->tv_instr;
+ instrp = (char *)instr;
+ for (i = 0; i < sizeof(int) * 3; i++, instrp++, tvp++)
+ pmap_writetext(tvp, *instrp);
+ fastvec &= ~(1 << level);
+ fastvec_share[level].cb = NULL;
+
+ splx(s);
}
#ifdef DIAGNOSTIC