]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blame - releases/4.19.34/tty-serial-atmel-rs485-hd-w-dma-enable-rx-after-tx-is-stopped.patch
Linux 4.19.34
[thirdparty/kernel/stable-queue.git] / releases / 4.19.34 / tty-serial-atmel-rs485-hd-w-dma-enable-rx-after-tx-is-stopped.patch
CommitLineData
a220fbe5
GKH
1From 69646d7a3689fbe1a65ae90397d22ac3f1b8d40f Mon Sep 17 00:00:00 2001
2From: Razvan Stefanescu <razvan.stefanescu@microchip.com>
3Date: Tue, 19 Mar 2019 15:20:35 +0200
4Subject: tty/serial: atmel: RS485 HD w/DMA: enable RX after TX is stopped
5
6From: Razvan Stefanescu <razvan.stefanescu@microchip.com>
7
8commit 69646d7a3689fbe1a65ae90397d22ac3f1b8d40f upstream.
9
10In half-duplex operation, RX should be started after TX completes.
11
12If DMA is used, there is a case when the DMA transfer completes but the
13TX FIFO is not emptied, so the RX cannot be restarted just yet.
14
15Use a boolean variable to store this state and rearm TX interrupt mask
16to be signaled again that the transfer finished. In interrupt transmit
17handler this variable is used to start RX. A warning message is generated
18if RX is activated before TX fifo is cleared.
19
20Fixes: b389f173aaa1 ("tty/serial: atmel: RS485 half duplex w/DMA: enable
21RX after TX is done")
22Signed-off-by: Razvan Stefanescu <razvan.stefanescu@microchip.com>
23Acked-by: Richard Genoud <richard.genoud@gmail.com>
24Cc: stable <stable@vger.kernel.org>
25Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
26
27---
28 drivers/tty/serial/atmel_serial.c | 24 +++++++++++++++++++++---
29 1 file changed, 21 insertions(+), 3 deletions(-)
30
31--- a/drivers/tty/serial/atmel_serial.c
32+++ b/drivers/tty/serial/atmel_serial.c
33@@ -163,6 +163,8 @@ struct atmel_uart_port {
34 unsigned int pending_status;
35 spinlock_t lock_suspended;
36
37+ bool hd_start_rx; /* can start RX during half-duplex operation */
38+
39 #ifdef CONFIG_PM
40 struct {
41 u32 cr;
42@@ -805,8 +807,13 @@ static void atmel_complete_tx_dma(void *
43 if (!uart_circ_empty(xmit))
44 atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
45 else if (atmel_uart_is_half_duplex(port)) {
46- /* DMA done, stop TX, start RX for RS485 */
47- atmel_start_rx(port);
48+ /*
49+ * DMA done, re-enable TXEMPTY and signal that we can stop
50+ * TX and start RX for RS485
51+ */
52+ atmel_port->hd_start_rx = true;
53+ atmel_uart_writel(port, ATMEL_US_IER,
54+ atmel_port->tx_done_mask);
55 }
56
57 spin_unlock_irqrestore(&port->lock, flags);
58@@ -1252,9 +1259,20 @@ atmel_handle_transmit(struct uart_port *
59 struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
60
61 if (pending & atmel_port->tx_done_mask) {
62- /* Either PDC or interrupt transmission */
63 atmel_uart_writel(port, ATMEL_US_IDR,
64 atmel_port->tx_done_mask);
65+
66+ /* Start RX if flag was set and FIFO is empty */
67+ if (atmel_port->hd_start_rx) {
68+ if (!(atmel_uart_readl(port, ATMEL_US_CSR)
69+ & ATMEL_US_TXEMPTY))
70+ dev_warn(port->dev, "Should start RX, but TX fifo is not empty\n");
71+
72+ atmel_port->hd_start_rx = false;
73+ atmel_start_rx(port);
74+ return;
75+ }
76+
77 atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
78 }
79 }