From: Fan Wu Date: Fri, 31 Jul 2026 08:59:14 +0000 (+0000) Subject: serial: amba-pl011: cancel RS485 hrtimers after freeing IRQ X-Git-Tag: v7.2-rc7~6^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=36672c8d7d14e9c43287528455d2c97b526ea6ad;p=thirdparty%2Flinux.git serial: amba-pl011: cancel RS485 hrtimers after freeing IRQ The RS485 trigger hrtimers are embedded in the devm-managed port and can fire after it is freed. The IRQ handler can arm a timer, so free the IRQ first and then cancel both timers. Complete the RS485 stop without arming a timer, and cancel the timers in remove() for the suspend-then-unbind path, where shutdown is not called. This issue was found by an in-house static analysis tool. Fixes: 2c1fd53af21b ("serial: amba-pl011: Fix RTS handling in RS485 mode") Cc: stable Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260731085915.326775-3-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index d88c1242e6d8..5232042fdd92 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1334,11 +1334,30 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) #define pl011_dma_flush_buffer NULL #endif -static void pl011_rs485_tx_stop(struct uart_amba_port *uap) +static void pl011_rs485_tx_stop_now(struct uart_amba_port *uap) { struct uart_port *port = &uap->port; u32 cr; + cr = pl011_read(uap, REG_CR); + + if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) + cr &= ~UART011_CR_RTS; + else + cr |= UART011_CR_RTS; + + /* Disable the transmitter and reenable the transceiver */ + cr &= ~UART011_CR_TXE; + cr |= UART011_CR_RXE; + pl011_write(cr, uap, REG_CR); + + uap->rs485_tx_state = OFF; +} + +static void pl011_rs485_tx_stop(struct uart_amba_port *uap) +{ + struct uart_port *port = &uap->port; + if (uap->rs485_tx_state == SEND) uap->rs485_tx_state = WAIT_AFTER_SEND; @@ -1362,19 +1381,7 @@ static void pl011_rs485_tx_stop(struct uart_amba_port *uap) hrtimer_try_to_cancel(&uap->trigger_start_tx); } - cr = pl011_read(uap, REG_CR); - - if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) - cr &= ~UART011_CR_RTS; - else - cr |= UART011_CR_RTS; - - /* Disable the transmitter and reenable the transceiver */ - cr &= ~UART011_CR_TXE; - cr |= UART011_CR_RXE; - pl011_write(cr, uap, REG_CR); - - uap->rs485_tx_state = OFF; + pl011_rs485_tx_stop_now(uap); } static void pl011_stop_tx(struct uart_port *port) @@ -2084,11 +2091,20 @@ static void pl011_shutdown(struct uart_port *port) pl011_dma_shutdown(uap); - if ((port->rs485.flags & SER_RS485_ENABLED && uap->rs485_tx_state != OFF)) - pl011_rs485_tx_stop(uap); - free_irq(uap->port.irq, uap); + /* + * free_irq() drains the UART interrupt handler, which can arm either + * timer. Cancel the timers afterwards to drain their callbacks too. + */ + hrtimer_cancel(&uap->trigger_start_tx); + hrtimer_cancel(&uap->trigger_stop_tx); + + uart_port_lock_irq(port); + if (uap->rs485_tx_state != OFF) + pl011_rs485_tx_stop_now(uap); + uart_port_unlock_irq(port); + pl011_disable_uart(uap); /* @@ -3067,6 +3083,8 @@ static void pl011_remove(struct amba_device *dev) struct uart_amba_port *uap = amba_get_drvdata(dev); uart_remove_one_port(&amba_reg, &uap->port); + hrtimer_cancel(&uap->trigger_start_tx); + hrtimer_cancel(&uap->trigger_stop_tx); pl011_unregister_port(uap); }