merged.

Bruce

In message: [linux-yocto][linux-yocto v5.15/standard/nxp-sdk-5.15/nxp-soc & 
v5.15/standard/preempt-rt/nxp-sdk-5.15/nxp-soc][PATCH 2/2] serial: imx: 
work-around for hardware RX flood
on 29/11/2023 Xiaolei Wang wrote:

> From: Sergey Organov <sorga...@gmail.com>
> 
> commit 496a4471b7c3ae5c0be1a3fccd69e7debc127e08 from upstream.
> 
> Check if hardware Rx flood is in progress, and issue soft reset to UART to
> stop the flood.
> 
> A way to reproduce the flood (checked on iMX6SX) is: open iMX UART at 9600
> 8N1, and from external source send 0xf0 char at 115200 8N1. In about 90% of
> cases this starts a flood of "receiving" of 0xff characters by the iMX UART
> that is terminated by any activity on RxD line, or could be stopped by
> issuing soft reset to the UART (just stop/start of RX does not help). Note
> that in essence what we did here is sending isolated start bit about 2.4
> times shorter than it is to be if issued on the UART configured baud rate.
> 
> There was earlier attempt to fix similar issue in: 'commit
> b38cb7d25711 ("serial: imx: Disable new features of autobaud detection")',
> but apparently it only gets harder to reproduce the issue after that
> commit.
> 
> Signed-off-by: Sergey Organov <sorga...@gmail.com>
> Link: https://lore.kernel.org/r/20230201142700.4346-3-sorga...@gmail.com
> Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>
> Signed-off-by: Xiaolei Wang <xiaolei.w...@windriver.com>
> ---
>  drivers/tty/serial/imx.c | 123 ++++++++++++++++++++++++++++++---------
>  1 file changed, 95 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index 8b32d8e549dc..001f88b15752 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -214,6 +214,9 @@ struct imx_port {
>  
>       struct mctrl_gpios *gpios;
>  
> +     /* counter to stop 0xff flood */
> +     int idle_counter;
> +
>       /* shadow registers */
>       unsigned int ucr1;
>       unsigned int ucr2;
> @@ -444,6 +447,8 @@ static void imx_uart_soft_reset(struct imx_port *sport)
>       imx_uart_writel(sport, ubir, UBIR);
>       imx_uart_writel(sport, ubmr, UBMR);
>       imx_uart_writel(sport, uts, IMX21_UTS);
> +
> +     sport->idle_counter = 0;
>  }
>  
>  /* called with port.lock taken and irqs off */
> @@ -844,15 +849,66 @@ static irqreturn_t imx_uart_txint(int irq, void *dev_id)
>       return IRQ_HANDLED;
>  }
>  
> +/* Check if hardware Rx flood is in progress, and issue soft reset to stop 
> it.
> + * This is to be called from Rx ISRs only when some bytes were actually
> + * received.
> + *
> + * A way to reproduce the flood (checked on iMX6SX) is: open iMX UART at 9600
> + * 8N1, and from external source send 0xf0 char at 115200 8N1. In about 90% 
> of
> + * cases this starts a flood of "receiving" of 0xff characters by the iMX6 
> UART
> + * that is terminated by any activity on RxD line, or could be stopped by
> + * issuing soft reset to the UART (just stop/start of RX does not help). Note
> + * that what we do here is sending isolated start bit about 2.4 times shorter
> + * than it is to be on UART configured baud rate.
> + */
> +static void imx_uart_check_flood(struct imx_port *sport, u32 usr2)
> +{
> +     /* To detect hardware 0xff flood we monitor RxD line between RX
> +      * interrupts to isolate "receiving" of char(s) with no activity
> +      * on RxD line, that'd never happen on actual data transfers.
> +      *
> +      * We use USR2_WAKE bit to check for activity on RxD line, but we have a
> +      * race here if we clear USR2_WAKE when receiving of a char is in
> +      * progress, so we might get RX interrupt later with USR2_WAKE bit
> +      * cleared. Note though that as we don't try to clear USR2_WAKE when we
> +      * detected no activity, this race may hide actual activity only once.
> +      *
> +      * Yet another case where receive interrupt may occur without RxD
> +      * activity is expiration of aging timer, so we consider this as well.
> +      *
> +      * We use 'idle_counter' to ensure that we got at least so many RX
> +      * interrupts without any detected activity on RxD line. 2 cases
> +      * described plus 1 to be on the safe side gives us a margin of 3,
> +      * below. In practice I was not able to produce a false positive to
> +      * induce soft reset at regular data transfers even using 1 as the
> +      * margin, so 3 is actually very strong.
> +      *
> +      * We count interrupts, not chars in 'idle-counter' for simplicity.
> +      */
> +
> +     if (usr2 & USR2_WAKE) {
> +             imx_uart_writel(sport, USR2_WAKE, USR2);
> +             sport->idle_counter = 0;
> +     } else if (++sport->idle_counter > 3) {
> +             dev_warn(sport->port.dev, "RX flood detected: soft reset.");
> +             imx_uart_soft_reset(sport); /* also clears 
> 'sport->idle_counter' */
> +     }
> +}
> +
>  static irqreturn_t __imx_uart_rxint(int irq, void *dev_id)
>  {
>       struct imx_port *sport = dev_id;
>       unsigned int rx, flg, ignored = 0;
>       struct tty_port *port = &sport->port.state->port;
> +     u32 usr2;
> +
> +     usr2 = imx_uart_readl(sport, USR2);
>  
> -     while (imx_uart_readl(sport, USR2) & USR2_RDR) {
> -             u32 usr2;
> +     /* If we received something, check for 0xff flood */
> +     if (usr2 & USR2_RDR)
> +             imx_uart_check_flood(sport, usr2);
>  
> +     for ( ; usr2 & USR2_RDR; usr2 = imx_uart_readl(sport, USR2)) {
>               flg = TTY_NORMAL;
>               sport->port.icount.rx++;
>  
> @@ -1190,55 +1246,64 @@ static void imx_uart_dma_rx_callback(void *data)
>       status = dmaengine_tx_status(chan, sport->rx_cookie, &state);
>  
>       if (status == DMA_ERROR) {
> +             spin_lock(&sport->port.lock);
>               imx_uart_clear_rx_errors(sport);
> +             spin_unlock(&sport->port.lock);
>               return;
>       }
>  
> -     if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) {
> +     /*
> +      * The state-residue variable represents the empty space
> +      * relative to the entire buffer. Taking this in consideration
> +      * the head is always calculated base on the buffer total
> +      * length - DMA transaction residue. The UART script from the
> +      * SDMA firmware will jump to the next buffer descriptor,
> +      * once a DMA transaction if finalized (IMX53 RM - A.4.1.2.4).
> +      * Taking this in consideration the tail is always at the
> +      * beginning of the buffer descriptor that contains the head.
> +      */
>  
> -             /*
> -              * The state-residue variable represents the empty space
> -              * relative to the entire buffer. Taking this in consideration
> -              * the head is always calculated base on the buffer total
> -              * length - DMA transaction residue. The UART script from the
> -              * SDMA firmware will jump to the next buffer descriptor,
> -              * once a DMA transaction if finalized (IMX53 RM - A.4.1.2.4).
> -              * Taking this in consideration the tail is always at the
> -              * beginning of the buffer descriptor that contains the head.
> -              */
> +     /* Calculate the head */
> +     rx_ring->head = sg_dma_len(sgl) - state.residue;
>  
> -             /* Calculate the head */
> -             rx_ring->head = sg_dma_len(sgl) - state.residue;
> +     /* Calculate the tail. */
> +     bd_size = sg_dma_len(sgl) / sport->rx_periods;
> +     rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size;
>  
> -             /* Calculate the tail. */
> -             bd_size = sg_dma_len(sgl) / sport->rx_periods;
> -             rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size;
> +     if (rx_ring->head <= sg_dma_len(sgl) &&
> +         rx_ring->head > rx_ring->tail) {
>  
> -             if (rx_ring->head <= sg_dma_len(sgl) &&
> -                 rx_ring->head > rx_ring->tail) {
> +             /* Move data from tail to head */
> +             r_bytes = rx_ring->head - rx_ring->tail;
>  
> -                     /* Move data from tail to head */
> -                     r_bytes = rx_ring->head - rx_ring->tail;
> +             /* If we received something, check for 0xff flood */
> +             if (r_bytes > 0) {
> +                     spin_lock(&sport->port.lock);
> +                     imx_uart_check_flood(sport, imx_uart_readl(sport, 
> USR2));
> +                     spin_unlock(&sport->port.lock);
> +             }
> +
> +             if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) {
>  
>                       /* CPU claims ownership of RX DMA buffer */
>                       dma_sync_sg_for_cpu(sport->port.dev, sgl, 1,
> -                             DMA_FROM_DEVICE);
> +                                         DMA_FROM_DEVICE);
>  
>                       w_bytes = tty_insert_flip_string(port,
> -                             sport->rx_buf + rx_ring->tail, r_bytes);
> +                                                      sport->rx_buf + 
> rx_ring->tail, r_bytes);
>  
>                       /* UART retrieves ownership of RX DMA buffer */
>                       dma_sync_sg_for_device(sport->port.dev, sgl, 1,
> -                             DMA_FROM_DEVICE);
> +                                            DMA_FROM_DEVICE);
>  
>                       if (w_bytes != r_bytes)
>                               sport->port.icount.buf_overrun++;
>  
>                       sport->port.icount.rx += w_bytes;
> -             } else  {
> -                     WARN_ON(rx_ring->head > sg_dma_len(sgl));
> -                     WARN_ON(rx_ring->head <= rx_ring->tail);
>               }
> +     } else  {
> +             WARN_ON(rx_ring->head > sg_dma_len(sgl));
> +             WARN_ON(rx_ring->head <= rx_ring->tail);
>       }
>  
>       if (w_bytes) {
> @@ -1314,6 +1379,8 @@ static void imx_uart_clear_rx_errors(struct imx_port 
> *sport)
>               imx_uart_writel(sport, USR2_ORE, USR2);
>       }
>  
> +     sport->idle_counter = 0;
> +
>  }
>  
>  #define TXTL_DEFAULT 2 /* reset default */
> -- 
> 2.25.1
> 
-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#13331): 
https://lists.yoctoproject.org/g/linux-yocto/message/13331
Mute This Topic: https://lists.yoctoproject.org/mt/102866413/21656
Group Owner: linux-yocto+ow...@lists.yoctoproject.org
Unsubscribe: 
https://lists.yoctoproject.org/g/linux-yocto/leave/6687884/21656/624485779/xyzzy
 [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to