提交 6eeb348c 编写于 作者: E Erwan Le Ray 提交者: Greg Kroah-Hartman

serial: stm32: terminate / restart DMA transfer at suspend / resume

DMA prevents the system to suspend when an UART RX wake-up source is
using DMA. DMA can't suspend while DMA channels are still active.

Terminate DMA transfer at suspend, and restart a new DMA transfer at
resume. Create stm32_usart_start_rx_dma_cyclic function to factorize
dma RX initialization. Move RX DMA code related to wakeup into
stm32_usart_serial_en_wakeup() routine to ease further improvements
on wakeup from low power modes.

Don't enable/disable wakeup on uninitialized port.

There may be data residue in the RX FIFO while suspending. Flush it at
suspend time. Receiver timeout interrupt won't trigger later in low power
mode, so call stm32_usart_receive_chars() in case there's data to handle.
Signed-off-by: NValentin Caron <valentin.caron@foss.st.com>
Signed-off-by: NErwan Le Ray <erwan.leray@foss.st.com>
Link: https://lore.kernel.org/r/20211025134229.8456-3-erwan.leray@foss.st.comSigned-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
上级 e0abc903
...@@ -775,13 +775,54 @@ static void stm32_usart_break_ctl(struct uart_port *port, int break_state) ...@@ -775,13 +775,54 @@ 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;
int ret;
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");
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);
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) static int stm32_usart_startup(struct uart_port *port)
{ {
struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_port *stm32_port = to_stm32_port(port);
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
const struct stm32_usart_config *cfg = &stm32_port->info->cfg; const struct stm32_usart_config *cfg = &stm32_port->info->cfg;
const char *name = to_platform_device(port->dev)->name; const char *name = to_platform_device(port->dev)->name;
struct dma_async_tx_descriptor *desc;
u32 val; u32 val;
int ret; int ret;
...@@ -803,49 +844,18 @@ static int stm32_usart_startup(struct uart_port *port) ...@@ -803,49 +844,18 @@ static int stm32_usart_startup(struct uart_port *port)
writel_relaxed(USART_RQR_RXFRQ, port->membase + ofs->rqr); writel_relaxed(USART_RQR_RXFRQ, port->membase + ofs->rqr);
if (stm32_port->rx_ch) { if (stm32_port->rx_ch) {
stm32_port->last_res = RX_BUF_L; ret = stm32_usart_start_rx_dma_cyclic(port);
/* 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");
ret = -ENODEV;
goto err;
}
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) { if (ret) {
dmaengine_terminate_sync(stm32_port->rx_ch); free_irq(port->irq, port);
goto err; 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);
/* RX enabling */ /* RX enabling */
val = stm32_port->cr1_irq | USART_CR1_RE | BIT(cfg->uart_enable_bit); val = stm32_port->cr1_irq | USART_CR1_RE | BIT(cfg->uart_enable_bit);
stm32_usart_set_bits(port, ofs->cr1, val); stm32_usart_set_bits(port, ofs->cr1, val);
return 0; return 0;
err:
free_irq(port->irq, port);
return ret;
} }
static void stm32_usart_shutdown(struct uart_port *port) static void stm32_usart_shutdown(struct uart_port *port)
...@@ -1661,14 +1671,16 @@ static struct uart_driver stm32_usart_driver = { ...@@ -1661,14 +1671,16 @@ static struct uart_driver stm32_usart_driver = {
.cons = STM32_SERIAL_CONSOLE, .cons = STM32_SERIAL_CONSOLE,
}; };
static void __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port, static int __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port,
bool enable) bool enable)
{ {
struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_port *stm32_port = to_stm32_port(port);
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct tty_port *tport = &port->state->port;
int ret;
if (!stm32_port->wakeup_src) if (!stm32_port->wakeup_src || !tty_port_initialized(tport))
return; return 0;
/* /*
* Enable low-power wake-up and wake-up irq if argument is set to * Enable low-power wake-up and wake-up irq if argument is set to
...@@ -1677,20 +1689,45 @@ static void __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port, ...@@ -1677,20 +1689,45 @@ static void __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port,
if (enable) { if (enable) {
stm32_usart_set_bits(port, ofs->cr1, USART_CR1_UESM); stm32_usart_set_bits(port, ofs->cr1, USART_CR1_UESM);
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_WUFIE); stm32_usart_set_bits(port, ofs->cr3, USART_CR3_WUFIE);
/*
* When DMA is used for reception, it must be disabled before
* entering low-power mode and re-enabled when exiting from
* low-power mode.
*/
if (stm32_port->rx_ch) {
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
dmaengine_terminate_sync(stm32_port->rx_ch);
}
/* Poll data from RX FIFO if any */
stm32_usart_receive_chars(port, false);
} else { } else {
if (stm32_port->rx_ch) {
ret = stm32_usart_start_rx_dma_cyclic(port);
if (ret)
return ret;
}
stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_UESM); stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_UESM);
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE); stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE);
} }
return 0;
} }
static int __maybe_unused stm32_usart_serial_suspend(struct device *dev) static int __maybe_unused stm32_usart_serial_suspend(struct device *dev)
{ {
struct uart_port *port = dev_get_drvdata(dev); struct uart_port *port = dev_get_drvdata(dev);
int ret;
uart_suspend_port(&stm32_usart_driver, port); uart_suspend_port(&stm32_usart_driver, port);
if (device_may_wakeup(dev) || device_wakeup_path(dev)) if (device_may_wakeup(dev) || device_wakeup_path(dev)) {
stm32_usart_serial_en_wakeup(port, true); ret = stm32_usart_serial_en_wakeup(port, true);
if (ret)
return ret;
}
/* /*
* When "no_console_suspend" is enabled, keep the pinctrl default state * When "no_console_suspend" is enabled, keep the pinctrl default state
...@@ -1711,11 +1748,15 @@ static int __maybe_unused stm32_usart_serial_suspend(struct device *dev) ...@@ -1711,11 +1748,15 @@ static int __maybe_unused stm32_usart_serial_suspend(struct device *dev)
static int __maybe_unused stm32_usart_serial_resume(struct device *dev) static int __maybe_unused stm32_usart_serial_resume(struct device *dev)
{ {
struct uart_port *port = dev_get_drvdata(dev); struct uart_port *port = dev_get_drvdata(dev);
int ret;
pinctrl_pm_select_default_state(dev); pinctrl_pm_select_default_state(dev);
if (device_may_wakeup(dev) || device_wakeup_path(dev)) if (device_may_wakeup(dev) || device_wakeup_path(dev)) {
stm32_usart_serial_en_wakeup(port, false); ret = stm32_usart_serial_en_wakeup(port, false);
if (ret)
return ret;
}
return uart_resume_port(&stm32_usart_driver, port); return uart_resume_port(&stm32_usart_driver, port);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册