2023-08-03 13:28:26

by Valentin Caron

[permalink] [raw]
Subject: [PATCH 0/6] serial: stm32: improve DMA pause and resume

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.

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



2023-08-03 13:28:51

by Valentin Caron

[permalink] [raw]
Subject: [PATCH 4/6] serial: stm32: group dma pause/resume error handling into single function

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 <[email protected]>
---
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


2023-08-03 13:30:07

by Valentin Caron

[permalink] [raw]
Subject: [PATCH 3/6] serial: stm32: modify parameter and rename stm32_usart_rx_dma_enabled

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 <[email protected]>
---
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


2023-08-03 14:04:04

by Valentin Caron

[permalink] [raw]
Subject: [PATCH 5/6] serial: stm32: replace access to DMAR bit by dmaengine_pause/resume

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 <[email protected]>
---
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


2023-08-03 16:02:40

by Valentin Caron

[permalink] [raw]
Subject: [PATCH 2/6] serial: stm32: use DMAT as a configuration bit

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 <[email protected]>
---
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