Skip to content

Commit

Permalink
tty: serial: imx: disable TXDC IRQ in imx_uart_shutdown() to avoid IR…
Browse files Browse the repository at this point in the history
…Q 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 <matthias.schiffer@ew.tq-group.com>
Link: https://lore.kernel.org/r/20200925082412.12960-1-matthias.schiffer@ew.tq-group.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Matthias Schiffer authored and Greg Kroah-Hartman committed Sep 30, 2020
1 parent 409cc45 commit edd64f3
Showing 1 changed file with 6 additions and 5 deletions.
11 changes: 6 additions & 5 deletions drivers/tty/serial/imx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1552,10 +1552,6 @@ static void imx_uart_shutdown(struct uart_port *port)
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 &= ~(UCR2_TXEN | UCR2_ATEN);
imx_uart_writel(sport, ucr2, UCR2);

ucr4 = imx_uart_readl(sport, UCR4);
ucr4 &= ~UCR4_OREN;
imx_uart_writel(sport, ucr4, UCR4);
spin_unlock_irqrestore(&sport->port.lock, flags);

/*
Expand All @@ -1568,10 +1564,15 @@ static void imx_uart_shutdown(struct uart_port *port)
*/

spin_lock_irqsave(&sport->port.lock, flags);

ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN);

imx_uart_writel(sport, ucr1, UCR1);

ucr4 = imx_uart_readl(sport, UCR4);
ucr4 &= ~(UCR4_OREN | UCR4_TCEN);
imx_uart_writel(sport, ucr4, UCR4);

spin_unlock_irqrestore(&sport->port.lock, flags);

clk_disable_unprepare(sport->clk_per);
Expand Down

0 comments on commit edd64f3

Please sign in to comment.