From owner-p4-projects@FreeBSD.ORG Thu Feb 16 21:03:26 2006 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 6040116A423; Thu, 16 Feb 2006 21:03:26 +0000 (GMT) X-Original-To: perforce@freebsd.org Delivered-To: perforce@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 185B416A422 for ; Thu, 16 Feb 2006 21:03:26 +0000 (GMT) (envelope-from marcel@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [216.136.204.115]) by mx1.FreeBSD.org (Postfix) with ESMTP id 8DA5C43D49 for ; Thu, 16 Feb 2006 21:03:25 +0000 (GMT) (envelope-from marcel@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.1/8.13.1) with ESMTP id k1GL3P3x060693 for ; Thu, 16 Feb 2006 21:03:25 GMT (envelope-from marcel@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.1/8.13.1/Submit) id k1GL3PjJ060690 for perforce@freebsd.org; Thu, 16 Feb 2006 21:03:25 GMT (envelope-from marcel@freebsd.org) Date: Thu, 16 Feb 2006 21:03:25 GMT Message-Id: <200602162103.k1GL3PjJ060690@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to marcel@freebsd.org using -f From: Marcel Moolenaar To: Perforce Change Reviews Cc: Subject: PERFORCE change 91901 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 16 Feb 2006 21:03:27 -0000 http://perforce.freebsd.org/chv.cgi?CH=91901 Change 91901 by marcel@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 + #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 #include #include +#include #include #include @@ -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 #include #include +#include #include #include @@ -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 #include #include +#include #include #include @@ -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 +#include +#include #include #include -#include -#include +#include #include # 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) {