diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 3edd83ec73de..79776a882ae3 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -327,6 +327,7 @@ struct uart_amba_port { char type[12]; ktime_t rs485_tx_drain_interval; /* nano */ enum pl011_rs485_tx_state rs485_tx_state; + bool rs485_break; struct hrtimer trigger_start_tx; struct hrtimer trigger_stop_tx; bool console_line_ended; @@ -1279,6 +1280,11 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) return uap->using_rx_dma && uap->dmarx.running; } +static inline bool pl011_dma_tx_queued(struct uart_amba_port *uap) +{ + return uap->dmatx.queued; +} + #else /* Blank functions if the DMA engine is not available */ static inline void pl011_dma_remove(struct uart_amba_port *uap) @@ -1330,6 +1336,11 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) return false; } +static inline bool pl011_dma_tx_queued(struct uart_amba_port *uap) +{ + return false; +} + #define pl011_dma_flush_buffer NULL #endif @@ -1357,6 +1368,10 @@ static void pl011_rs485_tx_stop(struct uart_amba_port *uap) { struct uart_port *port = &uap->port; + /* pl011_break_ctl() stops the transmission when the break ends */ + if (uap->rs485_break) + return; + if (uap->rs485_tx_state == SEND) uap->rs485_tx_state = WAIT_AFTER_SEND; @@ -1479,7 +1494,9 @@ static enum hrtimer_restart pl011_trigger_start_tx(struct hrtimer *t) uart_port_lock_irqsave(&uap->port, &flags); if (uap->rs485_tx_state == WAIT_AFTER_RTS) { uap->rs485_tx_state = SEND; - pl011_start_tx(&uap->port); + /* A break can arm the timer while TX is stopped */ + if (!uart_tx_stopped(&uap->port)) + pl011_start_tx(&uap->port); } uart_port_unlock_irqrestore(&uap->port, flags); @@ -1772,16 +1789,60 @@ static void pl011_break_ctl(struct uart_port *port, int break_state) { struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); + struct tty_port *tport = &port->state->port; + bool rs485 = port->rs485.flags & SER_RS485_ENABLED; unsigned long flags; unsigned int lcr_h; uart_port_lock_irqsave(&uap->port, &flags); + + /* + * In RS485 mode the transceiver only drives the bus while + * transmitting. Treat a break as a transmission. + */ + if (rs485 && break_state == -1 && !uap->rs485_break) { + uap->rs485_break = true; + + /* Pending data already enabled the transceiver */ + if (uap->rs485_tx_state != SEND && + uap->rs485_tx_state != WAIT_AFTER_RTS) + pl011_rs485_tx_start(uap); + + /* + * Wait out delay_rts_before_send before starting the break. + * The state stays WAIT_AFTER_RTS, so the TX path behaves as + * without a break. Start it here if trigger_start_tx is late. + */ + if (uap->rs485_tx_state == WAIT_AFTER_RTS) { + uart_port_unlock_irqrestore(&uap->port, flags); + msleep(port->rs485.delay_rts_before_send); + uart_port_lock_irqsave(&uap->port, &flags); + + if (uap->rs485_tx_state == WAIT_AFTER_RTS) { + hrtimer_try_to_cancel(&uap->trigger_start_tx); + uap->rs485_tx_state = SEND; + if (!uart_tx_stopped(port)) + pl011_start_tx(port); + } + } + } + lcr_h = pl011_read(uap, REG_LCRH_TX); if (break_state == -1) lcr_h |= UART01x_LCRH_BRK; else lcr_h &= ~UART01x_LCRH_BRK; pl011_write(lcr_h, uap, REG_LCRH_TX); + + if (break_state == 0 && uap->rs485_break) { + uap->rs485_break = false; + /* An active TX path stops the transmission when it is done */ + if (rs485 && (uart_tx_stopped(port) || + (!pl011_dma_tx_queued(uap) && + kfifo_is_empty(&tport->xmit_fifo)))) + pl011_rs485_tx_stop(uap); + } + uart_port_unlock_irqrestore(&uap->port, flags); } @@ -2102,6 +2163,7 @@ static void pl011_shutdown(struct uart_port *port) hrtimer_cancel(&uap->trigger_stop_tx); uart_port_lock_irq(port); + uap->rs485_break = false; if (uap->rs485_tx_state != OFF) pl011_rs485_tx_stop_now(uap); uart_port_unlock_irq(port); @@ -2398,6 +2460,7 @@ static int pl011_rs485_config(struct uart_port *port, struct ktermios *termios, struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); + uap->rs485_break = false; if ((port->rs485.flags & SER_RS485_ENABLED) && !(rs485->flags & SER_RS485_ENABLED)) { bool tx_pending = uap->rs485_tx_state == WAIT_AFTER_RTS;