Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tty/serial: atmel: RS485 HD w/DMA: enable RX after TX is stopped

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 <razvan.stefanescu@microchip.com>
Acked-by: Richard Genoud <richard.genoud@gmail.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Razvan Stefanescu and committed by
Greg Kroah-Hartman
69646d7a f3040983

+21 -3
+21 -3
drivers/tty/serial/atmel_serial.c
··· 166 166 unsigned int pending_status; 167 167 spinlock_t lock_suspended; 168 168 169 + bool hd_start_rx; /* can start RX during half-duplex operation */ 170 + 169 171 /* ISO7816 */ 170 172 unsigned int fidi_min; 171 173 unsigned int fidi_max; ··· 935 933 if (!uart_circ_empty(xmit)) 936 934 atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx); 937 935 else if (atmel_uart_is_half_duplex(port)) { 938 - /* DMA done, stop TX, start RX for RS485 */ 939 - atmel_start_rx(port); 936 + /* 937 + * DMA done, re-enable TXEMPTY and signal that we can stop 938 + * TX and start RX for RS485 939 + */ 940 + atmel_port->hd_start_rx = true; 941 + atmel_uart_writel(port, ATMEL_US_IER, 942 + atmel_port->tx_done_mask); 940 943 } 941 944 942 945 spin_unlock_irqrestore(&port->lock, flags); ··· 1389 1382 struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); 1390 1383 1391 1384 if (pending & atmel_port->tx_done_mask) { 1392 - /* Either PDC or interrupt transmission */ 1393 1385 atmel_uart_writel(port, ATMEL_US_IDR, 1394 1386 atmel_port->tx_done_mask); 1387 + 1388 + /* Start RX if flag was set and FIFO is empty */ 1389 + if (atmel_port->hd_start_rx) { 1390 + if (!(atmel_uart_readl(port, ATMEL_US_CSR) 1391 + & ATMEL_US_TXEMPTY)) 1392 + dev_warn(port->dev, "Should start RX, but TX fifo is not empty\n"); 1393 + 1394 + atmel_port->hd_start_rx = false; 1395 + atmel_start_rx(port); 1396 + return; 1397 + } 1398 + 1395 1399 atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx); 1396 1400 } 1397 1401 }