linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/6] serial: stm32: improve DMA pause and resume
@ 2023-08-08 16:19 Valentin Caron
  2023-08-08 16:19 ` [PATCH v2 1/6] serial: stm32: avoid clearing DMAT bit during transfer Valentin Caron
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Valentin Caron @ 2023-08-08 16:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Jiri Slaby, Alexandre Torgue, Amelie Delaunay, linux-serial,
	linux-kernel, linux-stm32, linux-arm-kernel, Valentin Caron

This series improves DMA management in stm32-usart driver.

It is not recommended to switch DMAT and DMAR bits when a DMA transfer
is in progress. This is going to be replaced by dmaengine_pause or
dmaengine_resume.

stm32_usart_rx_dma_pause, stm32_usart_rx_dma_resume, stm32_usart_tx_dma_pause
and stm32_usart_tx_dma_resume functions, are all redirected to a single function,
called "stm32_usart_dma_pause_resume", that has been created to simplify dma
pause/resume.

Since v1:
- Add my Signed-off in Amelie's patch.

Amelie Delaunay (1):
  serial: stm32: synchronize RX DMA channel in shutdown

Valentin Caron (5):
  serial: stm32: avoid clearing DMAT bit during transfer
  serial: stm32: use DMAT as a configuration bit
  serial: stm32: modify parameter and rename stm32_usart_rx_dma_enabled
  serial: stm32: group dma pause/resume error handling into single
    function
  serial: stm32: replace access to DMAR bit by dmaengine_pause/resume

 drivers/tty/serial/stm32-usart.c | 307 ++++++++++++++++++-------------
 drivers/tty/serial/stm32-usart.h |   1 +
 2 files changed, 185 insertions(+), 123 deletions(-)

-- 
2.25.1


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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 1/6] serial: stm32: avoid clearing DMAT bit during transfer
  2023-08-08 16:19 [PATCH v2 0/6] serial: stm32: improve DMA pause and resume Valentin Caron
@ 2023-08-08 16:19 ` Valentin Caron
  2023-08-08 16:19 ` [PATCH v2 2/6] serial: stm32: use DMAT as a configuration bit Valentin Caron
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Valentin Caron @ 2023-08-08 16:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Jiri Slaby, Alexandre Torgue, Amelie Delaunay, linux-serial,
	linux-kernel, linux-stm32, linux-arm-kernel, Valentin Caron

It's rather advised to rely on DMA pause / resume instead of
clearing/setting DMA request enable bit for the same purpose. Some DMA
request/acknowledge race may encountered by doing so. We prefer to use
dmaengine_pause and resume instead to pause a dma transfer when it is
necessary.

It is also safer to close DMA before reset DMAT in stm32_usart_shutdown.

Signed-off-by: Valentin Caron <valentin.caron@foss.st.com>
---
 drivers/tty/serial/stm32-usart.c | 76 ++++++++++++++++++--------------
 1 file changed, 44 insertions(+), 32 deletions(-)

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index be47cd343cf6..2f9672ba4ed3 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -506,13 +506,6 @@ static bool stm32_usart_tx_dma_started(struct stm32_port *stm32_port)
 	return stm32_port->tx_dma_busy;
 }
 
-static bool stm32_usart_tx_dma_enabled(struct stm32_port *stm32_port)
-{
-	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
-
-	return !!(readl_relaxed(stm32_port->port.membase + ofs->cr3) & USART_CR3_DMAT);
-}
-
 static void stm32_usart_tx_dma_complete(void *arg)
 {
 	struct uart_port *port = arg;
@@ -591,9 +584,6 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port)
 	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
 	struct circ_buf *xmit = &port->state->xmit;
 
-	if (stm32_usart_tx_dma_enabled(stm32_port))
-		stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
-
 	while (!uart_circ_empty(xmit)) {
 		/* Check that TDR is empty before filling FIFO */
 		if (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE))
@@ -616,10 +606,16 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
 	struct circ_buf *xmit = &port->state->xmit;
 	struct dma_async_tx_descriptor *desc = NULL;
 	unsigned int count;
+	int ret;
 
 	if (stm32_usart_tx_dma_started(stm32port)) {
-		if (!stm32_usart_tx_dma_enabled(stm32port))
-			stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT);
+		if (dmaengine_tx_status(stm32port->tx_ch,
+					stm32port->tx_ch->cookie,
+					NULL) == DMA_PAUSED) {
+			ret = dmaengine_resume(stm32port->tx_ch);
+			if (ret < 0)
+				goto dma_err;
+		}
 		return;
 	}
 
@@ -664,11 +660,9 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
 	desc->callback_param = port;
 
 	/* Push current DMA TX transaction in the pending queue */
-	if (dma_submit_error(dmaengine_submit(desc))) {
-		/* dma no yet started, safe to free resources */
-		stm32_usart_tx_dma_terminate(stm32port);
-		goto fallback_err;
-	}
+	/* DMA no yet started, safe to free resources */
+	if (dma_submit_error(dmaengine_submit(desc)))
+		goto dma_err;
 
 	/* Issue pending DMA TX requests */
 	dma_async_issue_pending(stm32port->tx_ch);
@@ -679,6 +673,10 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
 
 	return;
 
+dma_err:
+	dev_err(port->dev, "DMA failed with error code: %d\n", ret);
+	stm32_usart_tx_dma_terminate(stm32port);
+
 fallback_err:
 	stm32_usart_transmit_chars_pio(port);
 }
@@ -701,9 +699,15 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
 
 	if (port->x_char) {
 		if (stm32_usart_tx_dma_started(stm32_port) &&
-		    stm32_usart_tx_dma_enabled(stm32_port))
-			stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
-
+		    dmaengine_tx_status(stm32_port->tx_ch,
+					stm32_port->tx_ch->cookie,
+					NULL) == DMA_IN_PROGRESS) {
+			ret = dmaengine_pause(stm32_port->tx_ch);
+			if (ret < 0) {
+				dev_err(port->dev, "DMA failed with error code: %d\n", ret);
+				stm32_usart_tx_dma_terminate(stm32_port);
+			}
+		}
 		/* Check that TDR is empty before filling FIFO */
 		ret =
 		readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr,
@@ -716,8 +720,14 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
 		writel_relaxed(port->x_char, port->membase + ofs->tdr);
 		port->x_char = 0;
 		port->icount.tx++;
-		if (stm32_usart_tx_dma_started(stm32_port))
-			stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT);
+
+		if (stm32_usart_tx_dma_started(stm32_port)) {
+			ret = dmaengine_resume(stm32_port->tx_ch);
+			if (ret < 0) {
+				dev_err(port->dev, "DMA failed with error code: %d\n", ret);
+				stm32_usart_tx_dma_terminate(stm32_port);
+			}
+		}
 		return;
 	}
 
@@ -850,11 +860,16 @@ static void stm32_usart_disable_ms(struct uart_port *port)
 static void stm32_usart_stop_tx(struct uart_port *port)
 {
 	struct stm32_port *stm32_port = to_stm32_port(port);
-	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+	int ret;
 
 	stm32_usart_tx_interrupt_disable(port);
-	if (stm32_usart_tx_dma_started(stm32_port) && stm32_usart_tx_dma_enabled(stm32_port))
-		stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
+	if (stm32_usart_tx_dma_started(stm32_port)) {
+		ret = dmaengine_pause(stm32_port->tx_ch);
+		if (ret < 0) {
+			dev_err(port->dev, "DMA failed with error code: %d\n", ret);
+			stm32_usart_tx_dma_terminate(stm32_port);
+		}
+	}
 
 	stm32_usart_rs485_rts_disable(port);
 }
@@ -878,12 +893,9 @@ static void stm32_usart_start_tx(struct uart_port *port)
 static void stm32_usart_flush_buffer(struct uart_port *port)
 {
 	struct stm32_port *stm32_port = to_stm32_port(port);
-	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
 
-	if (stm32_port->tx_ch) {
+	if (stm32_port->tx_ch)
 		stm32_usart_tx_dma_terminate(stm32_port);
-		stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
-	}
 }
 
 /* Throttle the remote when input buffer is about to overflow. */
@@ -1042,12 +1054,12 @@ static void stm32_usart_shutdown(struct uart_port *port)
 	u32 val, isr;
 	int ret;
 
-	if (stm32_usart_tx_dma_enabled(stm32_port))
-		stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
-
 	if (stm32_usart_tx_dma_started(stm32_port))
 		stm32_usart_tx_dma_terminate(stm32_port);
 
+	if (stm32_port->tx_ch)
+		stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
+
 	/* Disable modem control interrupts */
 	stm32_usart_disable_ms(port);
 
-- 
2.25.1


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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 2/6] serial: stm32: use DMAT as a configuration bit
  2023-08-08 16:19 [PATCH v2 0/6] serial: stm32: improve DMA pause and resume Valentin Caron
  2023-08-08 16:19 ` [PATCH v2 1/6] serial: stm32: avoid clearing DMAT bit during transfer Valentin Caron
@ 2023-08-08 16:19 ` Valentin Caron
  2023-08-08 16:19 ` [PATCH v2 3/6] serial: stm32: modify parameter and rename stm32_usart_rx_dma_enabled Valentin Caron
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Valentin Caron @ 2023-08-08 16:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Jiri Slaby, Alexandre Torgue, Amelie Delaunay, linux-serial,
	linux-kernel, linux-stm32, linux-arm-kernel, Valentin Caron

DMAT is a configuration bit so it should be set at the startup of uart
port and not when a DMA transfer begins.

This patch move set of DMAT into set_termios and remove DMAT reset except
in shutdown.

Signed-off-by: Valentin Caron <valentin.caron@foss.st.com>
---
 drivers/tty/serial/stm32-usart.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 2f9672ba4ed3..a1585aa1ceb0 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -510,10 +510,8 @@ static void stm32_usart_tx_dma_complete(void *arg)
 {
 	struct uart_port *port = arg;
 	struct stm32_port *stm32port = to_stm32_port(port);
-	const struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
 	unsigned long flags;
 
-	stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
 	stm32_usart_tx_dma_terminate(stm32port);
 
 	/* Let's see if we have pending data to send */
@@ -602,7 +600,6 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port)
 static void stm32_usart_transmit_chars_dma(struct uart_port *port)
 {
 	struct stm32_port *stm32port = to_stm32_port(port);
-	const struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
 	struct circ_buf *xmit = &port->state->xmit;
 	struct dma_async_tx_descriptor *desc = NULL;
 	unsigned int count;
@@ -667,8 +664,6 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
 	/* Issue pending DMA TX requests */
 	dma_async_issue_pending(stm32port->tx_ch);
 
-	stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT);
-
 	uart_xmit_advance(port, count);
 
 	return;
@@ -1270,6 +1265,9 @@ static void stm32_usart_set_termios(struct uart_port *port,
 		cr3 |= USART_CR3_DDRE;
 	}
 
+	if (stm32_port->tx_ch)
+		cr3 |= USART_CR3_DMAT;
+
 	if (rs485conf->flags & SER_RS485_ENABLED) {
 		stm32_usart_config_reg_rs485(&cr1, &cr3,
 					     rs485conf->delay_rts_before_send,
-- 
2.25.1


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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 3/6] serial: stm32: modify parameter and rename stm32_usart_rx_dma_enabled
  2023-08-08 16:19 [PATCH v2 0/6] serial: stm32: improve DMA pause and resume Valentin Caron
  2023-08-08 16:19 ` [PATCH v2 1/6] serial: stm32: avoid clearing DMAT bit during transfer Valentin Caron
  2023-08-08 16:19 ` [PATCH v2 2/6] serial: stm32: use DMAT as a configuration bit Valentin Caron
@ 2023-08-08 16:19 ` Valentin Caron
  2023-08-08 16:19 ` [PATCH v2 4/6] serial: stm32: group dma pause/resume error handling into single function Valentin Caron
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Valentin Caron @ 2023-08-08 16:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Jiri Slaby, Alexandre Torgue, Amelie Delaunay, linux-serial,
	linux-kernel, linux-stm32, linux-arm-kernel, Valentin Caron

Rename stm32_usart_rx_dma_enabled to stm32_usart_rx_dma_started in order
to match with stm32_usart_tx_dma_started.

Modify argument of stm32_usart_rx_dma_started from uart_port structure to
stm32_port structure to match with stm32_usart_tx_dma_started.

Signed-off-by: Valentin Caron <valentin.caron@foss.st.com>
---
 drivers/tty/serial/stm32-usart.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index a1585aa1ceb0..3471e23bb02f 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -289,9 +289,9 @@ static int stm32_usart_init_rs485(struct uart_port *port,
 	return uart_get_rs485_mode(port);
 }
 
-static bool stm32_usart_rx_dma_enabled(struct uart_port *port)
+static bool stm32_usart_rx_dma_started(struct stm32_port *stm32_port)
 {
-	struct stm32_port *stm32_port = to_stm32_port(port);
+	struct uart_port *port = &stm32_port->port;
 	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
 
 	if (!stm32_port->rx_ch)
@@ -310,7 +310,7 @@ static bool stm32_usart_pending_rx_pio(struct uart_port *port, u32 *sr)
 	/* Get pending characters in RDR or FIFO */
 	if (*sr & USART_SR_RXNE) {
 		/* Get all pending characters from the RDR or the FIFO when using interrupts */
-		if (!stm32_usart_rx_dma_enabled(port))
+		if (!stm32_usart_rx_dma_started(stm32_port))
 			return true;
 
 		/* Handle only RX data errors when using DMA */
@@ -455,7 +455,7 @@ static unsigned int stm32_usart_receive_chars(struct uart_port *port, bool force
 	u32 sr;
 	unsigned int size = 0;
 
-	if (stm32_usart_rx_dma_enabled(port) || force_dma_flush) {
+	if (stm32_usart_rx_dma_started(stm32_port) || force_dma_flush) {
 		rx_dma_status = dmaengine_tx_status(stm32_port->rx_ch,
 						    stm32_port->rx_ch->cookie,
 						    &stm32_port->rx_dma_state);
@@ -789,8 +789,8 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr)
 	 * line has been masked by HW and rx data are stacking in FIFO.
 	 */
 	if (!stm32_port->throttled) {
-		if (((sr & USART_SR_RXNE) && !stm32_usart_rx_dma_enabled(port)) ||
-		    ((sr & USART_SR_ERR_MASK) && stm32_usart_rx_dma_enabled(port))) {
+		if (((sr & USART_SR_RXNE) && !stm32_usart_rx_dma_started(stm32_port)) ||
+		    ((sr & USART_SR_ERR_MASK) && stm32_usart_rx_dma_started(stm32_port))) {
 			spin_lock(&port->lock);
 			size = stm32_usart_receive_chars(port, false);
 			uart_unlock_and_check_sysrq(port);
@@ -806,7 +806,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr)
 	}
 
 	/* Receiver timeout irq for DMA RX */
-	if (stm32_usart_rx_dma_enabled(port) && !stm32_port->throttled) {
+	if (stm32_usart_rx_dma_started(stm32_port) && !stm32_port->throttled) {
 		spin_lock(&port->lock);
 		size = stm32_usart_receive_chars(port, false);
 		uart_unlock_and_check_sysrq(port);
@@ -906,7 +906,7 @@ static void stm32_usart_throttle(struct uart_port *port)
 	 * Disable DMA request line if enabled, so the RX data gets queued into the FIFO.
 	 * Hardware flow control is triggered when RX FIFO is full.
 	 */
-	if (stm32_usart_rx_dma_enabled(port))
+	if (stm32_usart_rx_dma_started(stm32_port))
 		stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
 
 	stm32_usart_clr_bits(port, ofs->cr1, stm32_port->cr1_irq);
-- 
2.25.1


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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 4/6] serial: stm32: group dma pause/resume error handling into single function
  2023-08-08 16:19 [PATCH v2 0/6] serial: stm32: improve DMA pause and resume Valentin Caron
                   ` (2 preceding siblings ...)
  2023-08-08 16:19 ` [PATCH v2 3/6] serial: stm32: modify parameter and rename stm32_usart_rx_dma_enabled Valentin Caron
@ 2023-08-08 16:19 ` Valentin Caron
  2023-08-08 16:19 ` [PATCH v2 5/6] serial: stm32: replace access to DMAR bit by dmaengine_pause/resume Valentin Caron
  2023-08-08 16:19 ` [PATCH v2 6/6] serial: stm32: synchronize RX DMA channel in shutdown Valentin Caron
  5 siblings, 0 replies; 7+ messages in thread
From: Valentin Caron @ 2023-08-08 16:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Jiri Slaby, Alexandre Torgue, Amelie Delaunay, linux-serial,
	linux-kernel, linux-stm32, linux-arm-kernel, Valentin Caron

Create new function "stm32_usart_dma_pause_resume" that called dmaengine_
pause/resume and in case of error, terminate dma transaction.

Two other functions are created to facilitate the use of stm32_usart_dma
_pause_resume : stm32_usart_tx_dma_pause, stm32_usart_tx_dma_resume.
Equivalent functions for rx will be added in future patch.

Signed-off-by: Valentin Caron <valentin.caron@foss.st.com>
---
 drivers/tty/serial/stm32-usart.c | 127 ++++++++++++++++++++-----------
 drivers/tty/serial/stm32-usart.h |   1 +
 2 files changed, 83 insertions(+), 45 deletions(-)

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 3471e23bb02f..0dae05a7abe6 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -290,14 +290,40 @@ static int stm32_usart_init_rs485(struct uart_port *port,
 }
 
 static bool stm32_usart_rx_dma_started(struct stm32_port *stm32_port)
+{
+	return stm32_port->rx_ch ? stm32_port->rx_dma_busy : false;
+}
+
+static void stm32_usart_rx_dma_terminate(struct stm32_port *stm32_port)
+{
+	dmaengine_terminate_async(stm32_port->rx_ch);
+	stm32_port->rx_dma_busy = false;
+}
+
+static int stm32_usart_dma_pause_resume(struct stm32_port *stm32_port,
+					struct dma_chan *chan,
+					enum dma_status expected_status,
+					int dmaengine_pause_or_resume(struct dma_chan *),
+					bool stm32_usart_xx_dma_started(struct stm32_port *),
+					void stm32_usart_xx_dma_terminate(struct stm32_port *))
 {
 	struct uart_port *port = &stm32_port->port;
-	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+	enum dma_status dma_status;
+	int ret;
+
+	if (!stm32_usart_xx_dma_started(stm32_port))
+		return -EPERM;
 
-	if (!stm32_port->rx_ch)
-		return false;
+	dma_status = dmaengine_tx_status(chan, chan->cookie, NULL);
+	if (dma_status != expected_status)
+		return -EAGAIN;
 
-	return !!(readl_relaxed(port->membase + ofs->cr3) & USART_CR3_DMAR);
+	ret = dmaengine_pause_or_resume(chan);
+	if (ret) {
+		dev_err(port->dev, "DMA failed with error code: %d\n", ret);
+		stm32_usart_xx_dma_terminate(stm32_port);
+	}
+	return ret;
 }
 
 /* Return true when data is pending (in pio mode), and false when no data is pending. */
@@ -475,7 +501,7 @@ static unsigned int stm32_usart_receive_chars(struct uart_port *port, bool force
 			}
 		} else {
 			/* Disable RX DMA */
-			dmaengine_terminate_async(stm32_port->rx_ch);
+			stm32_usart_rx_dma_terminate(stm32_port);
 			stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
 			/* Fall back to interrupt mode */
 			dev_dbg(port->dev, "DMA error, fallback to irq mode\n");
@@ -506,6 +532,22 @@ static bool stm32_usart_tx_dma_started(struct stm32_port *stm32_port)
 	return stm32_port->tx_dma_busy;
 }
 
+static int stm32_usart_tx_dma_pause(struct stm32_port *stm32_port)
+{
+	return stm32_usart_dma_pause_resume(stm32_port, stm32_port->tx_ch,
+					    DMA_IN_PROGRESS, dmaengine_pause,
+					    stm32_usart_tx_dma_started,
+					    stm32_usart_tx_dma_terminate);
+}
+
+static int stm32_usart_tx_dma_resume(struct stm32_port *stm32_port)
+{
+	return stm32_usart_dma_pause_resume(stm32_port, stm32_port->tx_ch,
+					    DMA_PAUSED, dmaengine_resume,
+					    stm32_usart_tx_dma_started,
+					    stm32_usart_tx_dma_terminate);
+}
+
 static void stm32_usart_tx_dma_complete(void *arg)
 {
 	struct uart_port *port = arg;
@@ -606,13 +648,9 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
 	int ret;
 
 	if (stm32_usart_tx_dma_started(stm32port)) {
-		if (dmaengine_tx_status(stm32port->tx_ch,
-					stm32port->tx_ch->cookie,
-					NULL) == DMA_PAUSED) {
-			ret = dmaengine_resume(stm32port->tx_ch);
-			if (ret < 0)
-				goto dma_err;
-		}
+		ret = stm32_usart_tx_dma_resume(stm32port);
+		if (ret < 0 && ret != -EAGAIN)
+			goto fallback_err;
 		return;
 	}
 
@@ -658,8 +696,12 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
 
 	/* Push current DMA TX transaction in the pending queue */
 	/* DMA no yet started, safe to free resources */
-	if (dma_submit_error(dmaengine_submit(desc)))
-		goto dma_err;
+	ret = dma_submit_error(dmaengine_submit(desc));
+	if (ret) {
+		dev_err(port->dev, "DMA failed with error code: %d\n", ret);
+		stm32_usart_tx_dma_terminate(stm32port);
+		goto fallback_err;
+	}
 
 	/* Issue pending DMA TX requests */
 	dma_async_issue_pending(stm32port->tx_ch);
@@ -668,10 +710,6 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
 
 	return;
 
-dma_err:
-	dev_err(port->dev, "DMA failed with error code: %d\n", ret);
-	stm32_usart_tx_dma_terminate(stm32port);
-
 fallback_err:
 	stm32_usart_transmit_chars_pio(port);
 }
@@ -693,16 +731,9 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
 	}
 
 	if (port->x_char) {
-		if (stm32_usart_tx_dma_started(stm32_port) &&
-		    dmaengine_tx_status(stm32_port->tx_ch,
-					stm32_port->tx_ch->cookie,
-					NULL) == DMA_IN_PROGRESS) {
-			ret = dmaengine_pause(stm32_port->tx_ch);
-			if (ret < 0) {
-				dev_err(port->dev, "DMA failed with error code: %d\n", ret);
-				stm32_usart_tx_dma_terminate(stm32_port);
-			}
-		}
+		/* dma terminate may have been called in case of dma pause failure */
+		stm32_usart_tx_dma_pause(stm32_port);
+
 		/* Check that TDR is empty before filling FIFO */
 		ret =
 		readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr,
@@ -716,13 +747,8 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
 		port->x_char = 0;
 		port->icount.tx++;
 
-		if (stm32_usart_tx_dma_started(stm32_port)) {
-			ret = dmaengine_resume(stm32_port->tx_ch);
-			if (ret < 0) {
-				dev_err(port->dev, "DMA failed with error code: %d\n", ret);
-				stm32_usart_tx_dma_terminate(stm32_port);
-			}
-		}
+		/* dma terminate may have been called in case of dma resume failure */
+		stm32_usart_tx_dma_resume(stm32_port);
 		return;
 	}
 
@@ -855,16 +881,11 @@ static void stm32_usart_disable_ms(struct uart_port *port)
 static void stm32_usart_stop_tx(struct uart_port *port)
 {
 	struct stm32_port *stm32_port = to_stm32_port(port);
-	int ret;
 
 	stm32_usart_tx_interrupt_disable(port);
-	if (stm32_usart_tx_dma_started(stm32_port)) {
-		ret = dmaengine_pause(stm32_port->tx_ch);
-		if (ret < 0) {
-			dev_err(port->dev, "DMA failed with error code: %d\n", ret);
-			stm32_usart_tx_dma_terminate(stm32_port);
-		}
-	}
+
+	/* dma terminate may have been called in case of dma pause failure */
+	stm32_usart_tx_dma_pause(stm32_port);
 
 	stm32_usart_rs485_rts_disable(port);
 }
@@ -965,8 +986,22 @@ static int stm32_usart_start_rx_dma_cyclic(struct uart_port *port)
 	struct stm32_port *stm32_port = to_stm32_port(port);
 	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
 	struct dma_async_tx_descriptor *desc;
+	enum dma_status rx_dma_status;
 	int ret;
 
+	if (stm32_port->rx_dma_busy) {
+		rx_dma_status = dmaengine_tx_status(stm32_port->rx_ch,
+						    stm32_port->rx_ch->cookie,
+						    NULL);
+		if (rx_dma_status == DMA_IN_PROGRESS)
+			return 0;
+
+		dev_err(port->dev, "DMA failed : status error.\n");
+		stm32_usart_rx_dma_terminate(stm32_port);
+	}
+
+	stm32_port->rx_dma_busy = true;
+
 	stm32_port->last_res = RX_BUF_L;
 	/* Prepare a DMA cyclic transaction */
 	desc = dmaengine_prep_dma_cyclic(stm32_port->rx_ch,
@@ -976,6 +1011,7 @@ static int stm32_usart_start_rx_dma_cyclic(struct uart_port *port)
 					 DMA_PREP_INTERRUPT);
 	if (!desc) {
 		dev_err(port->dev, "rx dma prep cyclic failed\n");
+		stm32_port->rx_dma_busy = false;
 		return -ENODEV;
 	}
 
@@ -986,6 +1022,7 @@ static int stm32_usart_start_rx_dma_cyclic(struct uart_port *port)
 	ret = dma_submit_error(dmaengine_submit(desc));
 	if (ret) {
 		dmaengine_terminate_sync(stm32_port->rx_ch);
+		stm32_port->rx_dma_busy = false;
 		return ret;
 	}
 
@@ -1074,7 +1111,7 @@ static void stm32_usart_shutdown(struct uart_port *port)
 
 	/* Disable RX DMA. */
 	if (stm32_port->rx_ch)
-		dmaengine_terminate_async(stm32_port->rx_ch);
+		stm32_usart_rx_dma_terminate(stm32_port);
 
 	/* flush RX & TX FIFO */
 	if (ofs->rqr != UNDEF_REG)
@@ -1988,7 +2025,7 @@ static int __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port,
 			stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
 			/* Poll data from DMA RX buffer if any */
 			size = stm32_usart_receive_chars(port, true);
-			dmaengine_terminate_async(stm32_port->rx_ch);
+			stm32_usart_rx_dma_terminate(stm32_port);
 			uart_unlock_and_check_sysrq_irqrestore(port, flags);
 			if (size)
 				tty_flip_buffer_push(tport);
diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h
index 903285b5aea7..f59f831b2a10 100644
--- a/drivers/tty/serial/stm32-usart.h
+++ b/drivers/tty/serial/stm32-usart.h
@@ -199,6 +199,7 @@ struct stm32_port {
 	u32 cr3_irq;		 /* USART_CR3_RXFTIE */
 	int last_res;
 	bool tx_dma_busy;	 /* dma tx transaction in progress */
+	bool rx_dma_busy;	 /* dma rx transaction in progress */
 	bool throttled;		 /* port throttled            */
 	bool hw_flow_control;
 	bool swap;		 /* swap RX & TX pins */
-- 
2.25.1


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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 5/6] serial: stm32: replace access to DMAR bit by dmaengine_pause/resume
  2023-08-08 16:19 [PATCH v2 0/6] serial: stm32: improve DMA pause and resume Valentin Caron
                   ` (3 preceding siblings ...)
  2023-08-08 16:19 ` [PATCH v2 4/6] serial: stm32: group dma pause/resume error handling into single function Valentin Caron
@ 2023-08-08 16:19 ` Valentin Caron
  2023-08-08 16:19 ` [PATCH v2 6/6] serial: stm32: synchronize RX DMA channel in shutdown Valentin Caron
  5 siblings, 0 replies; 7+ messages in thread
From: Valentin Caron @ 2023-08-08 16:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Jiri Slaby, Alexandre Torgue, Amelie Delaunay, linux-serial,
	linux-kernel, linux-stm32, linux-arm-kernel, Valentin Caron

It's rather advised to rely on DMA pause / resume instead of
clearing/setting DMA request enable bit for the same purpose. Some DMA
request/acknowledge race may encountered by doing so. We prefer to use
dmaengine_pause and resume instead to pause a dma transfer when it is
necessary.

Create two new functions (stm32_usart_rx_dma_pause, stm32_usart_rx_dma
_resume) to handle dma error when pausing/resuming.

And rename stm32_usart_start_rx_dma_cyclic() to
stm32_usart_rx_dma_start_or_resume() and use this function to resume an
rx dma transfer. If resume fail, stm32_usart_rx_dma_start_or_resume can
create a new transfer to continue.

It is also safer to close DMA before reset DMAR in stm32_usart_shutdown.

Signed-off-by: Valentin Caron <valentin.caron@foss.st.com>
---
 drivers/tty/serial/stm32-usart.c | 200 ++++++++++++++++---------------
 1 file changed, 106 insertions(+), 94 deletions(-)

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 0dae05a7abe6..8fc0526be898 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -326,6 +326,22 @@ static int stm32_usart_dma_pause_resume(struct stm32_port *stm32_port,
 	return ret;
 }
 
+static int stm32_usart_rx_dma_pause(struct stm32_port *stm32_port)
+{
+	return stm32_usart_dma_pause_resume(stm32_port, stm32_port->rx_ch,
+					    DMA_IN_PROGRESS, dmaengine_pause,
+					    stm32_usart_rx_dma_started,
+					    stm32_usart_rx_dma_terminate);
+}
+
+static int stm32_usart_rx_dma_resume(struct stm32_port *stm32_port)
+{
+	return stm32_usart_dma_pause_resume(stm32_port, stm32_port->rx_ch,
+					    DMA_PAUSED, dmaengine_resume,
+					    stm32_usart_rx_dma_started,
+					    stm32_usart_rx_dma_terminate);
+}
+
 /* Return true when data is pending (in pio mode), and false when no data is pending. */
 static bool stm32_usart_pending_rx_pio(struct uart_port *port, u32 *sr)
 {
@@ -485,7 +501,8 @@ static unsigned int stm32_usart_receive_chars(struct uart_port *port, bool force
 		rx_dma_status = dmaengine_tx_status(stm32_port->rx_ch,
 						    stm32_port->rx_ch->cookie,
 						    &stm32_port->rx_dma_state);
-		if (rx_dma_status == DMA_IN_PROGRESS) {
+		if (rx_dma_status == DMA_IN_PROGRESS ||
+		    rx_dma_status == DMA_PAUSED) {
 			/* Empty DMA buffer */
 			size = stm32_usart_receive_chars_dma(port);
 			sr = readl_relaxed(port->membase + ofs->isr);
@@ -502,7 +519,6 @@ static unsigned int stm32_usart_receive_chars(struct uart_port *port, bool force
 		} else {
 			/* Disable RX DMA */
 			stm32_usart_rx_dma_terminate(stm32_port);
-			stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
 			/* Fall back to interrupt mode */
 			dev_dbg(port->dev, "DMA error, fallback to irq mode\n");
 			size = stm32_usart_receive_chars_pio(port);
@@ -514,6 +530,76 @@ static unsigned int stm32_usart_receive_chars(struct uart_port *port, bool force
 	return size;
 }
 
+static void stm32_usart_rx_dma_complete(void *arg)
+{
+	struct uart_port *port = arg;
+	struct tty_port *tport = &port->state->port;
+	unsigned int size;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	size = stm32_usart_receive_chars(port, false);
+	uart_unlock_and_check_sysrq_irqrestore(port, flags);
+	if (size)
+		tty_flip_buffer_push(tport);
+}
+
+static int stm32_usart_rx_dma_start_or_resume(struct uart_port *port)
+{
+	struct stm32_port *stm32_port = to_stm32_port(port);
+	struct dma_async_tx_descriptor *desc;
+	enum dma_status rx_dma_status;
+	int ret;
+
+	if (stm32_port->throttled)
+		return 0;
+
+	if (stm32_port->rx_dma_busy) {
+		rx_dma_status = dmaengine_tx_status(stm32_port->rx_ch,
+						    stm32_port->rx_ch->cookie,
+						    NULL);
+		if (rx_dma_status == DMA_IN_PROGRESS)
+			return 0;
+
+		if (rx_dma_status == DMA_PAUSED && !stm32_usart_rx_dma_resume(stm32_port))
+			return 0;
+
+		dev_err(port->dev, "DMA failed : status error.\n");
+		stm32_usart_rx_dma_terminate(stm32_port);
+	}
+
+	stm32_port->rx_dma_busy = true;
+
+	stm32_port->last_res = RX_BUF_L;
+	/* Prepare a DMA cyclic transaction */
+	desc = dmaengine_prep_dma_cyclic(stm32_port->rx_ch,
+					 stm32_port->rx_dma_buf,
+					 RX_BUF_L, RX_BUF_P,
+					 DMA_DEV_TO_MEM,
+					 DMA_PREP_INTERRUPT);
+	if (!desc) {
+		dev_err(port->dev, "rx dma prep cyclic failed\n");
+		stm32_port->rx_dma_busy = false;
+		return -ENODEV;
+	}
+
+	desc->callback = stm32_usart_rx_dma_complete;
+	desc->callback_param = port;
+
+	/* Push current DMA transaction in the pending queue */
+	ret = dma_submit_error(dmaengine_submit(desc));
+	if (ret) {
+		dmaengine_terminate_sync(stm32_port->rx_ch);
+		stm32_port->rx_dma_busy = false;
+		return ret;
+	}
+
+	/* Issue pending DMA requests */
+	dma_async_issue_pending(stm32_port->rx_ch);
+
+	return 0;
+}
+
 static void stm32_usart_tx_dma_terminate(struct stm32_port *stm32_port)
 {
 	dmaengine_terminate_async(stm32_port->tx_ch);
@@ -585,20 +671,6 @@ static void stm32_usart_tc_interrupt_enable(struct uart_port *port)
 	stm32_usart_set_bits(port, ofs->cr1, USART_CR1_TCIE);
 }
 
-static void stm32_usart_rx_dma_complete(void *arg)
-{
-	struct uart_port *port = arg;
-	struct tty_port *tport = &port->state->port;
-	unsigned int size;
-	unsigned long flags;
-
-	spin_lock_irqsave(&port->lock, flags);
-	size = stm32_usart_receive_chars(port, false);
-	uart_unlock_and_check_sysrq_irqrestore(port, flags);
-	if (size)
-		tty_flip_buffer_push(tport);
-}
-
 static void stm32_usart_tx_interrupt_disable(struct uart_port *port)
 {
 	struct stm32_port *stm32_port = to_stm32_port(port);
@@ -924,11 +996,10 @@ static void stm32_usart_throttle(struct uart_port *port)
 	spin_lock_irqsave(&port->lock, flags);
 
 	/*
-	 * Disable DMA request line if enabled, so the RX data gets queued into the FIFO.
+	 * Pause DMA transfer, so the RX data gets queued into the FIFO.
 	 * Hardware flow control is triggered when RX FIFO is full.
 	 */
-	if (stm32_usart_rx_dma_started(stm32_port))
-		stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
+	stm32_usart_rx_dma_pause(stm32_port);
 
 	stm32_usart_clr_bits(port, ofs->cr1, stm32_port->cr1_irq);
 	if (stm32_port->cr3_irq)
@@ -950,14 +1021,15 @@ static void stm32_usart_unthrottle(struct uart_port *port)
 	if (stm32_port->cr3_irq)
 		stm32_usart_set_bits(port, ofs->cr3, stm32_port->cr3_irq);
 
+	stm32_port->throttled = false;
+
 	/*
-	 * Switch back to DMA mode (re-enable DMA request line).
+	 * Switch back to DMA mode (resume DMA).
 	 * Hardware flow control is stopped when FIFO is not full any more.
 	 */
 	if (stm32_port->rx_ch)
-		stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAR);
+		stm32_usart_rx_dma_start_or_resume(port);
 
-	stm32_port->throttled = false;
 	spin_unlock_irqrestore(&port->lock, flags);
 }
 
@@ -968,8 +1040,7 @@ static void stm32_usart_stop_rx(struct uart_port *port)
 	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
 
 	/* Disable DMA request line. */
-	if (stm32_port->rx_ch)
-		stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
+	stm32_usart_rx_dma_pause(stm32_port);
 
 	stm32_usart_clr_bits(port, ofs->cr1, stm32_port->cr1_irq);
 	if (stm32_port->cr3_irq)
@@ -981,64 +1052,6 @@ static void stm32_usart_break_ctl(struct uart_port *port, int break_state)
 {
 }
 
-static int stm32_usart_start_rx_dma_cyclic(struct uart_port *port)
-{
-	struct stm32_port *stm32_port = to_stm32_port(port);
-	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
-	struct dma_async_tx_descriptor *desc;
-	enum dma_status rx_dma_status;
-	int ret;
-
-	if (stm32_port->rx_dma_busy) {
-		rx_dma_status = dmaengine_tx_status(stm32_port->rx_ch,
-						    stm32_port->rx_ch->cookie,
-						    NULL);
-		if (rx_dma_status == DMA_IN_PROGRESS)
-			return 0;
-
-		dev_err(port->dev, "DMA failed : status error.\n");
-		stm32_usart_rx_dma_terminate(stm32_port);
-	}
-
-	stm32_port->rx_dma_busy = true;
-
-	stm32_port->last_res = RX_BUF_L;
-	/* Prepare a DMA cyclic transaction */
-	desc = dmaengine_prep_dma_cyclic(stm32_port->rx_ch,
-					 stm32_port->rx_dma_buf,
-					 RX_BUF_L, RX_BUF_P,
-					 DMA_DEV_TO_MEM,
-					 DMA_PREP_INTERRUPT);
-	if (!desc) {
-		dev_err(port->dev, "rx dma prep cyclic failed\n");
-		stm32_port->rx_dma_busy = false;
-		return -ENODEV;
-	}
-
-	desc->callback = stm32_usart_rx_dma_complete;
-	desc->callback_param = port;
-
-	/* Push current DMA transaction in the pending queue */
-	ret = dma_submit_error(dmaengine_submit(desc));
-	if (ret) {
-		dmaengine_terminate_sync(stm32_port->rx_ch);
-		stm32_port->rx_dma_busy = false;
-		return ret;
-	}
-
-	/* Issue pending DMA requests */
-	dma_async_issue_pending(stm32_port->rx_ch);
-
-	/*
-	 * DMA request line not re-enabled at resume when port is throttled.
-	 * It will be re-enabled by unthrottle ops.
-	 */
-	if (!stm32_port->throttled)
-		stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAR);
-
-	return 0;
-}
-
 static int stm32_usart_startup(struct uart_port *port)
 {
 	struct stm32_port *stm32_port = to_stm32_port(port);
@@ -1064,7 +1077,7 @@ static int stm32_usart_startup(struct uart_port *port)
 		writel_relaxed(USART_RQR_RXFRQ, port->membase + ofs->rqr);
 
 	if (stm32_port->rx_ch) {
-		ret = stm32_usart_start_rx_dma_cyclic(port);
+		ret = stm32_usart_rx_dma_start_or_resume(port);
 		if (ret) {
 			free_irq(port->irq, port);
 			return ret;
@@ -1811,11 +1824,6 @@ static int stm32_usart_serial_remove(struct platform_device *pdev)
 	pm_runtime_put_noidle(&pdev->dev);
 
 	stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_PEIE);
-	cr3 = readl_relaxed(port->membase + ofs->cr3);
-	cr3 &= ~USART_CR3_EIE;
-	cr3 &= ~USART_CR3_DMAR;
-	cr3 &= ~USART_CR3_DDRE;
-	writel_relaxed(cr3, port->membase + ofs->cr3);
 
 	if (stm32_port->tx_ch) {
 		stm32_usart_of_dma_tx_remove(stm32_port, pdev);
@@ -1827,7 +1835,12 @@ static int stm32_usart_serial_remove(struct platform_device *pdev)
 		dma_release_channel(stm32_port->rx_ch);
 	}
 
-	stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
+	cr3 = readl_relaxed(port->membase + ofs->cr3);
+	cr3 &= ~USART_CR3_EIE;
+	cr3 &= ~USART_CR3_DMAR;
+	cr3 &= ~USART_CR3_DMAT;
+	cr3 &= ~USART_CR3_DDRE;
+	writel_relaxed(cr3, port->membase + ofs->cr3);
 
 	if (stm32_port->wakeup_src) {
 		dev_pm_clear_wake_irq(&pdev->dev);
@@ -1999,7 +2012,7 @@ static int __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port,
 	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
 	struct tty_port *tport = &port->state->port;
 	int ret;
-	unsigned int size;
+	unsigned int size = 0;
 	unsigned long flags;
 
 	if (!stm32_port->wakeup_src || !tty_port_initialized(tport))
@@ -2021,10 +2034,9 @@ static int __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port,
 		 */
 		if (stm32_port->rx_ch) {
 			spin_lock_irqsave(&port->lock, flags);
-			/* Avoid race with RX IRQ when DMAR is cleared */
-			stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
 			/* Poll data from DMA RX buffer if any */
-			size = stm32_usart_receive_chars(port, true);
+			if (!stm32_usart_rx_dma_pause(stm32_port))
+				size += stm32_usart_receive_chars(port, true);
 			stm32_usart_rx_dma_terminate(stm32_port);
 			uart_unlock_and_check_sysrq_irqrestore(port, flags);
 			if (size)
@@ -2035,7 +2047,7 @@ static int __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port,
 		stm32_usart_receive_chars(port, false);
 	} else {
 		if (stm32_port->rx_ch) {
-			ret = stm32_usart_start_rx_dma_cyclic(port);
+			ret = stm32_usart_rx_dma_start_or_resume(port);
 			if (ret)
 				return ret;
 		}
-- 
2.25.1


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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 6/6] serial: stm32: synchronize RX DMA channel in shutdown
  2023-08-08 16:19 [PATCH v2 0/6] serial: stm32: improve DMA pause and resume Valentin Caron
                   ` (4 preceding siblings ...)
  2023-08-08 16:19 ` [PATCH v2 5/6] serial: stm32: replace access to DMAR bit by dmaengine_pause/resume Valentin Caron
@ 2023-08-08 16:19 ` Valentin Caron
  5 siblings, 0 replies; 7+ messages in thread
From: Valentin Caron @ 2023-08-08 16:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Jiri Slaby, Alexandre Torgue, Amelie Delaunay, linux-serial,
	linux-kernel, linux-stm32, linux-arm-kernel, Valentin Caron

From: Amelie Delaunay <amelie.delaunay@foss.st.com>

In shutdown, RX DMA channel is terminated. If the DMA RX callback is
scheduled but not yet executed, while a new RX DMA transfer is started, the
callback can be executed, and then disturb the ongoing RX DMA transfer.
To avoid such a case, call dmaengine_synchronize in shutdown, after the
DMA RX channel is terminated.

Signed-off-by: Amelie Delaunay <amelie.delaunay@foss.st.com>
Signed-off-by: Valentin Caron <valentin.caron@foss.st.com>
---
 drivers/tty/serial/stm32-usart.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 8fc0526be898..5e9cf0c48813 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -1123,8 +1123,10 @@ static void stm32_usart_shutdown(struct uart_port *port)
 		dev_err(port->dev, "Transmission is not complete\n");
 
 	/* Disable RX DMA. */
-	if (stm32_port->rx_ch)
+	if (stm32_port->rx_ch) {
 		stm32_usart_rx_dma_terminate(stm32_port);
+		dmaengine_synchronize(stm32_port->rx_ch);
+	}
 
 	/* flush RX & TX FIFO */
 	if (ofs->rqr != UNDEF_REG)
-- 
2.25.1


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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-08-08 17:30 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-08-08 16:19 [PATCH v2 0/6] serial: stm32: improve DMA pause and resume Valentin Caron
2023-08-08 16:19 ` [PATCH v2 1/6] serial: stm32: avoid clearing DMAT bit during transfer Valentin Caron
2023-08-08 16:19 ` [PATCH v2 2/6] serial: stm32: use DMAT as a configuration bit Valentin Caron
2023-08-08 16:19 ` [PATCH v2 3/6] serial: stm32: modify parameter and rename stm32_usart_rx_dma_enabled Valentin Caron
2023-08-08 16:19 ` [PATCH v2 4/6] serial: stm32: group dma pause/resume error handling into single function Valentin Caron
2023-08-08 16:19 ` [PATCH v2 5/6] serial: stm32: replace access to DMAR bit by dmaengine_pause/resume Valentin Caron
2023-08-08 16:19 ` [PATCH v2 6/6] serial: stm32: synchronize RX DMA channel in shutdown Valentin Caron

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).