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