From: Razvan Stefanescu Date: Tue, 19 Mar 2019 13:20:35 +0000 (+0200) Subject: tty/serial: atmel: RS485 HD w/DMA: enable RX after TX is stopped X-Git-Tag: Ubuntu-4.15.0-61.68~2500 X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=3009f741cfb6e7512f305bcd388f3ce09ec905a4;p=mirror_ubuntu-bionic-kernel.git tty/serial: atmel: RS485 HD w/DMA: enable RX after TX is stopped BugLink: https://bugs.launchpad.net/bugs/1838116 commit 69646d7a3689fbe1a65ae90397d22ac3f1b8d40f upstream. In half-duplex operation, RX should be started after TX completes. If DMA is used, there is a case when the DMA transfer completes but the TX FIFO is not emptied, so the RX cannot be restarted just yet. Use a boolean variable to store this state and rearm TX interrupt mask to be signaled again that the transfer finished. In interrupt transmit handler this variable is used to start RX. A warning message is generated if RX is activated before TX fifo is cleared. Fixes: b389f173aaa1 ("tty/serial: atmel: RS485 half duplex w/DMA: enable RX after TX is done") Signed-off-by: Razvan Stefanescu Acked-by: Richard Genoud Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Kamal Mostafa Signed-off-by: Khalid Elmously --- diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index e721dec89231..f2b5c6f77ada 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -163,6 +163,8 @@ struct atmel_uart_port { unsigned int pending_status; spinlock_t lock_suspended; + bool hd_start_rx; /* can start RX during half-duplex operation */ + #ifdef CONFIG_PM struct { u32 cr; @@ -805,8 +807,13 @@ static void atmel_complete_tx_dma(void *arg) if (!uart_circ_empty(xmit)) atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx); else if (atmel_uart_is_half_duplex(port)) { - /* DMA done, stop TX, start RX for RS485 */ - atmel_start_rx(port); + /* + * DMA done, re-enable TXEMPTY and signal that we can stop + * TX and start RX for RS485 + */ + atmel_port->hd_start_rx = true; + atmel_uart_writel(port, ATMEL_US_IER, + atmel_port->tx_done_mask); } spin_unlock_irqrestore(&port->lock, flags); @@ -1252,9 +1259,20 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending) struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); if (pending & atmel_port->tx_done_mask) { - /* Either PDC or interrupt transmission */ atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask); + + /* Start RX if flag was set and FIFO is empty */ + if (atmel_port->hd_start_rx) { + if (!(atmel_uart_readl(port, ATMEL_US_CSR) + & ATMEL_US_TXEMPTY)) + dev_warn(port->dev, "Should start RX, but TX fifo is not empty\n"); + + atmel_port->hd_start_rx = false; + atmel_start_rx(port); + return; + } + atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx); } }