Skip to content

Commit edd64f3

Browse files
tq-schiffermgregkh
authored andcommitted
tty: serial: imx: disable TXDC IRQ in imx_uart_shutdown() to avoid IRQ storm
The IPG clock is disabled at the end of imx_uart_shutdown(); we really don't want to run any IRQ handlers after this point. At least on i.MX8MN, the UART will happily continue to generate interrupts even with its clocks disabled, but in this state, all register writes are ignored (which will cause the shadow registers to differ from the actual register values, resulting in all kinds of weirdness). In a transfer without DMA, this could lead to the following sequence of events: - The UART finishes its transmission while imx_uart_shutdown() is run, triggering the TXDC interrupt (we can trigger this fairly reliably by writing a single byte to the TTY and closing it right away) - imx_uart_shutdown() finishes, disabling the UART clocks - imx_uart_int() -> imx_uart_transmit_buffer() -> imx_uart_stop_tx() imx_uart_stop_tx() should now clear UCR4_TCEN to disable the TXDC interrupt, but this register write is ineffective. This results in an interrupt storm. To disable all interrupts in the same place, and to avoid setting UCR4 twice, clearing UCR4_OREN is moved below del_timer_sync() as well; this should be harmless. Signed-off-by: Matthias Schiffer <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 409cc45 commit edd64f3

File tree

1 file changed

+6
-5
lines changed

1 file changed

+6
-5
lines changed

drivers/tty/serial/imx.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,10 +1552,6 @@ static void imx_uart_shutdown(struct uart_port *port)
15521552
ucr2 = imx_uart_readl(sport, UCR2);
15531553
ucr2 &= ~(UCR2_TXEN | UCR2_ATEN);
15541554
imx_uart_writel(sport, ucr2, UCR2);
1555-
1556-
ucr4 = imx_uart_readl(sport, UCR4);
1557-
ucr4 &= ~UCR4_OREN;
1558-
imx_uart_writel(sport, ucr4, UCR4);
15591555
spin_unlock_irqrestore(&sport->port.lock, flags);
15601556

15611557
/*
@@ -1568,10 +1564,15 @@ static void imx_uart_shutdown(struct uart_port *port)
15681564
*/
15691565

15701566
spin_lock_irqsave(&sport->port.lock, flags);
1567+
15711568
ucr1 = imx_uart_readl(sport, UCR1);
15721569
ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN);
1573-
15741570
imx_uart_writel(sport, ucr1, UCR1);
1571+
1572+
ucr4 = imx_uart_readl(sport, UCR4);
1573+
ucr4 &= ~(UCR4_OREN | UCR4_TCEN);
1574+
imx_uart_writel(sport, ucr4, UCR4);
1575+
15751576
spin_unlock_irqrestore(&sport->port.lock, flags);
15761577

15771578
clk_disable_unprepare(sport->clk_per);

0 commit comments

Comments
 (0)