summaryrefslogtreecommitdiffstats
path: root/sys/dev/uart
diff options
context:
space:
mode:
authormarcel <marcel@FreeBSD.org>2003-09-17 03:11:32 +0000
committermarcel <marcel@FreeBSD.org>2003-09-17 03:11:32 +0000
commit920cfd2329dab9db239d8e448364bc546c00cc92 (patch)
tree4391e07d78aa2696fba66501852f450d6193838e /sys/dev/uart
parentd07df62911128d99b66cd2768f1fbe4ae118b8a5 (diff)
downloadFreeBSD-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.c26
-rw-r--r--sys/dev/uart/uart_dev_i8251.c15
-rw-r--r--sys/dev/uart/uart_dev_ns8250.c15
-rw-r--r--sys/dev/uart/uart_dev_z8530.c20
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);
OpenPOWER on IntegriCloud