tty/serial: atmel: RS485 HD w/DMA: enable RX after TX is stopped
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 <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>
This commit is contained in:
parent
59ae59920a
commit
a6efba2fc3
1 changed files with 21 additions and 3 deletions
|
@ -169,6 +169,8 @@ struct atmel_uart_port {
|
|||
unsigned int pending_status;
|
||||
spinlock_t lock_suspended;
|
||||
|
||||
bool hd_start_rx; /* can start RX during half-duplex operation */
|
||||
|
||||
int (*prepare_rx)(struct uart_port *port);
|
||||
int (*prepare_tx)(struct uart_port *port);
|
||||
void (*schedule_rx)(struct uart_port *port);
|
||||
|
@ -816,8 +818,13 @@ static void atmel_complete_tx_dma(void *arg)
|
|||
if (!uart_circ_empty(xmit))
|
||||
tasklet_schedule(&atmel_port->tasklet);
|
||||
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);
|
||||
|
@ -1257,9 +1264,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;
|
||||
}
|
||||
|
||||
tasklet_schedule(&atmel_port->tasklet);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue