tty: serial: meson: fix hard LOCKUP on crtscts mode
authorPavel Krasavin <pkrasavin@imaqliq.com>
Sat, 14 Oct 2023 11:39:26 +0000 (11:39 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 28 Nov 2023 17:19:57 +0000 (17:19 +0000)
commit 2a1d728f20edeee7f26dc307ed9df4e0d23947ab upstream.

There might be hard lockup if we set crtscts mode on port without RTS/CTS configured:

# stty -F /dev/ttyAML6 crtscts; echo 1 > /dev/ttyAML6; echo 2 > /dev/ttyAML6
[   95.890386] rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
[   95.890857] rcu:     3-...0: (201 ticks this GP) idle=e33c/1/0x4000000000000000 softirq=5844/5846 fqs=4984
[   95.900212] rcu:     (detected by 2, t=21016 jiffies, g=7753, q=296 ncpus=4)
[   95.906972] Task dump for CPU 3:
[   95.910178] task:bash            state:R  running task     stack:0     pid:205   ppid:1      flags:0x00000202
[   95.920059] Call trace:
[   95.922485]  __switch_to+0xe4/0x168
[   95.925951]  0xffffff8003477508
[   95.974379] watchdog: Watchdog detected hard LOCKUP on cpu 3
[   95.974424] Modules linked in: 88x2cs(O) rtc_meson_vrtc

Possible solution would be to not allow to setup crtscts on such port.

Tested on S905X3 based board.

Fixes: ff7693d079e5 ("ARM: meson: serial: add MesonX SoC on-chip uart driver")
Cc: stable@vger.kernel.org
Signed-off-by: Pavel Krasavin <pkrasavin@imaqliq.com>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Reviewed-by: Dmitry Rokosov <ddrokosov@salutedevices.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/meson_uart.c

index 790d910..9388b9d 100644 (file)
@@ -380,10 +380,14 @@ static void meson_uart_set_termios(struct uart_port *port,
        else
                val |= AML_UART_STOP_BIT_1SB;
 
-       if (cflags & CRTSCTS)
-               val &= ~AML_UART_TWO_WIRE_EN;
-       else
+       if (cflags & CRTSCTS) {
+               if (port->flags & UPF_HARD_FLOW)
+                       val &= ~AML_UART_TWO_WIRE_EN;
+               else
+                       termios->c_cflag &= ~CRTSCTS;
+       } else {
                val |= AML_UART_TWO_WIRE_EN;
+       }
 
        writel(val, port->membase + AML_UART_CONTROL);
 
@@ -705,6 +709,7 @@ static int meson_uart_probe(struct platform_device *pdev)
        u32 fifosize = 64; /* Default is 64, 128 for EE UART_0 */
        int ret = 0;
        int irq;
+       bool has_rtscts;
 
        if (pdev->dev.of_node)
                pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");
@@ -732,6 +737,7 @@ static int meson_uart_probe(struct platform_device *pdev)
                return irq;
 
        of_property_read_u32(pdev->dev.of_node, "fifo-size", &fifosize);
+       has_rtscts = of_property_read_bool(pdev->dev.of_node, "uart-has-rtscts");
 
        if (meson_ports[pdev->id]) {
                return dev_err_probe(&pdev->dev, -EBUSY,
@@ -762,6 +768,8 @@ static int meson_uart_probe(struct platform_device *pdev)
        port->mapsize = resource_size(res_mem);
        port->irq = irq;
        port->flags = UPF_BOOT_AUTOCONF | UPF_LOW_LATENCY;
+       if (has_rtscts)
+               port->flags |= UPF_HARD_FLOW;
        port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MESON_CONSOLE);
        port->dev = &pdev->dev;
        port->line = pdev->id;