| From 4e6f74283f054cace656f63fc89ef495afdf6a00 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 4 Mar 2021 17:23:01 +0100 |
| Subject: serial: stm32: fix wake-up flag handling |
| |
| From: Erwan Le Ray <erwan.leray@foss.st.com> |
| |
| [ Upstream commit 12761869f0efa524348e2ae31827fd52eebf3f0d ] |
| |
| This patch fixes several issue with wake-up handling: |
| - the WUF irq is handled several times at wake-up |
| - the USART is disabled / enabled at suspend to set wake-up flag. |
| It can cause glitches during RX. |
| |
| This patch fix those issues: |
| - clear wake-up flag and disable wake-up irq in WUF irq handling |
| - enable wake-up from low power on start bit detection at port |
| configuration |
| - Unmask the wake-up flag irq at suspend and mask it at resume |
| |
| In addition, pm_wakeup_event handling is moved from receice_chars to WUF |
| irq handling. |
| |
| Fixes: 270e5a74fe4c ("serial: stm32: add wakeup mechanism") |
| Signed-off-by: Erwan Le Ray <erwan.leray@foss.st.com> |
| Link: https://lore.kernel.org/r/20210304162308.8984-7-erwan.leray@foss.st.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/tty/serial/stm32-usart.c | 32 +++++++++++++++++++------------- |
| 1 file changed, 19 insertions(+), 13 deletions(-) |
| |
| diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c |
| index 5ae3841a4a08..85e9a4d4e91d 100644 |
| --- a/drivers/tty/serial/stm32-usart.c |
| +++ b/drivers/tty/serial/stm32-usart.c |
| @@ -217,9 +217,6 @@ static void stm32_usart_receive_chars(struct uart_port *port, bool threaded) |
| u32 sr; |
| char flag; |
| |
| - if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) |
| - pm_wakeup_event(tport->tty->dev, 0); |
| - |
| if (threaded) |
| spin_lock_irqsave(&port->lock, flags); |
| else |
| @@ -462,6 +459,7 @@ static void stm32_usart_transmit_chars(struct uart_port *port) |
| static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) |
| { |
| struct uart_port *port = ptr; |
| + struct tty_port *tport = &port->state->port; |
| struct stm32_port *stm32_port = to_stm32_port(port); |
| const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; |
| u32 sr; |
| @@ -472,9 +470,14 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) |
| writel_relaxed(USART_ICR_RTOCF, |
| port->membase + ofs->icr); |
| |
| - if ((sr & USART_SR_WUF) && ofs->icr != UNDEF_REG) |
| + if ((sr & USART_SR_WUF) && ofs->icr != UNDEF_REG) { |
| + /* Clear wake up flag and disable wake up interrupt */ |
| writel_relaxed(USART_ICR_WUCF, |
| port->membase + ofs->icr); |
| + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE); |
| + if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) |
| + pm_wakeup_event(tport->tty->dev, 0); |
| + } |
| |
| if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch)) |
| stm32_usart_receive_chars(port, false); |
| @@ -899,6 +902,12 @@ static void stm32_usart_set_termios(struct uart_port *port, |
| cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK); |
| } |
| |
| + /* Configure wake up from low power on start bit detection */ |
| + if (stm32_port->wakeirq > 0) { |
| + cr3 &= ~USART_CR3_WUS_MASK; |
| + cr3 |= USART_CR3_WUS_START_BIT; |
| + } |
| + |
| writel_relaxed(cr3, port->membase + ofs->cr3); |
| writel_relaxed(cr2, port->membase + ofs->cr2); |
| writel_relaxed(cr1, port->membase + ofs->cr1); |
| @@ -1466,23 +1475,20 @@ static void __maybe_unused stm32_usart_serial_en_wakeup(struct uart_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_config *cfg = &stm32_port->info->cfg; |
| - u32 val; |
| |
| if (stm32_port->wakeirq <= 0) |
| return; |
| |
| + /* |
| + * Enable low-power wake-up and wake-up irq if argument is set to |
| + * "enable", disable low-power wake-up and wake-up irq otherwise |
| + */ |
| if (enable) { |
| - stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); |
| stm32_usart_set_bits(port, ofs->cr1, USART_CR1_UESM); |
| - val = readl_relaxed(port->membase + ofs->cr3); |
| - val &= ~USART_CR3_WUS_MASK; |
| - /* Enable Wake up interrupt from low power on start bit */ |
| - val |= USART_CR3_WUS_START_BIT | USART_CR3_WUFIE; |
| - writel_relaxed(val, port->membase + ofs->cr3); |
| - stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); |
| + stm32_usart_set_bits(port, ofs->cr3, USART_CR3_WUFIE); |
| } else { |
| stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_UESM); |
| + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE); |
| } |
| } |
| |
| -- |
| 2.30.2 |
| |