linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: Sergey Organov <sorganov@gmail.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-serial@vger.kernel.org,
	"Fabio Estevam" <festevam@gmail.com>,
	"Jiri Slaby" <jirislaby@kernel.org>,
	"Richard Genoud" <richard.genoud@gmail.com>,
	"Sascha Hauer" <s.hauer@pengutronix.de>,
	"Shawn Guo" <shawnguo@kernel.org>,
	"Tim Harvey" <tharvey@gateworks.com>,
	"Tomasz Moń" <tomasz.mon@camlingroup.com>,
	"Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>,
	"Johan Hovold" <johan@kernel.org>,
	"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>,
	"Sherry Sun" <sherry.sun@nxp.com>,
	"Stefan Wahren" <stefan.wahren@i2se.com>,
	linux-arm-kernel@lists.infradead.org,
	"NXP Linux Team" <linux-imx@nxp.com>,
	"Pengutronix Kernel Team" <kernel@pengutronix.de>,
	"Sergey Organov" <sorganov@gmail.com>
Subject: [PATCH v1 2/7] serial: imx: work-around for hardware RX flood
Date: Wed,  1 Feb 2023 17:26:55 +0300	[thread overview]
Message-ID: <20230201142700.4346-3-sorganov@gmail.com> (raw)
In-Reply-To: <20230201142700.4346-1-sorganov@gmail.com>

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 <sorganov@gmail.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 bf222d8568a9..e7fce31e460d 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -210,6 +210,9 @@ struct imx_port {
 
 	struct mctrl_gpios *gpios;
 
+	/* counter to stop 0xff flood */
+	int idle_counter;
+
 	/* shadow registers */
 	unsigned int ucr1;
 	unsigned int ucr2;
@@ -428,6 +431,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 */
@@ -834,15 +839,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;
 
-	while (imx_uart_readl(sport, USR2) & USR2_RDR) {
-		u32 usr2;
+	usr2 = imx_uart_readl(sport, 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++;
 
@@ -1180,55 +1236,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) {
@@ -1304,6 +1369,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.30.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  parent reply	other threads:[~2023-02-01 14:41 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <87bko4e65y.fsf@osv.gnss.ru>
2023-01-13 18:43 ` [PATCH 0/8] serial: imx: work-around for hardware RX flood, and then isr improvements Sergey Organov
2023-01-13 18:43   ` [PATCH 1/8] serial: imx: factor-out common code to imx_uart_soft_reset() Sergey Organov
2023-01-16 10:30     ` Ilpo Järvinen
2023-01-17 13:42       ` Sergey Organov
2023-01-13 18:43   ` [PATCH 2/8] serial: imx: work-around for hardware RX flood Sergey Organov
2023-01-13 18:43   ` [PATCH 3/8] serial: imx: do not sysrq broken chars Sergey Organov
2023-01-16 10:41     ` Ilpo Järvinen
2023-01-16 15:24     ` Johan Hovold
2023-01-17 17:35       ` Sergey Organov
2023-01-18  8:06         ` Johan Hovold
2023-01-13 18:43   ` [PATCH 4/8] serial: imx: do not break from FIFO reading loop prematurely Sergey Organov
2023-01-13 18:43   ` [PATCH 5/8] serial: imx: remove redundant USR2 read from FIFO reading loop Sergey Organov
2023-01-16 10:50     ` Ilpo Järvinen
2023-01-13 18:43   ` [PATCH 6/8] serial: imx: stop using USR2 in " Sergey Organov
2023-01-16 10:54     ` Ilpo Järvinen
2023-01-17 13:30       ` Sergey Organov
2023-01-13 18:43   ` [PATCH 7/8] serial: imx: use readl() to optimize " Sergey Organov
2023-01-16 11:03     ` Ilpo Järvinen
2023-01-17 17:43       ` Sergey Organov
2023-01-18  8:24         ` Ilpo Järvinen
2023-01-18 15:43           ` Sergey Organov
2023-01-18 19:29             ` Ilpo Järvinen
2023-01-17 10:20     ` Sherry Sun
2023-01-17 17:48       ` Sergey Organov
2023-01-17 11:32     ` Uwe Kleine-König
2023-01-17 13:22       ` Sergey Organov
2023-01-17 21:27         ` Uwe Kleine-König
2023-01-18 15:40           ` Sergey Organov
2023-01-19  7:01             ` Uwe Kleine-König
2023-01-21 18:04               ` Sergey Organov
2023-01-13 18:43   ` [PATCH 8/8] serial: imx: refine local variables in rxint() Sergey Organov
2023-01-21 15:36 ` [PATCH v1 0/7] serial: imx: work-around for hardware RX flood, and then isr improvements Sergey Organov
2023-01-21 15:36   ` [PATCH v1 1/7] serial: imx: factor-out common code to imx_uart_soft_reset() Sergey Organov
2023-01-21 15:36   ` [PATCH v1 2/7] serial: imx: work-around for hardware RX flood Sergey Organov
2023-01-21 15:36   ` [PATCH v1 3/7] serial: imx: do not sysrq broken chars Sergey Organov
2023-01-21 16:12     ` Stefan Wahren
2023-01-21 21:04       ` Sergey Organov
2023-01-23 12:38         ` Ilpo Järvinen
2023-01-23 16:34           ` Sergey Organov
2023-01-21 15:36   ` [PATCH v1 4/7] serial: imx: do not break from FIFO reading loop prematurely Sergey Organov
2023-01-21 15:36   ` [PATCH v1 5/7] serial: imx: remove redundant USR2 read from FIFO reading loop Sergey Organov
2023-01-21 15:36   ` [PATCH v1 6/7] serial: imx: stop using USR2 in " Sergey Organov
2023-01-21 15:36   ` [PATCH v1 7/7] serial: imx: refine local variables in rxint() Sergey Organov
2023-02-01 14:16 ` [PATCH RESEND] serial: imx: get rid of registers shadowing Sergey Organov
2023-02-01 14:26 ` [PATCH v1 RESEND 0/7] serial: imx: work-around for hardware RX flood, and then isr improvements Sergey Organov
2023-02-01 14:26   ` [PATCH v1 1/7] serial: imx: factor-out common code to imx_uart_soft_reset() Sergey Organov
2023-02-01 14:26   ` Sergey Organov [this message]
2023-02-01 14:26   ` [PATCH v1 3/7] serial: imx: do not sysrq broken chars Sergey Organov
2023-02-01 14:26   ` [PATCH v1 4/7] serial: imx: do not break from FIFO reading loop prematurely Sergey Organov
2023-02-01 14:26   ` [PATCH v1 5/7] serial: imx: remove redundant USR2 read from FIFO reading loop Sergey Organov
2023-02-01 14:26   ` [PATCH v1 6/7] serial: imx: stop using USR2 in " Sergey Organov
2023-02-01 14:27   ` [PATCH v1 7/7] serial: imx: refine local variables in rxint() Sergey Organov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230201142700.4346-3-sorganov@gmail.com \
    --to=sorganov@gmail.com \
    --cc=festevam@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=jirislaby@kernel.org \
    --cc=johan@kernel.org \
    --cc=kernel@pengutronix.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-imx@nxp.com \
    --cc=linux-serial@vger.kernel.org \
    --cc=richard.genoud@gmail.com \
    --cc=s.hauer@pengutronix.de \
    --cc=shawnguo@kernel.org \
    --cc=sherry.sun@nxp.com \
    --cc=stefan.wahren@i2se.com \
    --cc=tharvey@gateworks.com \
    --cc=tomasz.mon@camlingroup.com \
    --cc=u.kleine-koenig@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).