diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2009-04-10 20:55:00 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2009-04-10 20:55:00 +0000 |
commit | 0c071e02e2f0c9f5a71d1db3e1a44348594db00c (patch) | |
tree | a5c4979163c30468861ea56f24521424dd959ea4 /sys/dev/sbus | |
parent | 1e83f2e159c7ca02f2d157b49860ed9bb85e1ff2 (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')
-rw-r--r-- | sys/dev/sbus/stp4020.c | 71 | ||||
-rw-r--r-- | sys/dev/sbus/stp4020var.h | 6 |
2 files changed, 65 insertions, 12 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; } diff --git a/sys/dev/sbus/stp4020var.h b/sys/dev/sbus/stp4020var.h index 59e9f24fbb8..0ab6264b820 100644 --- a/sys/dev/sbus/stp4020var.h +++ b/sys/dev/sbus/stp4020var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: stp4020var.h,v 1.4 2008/06/26 05:42:18 ray Exp $ */ +/* $OpenBSD: stp4020var.h,v 1.5 2009/04/10 20:54:59 miod Exp $ */ /* $NetBSD: stp4020.c,v 1.23 2002/06/01 23:51:03 lukem Exp $ */ /*- @@ -46,13 +46,15 @@ struct stp4020_socket { #define STP4020_SOCKET_ENABLING 0x0004 int sense; int sock; /* Socket number (0 or 1) */ + int int_enable; /* ICR0 value for interrupt enabled */ + int int_disable; /* ICR0 value for interrupt disabled */ bus_space_tag_t tag; /* socket control space */ bus_space_handle_t regs; /* */ struct device *pcmcia; /* Associated PCMCIA device */ int (*intrhandler) /* Card driver interrupt handler */ (void *); void *intrarg; /* Card interrupt handler argument */ - int ipl; /* Interrupt level suggested by card */ + void *softint; /* softintr cookie */ bus_space_tag_t wintag; /* windows access tag */ struct { bus_space_handle_t winaddr;/* this window's address */ |