]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
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, 17 Oct 2023 08:17:01 +0000 (10:17 +0200)
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>
v6: stable tag added
v5: https://lore.kernel.org/lkml/OF43DA36FF.2BD3BB21-ON00258A47.005A8125-00258A47.005A9513@gdc.ru/
added missed Reviewed-by tags, Fixes tag added according to Dmitry and Neil notes
v4: https://lore.kernel.org/lkml/OF55521400.7512350F-ON00258A47.003F7254-00258A47.0040E15C@gdc.ru/
More correct patch subject according to Jiri's note
v3: https://lore.kernel.org/lkml/OF6CF5FFA0.CCFD0E8E-ON00258A46.00549EDF-00258A46.0054BB62@gdc.ru/
"From:" line added to the mail
v2: https://lore.kernel.org/lkml/OF950BEF72.7F425944-ON00258A46.00488A76-00258A46.00497D44@gdc.ru/
braces for single statement removed according to Dmitry's note
v1: https://lore.kernel.org/lkml/OF28B2B8C9.5BC0CD28-ON00258A46.0037688F-00258A46.0039155B@gdc.ru/
Link: https://lore.kernel.org/r/OF66360032.51C36182-ON00258A48.003F656B-00258A48.0040092C@gdc.ru
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/meson_uart.c

index de298bf75d9b808355c621a61c7a86dceddc83fd..8dd84617e7158e6651cf17726bb27d3d0c0f81fc 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;