summaryrefslogtreecommitdiff
path: root/sys/dev/sbus/stp4020.c
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2009-04-10 20:55:00 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2009-04-10 20:55:00 +0000
commit0c071e02e2f0c9f5a71d1db3e1a44348594db00c (patch)
treea5c4979163c30468861ea56f24521424dd959ea4 /sys/dev/sbus/stp4020.c
parent1e83f2e159c7ca02f2d157b49860ed9bb85e1ff2 (diff)
Use soft interrupts to dispatch pcmcia device interrupts; this ensures the
driver handlers get invoked at the right level. Parts from NetBSD.
Diffstat (limited to 'sys/dev/sbus/stp4020.c')
-rw-r--r--sys/dev/sbus/stp4020.c71
1 files changed, 61 insertions, 10 deletions
diff --git a/sys/dev/sbus/stp4020.c b/sys/dev/sbus/stp4020.c
index 44436a2de71..63645b3bc7d 100644
--- a/sys/dev/sbus/stp4020.c
+++ b/sys/dev/sbus/stp4020.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: stp4020.c,v 1.15 2008/06/26 05:42:18 ray Exp $ */
+/* $OpenBSD: stp4020.c,v 1.16 2009/04/10 20:54:58 miod Exp $ */
/* $NetBSD: stp4020.c,v 1.23 2002/06/01 23:51:03 lukem Exp $ */
/*-
@@ -49,6 +49,7 @@
#include <dev/pcmcia/pcmciachip.h>
#include <machine/bus.h>
+#include <machine/intr.h>
#include <dev/sbus/stp4020reg.h>
#include <dev/sbus/stp4020var.h>
@@ -72,6 +73,7 @@ int stp4020_debug = 0;
int stp4020print(void *, const char *);
void stp4020_map_window(struct stp4020_socket *, int, int);
void stp4020_calc_speed(int, int, int *, int *);
+void stp4020_intr_dispatch(void *);
struct cfdriver stp_cd = {
NULL, "stp", DV_DULL
@@ -232,6 +234,12 @@ stp4020_attach_socket(h, speed)
struct pcmciabus_attach_args paa;
int v;
+ /* no interrupt handlers yet */
+ h->intrhandler = NULL;
+ h->intrarg = NULL;
+ h->softint = NULL;
+ h->int_enable = h->int_disable = 0;
+
/* Map all three windows */
stp4020_map_window(h, STP_WIN_ATTR, speed);
stp4020_map_window(h, STP_WIN_MEM, speed);
@@ -370,12 +378,34 @@ stp4020_queue_event(sc, sock)
wakeup(&sc->events);
}
+/*
+ * Software interrupt called to invoke the real driver interrupt handler.
+ */
+void
+stp4020_intr_dispatch(void *arg)
+{
+ struct stp4020_socket *h = (struct stp4020_socket *)arg;
+ int s;
+
+ /* invoke driver handler */
+ h->intrhandler(h->intrarg);
+
+ /* enable SBUS interrupts for PCMCIA interrupts again */
+ s = splhigh();
+ stp4020_wr_sockctl(h, STP4020_ICR0_IDX, h->int_enable);
+ splx(s);
+}
+
int
stp4020_statintr(arg)
void *arg;
{
struct stp4020_softc *sc = arg;
int i, sense, r = 0;
+ int s;
+
+ /* protect hardware access against soft interrupts */
+ s = splhigh();
/*
* Check each socket for pending requests.
@@ -467,6 +497,8 @@ stp4020_statintr(arg)
r = 1;
}
+ splx(s);
+
return (r);
}
@@ -476,6 +508,10 @@ stp4020_iointr(arg)
{
struct stp4020_softc *sc = arg;
int i, r = 0;
+ int s;
+
+ /* protect hardware access against soft interrupts */
+ s = splhigh();
/*
* Check each socket for pending requests.
@@ -502,20 +538,22 @@ stp4020_iointr(arg)
continue;
}
/* Call card handler, if any */
- if (h->intrhandler != NULL) {
+ if (h->softint != NULL) {
+ softintr_schedule(h->softint);
+
/*
- * We ought to be at an higher ipl level
- * than the callback, since the first
- * interrupt of this device is usually
- * higher than IPL_CLOCK.
+ * Disable this sbus interrupt, until the
+ * softintr handler had a chance to run.
*/
- splassert(h->ipl);
- (*h->intrhandler)(h->intrarg);
+ stp4020_wr_sockctl(h, STP4020_ICR0_IDX,
+ h->int_disable);
}
}
}
+ splx(s);
+
return (r);
}
@@ -763,12 +801,15 @@ stp4020_chip_socket_enable(pch)
v &= ~(STP4020_ICR0_IOILVL | STP4020_ICR0_IFTYPE);
v |= STP4020_ICR0_IFTYPE_IO | STP4020_ICR0_IOIE |
STP4020_ICR0_IOILVL_SB0 | STP4020_ICR0_SPKREN;
+ h->int_enable = v;
+ h->int_disable = v & ~STP4020_ICR0_IOIE;
DPRINTF(("%s: configuring card for IO usage\n",
h->sc->sc_dev.dv_xname));
} else {
v &= ~(STP4020_ICR0_IOILVL | STP4020_ICR0_IFTYPE |
STP4020_ICR0_SPKREN | STP4020_ICR0_IOIE);
v |= STP4020_ICR0_IFTYPE_MEM;
+ h->int_enable = h->int_disable = v;
DPRINTF(("%s: configuring card for MEM ONLY usage\n",
h->sc->sc_dev.dv_xname));
}
@@ -812,10 +853,16 @@ stp4020_chip_intr_establish(pch, pf, ipl, handler, arg, xname)
{
struct stp4020_socket *h = (struct stp4020_socket *)pch;
+ /*
+ * Note that this code relies on softintr_establish() to be
+ * used with real, hardware ipl values. All platforms with
+ * SBus support support this.
+ */
h->intrhandler = handler;
h->intrarg = arg;
- h->ipl = ipl;
- return (h);
+ h->softint = softintr_establish(ipl, stp4020_intr_dispatch, h);
+
+ return h->softint != NULL ? h : NULL;
}
void
@@ -825,6 +872,10 @@ stp4020_chip_intr_disestablish(pch, ih)
{
struct stp4020_socket *h = (struct stp4020_socket *)pch;
+ if (h->softint != NULL) {
+ softintr_disestablish(h->softint);
+ h->softint = NULL;
+ }
h->intrhandler = NULL;
h->intrarg = NULL;
}