| From 672448ccf9b6a676f96f9352cbf91f4d35f4084a Mon Sep 17 00:00:00 2001 |
| From: Rickard x Andersson <rickaran@axis.com> |
| Date: Wed, 21 Feb 2024 12:53:04 +0100 |
| Subject: tty: serial: imx: Fix broken RS485 |
| |
| From: Rickard x Andersson <rickaran@axis.com> |
| |
| commit 672448ccf9b6a676f96f9352cbf91f4d35f4084a upstream. |
| |
| When about to transmit the function imx_uart_start_tx is called and in |
| some RS485 configurations this function will call imx_uart_stop_rx. The |
| problem is that imx_uart_stop_rx will enable loopback in order to |
| release the RS485 bus, but when loopback is enabled transmitted data |
| will just be looped to RX. |
| |
| This patch fixes the above problem by not enabling loopback when about |
| to transmit. |
| |
| This driver now works well when used for RS485 half duplex master |
| configurations. |
| |
| Fixes: 79d0224f6bf2 ("tty: serial: imx: Handle RS485 DE signal active high") |
| Cc: stable <stable@kernel.org> |
| Signed-off-by: Rickard x Andersson <rickaran@axis.com> |
| Tested-by: Christoph Niedermaier <cniedermaier@dh-electronics.com> |
| Link: https://lore.kernel.org/r/20240221115304.509811-1-rickaran@axis.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/tty/serial/imx.c | 22 ++++++++++++++++++---- |
| 1 file changed, 18 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/tty/serial/imx.c |
| +++ b/drivers/tty/serial/imx.c |
| @@ -462,8 +462,7 @@ static void imx_uart_stop_tx(struct uart |
| } |
| } |
| |
| -/* called with port.lock taken and irqs off */ |
| -static void imx_uart_stop_rx(struct uart_port *port) |
| +static void imx_uart_stop_rx_with_loopback_ctrl(struct uart_port *port, bool loopback) |
| { |
| struct imx_port *sport = (struct imx_port *)port; |
| u32 ucr1, ucr2, ucr4, uts; |
| @@ -485,7 +484,7 @@ static void imx_uart_stop_rx(struct uart |
| /* See SER_RS485_ENABLED/UTS_LOOP comment in imx_uart_probe() */ |
| if (port->rs485.flags & SER_RS485_ENABLED && |
| port->rs485.flags & SER_RS485_RTS_ON_SEND && |
| - sport->have_rtscts && !sport->have_rtsgpio) { |
| + sport->have_rtscts && !sport->have_rtsgpio && loopback) { |
| uts = imx_uart_readl(sport, imx_uart_uts_reg(sport)); |
| uts |= UTS_LOOP; |
| imx_uart_writel(sport, uts, imx_uart_uts_reg(sport)); |
| @@ -498,6 +497,16 @@ static void imx_uart_stop_rx(struct uart |
| } |
| |
| /* called with port.lock taken and irqs off */ |
| +static void imx_uart_stop_rx(struct uart_port *port) |
| +{ |
| + /* |
| + * Stop RX and enable loopback in order to make sure RS485 bus |
| + * is not blocked. Se comment in imx_uart_probe(). |
| + */ |
| + imx_uart_stop_rx_with_loopback_ctrl(port, true); |
| +} |
| + |
| +/* called with port.lock taken and irqs off */ |
| static void imx_uart_enable_ms(struct uart_port *port) |
| { |
| struct imx_port *sport = (struct imx_port *)port; |
| @@ -682,9 +691,14 @@ static void imx_uart_start_tx(struct uar |
| imx_uart_rts_inactive(sport, &ucr2); |
| imx_uart_writel(sport, ucr2, UCR2); |
| |
| + /* |
| + * Since we are about to transmit we can not stop RX |
| + * with loopback enabled because that will make our |
| + * transmitted data being just looped to RX. |
| + */ |
| if (!(port->rs485.flags & SER_RS485_RX_DURING_TX) && |
| !port->rs485_rx_during_tx_gpio) |
| - imx_uart_stop_rx(port); |
| + imx_uart_stop_rx_with_loopback_ctrl(port, false); |
| |
| sport->tx_state = WAIT_AFTER_RTS; |
| |