summaryrefslogtreecommitdiff
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
parent1e83f2e159c7ca02f2d157b49860ed9bb85e1ff2 (diff)
Use soft interrupts to dispatch pcmcia device interrupts; this ensures the
driver handlers get invoked at the right level. Parts from NetBSD.
-rw-r--r--sys/arch/sparc/dev/ts102.c52
-rw-r--r--sys/dev/sbus/stp4020.c71
-rw-r--r--sys/dev/sbus/stp4020var.h6
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 <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 */