diff --git a/sys/dev/ic/ns16550.h b/sys/dev/ic/ns16550.h index 659f59184970..33a7dd14b82b 100644 --- a/sys/dev/ic/ns16550.h +++ b/sys/dev/ic/ns16550.h @@ -185,6 +185,7 @@ #define DW_REG_USR 31 /* DesignWare derived Uart Status Reg */ #define com_usr 39 /* Octeon 16750/16550 Uart Status Reg */ #define REG_USR com_usr +#define USR_BUSY 1 /* Uart Busy. Serial transfer in progress */ #define USR_TXFIFO_NOTFULL 2 /* Uart TX FIFO Not full */ /* 16950 register #1. Access enabled by ACR[7]. Also requires !LCR[7]. */ diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c index 211d11321860..249be4ce0e33 100644 --- a/sys/dev/uart/uart_dev_ns8250.c +++ b/sys/dev/uart/uart_dev_ns8250.c @@ -647,11 +647,35 @@ int ns8250_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { + struct ns8250_softc *ns8250; struct uart_bas *bas; - int error; + int error, limit; + ns8250 = (struct ns8250_softc*)sc; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); + /* + * When using DW UART with BUSY detection it is necessary to wait + * until all serial transfers are finished before manipulating the + * line control. LCR will not be affected when UART is busy. + */ + if (ns8250->busy_detect != 0) { + /* + * Pick an arbitrary high limit to avoid getting stuck in + * an infinite loop in case when the hardware is broken. + */ + limit = 10 * 1024; + while (((uart_getreg(bas, DW_REG_USR) & USR_BUSY) != 0) && + --limit) + DELAY(4); + + if (limit <= 0) { + /* UART appears to be stuck */ + uart_unlock(sc->sc_hwmtx); + return (EIO); + } + } + error = ns8250_param(bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error);