]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/4.19.34/tty-serial-atmel-rs485-hd-w-dma-enable-rx-after-tx-is-stopped.patch
Linux 4.14.111
[thirdparty/kernel/stable-queue.git] / releases / 4.19.34 / tty-serial-atmel-rs485-hd-w-dma-enable-rx-after-tx-is-stopped.patch
1 From 69646d7a3689fbe1a65ae90397d22ac3f1b8d40f Mon Sep 17 00:00:00 2001
2 From: Razvan Stefanescu <razvan.stefanescu@microchip.com>
3 Date: Tue, 19 Mar 2019 15:20:35 +0200
4 Subject: tty/serial: atmel: RS485 HD w/DMA: enable RX after TX is stopped
5
6 From: Razvan Stefanescu <razvan.stefanescu@microchip.com>
7
8 commit 69646d7a3689fbe1a65ae90397d22ac3f1b8d40f upstream.
9
10 In half-duplex operation, RX should be started after TX completes.
11
12 If DMA is used, there is a case when the DMA transfer completes but the
13 TX FIFO is not emptied, so the RX cannot be restarted just yet.
14
15 Use a boolean variable to store this state and rearm TX interrupt mask
16 to be signaled again that the transfer finished. In interrupt transmit
17 handler this variable is used to start RX. A warning message is generated
18 if RX is activated before TX fifo is cleared.
19
20 Fixes: b389f173aaa1 ("tty/serial: atmel: RS485 half duplex w/DMA: enable
21 RX after TX is done")
22 Signed-off-by: Razvan Stefanescu <razvan.stefanescu@microchip.com>
23 Acked-by: Richard Genoud <richard.genoud@gmail.com>
24 Cc: stable <stable@vger.kernel.org>
25 Signed-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 }