diff options
author | marcel <marcel@FreeBSD.org> | 2003-09-17 03:11:32 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2003-09-17 03:11:32 +0000 |
commit | 920cfd2329dab9db239d8e448364bc546c00cc92 (patch) | |
tree | 4391e07d78aa2696fba66501852f450d6193838e /sys/dev/uart | |
parent | d07df62911128d99b66cd2768f1fbe4ae118b8a5 (diff) | |
download | FreeBSD-src-920cfd2329dab9db239d8e448364bc546c00cc92.zip FreeBSD-src-920cfd2329dab9db239d8e448364bc546c00cc92.tar.gz |
In uart_intr() loop until all interrupts have been handled. Previously
an UART interface could get stuck when a new interrupt condition
arose while servicing a previous interrupt. Since an interrupt was
already pending, no new interrupt would be triggered.
Avoid infinite recursion by flushing the Rx FIFO and marking an
overrun condition when we could not move the data from the Rx
FIFO to the receive buffer in toto. Failure to flush the Rx FIFO
would leave the Rx ready condition pending.
Note that the SAB 82532 already did this due to the nature of the
chip.
Diffstat (limited to 'sys/dev/uart')
-rw-r--r-- | sys/dev/uart/uart_core.c | 26 | ||||
-rw-r--r-- | sys/dev/uart/uart_dev_i8251.c | 15 | ||||
-rw-r--r-- | sys/dev/uart/uart_dev_ns8250.c | 15 | ||||
-rw-r--r-- | sys/dev/uart/uart_dev_z8530.c | 20 |
4 files changed, 57 insertions, 19 deletions
diff --git a/sys/dev/uart/uart_core.c b/sys/dev/uart/uart_core.c index dd4f91c..5889211 100644 --- a/sys/dev/uart/uart_core.c +++ b/sys/dev/uart/uart_core.c @@ -200,17 +200,21 @@ uart_intr(void *arg) if (sc->sc_leaving) return; - ipend = UART_IPEND(sc); - if (ipend & UART_IPEND_OVERRUN) - uart_intr_overrun(sc); - if (ipend & UART_IPEND_BREAK) - uart_intr_break(sc); - if (ipend & UART_IPEND_RXREADY) - uart_intr_rxready(sc); - if (ipend & UART_IPEND_SIGCHG) - uart_intr_sigchg(sc); - if (ipend & UART_IPEND_TXIDLE) - uart_intr_txidle(sc); + do { + ipend = UART_IPEND(sc); + if (ipend == 0) + break; + if (ipend & UART_IPEND_OVERRUN) + uart_intr_overrun(sc); + if (ipend & UART_IPEND_BREAK) + uart_intr_break(sc); + if (ipend & UART_IPEND_RXREADY) + uart_intr_rxready(sc); + if (ipend & UART_IPEND_SIGCHG) + uart_intr_sigchg(sc); + if (ipend & UART_IPEND_TXIDLE) + uart_intr_txidle(sc); + } while (1); if (sc->sc_opened && sc->sc_ttypend != 0) swi_sched(sc->sc_softih, 0); diff --git a/sys/dev/uart/uart_dev_i8251.c b/sys/dev/uart/uart_dev_i8251.c index 9e9a5b2..0dd3afb 100644 --- a/sys/dev/uart/uart_dev_i8251.c +++ b/sys/dev/uart/uart_dev_i8251.c @@ -680,16 +680,25 @@ i8251_bus_receive(struct uart_softc *sc) bas = &sc->sc_bas; mtx_lock_spin(&sc->sc_hwmtx); - while (!uart_rx_full(sc)) { - lsr = uart_getreg(bas, REG_LSR); - if ((lsr & LSR_RXRDY) == 0) + lsr = uart_getreg(bas, REG_LSR); + while (lsr & LSR_RXRDY) { + if (uart_rx_full(sc)) { + sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; + } xc = uart_getreg(bas, REG_DATA); if (lsr & LSR_FE) xc |= UART_STAT_FRAMERR; if (lsr & LSR_PE) xc |= UART_STAT_PARERR; uart_rx_put(sc, xc); + lsr = uart_getreg(bas, REG_LSR); + } + /* Discard everything left in the Rx FIFO. */ + while (lsr & LSR_RXRDY) { + (void)uart_getreg(bas, REG_DATA); + uart_barrier(bas); + lsr = uart_getreg(bas, REG_LSR); } mtx_unlock_spin(&sc->sc_hwmtx); return (0); diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c index 7c4d264..44d1304 100644 --- a/sys/dev/uart/uart_dev_ns8250.c +++ b/sys/dev/uart/uart_dev_ns8250.c @@ -716,16 +716,25 @@ ns8250_bus_receive(struct uart_softc *sc) bas = &sc->sc_bas; mtx_lock_spin(&sc->sc_hwmtx); - while (!uart_rx_full(sc)) { - lsr = uart_getreg(bas, REG_LSR); - if ((lsr & LSR_RXRDY) == 0) + lsr = uart_getreg(bas, REG_LSR); + while (lsr & LSR_RXRDY) { + if (uart_rx_full(sc)) { + sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; + } xc = uart_getreg(bas, REG_DATA); if (lsr & LSR_FE) xc |= UART_STAT_FRAMERR; if (lsr & LSR_PE) xc |= UART_STAT_PARERR; uart_rx_put(sc, xc); + lsr = uart_getreg(bas, REG_LSR); + } + /* Discard everything left in the Rx FIFO. */ + while (lsr & LSR_RXRDY) { + (void)uart_getreg(bas, REG_DATA); + uart_barrier(bas); + lsr = uart_getreg(bas, REG_LSR); } mtx_unlock_spin(&sc->sc_hwmtx); return (0); diff --git a/sys/dev/uart/uart_dev_z8530.c b/sys/dev/uart/uart_dev_z8530.c index 319783a..eda35b8 100644 --- a/sys/dev/uart/uart_dev_z8530.c +++ b/sys/dev/uart/uart_dev_z8530.c @@ -458,7 +458,11 @@ z8530_bus_receive(struct uart_softc *sc) bas = &sc->sc_bas; mtx_lock_spin(&sc->sc_hwmtx); bes = uart_getmreg(bas, RR_BES); - while ((bes & BES_RXA) && !uart_rx_full(sc)) { + while (bes & BES_RXA) { + if (uart_rx_full(sc)) { + sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; + break; + } src = uart_getmreg(bas, RR_SRC); xc = uart_getreg(bas, REG_DATA); if (src & SRC_FE) @@ -466,8 +470,20 @@ z8530_bus_receive(struct uart_softc *sc) if (src & SRC_PE) xc |= UART_STAT_PARERR; uart_rx_put(sc, xc); - if (src & (SRC_FE | SRC_PE)) + if (src & (SRC_FE | SRC_PE)) { uart_setreg(bas, REG_CTRL, CR_RSTERR); + uart_barrier(bas); + } + bes = uart_getmreg(bas, RR_BES); + } + /* Discard everything left in the Rx FIFO. */ + while (bes & BES_RXA) { + src = uart_getmreg(bas, RR_SRC); + (void)uart_getreg(bas, REG_DATA); + if (src & (SRC_FE | SRC_PE)) { + uart_setreg(bas, REG_CTRL, CR_RSTERR); + uart_barrier(bas); + } bes = uart_getmreg(bas, RR_BES); } mtx_unlock_spin(&sc->sc_hwmtx); |