From 0c071e02e2f0c9f5a71d1db3e1a44348594db00c Mon Sep 17 00:00:00 2001 From: Miod Vallat Date: Fri, 10 Apr 2009 20:55:00 +0000 Subject: Use soft interrupts to dispatch pcmcia device interrupts; this ensures the driver handlers get invoked at the right level. Parts from NetBSD. --- sys/dev/sbus/stp4020.c | 71 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 10 deletions(-) (limited to 'sys/dev/sbus/stp4020.c') 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 #include +#include #include #include @@ -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; } -- cgit v1.2.3