Disabling RS485 during a transmission keeps it running. If RS485 is enabled again before it ends, the state is OFF while data is still going out. The TX path does not end a transmission in state OFF, so nothing sets the idle RTS level afterwards and the transceiver keeps driving the bus. Hand such a transmission back to RS485 when it is enabled on an open port. Assert RTS and mark it as sending, so that the TX path ends it like any other. With only the FIFO left or output stopped, the TX path won't run again, so drain the FIFO from trigger_stop_tx instead. Data queued while output is stopped is not part of the transmission. Pause DMA first, as output is marked stopped before the driver pauses it. Fixes: 97f85204275a ("serial: amba-pl011: keep transmitter enabled when disabling RS485") Cc: stable@vger.kernel.org # 6.18.x: 1b6d660e038b: serial: amba-pl011: drive the bus during break in RS485 mode Cc: stable@vger.kernel.org Signed-off-by: Nicolai Buchwitz --- drivers/tty/serial/amba-pl011.c | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 79776a882ae35..3b9cce435c650 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1734,6 +1734,18 @@ static unsigned int pl011_tx_empty(struct uart_port *port) 0 : TIOCSER_TEMT; } +/* While output is stopped, only data already in the FIFO counts */ +static bool pl011_tx_pending(struct uart_amba_port *uap, bool stopped) +{ + struct tty_port *tport = &uap->port.state->port; + + if (!pl011_tx_empty(&uap->port)) + return true; + + return !stopped && + (!kfifo_is_empty(&tport->xmit_fifo) || pl011_dma_tx_queued(uap)); +} + static void pl011_maybe_set_bit(bool cond, unsigned int *ptr, unsigned int mask) { if (cond) @@ -2454,13 +2466,56 @@ static int pl011_verify_port(struct uart_port *port, struct serial_struct *ser) return ret; } +/* + * Data from before RS485 was enabled is still going out. Finish it as an + * RS485 transmission, so that its end sets the idle level. + */ +static void pl011_rs485_tx_resume(struct uart_amba_port *uap, + const struct serial_rs485 *rs485, bool stopped) +{ + struct tty_port *tport = &uap->port.state->port; + u32 cr = pl011_read(uap, REG_CR); + + cr |= UART011_CR_TXE; + if (!(rs485->flags & SER_RS485_RX_DURING_TX)) + cr &= ~UART011_CR_RXE; + if (rs485->flags & SER_RS485_RTS_ON_SEND) + cr &= ~UART011_CR_RTS; + else + cr |= UART011_CR_RTS; + pl011_write(cr, uap, REG_CR); + + /* + * With only the FIFO left or output stopped, the TX path won't end it. + * The timer does, once the new settings are stored. + */ + if (stopped || + (kfifo_is_empty(&tport->xmit_fifo) && !pl011_dma_tx_queued(uap))) { + uap->rs485_tx_state = WAIT_AFTER_SEND; + hrtimer_start(&uap->trigger_stop_tx, uap->rs485_tx_drain_interval, + HRTIMER_MODE_REL); + } else { + uap->rs485_tx_state = SEND; + } +} + static int pl011_rs485_config(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485) { struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); + bool tx_stopped = uart_tx_stopped(port); uap->rs485_break = false; + + /* + * Output is marked stopped before stop_tx pauses DMA. Pause it now, so + * that clearing TXE below cannot strand DMA data in the FIFO. Output + * stopped after this check is ended by stop_tx. + */ + if ((rs485->flags & SER_RS485_ENABLED) && tx_stopped) + pl011_dma_tx_stop(uap); + if ((port->rs485.flags & SER_RS485_ENABLED) && !(rs485->flags & SER_RS485_ENABLED)) { bool tx_pending = uap->rs485_tx_state == WAIT_AFTER_RTS; @@ -2483,6 +2538,11 @@ static int pl011_rs485_config(struct uart_port *port, struct ktermios *termios, if (tx_pending && !uart_tx_stopped(port) && !pl011_dma_tx_start(uap)) pl011_start_tx_pio(uap); + } else if ((rs485->flags & SER_RS485_ENABLED) && + uap->rs485_tx_state == OFF && + tty_port_initialized(&port->state->port) && + pl011_tx_pending(uap, tx_stopped)) { + pl011_rs485_tx_resume(uap, rs485, tx_stopped); } else if (port->rs485.flags & SER_RS485_ENABLED) { pl011_rs485_tx_stop(uap); } -- 2.53.0 Changing the RS485 settings while RS485 stays enabled calls pl011_rs485_tx_stop(), also while a transmission is in progress. Unlike pl011_stop_tx() it leaves the TX interrupt enabled, so the remaining data is still pushed into the FIFO after the drain turned off the transmitter. The FIFO never empties, the port stalls, and closing it hangs in pl011_dma_shutdown() waiting for the transmitter to go idle. A start still waiting for delay_rts_before_send is dropped as well. Only stop the transmitter when no transmission is in progress. The call is still needed there, it sets the idle RTS level when the port is configured or resumed. A transmission in progress sets that level when it ends. An active break counts as a transmission too. Forget it only when leaving RS485 mode, so that pl011_break_ctl() still ends the transmission when the break ends. Fixes: 8d479237727c ("serial: amba-pl011: add RS485 support") Cc: stable@vger.kernel.org # 6.18.x Signed-off-by: Nicolai Buchwitz --- drivers/tty/serial/amba-pl011.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 3b9cce435c650..036a7245f1f29 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2506,8 +2506,6 @@ static int pl011_rs485_config(struct uart_port *port, struct ktermios *termios, container_of(port, struct uart_amba_port, port); bool tx_stopped = uart_tx_stopped(port); - uap->rs485_break = false; - /* * Output is marked stopped before stop_tx pauses DMA. Pause it now, so * that clearing TXE below cannot strand DMA data in the FIFO. Output @@ -2521,6 +2519,8 @@ static int pl011_rs485_config(struct uart_port *port, struct ktermios *termios, bool tx_pending = uap->rs485_tx_state == WAIT_AFTER_RTS; u32 cr = pl011_read(uap, REG_CR); + uap->rs485_break = false; + /* * Leaving RS485 mode, which only enables the transmitter while * sending. Don't stop it, as that stalls a DMA transfer still @@ -2543,7 +2543,9 @@ static int pl011_rs485_config(struct uart_port *port, struct ktermios *termios, tty_port_initialized(&port->state->port) && pl011_tx_pending(uap, tx_stopped)) { pl011_rs485_tx_resume(uap, rs485, tx_stopped); - } else if (port->rs485.flags & SER_RS485_ENABLED) { + } else if ((port->rs485.flags & SER_RS485_ENABLED) && + uap->rs485_tx_state == OFF) { + /* Set the idle level now, a transmission sets it when done */ pl011_rs485_tx_stop(uap); } -- 2.53.0