PERFORCE change 94202 for review

Marcel Moolenaar marcel at FreeBSD.org
Tue Mar 28 19:54:19 UTC 2006


http://perforce.freebsd.org/chv.cgi?CH=94202

Change 94202 by marcel at marcel_nfs on 2006/03/28 19:53:55

	Implement fine-grained interrupt handling:
	o  When a subordinate driver sets up an interrupt handler, the
	   scc(4) driver calls back for interrupt source-specific
	   handlers. When a subordinate driver has at least one
	   interrupt source-specific handler, it's generic handler
	   (i.e. the one passed to BUS_SETUP_INTR) is nullified.
	o  On interrupt, scc(4) queries the hardware for all pending
	   interrupt conditions for all channels. Interrupts are
	   handled in priority order across channels. Interrupt
	   conditions are cleared for a particular source and channel
	   if the subordinate driver has an interrupt source-specific
	   handler and it indicated to have cleared the condition.
	   Afterwards, all channels with pending interrupt conditions
	   will have the generic interrupt handler of the subordinate
	   driver called or otherwise the condition is cleared by scc(4).

Affected files ...

.. //depot/projects/uart/dev/scc/scc_bfe.h#10 edit
.. //depot/projects/uart/dev/scc/scc_bus.h#5 edit
.. //depot/projects/uart/dev/scc/scc_core.c#11 edit
.. //depot/projects/uart/dev/scc/scc_dev_sab82532.c#5 edit
.. //depot/projects/uart/dev/scc/scc_dev_z8530.c#5 edit
.. //depot/projects/uart/dev/scc/scc_if.m#4 edit

Differences ...

==== //depot/projects/uart/dev/scc/scc_bfe.h#10 (text+ko) ====

@@ -29,6 +29,8 @@
 #ifndef _DEV_SCC_BFE_H_
 #define _DEV_SCC_BFE_H_
 
+#include <sys/serial.h>
+
 /*
  * Bus access structure. This structure holds the minimum information needed
  * to access the SCC. The rclk field, although not important to actually
@@ -59,6 +61,7 @@
  */
 
 #define	SCC_NMODES	3
+#define	SCC_ISRCCNT	5
 
 struct scc_chan;
 
@@ -74,13 +77,8 @@
 	int		m_fastintr:1;
 
 	driver_intr_t	*ih;
-	driver_intr_t	*ih_overrun;
-	driver_intr_t	*ih_break;
-	driver_intr_t	*ih_rxready;
-	driver_intr_t	*ih_sigchg;
-	driver_intr_t	*ih_txidle;
+	serdev_intr_t	*ih_src[SCC_ISRCCNT];
 	void		*ih_arg;
-	void		*softih;
 };
 
 struct scc_chan {
@@ -89,7 +87,9 @@
 
 	struct scc_mode	ch_mode[SCC_NMODES];
 
-	u_int		ch_nr;
+	uint32_t	ch_nr;
+	uint32_t	ch_ipend;
+	uint32_t	ch_hwsig;
 };
 
 /*

==== //depot/projects/uart/dev/scc/scc_bus.h#5 (text+ko) ====

@@ -29,6 +29,7 @@
 #ifndef _DEV_SCC_BUS_H_
 #define	_DEV_SCC_BUS_H_
 
+#include <sys/serial.h>
 #include <serdev_if.h>
 
 #define	SCC_IVAR_CHANNEL	0

==== //depot/projects/uart/dev/scc/scc_core.c#11 (text+ko) ====

@@ -45,47 +45,99 @@
 
 #include "scc_if.h"
 
+#define	SCC_ITRACE
+
 devclass_t scc_devclass;
 char scc_driver_name[] = "scc";
 
 MALLOC_DEFINE(M_SCC, "SCC", "SCC driver");
 
+#ifdef SCC_ITRACE
+#define	SCC_ITRACE_BUFSZ	128
+static uint32_t scc_itrace_entry[SCC_ITRACE_BUFSZ];
+static uint32_t scc_itrace_count[SCC_ITRACE_BUFSZ];
+static int scc_itrace_head = 0;
+
 static void
+scc_itrace_log(struct scc_softc *sc, int ch, int ipend)
+{
+	uint32_t entry;
+	int unit;
+
+	unit = device_get_unit(sc->sc_dev);
+	entry = ipend | (ch << 8) | unit;
+
+	if (scc_itrace_entry[scc_itrace_head] == entry) {
+		scc_itrace_count[scc_itrace_head]++;
+	} else {
+		scc_itrace_head = (scc_itrace_head + 1) % SCC_ITRACE_BUFSZ;
+		scc_itrace_entry[scc_itrace_head] = entry;
+		scc_itrace_count[scc_itrace_head] = 1;
+	}
+}
+
+void scc_itrace_dump(void);
+void
+scc_itrace_dump()
+{
+	int idx;
+
+	idx = scc_itrace_head;
+	do {
+		if (scc_itrace_entry[idx] != 0)
+			printf("%06x %u\n", scc_itrace_entry[idx],
+			    scc_itrace_count[idx]);
+		idx = (idx == 0) ? SCC_ITRACE_BUFSZ - 1 : idx - 1;
+	} while (idx != scc_itrace_head);
+}
+#endif /* SCC_ITRACE */
+
+static void
 scc_bfe_intr(void *arg)
 {
 	struct scc_softc *sc = arg;
 	struct scc_chan *ch;
 	struct scc_class *cl;
 	struct scc_mode *m;
-	int c;
-
-	if (sc->sc_leaving)
-		return;
+	int c, i, ipend, isrc;
 
 	cl = sc->sc_class;
-	for (c = 0; c < cl->cl_channels; c++) {
-		ch = &sc->sc_chan[c];
-		m = &ch->ch_mode[0];
-		if (!m->m_attached)
-			continue;
-		if (m->ih != NULL)
-			(*m->ih)(m->ih_arg);
-	}
-
-#if 0
 	while (!sc->sc_leaving && (ipend = SCC_IPEND(sc)) != 0) {
-		if (ipend & SER_INT_OVERRUN)
-			uart_intr_overrun(sc);
-		if (ipend & SER_INT_BREAK)
-			uart_intr_break(sc);
-		if (ipend & SER_INT_RXREADY)
-			uart_intr_rxready(sc);
-		if (ipend & SER_INT_SIGCHG)
-			uart_intr_sigchg(sc);
-		if (ipend & SER_INT_TXIDLE)
-			uart_intr_txidle(sc);
+#ifdef SCC_ITRACE
+		for (c = 0; c < cl->cl_channels; c++) {
+			ch = &sc->sc_chan[c];
+			if (ch->ch_ipend)
+				scc_itrace_log(sc, ch->ch_nr, ch->ch_ipend);
+		}
+#endif
+		i = 0, isrc = SER_INT_OVERRUN;
+		while (ipend) {
+			while (i < SCC_ISRCCNT && !(ipend & isrc))
+				i++, isrc <<= 1;
+			KASSERT(i < SCC_ISRCCNT, ("%s", __func__));
+			ipend &= ~isrc;
+			for (c = 0; c < cl->cl_channels; c++) {
+				ch = &sc->sc_chan[c];
+				if (!(ch->ch_ipend & isrc))
+					continue;
+				m = &ch->ch_mode[0];
+				if (m->ih_src[i] == NULL)
+					continue;
+				if ((*m->ih_src[i])(m->ih_arg))
+					ch->ch_ipend &= ~isrc;
+			}
+		}
+		for (c = 0; c < cl->cl_channels; c++) {
+			ch = &sc->sc_chan[c];
+			if (!ch->ch_ipend)
+				continue;
+			m = &ch->ch_mode[0];
+			if (m->ih != NULL)
+				(*m->ih)(m->ih_arg);
+			else
+				SCC_ICLEAR(sc, ch);
+		}
 	}
-#endif
 }
 
 int
@@ -99,7 +151,7 @@
 	const char *sep;
 	bus_space_handle_t bh;
 	u_long base, size, start;
-	int c, error, intr, mode, reset;
+	int c, error, mode, reset;
 
 	/*
 	 * The sc_class field defines the type of SCC we're going to work
@@ -118,13 +170,6 @@
 
 	size = abs(cl->cl_range);
 
-	/*
-	 * Protect ourselves against interrupts while we're not completely
-	 * finished attaching and initializing. We don't expect interrupts
-	 * until after SCC_ATTACH() though.
-	 */
-	sc->sc_leaving = 1;
-
 	mtx_init(&sc->sc_hwmtx, "scc_hwmtx", NULL, MTX_SPIN);
 
 	/*
@@ -201,11 +246,38 @@
 		goto fail;
 
 	/*
+	 * Setup our interrupt handler. Make it FAST under the assumption
+	 * that our children's are fast as well. We make it MPSAFE as soon
+	 * as a child sets up a MPSAFE interrupt handler.
+	 * Of course, if we can't setup a fast handler, we make it MPSAFE
+	 * right away.
+	 */
+	if (sc->sc_ires != NULL) {
+		error = bus_setup_intr(dev, sc->sc_ires,
+		    INTR_TYPE_TTY | INTR_FAST, scc_bfe_intr, sc,
+		    &sc->sc_icookie);
+		if (error) {
+			error = bus_setup_intr(dev, sc->sc_ires,
+			    INTR_TYPE_TTY | INTR_MPSAFE, scc_bfe_intr, sc,
+			    &sc->sc_icookie);
+		} else
+			sc->sc_fastintr = 1;
+
+		if (error) {
+			device_printf(dev, "could not activate interrupt\n");
+			bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
+			    sc->sc_ires);
+			sc->sc_ires = NULL;
+		}
+	}
+	if (sc->sc_ires == NULL) {
+		/* XXX no interrupt resource. Force polled mode. */
+		sc->sc_polled = 1;
+	}
+
+	/*
 	 * Attach all child devices that were probed successfully.
-	 * Keep track of whether we can setup a fast interrupt
-	 * handler ourselves.
 	 */
-	intr = INTR_MPSAFE;
 	for (c = 0; c < cl->cl_channels; c++) {
 		ch = &sc->sc_chan[c];
 		for (mode = 0; mode < SCC_NMODES; mode++) {
@@ -216,39 +288,9 @@
 			if (error)
 				continue;
 			m->m_attached = 1;
-			if (m->m_hasintr && !m->m_fastintr)
-				intr = INTR_MPSAFE;
 		}
 	}
 
-	/*
-	 * Setup our interrupt handler. Make it FAST if all our
-	 * children are fast as well. Otherwise make it MPSAFE.
-	 */
-	if (sc->sc_ires != NULL) {
-		error = BUS_SETUP_INTR(device_get_parent(dev), dev,
-		    sc->sc_ires, INTR_TYPE_TTY | intr, scc_bfe_intr,
-		    sc, &sc->sc_icookie);
-		if (error && intr == INTR_FAST) {
-			intr = INTR_MPSAFE;
-			error = BUS_SETUP_INTR(device_get_parent(dev), dev,
-			    sc->sc_ires, INTR_TYPE_TTY | intr, scc_bfe_intr,
-			    sc, &sc->sc_icookie);
-		}
-		if (error) {
-			device_printf(dev, "could not activate interrupt\n");
-			bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
-			    sc->sc_ires);
-			sc->sc_ires = NULL;
-		}
-		else
-			sc->sc_fastintr = (intr == INTR_FAST) ? 1 : 0;
-	}
-	if (sc->sc_ires == NULL) {
-		/* XXX no interrupt resource. Force polled mode. */
-		sc->sc_polled = 1;
-	}
-
 	if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) {
 		sep = "";
 		device_print_prettyname(dev);
@@ -263,9 +305,6 @@
 		printf("\n");
 	}
 
-	sc->sc_leaving = 0;
-	scc_bfe_intr(sc);
-
 	return (0);
 
  fail:
@@ -451,6 +490,8 @@
     void (*ihand)(void *), void *arg, void **cookiep)
 {
 	struct scc_mode *m;
+	struct scc_softc *sc;
+	int i, isrc;
 
 	if (device_get_parent(child) != dev)
 		return (EINVAL);
@@ -459,17 +500,30 @@
 	if ((flags & (INTR_FAST|INTR_MPSAFE)) == 0)
 		return (EINVAL);
 
+	sc = device_get_softc(dev);
+	if (sc->sc_polled)
+		return (ENXIO);
+
+	if (sc->sc_fastintr && !(flags & INTR_FAST)) {
+		sc->sc_fastintr = 0;
+		bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
+		bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE,
+		    scc_bfe_intr, sc, &sc->sc_icookie);
+	}
+
 	m = device_get_ivars(child);
 	m->m_hasintr = 1;
 	m->m_fastintr = (flags & INTR_FAST) ? 1 : 0;
 	m->ih = ihand;
 	m->ih_arg = arg;
 
-	m->ih_break = SERDEV_IHAND(child, SER_INT_BREAK);
-	m->ih_overrun = SERDEV_IHAND(child, SER_INT_OVERRUN);
-	m->ih_rxready = SERDEV_IHAND(child, SER_INT_RXREADY);
-	m->ih_sigchg = SERDEV_IHAND(child, SER_INT_SIGCHG);
-	m->ih_txidle = SERDEV_IHAND(child, SER_INT_TXIDLE);
+	i = 0, isrc = SER_INT_OVERRUN;
+	while (i < SCC_ISRCCNT) {
+		m->ih_src[i] = SERDEV_IHAND(child, isrc);
+		if (m->ih_src[i] != NULL)
+			m->ih = NULL;
+		i++, isrc <<= 1;
+	}
 	return (0);
 }
 
@@ -478,6 +532,7 @@
     void *cookie)
 {
 	struct scc_mode *m;
+	int i;
 
 	if (device_get_parent(child) != dev)
 		return (EINVAL);
@@ -490,10 +545,7 @@
 	m->m_fastintr = 0;
 	m->ih = NULL;
 	m->ih_arg = NULL;
-	m->ih_break = NULL;
-	m->ih_overrun = NULL;
-	m->ih_rxready = NULL;
-	m->ih_sigchg = NULL;
-	m->ih_txidle = NULL;
+	for (i = 0; i < SCC_ISRCCNT; i++)
+		m->ih_src[i] = NULL;
 	return (0);
 }

==== //depot/projects/uart/dev/scc/scc_dev_sab82532.c#5 (text+ko) ====

@@ -45,11 +45,13 @@
 #define	DEFAULT_RCLK	29491200
 
 static int sab82532_bfe_attach(struct scc_softc *, int);
+static int sab82532_bfe_iclear(struct scc_softc *, struct scc_chan *);
 static int sab82532_bfe_ipend(struct scc_softc *);
 static int sab82532_bfe_probe(struct scc_softc *);
 
 static kobj_method_t sab82532_methods[] = {
 	KOBJMETHOD(scc_attach,	sab82532_bfe_attach),
+	KOBJMETHOD(scc_iclear,	sab82532_bfe_iclear),
 	KOBJMETHOD(scc_ipend,	sab82532_bfe_ipend),
 	KOBJMETHOD(scc_probe,	sab82532_bfe_probe),
 	{ 0, 0 }
@@ -77,37 +79,51 @@
 }
 
 static int
+sab82532_bfe_iclear(struct scc_softc *sc, struct scc_chan *ch)
+{
+
+	return (0);
+}
+
+static int
 sab82532_bfe_ipend(struct scc_softc *sc)
 {
 	struct scc_bas *bas;
+	struct scc_chan *ch;
 	int ipend;
+	int c, ofs;
 	uint8_t isr0, isr1;
 
 	bas = &sc->sc_bas;
-	mtx_lock_spin(&sc->sc_hwmtx);
-	isr0 = scc_getreg(bas, SAB_ISR0);
-	isr1 = scc_getreg(bas, SAB_ISR1);
-	scc_barrier(bas);
-	if (isr0 & SAB_ISR0_TIME) {
-		while (scc_getreg(bas, SAB_STAR) & SAB_STAR_CEC)
-			;
-		scc_setreg(bas, SAB_CMDR, SAB_CMDR_RFRD);
+	ipend = 0;
+	for (c = 0; c < SAB_NCHAN; c++) {
+		ch = &sc->sc_chan[c];
+		ofs = c * SAB_CHANLEN;
+		mtx_lock_spin(&sc->sc_hwmtx);
+		isr0 = scc_getreg(bas, ofs + SAB_ISR0);
+		isr1 = scc_getreg(bas, ofs + SAB_ISR1);
 		scc_barrier(bas);
+		if (isr0 & SAB_ISR0_TIME) {
+			while (scc_getreg(bas, ofs + SAB_STAR) & SAB_STAR_CEC)
+				;
+			scc_setreg(bas, ofs + SAB_CMDR, SAB_CMDR_RFRD);
+			scc_barrier(bas);
+		}
+		mtx_unlock_spin(&sc->sc_hwmtx);
+
+		ch->ch_ipend = 0;
+		if (isr1 & SAB_ISR1_BRKT)
+			ch->ch_ipend |= SER_INT_BREAK;
+		if (isr0 & SAB_ISR0_RFO)
+			ch->ch_ipend |= SER_INT_OVERRUN;
+		if (isr0 & (SAB_ISR0_TCD|SAB_ISR0_RPF))
+			ch->ch_ipend |= SER_INT_RXREADY;
+		if ((isr0 & SAB_ISR0_CDSC) || (isr1 & SAB_ISR1_CSC))
+			ch->ch_ipend |= SER_INT_SIGCHG;
+		if (isr1 & SAB_ISR1_ALLS)
+			ch->ch_ipend |= SER_INT_TXIDLE;
+		ipend |= ch->ch_ipend;
 	}
-	mtx_unlock_spin(&sc->sc_hwmtx);
-
-	ipend = 0;
-	if (isr1 & SAB_ISR1_BRKT)
-		ipend |= SER_INT_BREAK;
-	if (isr0 & SAB_ISR0_RFO)
-		ipend |= SER_INT_OVERRUN;
-	if (isr0 & (SAB_ISR0_TCD|SAB_ISR0_RPF))
-		ipend |= SER_INT_RXREADY;
-	if ((isr0 & SAB_ISR0_CDSC) || (isr1 & SAB_ISR1_CSC))
-		ipend |= SER_INT_SIGCHG;
-	if (isr1 & SAB_ISR1_ALLS)
-		ipend |= SER_INT_TXIDLE;
-
 	return (ipend);
 }
 

==== //depot/projects/uart/dev/scc/scc_dev_z8530.c#5 (text+ko) ====

@@ -45,11 +45,13 @@
 #define	DEFAULT_RCLK	307200
 
 static int z8530_bfe_attach(struct scc_softc *, int);
+static int z8530_bfe_iclear(struct scc_softc *, struct scc_chan *);
 static int z8530_bfe_ipend(struct scc_softc *);
 static int z8530_bfe_probe(struct scc_softc *);
 
 static kobj_method_t z8530_methods[] = {
 	KOBJMETHOD(scc_attach,	z8530_bfe_attach),
+	KOBJMETHOD(scc_iclear,	z8530_bfe_iclear),
 	KOBJMETHOD(scc_ipend,	z8530_bfe_ipend),
 	KOBJMETHOD(scc_probe,	z8530_bfe_probe),
 	{ 0, 0 }
@@ -96,12 +98,98 @@
 }
 
 static int
+z8530_bfe_iclear(struct scc_softc *sc, struct scc_chan *ch)
+{
+	struct scc_bas *bas;
+	int c;
+
+	bas = &sc->sc_bas;
+	c = (ch->ch_nr == 1) ? CHAN_A : CHAN_B;
+	mtx_lock_spin(&sc->sc_hwmtx);
+	if (ch->ch_ipend & SER_INT_TXIDLE) {
+		scc_setreg(bas, c + REG_CTRL, CR_RSTTXI);
+		scc_barrier(bas);
+	}
+	if (ch->ch_ipend & SER_INT_RXREADY) {
+		scc_getreg(bas, c + REG_DATA);
+		scc_barrier(bas);
+	}
+	if (ch->ch_ipend & SER_INT_SIGCHG) {
+		scc_setreg(bas, c + REG_CTRL, CR_RSTXSI);
+		scc_barrier(bas);
+	}
+	if (ch->ch_ipend & (SER_INT_OVERRUN|SER_INT_BREAK))
+		scc_setreg(bas, c + REG_CTRL, CR_RSTERR);
+	mtx_unlock_spin(&sc->sc_hwmtx);
+	return (0);
+}
+
+#define	SIGCHG(c, i, s, d)				\
+	if (c) {					\
+		i |= (i & s) ? s : s | d;		\
+	} else {					\
+		i = (i & s) ? (i & ~s) | d : i;		\
+	}
+
+static int
 z8530_bfe_ipend(struct scc_softc *sc)
 {
 	struct scc_bas *bas;
+	struct scc_chan *ch[2];
+	uint32_t sig;
+	uint8_t bes, ip, src;
 
 	bas = &sc->sc_bas;
-	return (0);
+	ch[0] = &sc->sc_chan[0];
+	ch[1] = &sc->sc_chan[1];
+	ch[0]->ch_ipend = 0;
+	ch[1]->ch_ipend = 0;
+
+	mtx_lock_spin(&sc->sc_hwmtx);
+	ip = scc_getmreg(bas, CHAN_A, RR_IP);
+	if (ip & IP_RIA)
+		ch[0]->ch_ipend |= SER_INT_RXREADY;
+	if (ip & IP_RIB)
+		ch[1]->ch_ipend |= SER_INT_RXREADY;
+	if (ip & IP_TIA)
+		ch[0]->ch_ipend |= SER_INT_TXIDLE;
+	if (ip & IP_TIB)
+		ch[1]->ch_ipend |= SER_INT_TXIDLE;
+	if (ip & IP_SIA) {
+		bes = scc_getreg(bas, CHAN_A + RR_BES);
+		if (bes & BES_BRK)
+			ch[0]->ch_ipend |= SER_INT_BREAK;
+		sig = ch[0]->ch_hwsig;
+		SIGCHG(bes & BES_CTS, sig, SER_CTS, SER_DCTS);
+		SIGCHG(bes & BES_DCD, sig, SER_DCD, SER_DDCD);
+		SIGCHG(bes & BES_SYNC, sig, SER_DSR, SER_DDSR);
+		if (sig & SER_MASK_DELTA) {
+			ch[0]->ch_hwsig = sig;
+			ch[0]->ch_ipend |= SER_INT_SIGCHG;
+		}
+		src = scc_getmreg(bas, CHAN_A, RR_SRC);
+		if (src & SRC_OVR)
+			ch[0]->ch_ipend |= SER_INT_OVERRUN;
+	}
+	if (ip & IP_SIB) {
+		bes = scc_getreg(bas, CHAN_B + RR_BES);
+		if (bes & BES_BRK)
+			ch[1]->ch_ipend |= SER_INT_BREAK;
+		sig = ch[1]->ch_hwsig;
+		SIGCHG(bes & BES_CTS, sig, SER_CTS, SER_DCTS);
+		SIGCHG(bes & BES_DCD, sig, SER_DCD, SER_DDCD);
+		SIGCHG(bes & BES_SYNC, sig, SER_DSR, SER_DDSR);
+		if (sig & SER_MASK_DELTA) {
+			ch[1]->ch_hwsig = sig;
+			ch[1]->ch_ipend |= SER_INT_SIGCHG;
+		}
+		src = scc_getmreg(bas, CHAN_B, RR_SRC);
+		if (src & SRC_OVR)
+			ch[1]->ch_ipend |= SER_INT_OVERRUN;
+	}
+	mtx_unlock_spin(&sc->sc_hwmtx);
+
+	return (ch[0]->ch_ipend | ch[1]->ch_ipend);
 }
 
 static int

==== //depot/projects/uart/dev/scc/scc_if.m#4 (text+ko) ====

@@ -49,6 +49,11 @@
 	int reset;
 };
 
+METHOD void iclear {
+	struct scc_softc *this;
+	struct scc_chan *chan;
+};
+
 # ipend() - query SCC for pending interrupts.
 # When an interrupt is signalled, the handler will call this method to find
 # out which of the interrupt sources needs attention. The handler will use


More information about the p4-projects mailing list