| From 06a60b19795203064281636a3e1872a842653c5d Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 24 Nov 2023 14:11:10 +0100 |
| Subject: serial: imx: fix tx statemachine deadlock |
| |
| From: Paul Geurts <paul_geurts@live.nl> |
| |
| [ Upstream commit 78d60dae9a0c9f09aa3d6477c94047df2fe6f7b0 ] |
| |
| When using the serial port as RS485 port, the tx statemachine is used to |
| control the RTS pin to drive the RS485 transceiver TX_EN pin. When the |
| TTY port is closed in the middle of a transmission (for instance during |
| userland application crash), imx_uart_shutdown disables the interface |
| and disables the Transmission Complete interrupt. afer that, |
| imx_uart_stop_tx bails on an incomplete transmission, to be retriggered |
| by the TC interrupt. This interrupt is disabled and therefore the tx |
| statemachine never transitions out of SEND. The statemachine is in |
| deadlock now, and the TX_EN remains low, making the interface useless. |
| |
| imx_uart_stop_tx now checks for incomplete transmission AND whether TC |
| interrupts are enabled before bailing to be retriggered. This makes sure |
| the state machine handling is reached, and is properly set to |
| WAIT_AFTER_SEND. |
| |
| Fixes: cb1a60923609 ("serial: imx: implement rts delaying for rs485") |
| Signed-off-by: Paul Geurts <paul_geurts@live.nl> |
| Tested-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk> |
| Tested-by: Eberhard Stoll <eberhard.stoll@gmx.de> |
| Link: https://lore.kernel.org/r/AM0PR09MB26758F651BC1B742EB45775995B8A@AM0PR09MB2675.eurprd09.prod.outlook.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/tty/serial/imx.c | 4 ++-- |
| 1 file changed, 2 insertions(+), 2 deletions(-) |
| |
| diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c |
| index 2e85c77e7c0d..c084ac3bb441 100644 |
| --- a/drivers/tty/serial/imx.c |
| +++ b/drivers/tty/serial/imx.c |
| @@ -421,13 +421,13 @@ static void imx_uart_stop_tx(struct uart_port *port) |
| ucr1 = imx_uart_readl(sport, UCR1); |
| imx_uart_writel(sport, ucr1 & ~UCR1_TRDYEN, UCR1); |
| |
| + ucr4 = imx_uart_readl(sport, UCR4); |
| usr2 = imx_uart_readl(sport, USR2); |
| - if (!(usr2 & USR2_TXDC)) { |
| + if ((!(usr2 & USR2_TXDC)) && (ucr4 & UCR4_TCEN)) { |
| /* The shifter is still busy, so retry once TC triggers */ |
| return; |
| } |
| |
| - ucr4 = imx_uart_readl(sport, UCR4); |
| ucr4 &= ~UCR4_TCEN; |
| imx_uart_writel(sport, ucr4, UCR4); |
| |
| -- |
| 2.43.0 |
| |