summaryrefslogtreecommitdiffstats
path: root/sys/dev/uart/uart_dev_z8530.c
diff options
context:
space:
mode:
authormarcel <marcel@FreeBSD.org>2004-05-04 06:58:10 +0000
committermarcel <marcel@FreeBSD.org>2004-05-04 06:58:10 +0000
commitc0e815bd411a2af70011f42f877d762e8ad78b98 (patch)
tree0cbf1f52b8d84ae0300f5723dc51b83abb2590b5 /sys/dev/uart/uart_dev_z8530.c
parent87d450b90b0c650b0715e2913d283de246c60de0 (diff)
downloadFreeBSD-src-c0e815bd411a2af70011f42f877d762e8ad78b98.zip
FreeBSD-src-c0e815bd411a2af70011f42f877d762e8ad78b98.tar.gz
Fix hangs caused by z8530_bus_ipend() returning UART_IPEND_TXIDLE
not as a pending interrupt status, but as a matter of status quo. Consequently, when there's no data to be transmitted the condition is not cleared and uart_intr() is stuck in an infinite loop trying to clear the UART_IPEND_TXIDLE status. The z8530_bus_ipend() function is changed to return idle only once after having sent any data. The root cause for this problem is that we cannot use the interrupt status bits of the SCC itself. The register that holds the interrupt status can only be accessed by channel A and holds the status for both channels. Using the interrupt status register would complicate the driver because we need to synchronize access to the SCC between the channels. Elementary testing: marius
Diffstat (limited to 'sys/dev/uart/uart_dev_z8530.c')
-rw-r--r--sys/dev/uart/uart_dev_z8530.c8
1 files changed, 7 insertions, 1 deletions
diff --git a/sys/dev/uart/uart_dev_z8530.c b/sys/dev/uart/uart_dev_z8530.c
index 25bdca7..4c7c45f 100644
--- a/sys/dev/uart/uart_dev_z8530.c
+++ b/sys/dev/uart/uart_dev_z8530.c
@@ -244,6 +244,7 @@ z8530_getc(struct uart_bas *bas)
struct z8530_softc {
struct uart_softc base;
uint8_t tpc;
+ uint8_t txidle;
};
static int z8530_bus_attach(struct uart_softc *);
@@ -305,6 +306,7 @@ z8530_bus_attach(struct uart_softc *sc)
z8530->tpc = z8530_setup(bas, 9600, 8, 1, UART_PARITY_NONE);
z8530->tpc &= ~(TPC_DTR|TPC_RTS);
}
+ z8530->txidle = 1; /* Report UART_IPEND_TXIDLE. */
sc->sc_rxfifosz = 3;
sc->sc_txfifosz = 1;
@@ -385,6 +387,7 @@ z8530_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
static int
z8530_bus_ipend(struct uart_softc *sc)
{
+ struct z8530_softc *z8530 = (struct z8530_softc*)sc;
struct uart_bas *bas;
int ipend;
uint32_t sig;
@@ -400,9 +403,10 @@ z8530_bus_ipend(struct uart_softc *sc)
uart_setreg(bas, REG_CTRL, CR_RSTXSI);
ipend |= UART_IPEND_BREAK;
}
- if (bes & BES_TXE) {
+ if (bes & BES_TXE && z8530->txidle) {
uart_setreg(bas, REG_CTRL, CR_RSTTXI);
ipend |= UART_IPEND_TXIDLE;
+ z8530->txidle = 0; /* Suppress UART_IPEND_TXIDLE. */
}
if (bes & BES_RXA)
ipend |= UART_IPEND_RXREADY;
@@ -533,6 +537,7 @@ z8530_bus_setsig(struct uart_softc *sc, int sig)
static int
z8530_bus_transmit(struct uart_softc *sc)
{
+ struct z8530_softc *z8530 = (struct z8530_softc*)sc;
struct uart_bas *bas;
bas = &sc->sc_bas;
@@ -542,6 +547,7 @@ z8530_bus_transmit(struct uart_softc *sc)
uart_setreg(bas, REG_DATA, sc->sc_txbuf[0]);
uart_barrier(bas);
sc->sc_txbusy = 1;
+ z8530->txidle = 1; /* Report UART_IPEND_TXIDLE again. */
mtx_unlock_spin(&sc->sc_hwmtx);
return (0);
}
OpenPOWER on IntegriCloud