]>
Commit | Line | Data |
---|---|---|
a220fbe5 GKH |
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 | } |