PERFORCE change 91901 for review

Marcel Moolenaar marcel at FreeBSD.org
Thu Feb 16 13:03:27 PST 2006


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

Change 91901 by marcel at marcel_nfs on 2006/02/16 21:02:43

	Make scc(4) work in a minimal fashion:
	o  Add a new serdev interface to bring the umbrella driver (e.g.
	   scc(4)) and the child driver (e.g. uart(4)) closer together.
	   The interface helps out with configuration and interrupts.
	   This is intended to be usable by multi-port serial cards as
	   well.
	o  Split the probe and attach of the child drivers. First the
	   scc(4) driver creates control structures for each channel and
	   each mode of operation and probes for devices. Then it queries
	   the probed drivers whether a hardware reset is allowed or not
	   (to avoid disrupting the system console if it happens to be on
	   a channel). The scc(4) driver passes that information on to the
	   hardware driver, which does the initial hardware setup. Then
	   all child drivers are attached.
	o  Avoid using INTR_FAST for now as the umbrella driver is still
	   as stupid as puc(4). Of course scc(4) will become smarter and
	   will be able to clear interrupts of un-attached channels or
	   otherwise invoke source-dependent interrupt handlers of the
	   child driver. When that's done, INTR_FAST is safe.
	
	This works on my U2 with the console on channel B of the Z8530.

Affected files ...

.. //depot/projects/uart/conf/files#47 edit
.. //depot/projects/uart/dev/scc/scc_bfe.h#8 edit
.. //depot/projects/uart/dev/scc/scc_bus.h#4 edit
.. //depot/projects/uart/dev/scc/scc_core.c#8 edit
.. //depot/projects/uart/dev/scc/scc_dev_sab82532.c#4 edit
.. //depot/projects/uart/dev/scc/scc_dev_z8530.c#4 edit
.. //depot/projects/uart/dev/scc/scc_if.m#3 edit
.. //depot/projects/uart/dev/uart/uart_bus.h#36 edit
.. //depot/projects/uart/dev/uart/uart_bus_scc.c#4 edit
.. //depot/projects/uart/dev/uart/uart_core.c#41 edit
.. //depot/projects/uart/kern/serdev_if.m#1 add

Differences ...

==== //depot/projects/uart/conf/files#47 (text+ko) ====

@@ -1315,6 +1315,7 @@
 kern/md5c.c			standard
 kern/sched_4bsd.c		optional sched_4bsd
 kern/sched_ule.c		optional sched_ule
+kern/serdev_if.m		optional scc
 kern/subr_autoconf.c		standard
 kern/subr_blist.c		standard
 kern/subr_bus.c			standard

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

@@ -57,18 +57,22 @@
 /*
  * SCC mode (child) and channel control structures.
  */
+
+#define	SCC_NMODES	3
+
 struct scc_chan;
 
 struct scc_mode {
-	STAILQ_ENTRY(scc_mode) m_link;
 	struct scc_chan	*m_chan;
 	device_t	m_dev;
 
-	int		m_mode;
+	u_int		m_mode;
 	int		m_alloc_rres:1;
+	int		m_attached:1;
+	int		m_probed:1;
 
 	driver_intr_t	*ih;
-	driver_intr_t	*ih_ovrrun;
+	driver_intr_t	*ih_overrun;
 	driver_intr_t	*ih_break;
 	driver_intr_t	*ih_rxready;
 	driver_intr_t	*ih_sigchg;
@@ -78,10 +82,10 @@
 };
 
 struct scc_chan {
-	STAILQ_ENTRY(scc_chan) ch_link;
+	struct resource ch_rres;
 	struct resource_list ch_rlist;
 
-	STAILQ_HEAD(, scc_mode) ch_mode;
+	struct scc_mode	ch_mode[SCC_NMODES];
 
 	u_int		ch_nr;
 };
@@ -91,12 +95,12 @@
  */
 struct scc_class {
 	KOBJ_CLASS_FIELDS;
-	int		cl_channels;	/* Number of independent channels. */
-	int		cl_class;	/* SCC bus class ID. */
-	int		cl_modes;	/* Supported modes (bitset). */
+	u_int		cl_channels;	/* Number of independent channels. */
+	u_int		cl_class;	/* SCC bus class ID. */
+	u_int		cl_modes;	/* Supported modes (bitset). */
 	int		cl_range;
-	int		cl_rclk;
-	int		cl_regshft;
+	u_int		cl_rclk;
+	u_int		cl_regshft;
 };
 
 extern struct scc_class scc_sab82532_class;
@@ -117,7 +121,7 @@
 	void		*sc_icookie;
 	int		sc_irid;
 
-	STAILQ_HEAD(, scc_chan) sc_chan;
+	struct scc_chan	*sc_chan;
 
 	int		sc_fastintr:1;
 	int		sc_leaving:1;

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

@@ -29,6 +29,8 @@
 #ifndef _DEV_SCC_BUS_H_
 #define	_DEV_SCC_BUS_H_
 
+#include <serdev_if.h>
+
 #define	SCC_IVAR_CHANNEL	0
 #define	SCC_IVAR_CLASS		1
 #define	SCC_IVAR_CLOCK		2

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

@@ -34,6 +34,7 @@
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/queue.h>
+#include <sys/serial.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
@@ -53,10 +54,26 @@
 scc_bfe_intr(void *arg)
 {
 	struct scc_softc *sc = arg;
-	int ipend;
+	struct scc_chan *ch;
+	struct scc_class *cl;
+	struct scc_mode *m;
+	int c;
+
+	if (sc->sc_leaving)
+		return;
+
+	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 0
 		if (ipend & SER_INT_OVERRUN)
 			uart_intr_overrun(sc);
 		if (ipend & SER_INT_BREAK)
@@ -67,8 +84,8 @@
 			uart_intr_sigchg(sc);
 		if (ipend & SER_INT_TXIDLE)
 			uart_intr_txidle(sc);
+	}
 #endif
-	}
 }
 
 int
@@ -81,8 +98,8 @@
 	struct scc_softc *sc, *sc0;
 	const char *sep;
 	bus_space_handle_t bh;
-	u_long size, start;
-	int c, error, i, mode;
+	u_long base, size, start;
+	int c, error, mode, reset;
 
 	/*
 	 * The sc_class field defines the type of SCC we're going to work
@@ -99,6 +116,8 @@
 	} else
 		sc = sc0;
 
+	size = abs(cl->cl_range);
+
 	/*
 	 * Protect ourselves against interrupts while we're not completely
 	 * finished attaching and initializing. We don't expect interrupts
@@ -113,7 +132,7 @@
 	 * collected by scc_bfe_probe() intact.
 	 */
 	sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid,
-	    0, ~0, cl->cl_channels * cl->cl_range, RF_ACTIVE);
+	    0, ~0, cl->cl_channels * size, RF_ACTIVE);
 	if (sc->sc_rres == NULL)
 		return (ENXIO);
 	sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres);
@@ -121,11 +140,15 @@
 
 	sc->sc_irid = 0;
 	sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid,
-	    RF_ACTIVE);
+	    RF_ACTIVE | RF_SHAREABLE);
 	if (sc->sc_ires != NULL) {
+#if 0
 		error = BUS_SETUP_INTR(device_get_parent(dev), dev,
 		    sc->sc_ires, INTR_TYPE_TTY | INTR_FAST, scc_bfe_intr,
 		    sc, &sc->sc_icookie);
+#else
+		error = -1;
+#endif
 		if (error)
 			error = BUS_SETUP_INTR(device_get_parent(dev), dev,
 			    sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE,
@@ -145,7 +168,61 @@
 		sc->sc_polled = 1;
 	}
 
-	error = SCC_ATTACH(sc);
+	/*
+	 * Create the control structures for our children. Probe devices
+	 * and query them to see if we can reset the hardware.
+	 */
+	reset = 1;
+
+	sc->sc_chan = malloc(sizeof(struct scc_chan) * cl->cl_channels,
+	    M_SCC, M_WAITOK | M_ZERO);
+	base = rman_get_start(sc->sc_rres);
+	start = base + ((cl->cl_range < 0) ? size * (cl->cl_channels - 1) : 0);
+	for (c = 0; c < cl->cl_channels; c++) {
+		ch = &sc->sc_chan[c];
+		resource_list_init(&ch->ch_rlist);
+		ch->ch_nr = c + 1;
+
+		resource_list_add(&ch->ch_rlist, sc->sc_rtype, 0, start,
+		    start + size - 1, size);
+		rle = resource_list_find(&ch->ch_rlist, sc->sc_rtype, 0);
+		rle->res = &ch->ch_rres;
+		bus_space_subregion(rman_get_bustag(sc->sc_rres),
+		    rman_get_bushandle(sc->sc_rres), start - base, size, &bh);
+		rman_set_bushandle(rle->res, bh);
+		rman_set_bustag(rle->res, rman_get_bustag(sc->sc_rres));
+
+		resource_list_add(&ch->ch_rlist, SYS_RES_IRQ, 0, c, c, 1);
+		rle = resource_list_find(&ch->ch_rlist, SYS_RES_IRQ, 0);
+		rle->res = sc->sc_ires;
+
+		for (mode = 0; mode < SCC_NMODES; mode++) {
+			m = &ch->ch_mode[mode];
+			m->m_chan = ch;
+			m->m_mode = 1U << mode;
+			if ((cl->cl_modes & m->m_mode) == 0)
+				continue;
+			m->m_dev = device_add_child(dev, NULL, -1);
+			device_set_ivars(m->m_dev, (void *)m);
+			error = device_probe_child(dev, m->m_dev);
+			if (!error) {
+				m->m_probed = 1;
+				reset = (reset && SERDEV_RESET(m->m_dev));
+			}
+                }
+
+		start += (cl->cl_range < 0) ? -size : size;
+        }
+
+	/*
+	 * Have the hardware driver initialize the hardware. Tell it
+	 * whether or not a hardware reset should be performed.
+	 */
+	if (bootverbose) {
+		device_printf(dev, "%sresetting hardware\n",
+		    (reset) ? "" : "not ");
+	}
+	error = SCC_ATTACH(sc, reset);
 	if (error)
 		goto fail;
 
@@ -163,53 +240,30 @@
 		printf("\n");
 	}
 
+	/*
+	 * Attach all child devices that were probed successfully.
+	 */
+	for (c = 0; c < cl->cl_channels; c++) {
+		ch = &sc->sc_chan[c];
+		for (mode = 0; mode < SCC_NMODES; mode++) {
+			m = &ch->ch_mode[mode];
+			if (!m->m_probed)
+				continue;
+			error = device_attach(m->m_dev);
+			if (error)
+				continue;
+			m->m_attached = 1;
+			m->ih_break = SERDEV_IHAND(m->m_dev, SER_INT_BREAK);
+			m->ih_overrun = SERDEV_IHAND(m->m_dev, SER_INT_OVERRUN);
+			m->ih_rxready = SERDEV_IHAND(m->m_dev, SER_INT_RXREADY);
+			m->ih_sigchg = SERDEV_IHAND(m->m_dev, SER_INT_SIGCHG);
+			m->ih_txidle = SERDEV_IHAND(m->m_dev, SER_INT_TXIDLE);
+		}
+	}
+
 	sc->sc_leaving = 0;
 	scc_bfe_intr(sc);
 
-	STAILQ_INIT(&sc->sc_chan);
-	size = cl->cl_range;
-	start = rman_get_start(sc->sc_rres);
-	for (c = 1; c <= cl->cl_channels; c++) {
-		ch = malloc(sizeof(struct scc_chan), M_SCC, M_WAITOK | M_ZERO);
-		STAILQ_INSERT_TAIL(&sc->sc_chan, ch, ch_link);
-		resource_list_init(&ch->ch_rlist);
-		ch->ch_nr = c;
-
-		resource_list_add(&ch->ch_rlist, sc->sc_rtype, sc->sc_rrid,
-		    start, start + size - 1, size);
-		rle = resource_list_find(&ch->ch_rlist, sc->sc_rtype,
-		    sc->sc_rrid);
-		rle->res = malloc(sizeof(struct resource), M_SCC,
-		    M_WAITOK | M_ZERO);
-		bus_space_subregion(rman_get_bustag(sc->sc_rres),
-		    rman_get_bushandle(sc->sc_rres),
-		    start - rman_get_start(sc->sc_rres), size, &bh);
-		rman_set_bushandle(rle->res, bh);
-		rman_set_bustag(rle->res, rman_get_bustag(sc->sc_rres));
- 
-		resource_list_add(&ch->ch_rlist, SYS_RES_IRQ, sc->sc_irid,
-		    rman_get_start(sc->sc_ires), rman_get_end(sc->sc_ires), 1);
-		rle = resource_list_find(&ch->ch_rlist, SYS_RES_IRQ,
-		    sc->sc_irid);
-		rle->res = sc->sc_ires;
- 
-		STAILQ_INIT(&ch->ch_mode);
-		mode = cl->cl_modes;
-		while (mode != 0) {
-			m = malloc(sizeof(struct scc_mode), M_SCC,
-			    M_WAITOK | M_ZERO);
-			STAILQ_INSERT_TAIL(&ch->ch_mode, m, m_link);
-			m->m_chan = ch;
-			m->m_dev = device_add_child(dev, NULL, -1);
-			device_set_ivars(m->m_dev, (void *)m);
-			i = ffs(mode) - 1;
-			m->m_mode = 1 << i;
-			mode &= ~m->m_mode;
-			device_probe_and_attach(m->m_dev);
-		}
-
-		start += size >> cl->cl_regshft;
-	}
 	return (0);
 
  fail:
@@ -251,6 +305,8 @@
 	if (device_get_desc(dev) == NULL)
 		device_set_desc(dev, cl->name);
 
+	size = abs(cl->cl_range);
+
 	/*
 	 * Allocate the register resource. We assume that all SCCs have a
 	 * single register window in either I/O port space or memory mapped
@@ -259,14 +315,13 @@
 	 */
 	sc->sc_rrid = 0;
 	sc->sc_rtype = SYS_RES_MEMORY;
-	size = cl->cl_channels * cl->cl_range;
 	sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid,
-	    0, ~0, size, RF_ACTIVE);
+	    0, ~0, cl->cl_channels * size, RF_ACTIVE);
 	if (sc->sc_rres == NULL) {
 		sc->sc_rrid = 0;
 		sc->sc_rtype = SYS_RES_IOPORT;
 		sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype,
-		    &sc->sc_rrid, 0, ~0, size, RF_ACTIVE);
+		    &sc->sc_rrid, 0, ~0, cl->cl_channels * size, RF_ACTIVE);
 		if (sc->sc_rres == NULL)
 			return (ENXIO);
 	}
@@ -277,7 +332,7 @@
 	 */
 	sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres);
 	sc->sc_bas.bst = rman_get_bustag(sc->sc_rres);
-	sc->sc_bas.range = sc->sc_class->cl_range;
+	sc->sc_bas.range = size;
 	sc->sc_bas.rclk = sc->sc_class->cl_rclk;
 	sc->sc_bas.regshft = sc->sc_class->cl_regshft;
 
@@ -304,9 +359,10 @@
 
 	m = device_get_ivars(child);
 	ch = m->m_chan;
-	rle = resource_list_find(&ch->ch_rlist, type, *rid);
+	rle = resource_list_find(&ch->ch_rlist, type, 0);
 	if (rle == NULL)
 		return (NULL);
+	*rid = 0;
 	return (rle->res);
 }
 
@@ -392,8 +448,16 @@
 scc_bus_setup_intr(device_t dev, device_t child, struct resource *r, int flags,
     void (*ihand)(void *), void *arg, void **cookiep)
 {
+	struct scc_mode *m;
+
+	/* We don't support any grandchildren or children thereof. */
+	if (device_get_parent(child) != dev)
+		return (EINVAL);
 
-	return (ENXIO);
+	m = device_get_ivars(child);
+	m->ih = ihand;
+	m->ih_arg = arg;
+	return (0);
 }
 
 int

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

@@ -32,6 +32,7 @@
 #include <sys/bus.h>
 #include <sys/conf.h>
 #include <machine/bus.h>
+#include <sys/rman.h>
 #include <sys/serial.h>
 
 #include <dev/scc/scc_bfe.h>
@@ -43,7 +44,7 @@
 
 #define	DEFAULT_RCLK	29491200
 
-static int sab82532_bfe_attach(struct scc_softc *);
+static int sab82532_bfe_attach(struct scc_softc *, int);
 static int sab82532_bfe_ipend(struct scc_softc *);
 static int sab82532_bfe_probe(struct scc_softc *);
 
@@ -67,7 +68,7 @@
 };
 
 static int
-sab82532_bfe_attach(struct scc_softc *sc)
+sab82532_bfe_attach(struct scc_softc *sc, int reset)
 {
 	struct scc_bas *bas;
 

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

@@ -32,6 +32,7 @@
 #include <sys/bus.h>
 #include <sys/conf.h>
 #include <machine/bus.h>
+#include <sys/rman.h>
 #include <sys/serial.h>
 
 #include <dev/scc/scc_bfe.h>
@@ -43,7 +44,7 @@
 
 #define	DEFAULT_RCLK	307200
 
-static int z8530_bfe_attach(struct scc_softc *);
+static int z8530_bfe_attach(struct scc_softc *, int);
 static int z8530_bfe_ipend(struct scc_softc *);
 static int z8530_bfe_probe(struct scc_softc *);
 
@@ -61,7 +62,7 @@
 	.cl_channels = 2,
 	.cl_class = SCC_CLASS_Z8530,
 	.cl_modes = SCC_MODE_ASYNC | SCC_MODE_BISYNC | SCC_MODE_HDLC,
-	.cl_range = CHAN_A - CHAN_B,
+	.cl_range = (CHAN_B - CHAN_A) << 1,
 	.cl_rclk = DEFAULT_RCLK,
 	.cl_regshft = 1,
 };
@@ -86,7 +87,7 @@
 }
 
 static int
-z8530_bfe_attach(struct scc_softc *sc)
+z8530_bfe_attach(struct scc_softc *sc, int reset)
 {
 	struct scc_bas *bas;
 

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

@@ -25,10 +25,11 @@
 # $FreeBSD$
 
 #include <sys/param.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
-#include <sys/bus.h>
-#include <machine/bus.h>
+#include <sys/rman.h>
 #include <dev/scc/scc_bfe.h>
 
 # The SCC hardware interface. The core SCC code is hardware independent.
@@ -40,8 +41,12 @@
 # This method is called when the device is being attached. All resources
 # have been allocated. The intend of this method is to setup the hardware
 # for normal operation.
+# The reset parameter informs the hardware driver whether a full device
+# reset is allowed or not. This is important when one of the channels can
+# be used as system console and a hardware reset would disrupt output.
 METHOD int attach {
 	struct scc_softc *this;
+	int reset;
 };
 
 # ipend() - query SCC for pending interrupts.

==== //depot/projects/uart/dev/uart/uart_bus.h#36 (text+ko) ====

@@ -144,7 +144,9 @@
 
 int uart_bus_attach(device_t dev);
 int uart_bus_detach(device_t dev);
+driver_intr_t *uart_bus_ihand(device_t dev, int ipend);
 int uart_bus_probe(device_t dev, int regshft, int rclk, int rid, int chan);
+int uart_bus_reset(device_t dev);
 
 int uart_tty_attach(struct uart_softc *);
 int uart_tty_detach(struct uart_softc *);

==== //depot/projects/uart/dev/uart/uart_bus_scc.c#4 (text+ko) ====

@@ -50,6 +50,9 @@
 	DEVMETHOD(device_probe,		uart_scc_probe),
 	DEVMETHOD(device_attach,	uart_bus_attach),
 	DEVMETHOD(device_detach,	uart_bus_detach),
+	/* Serdev interface */
+	DEVMETHOD(serdev_ihand,		uart_bus_ihand),
+	DEVMETHOD(serdev_reset,		uart_bus_reset),
 	{ 0, 0 }
 };
 

==== //depot/projects/uart/dev/uart/uart_core.c#41 (text+ko) ====

@@ -225,6 +225,36 @@
 		swi_sched(sc->sc_softih, 0);
 }
 
+driver_intr_t *
+uart_bus_ihand(device_t dev, int ipend)
+{
+
+	switch (ipend) {
+	case SER_INT_BREAK:
+		return (uart_intr_break);
+	case SER_INT_OVERRUN:
+		return (uart_intr_overrun);
+	case SER_INT_RXREADY:
+		return (uart_intr_rxready);
+	case SER_INT_SIGCHG:
+		return (uart_intr_sigchg);
+	case SER_INT_TXIDLE:
+		return (uart_intr_txidle);
+	}
+	return (NULL);
+}
+
+int
+uart_bus_reset(device_t dev)
+{
+	struct uart_softc *sc;
+	int reset;
+
+	sc = device_get_softc(dev);
+	reset = (sc->sc_sysdev == NULL) ? 1 : 0;
+	return (reset);
+}
+
 int
 uart_bus_probe(device_t dev, int regshft, int rclk, int rid, int chan)
 {


More information about the p4-projects mailing list