]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
tty: serial: fsl_lpuart: disable the CTS when send break signal
authorSherry Sun <sherry.sun@nxp.com>
Wed, 14 Dec 2022 03:11:35 +0000 (11:11 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 11 Mar 2023 12:50:40 +0000 (13:50 +0100)
[ Upstream commit c4c81db5cf8bc53d6160c3abf26d382c841aa434 ]

LPUART IP has a bug that it treats the CTS as higher priority than the
break signal, which cause the break signal sending through UARTCTRL_SBK
may impacted by the CTS input if the HW flow control is enabled.

Add this workaround patch to fix the IP bug, we can disable CTS before
asserting SBK to avoid any interference from CTS, and re-enable it when
break off.

Such as for the bluetooth chip power save feature, host can let the BT
chip get into sleep state by sending a UART break signal, and wake it up
by turning off the UART break. If the BT chip enters the sleep mode
successfully, it will pull up the CTS line, if the BT chip is woken up,
it will pull down the CTS line. If without this workaround patch, the
UART TX pin cannot send the break signal successfully as it affected by
the BT CTS pin. After adding this patch, the BT power save feature can
work well.

Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
Link: https://lore.kernel.org/r/20221214031137.28815-2-sherry.sun@nxp.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/tty/serial/fsl_lpuart.c

index 23910ac724b114ef9bce8c4b0232e45e38872ae0..b136c596fe6aeb6ec0adbb7ba83282c50416330c 100644 (file)
@@ -1462,12 +1462,32 @@ static void lpuart_break_ctl(struct uart_port *port, int break_state)
 
 static void lpuart32_break_ctl(struct uart_port *port, int break_state)
 {
-       unsigned long temp;
+       unsigned long temp, modem;
+       struct tty_struct *tty;
+       unsigned int cflag = 0;
+
+       tty = tty_port_tty_get(&port->state->port);
+       if (tty) {
+               cflag = tty->termios.c_cflag;
+               tty_kref_put(tty);
+       }
 
        temp = lpuart32_read(port, UARTCTRL) & ~UARTCTRL_SBK;
+       modem = lpuart32_read(port, UARTMODIR);
 
-       if (break_state != 0)
+       if (break_state != 0) {
                temp |= UARTCTRL_SBK;
+               /*
+                * LPUART CTS has higher priority than SBK, need to disable CTS before
+                * asserting SBK to avoid any interference if flow control is enabled.
+                */
+               if (cflag & CRTSCTS && modem & UARTMODIR_TXCTSE)
+                       lpuart32_write(port, modem & ~UARTMODIR_TXCTSE, UARTMODIR);
+       } else {
+               /* Re-enable the CTS when break off. */
+               if (cflag & CRTSCTS && !(modem & UARTMODIR_TXCTSE))
+                       lpuart32_write(port, modem | UARTMODIR_TXCTSE, UARTMODIR);
+       }
 
        lpuart32_write(port, temp, UARTCTRL);
 }