diff options
author | marcel <marcel@FreeBSD.org> | 2006-03-30 18:37:03 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2006-03-30 18:37:03 +0000 |
commit | 11ffb007e1549b15e2bf78d86bac71a035dbfe40 (patch) | |
tree | 2ef44b482f61d5295bba3a89871f7ab766e70886 /sys/dev/uart/uart_core.c | |
parent | 0215f8085c24d9281307a8a89dd8c19cbd285316 (diff) | |
download | FreeBSD-src-11ffb007e1549b15e2bf78d86bac71a035dbfe40.zip FreeBSD-src-11ffb007e1549b15e2bf78d86bac71a035dbfe40.tar.gz |
Add support for scc(4).
Diffstat (limited to 'sys/dev/uart/uart_core.c')
-rw-r--r-- | sys/dev/uart/uart_core.c | 125 |
1 files changed, 95 insertions, 30 deletions
diff --git a/sys/dev/uart/uart_core.c b/sys/dev/uart/uart_core.c index 9e4a06d..d3ff9e7 100644 --- a/sys/dev/uart/uart_core.c +++ b/sys/dev/uart/uart_core.c @@ -71,6 +71,24 @@ uart_add_sysdev(struct uart_devinfo *di) } /* + * Schedule a soft interrupt. We do this on the 0 to !0 transition + * of the TTY pending interrupt status. + */ +static void +uart_sched_softih(struct uart_softc *sc, uint32_t ipend) +{ + uint32_t new, old; + + do { + old = sc->sc_ttypend; + new = old | ipend; + } while (!atomic_cmpset_32(&sc->sc_ttypend, old, new)); + + if ((old & SER_INT_MASK) == 0) + swi_sched(sc->sc_softih, 0); +} + +/* * A break condition has been detected. We treat the break condition as * a special case that should not happen during normal operation. When * the break condition is to be passed to higher levels in the form of @@ -79,18 +97,20 @@ uart_add_sysdev(struct uart_devinfo *di) * the exceptional nature of the break condition, so we permit ourselves * to be sloppy. */ -static void -uart_intr_break(struct uart_softc *sc) +static __inline int +uart_intr_break(void *arg) { + struct uart_softc *sc = arg; #if defined(KDB) && defined(BREAK_TO_DEBUGGER) if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { kdb_enter("Line break on console"); - return; + return (0); } #endif if (sc->sc_opened) - atomic_set_32(&sc->sc_ttypend, SER_INT_BREAK); + uart_sched_softih(sc, SER_INT_BREAK); + return (0); } /* @@ -108,25 +128,28 @@ uart_intr_break(struct uart_softc *sc) * token represents the loss of at least one, but possible more bytes in * the input stream. */ -static void -uart_intr_overrun(struct uart_softc *sc) +static __inline int +uart_intr_overrun(void *arg) { + struct uart_softc *sc = arg; if (sc->sc_opened) { UART_RECEIVE(sc); if (uart_rx_put(sc, UART_STAT_OVERRUN)) sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; - atomic_set_32(&sc->sc_ttypend, SER_INT_RXREADY); + uart_sched_softih(sc, SER_INT_RXREADY); } UART_FLUSH(sc, UART_FLUSH_RECEIVER); + return (0); } /* * Received data ready. */ -static void -uart_intr_rxready(struct uart_softc *sc) +static __inline int +uart_intr_rxready(void *arg) { + struct uart_softc *sc = arg; int rxp; rxp = sc->sc_rxput; @@ -142,9 +165,10 @@ uart_intr_rxready(struct uart_softc *sc) } #endif if (sc->sc_opened) - atomic_set_32(&sc->sc_ttypend, SER_INT_RXREADY); + uart_sched_softih(sc, SER_INT_RXREADY); else sc->sc_rxput = sc->sc_rxget; /* Ignore received data. */ + return (1); } /* @@ -154,9 +178,10 @@ uart_intr_rxready(struct uart_softc *sc) * bits. This is to avoid loosing state transitions due to having more * than 1 hardware interrupt between software interrupts. */ -static void -uart_intr_sigchg(struct uart_softc *sc) +static __inline int +uart_intr_sigchg(void *arg) { + struct uart_softc *sc = arg; int new, old, sig; sig = UART_GETSIG(sc); @@ -169,24 +194,37 @@ uart_intr_sigchg(struct uart_softc *sc) } } + /* + * Keep track of signal changes, even when the device is not + * opened. This allows us to inform upper layers about a + * possible loss of DCD and thus the existence of a (possibly) + * different connection when we have DCD back, during the time + * that the device was closed. + */ do { old = sc->sc_ttypend; new = old & ~SER_MASK_STATE; new |= sig & SER_INT_SIGMASK; - new |= SER_INT_SIGCHG; } while (!atomic_cmpset_32(&sc->sc_ttypend, old, new)); + + if (sc->sc_opened) + uart_sched_softih(sc, SER_INT_SIGCHG); + return (1); } /* * The transmitter can accept more data. */ -static void -uart_intr_txidle(struct uart_softc *sc) +static __inline int +uart_intr_txidle(void *arg) { + struct uart_softc *sc = arg; + if (sc->sc_txbusy) { sc->sc_txbusy = 0; - atomic_set_32(&sc->sc_ttypend, SER_INT_TXIDLE); + uart_sched_softih(sc, SER_INT_TXIDLE); } + return (0); } static void @@ -195,13 +233,7 @@ uart_intr(void *arg) struct uart_softc *sc = arg; int ipend; - if (sc->sc_leaving) - return; - - do { - ipend = UART_IPEND(sc); - if (ipend == 0) - break; + while (!sc->sc_leaving && (ipend = UART_IPEND(sc)) != 0) { if (ipend & SER_INT_OVERRUN) uart_intr_overrun(sc); if (ipend & SER_INT_BREAK) @@ -212,10 +244,35 @@ uart_intr(void *arg) uart_intr_sigchg(sc); if (ipend & SER_INT_TXIDLE) uart_intr_txidle(sc); - } while (1); + } +} - if (sc->sc_opened && sc->sc_ttypend != 0) - swi_sched(sc->sc_softih, 0); +serdev_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_sysdev(device_t dev) +{ + struct uart_softc *sc; + + sc = device_get_softc(dev); + return ((sc->sc_sysdev != NULL) ? 1 : 0); } int @@ -313,7 +370,9 @@ uart_bus_attach(device_t dev) */ sc->sc_leaving = 1; - mtx_init(&sc->sc_hwmtx, "uart_hwmtx", NULL, MTX_SPIN); + mtx_init(&sc->sc_hwmtx_s, "uart_hwmtx", NULL, MTX_SPIN); + if (sc->sc_hwmtx == NULL) + sc->sc_hwmtx = &sc->sc_hwmtx_s; /* * Re-allocate. We expect that the softc contains the information @@ -322,7 +381,7 @@ uart_bus_attach(device_t dev) sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid, 0, ~0, sc->sc_class->uc_range, RF_ACTIVE); if (sc->sc_rres == NULL) { - mtx_destroy(&sc->sc_hwmtx); + mtx_destroy(&sc->sc_hwmtx_s); return (ENXIO); } sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); @@ -425,6 +484,9 @@ uart_bus_attach(device_t dev) if (error) goto fail; + if (sc->sc_sysdev != NULL) + sc->sc_sysdev->hwmtx = sc->sc_hwmtx; + sc->sc_leaving = 0; uart_intr(sc); return (0); @@ -440,7 +502,7 @@ uart_bus_attach(device_t dev) } bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); - mtx_destroy(&sc->sc_hwmtx); + mtx_destroy(&sc->sc_hwmtx_s); return (error); } @@ -454,6 +516,9 @@ uart_bus_detach(device_t dev) sc->sc_leaving = 1; + if (sc->sc_sysdev != NULL) + sc->sc_sysdev->hwmtx = NULL; + UART_DETACH(sc); if (sc->sc_sysdev != NULL && sc->sc_sysdev->detach != NULL) @@ -471,7 +536,7 @@ uart_bus_detach(device_t dev) } bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); - mtx_destroy(&sc->sc_hwmtx); + mtx_destroy(&sc->sc_hwmtx_s); if (sc->sc_class->size > sizeof(*sc)) { device_set_softc(dev, NULL); |