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/arch/sparc/dev/ts102.c | 52 +++++++++++++++++++++++++++++---- sys/dev/sbus/stp4020.c | 71 +++++++++++++++++++++++++++++++++++++++------- sys/dev/sbus/stp4020var.h | 6 ++-- 3 files changed, 111 insertions(+), 18 deletions(-) diff --git a/sys/arch/sparc/dev/ts102.c b/sys/arch/sparc/dev/ts102.c index c8bf6f8cb6b..2223582a188 100644 --- a/sys/arch/sparc/dev/ts102.c +++ b/sys/arch/sparc/dev/ts102.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ts102.c,v 1.18 2006/08/11 18:57:35 miod Exp $ */ +/* $OpenBSD: ts102.c,v 1.19 2009/04/10 20:54:57 miod Exp $ */ /* * Copyright (c) 2003, 2004, Miodrag Vallat. * @@ -112,6 +112,7 @@ struct tslot_data { /* Interrupt handler */ int (*td_intr)(void *); void *td_intrarg; + void *td_softintr; /* Socket status */ int td_slot; @@ -137,6 +138,7 @@ void tslot_create_event_thread(void *); void tslot_event_thread(void *); int tslot_intr(void *); void tslot_intr_disestablish(pcmcia_chipset_handle_t, void *); +void tslot_intr_dispatch(void *); void *tslot_intr_establish(pcmcia_chipset_handle_t, struct pcmcia_function *, int, int (*)(void *), void *, char *); const char *tslot_intr_string(pcmcia_chipset_handle_t, void *); @@ -664,6 +666,10 @@ tslot_intr(void *v) struct tslot_data *td; int intregs[TS102_NUM_SLOTS], *intreg; int i, rc = 0; + int s; + + /* protect hardware access against soft interrupts */ + s = splhigh(); /* * Scan slots, and acknowledge the interrupt if necessary first @@ -704,6 +710,7 @@ tslot_intr(void *v) tslot_slot_intr(td, *intreg); } + splx(s); return (rc); } @@ -765,21 +772,48 @@ tslot_slot_intr(struct tslot_data *td, int intreg) return; } - if (td->td_intr != NULL) { + if (td->td_softintr != NULL) { /* - * XXX There is no way to honour the interrupt handler - * requested IPL level... + * Disable this sbus interrupt, until the + * softintr handler had a chance to run. */ - (*td->td_intr)(td->td_intrarg); + TSLOT_WRITE(td, TS102_REG_CARD_A_INT, + TSLOT_READ(td, TS102_REG_CARD_A_INT) & + ~TS102_CARD_INT_MASK_IRQ); + + softintr_schedule(td->td_softintr); } } } +/* + * Software interrupt called to invoke the real driver interrupt handler. + */ +void +tslot_intr_dispatch(void *arg) +{ + struct tslot_data *td = (struct tslot_data *)arg; + int s; + + /* invoke driver handler */ + td->td_intr(td->td_intrarg); + + /* enable SBUS interrupts for PCMCIA interrupts again */ + s = splhigh(); + TSLOT_WRITE(td, TS102_REG_CARD_A_INT, + TSLOT_READ(td, TS102_REG_CARD_A_INT) | TS102_CARD_INT_MASK_IRQ); + splx(s); +} + void tslot_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih) { struct tslot_data *td = (struct tslot_data *)pch; + if (td->td_softintr != NULL) { + softintr_disestablish(td->td_softintr); + td->td_softintr = NULL; + } td->td_intr = NULL; td->td_intrarg = NULL; } @@ -800,8 +834,14 @@ tslot_intr_establish(pcmcia_chipset_handle_t pch, struct pcmcia_function *pf, { struct tslot_data *td = (struct tslot_data *)pch; + /* + * Note that this code relies on softintr_establish() to be + * used with real, hardware ipl values. All platforms with + * SBus support support this. + */ td->td_intr = handler; td->td_intrarg = arg; + td->td_softintr = softintr_establish(ipl, tslot_intr_dispatch, td); - return (td); + return td->td_softintr != NULL ? td : NULL; } 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; } 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 */ -- cgit v1.2.3