From 2c0a0aef9f3cd654d7094a5b6c3610660baf2fe8 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 21 Sep 2018 06:43:52 -0600 Subject: [PATCH] arch/arm/src/stm32f7: Remove adhoc PM interfaces and add CONFIG_PM serial suspend. --- arch/arm/src/stm32/stm32_serial.c | 4 +- arch/arm/src/stm32f7/stm32_i2c.c | 5 + arch/arm/src/stm32f7/stm32_serial.c | 377 ++++++++++++++++++++++---- arch/arm/src/stm32f7/stm32_spi.c | 5 + arch/arm/src/stm32f7/stm32_uart.h | 10 - arch/arm/src/stm32l4/stm32l4_1wire.c | 5 + arch/arm/src/stm32l4/stm32l4_i2c.c | 5 + arch/arm/src/stm32l4/stm32l4_serial.c | 13 +- arch/arm/src/stm32l4/stm32l4_spi.c | 5 + include/nuttx/power/pm.h | 10 +- 10 files changed, 362 insertions(+), 77 deletions(-) diff --git a/arch/arm/src/stm32/stm32_serial.c b/arch/arm/src/stm32/stm32_serial.c index bfe1b3d08a0..474fe1bc01c 100644 --- a/arch/arm/src/stm32/stm32_serial.c +++ b/arch/arm/src/stm32/stm32_serial.c @@ -1070,12 +1070,12 @@ static inline void up_setusartint(struct up_dev_s *priv, uint16_t ie) /* And restore the interrupt state (see the interrupt enable/usage table above) */ - cr = up_serialin(priv, STM32_USART_CR1_OFFSET); + cr = up_serialin(priv, STM32_USART_CR1_OFFSET); cr &= ~(USART_CR1_USED_INTS); cr |= (ie & (USART_CR1_USED_INTS)); up_serialout(priv, STM32_USART_CR1_OFFSET, cr); - cr = up_serialin(priv, STM32_USART_CR3_OFFSET); + cr = up_serialin(priv, STM32_USART_CR3_OFFSET); cr &= ~USART_CR3_EIE; cr |= (ie & USART_CR3_EIE); up_serialout(priv, STM32_USART_CR3_OFFSET, cr); diff --git a/arch/arm/src/stm32f7/stm32_i2c.c b/arch/arm/src/stm32f7/stm32_i2c.c index 4d132cb83b0..51a9f31b550 100644 --- a/arch/arm/src/stm32f7/stm32_i2c.c +++ b/arch/arm/src/stm32f7/stm32_i2c.c @@ -2729,6 +2729,11 @@ static int stm32_i2c_pm_prepare(FAR struct pm_callback_s *cb, int domain, } break; + + default: + /* Should not get here */ + + break; } return OK; diff --git a/arch/arm/src/stm32f7/stm32_serial.c b/arch/arm/src/stm32f7/stm32_serial.c index 7f42dde2d7b..1d871e98573 100644 --- a/arch/arm/src/stm32f7/stm32_serial.c +++ b/arch/arm/src/stm32f7/stm32_serial.c @@ -302,6 +302,14 @@ struct up_dev_s bool initialized; +#ifdef CONFIG_PM + bool suspended; /* UART device has been suspended. */ + + /* Interrupt mask value stored before suspending for stop mode. */ + + uint16_t suspended_ie; +#endif + /* If termios are supported, then the following fields may vary at * runtime. */ @@ -351,6 +359,9 @@ struct up_dev_s #ifdef SERIAL_HAVE_DMA DMA_HANDLE rxdma; /* currently-open receive DMA stream */ bool rxenable; /* DMA-based reception en/disable */ +#ifdef CONFIG_PM + bool rxdmasusp; /* Rx DMA suspended */ +#endif uint32_t rxdmanext; /* Next byte in the DMA buffer to be read */ #ifdef CONFIG_ARMV7M_DCACHE uint32_t rxdmaavail; /* Number of bytes available without need to @@ -371,7 +382,9 @@ struct up_dev_s * Private Function Prototypes ****************************************************************************/ +#ifndef CONFIG_SUPPRESS_UART_CONFIG static void up_set_format(struct uart_dev_s *dev); +#endif static int up_setup(struct uart_dev_s *dev); static void up_shutdown(struct uart_dev_s *dev); static int up_attach(struct uart_dev_s *dev); @@ -395,6 +408,9 @@ static bool up_txready(struct uart_dev_s *dev); static int up_dma_setup(struct uart_dev_s *dev); static void up_dma_shutdown(struct uart_dev_s *dev); static int up_dma_receive(struct uart_dev_s *dev, unsigned int *status); +#ifdef CONFIG_PM +static void up_dma_reenable(struct up_dev_s *priv); +#endif static void up_dma_rxint(struct uart_dev_s *dev, bool enable); static bool up_dma_rxavailable(struct uart_dev_s *dev); @@ -402,6 +418,8 @@ static void up_dma_rxcallback(DMA_HANDLE handle, uint8_t status, void *arg); #endif #ifdef CONFIG_PM +static void up_setsuspend(struct uart_dev_s *dev, bool suspend); +static void up_pm_setsuspend(bool suspend); static void up_pm_notify(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate); static int up_pm_prepare(struct pm_callback_s *cb, int domain, @@ -1074,10 +1092,15 @@ static struct up_dev_s * const g_uart_devs[STM32_NSERIAL] = }; #ifdef CONFIG_PM -static struct pm_callback_s g_serialcb = +static struct { - .notify = up_pm_notify, - .prepare = up_pm_prepare, + struct pm_callback_s pm_cb; + bool serial_suspended; +} g_serialpm = +{ + .pm_cb.notify = up_pm_notify, + .pm_cb.prepare = up_pm_prepare, + .serial_suspended = false }; #endif @@ -1369,6 +1392,154 @@ static void up_set_format(struct uart_dev_s *dev) } #endif /* CONFIG_SUPPRESS_UART_CONFIG */ +/**************************************************************************** + * Name: up_setsuspend + * + * Description: + * Suspend or resume serial peripheral. + * + ****************************************************************************/ + +#ifdef CONFIG_PM +static void up_setsuspend(struct uart_dev_s *dev, bool suspend) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + int passes; +#ifdef SERIAL_HAVE_DMA + bool dmarestored = false; +#endif + + if (priv->suspended == suspend) + { + return; + } + + priv->suspended = suspend; + + if (suspend) + { +#ifdef CONFIG_SERIAL_IFLOWCONTROL + if (priv->iflow) + { + /* Force RTS high to prevent further Rx. */ + + stm32_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK) + | (GPIO_OUTPUT | GPIO_OUTPUT_SET)); + } +#endif + + /* Disable interrupts to prevent Tx. */ + + up_disableusartint(priv, &priv->suspended_ie); + + /* Loop until last Tx has completed or, + * until we have been looping for a long time. + */ + + for (passes = 0; passes < 256; passes++) + { + if ((up_serialin(priv, STM32_USART_ISR_OFFSET) & USART_ISR_TC) != 0) + { + break; + } + } + +#ifdef SERIAL_HAVE_DMA + if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp) + { + /* Suspend Rx DMA. */ + + stm32_dmastop(priv->rxdma); + priv->rxdmasusp = true; + } +#endif + } + else + { +#ifdef SERIAL_HAVE_DMA + if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp) + { + /* Re-enable DMA. */ + + up_dma_reenable(priv); + priv->rxdmasusp = false; + + dmarestored = true; + } +#endif + + /* Re-enable interrupts to resume Tx. */ + + up_restoreusartint(priv, priv->suspended_ie); + +#ifdef CONFIG_SERIAL_IFLOWCONTROL + if (priv->iflow) + { + /* Restore peripheral RTS control. */ + + stm32_configgpio(priv->rts_gpio); + } +#endif + } + +#ifdef SERIAL_HAVE_DMA + if (dmarestored) + { + irqstate_t flags; + + flags = enter_critical_section(); + + /* Perform initial Rx DMA buffer fetch to wake-up serial device + * activity. + */ + + if (priv->rxdma != NULL) + { + up_dma_rxcallback(priv->rxdma, 0, priv); + } + + leave_critical_section(flags); + } +#endif +} +#endif + +/**************************************************************************** + * Name: up_pm_setsuspend + * + * Description: + * Suspend or resume serial peripherals for/from deep-sleep/stop modes. + * + ****************************************************************************/ + +#ifdef CONFIG_PM +static void up_pm_setsuspend(bool suspend) +{ + int n; + + /* Already in desired state? */ + + if (suspend == g_serialpm.serial_suspended) + { + return; + } + + g_serialpm.serial_suspended = suspend; + + for (n = 0; n < STM32F7_NUSART + STM32F7_NUART; n++) + { + struct up_dev_s *priv = g_uart_devs[n]; + + if (!priv || !priv->initialized) + { + continue; + } + + up_setsuspend(&priv->dev, suspend); + } +} +#endif + /**************************************************************************** * Name: up_set_apb_clock * @@ -1541,7 +1712,8 @@ static int up_setup(struct uart_dev_s *dev) /* Clear CTSE, RTSE, and all interrupt enable bits */ regval = up_serialin(priv, STM32_USART_CR3_OFFSET); - regval &= ~(USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE | USART_CR3_EIE); + regval &= ~(USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE | + USART_CR3_EIE); up_serialout(priv, STM32_USART_CR3_OFFSET, regval); @@ -1599,13 +1771,13 @@ static int up_dma_setup(struct uart_dev_s *dev) priv->rxdma = stm32_dmachannel(priv->rxdma_channel); - /* Configure for circular DMA reception into the RX FIFO */ + /* Configure for circular DMA reception into the RX FIFO */ - stm32_dmasetup(priv->rxdma, - priv->usartbase + STM32_USART_RDR_OFFSET, - (uint32_t)priv->rxfifo, - RXDMA_BUFFER_SIZE, - SERIAL_DMA_CONTROL_WORD); + stm32_dmasetup(priv->rxdma, + priv->usartbase + STM32_USART_RDR_OFFSET, + (uint32_t)priv->rxfifo, + RXDMA_BUFFER_SIZE, + SERIAL_DMA_CONTROL_WORD); /* Reset our DMA shadow pointer and Rx data availability count to match * the address just programmed above. @@ -1622,12 +1794,12 @@ static int up_dma_setup(struct uart_dev_s *dev) regval |= USART_CR3_DMAR; up_serialout(priv, STM32_USART_CR3_OFFSET, regval); - /* Start the DMA channel, and arrange for callbacks at the half and - * full points in the FIFO. This ensures that we have half a FIFO - * worth of time to claim bytes before they are overwritten. - */ + /* Start the DMA channel, and arrange for callbacks at the half and + * full points in the FIFO. This ensures that we have half a FIFO + * worth of time to claim bytes before they are overwritten. + */ - stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, true); + stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, true); return OK; } @@ -2437,6 +2609,44 @@ static int up_dma_receive(struct uart_dev_s *dev, unsigned int *status) } #endif +/**************************************************************************** + * Name: up_dma_reenable + * + * Description: + * Call to re-enable RX DMA. + * + ****************************************************************************/ + +#if defined(SERIAL_HAVE_DMA) && defined(CONFIG_PM) +static void up_dma_reenable(struct up_dev_s *priv) +{ + /* Configure for circular DMA reception into the RX FIFO */ + + stm32_dmasetup(priv->rxdma, + priv->usartbase + STM32_USART_RDR_OFFSET, + (uint32_t)priv->rxfifo, + RXDMA_BUFFER_SIZE, + SERIAL_DMA_CONTROL_WORD); + + /* Reset our DMA shadow pointer to match the address just + * programmed above. + */ + + priv->rxdmanext = 0; + + /* Start the DMA channel, and arrange for callbacks at the half and + * full points in the FIFO. This ensures that we have half a FIFO + * worth of time to claim bytes before they are overwritten. + */ + + stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, true); + + /* Clear DMA suspended flag. */ + + priv->rxdmasusp = false; +} +#endif + /**************************************************************************** * Name: up_dma_rxint * @@ -2604,6 +2814,24 @@ static void up_dma_rxcallback(DMA_HANDLE handle, uint8_t status, void *arg) { uart_recvchars(&priv->dev); } + + /* Get the masked USART status word to check and clear error flags. + * + * When wake-up from low power mode was not fast enough, UART is resumed too + * late and sometimes exactly when character was coming over UART, resulting + * to frame error. + + * If error flag is not cleared, Rx DMA will be stuck. Clearing errors + * will release Rx DMA. + */ + + priv->sr = up_serialin(priv, STM32_USART_ISR_OFFSET); + + if ((priv->sr & (USART_ISR_ORE | USART_ISR_NF | USART_ISR_FE)) != 0) + { + up_serialout(priv, STM32_USART_ICR_OFFSET, + (USART_ICR_NCF | USART_ICR_ORECF | USART_ICR_FECF)); + } } #endif @@ -2635,36 +2863,33 @@ static void up_pm_notify(struct pm_callback_s *cb, int domain, { switch (pmstate) { - case(PM_NORMAL): + case PM_NORMAL: { - /* Logic for PM_NORMAL goes here */ - + up_pm_setsuspend(false); } break; - case(PM_IDLE): + case PM_IDLE: { - /* Logic for PM_IDLE goes here */ - + up_pm_setsuspend(false); } break; - case(PM_STANDBY): + case PM_STANDBY: { - /* Logic for PM_STANDBY goes here */ - + up_pm_setsuspend(true); } break; - case(PM_SLEEP): + case PM_SLEEP: { - /* Logic for PM_SLEEP goes here */ - + up_pm_setsuspend(true); } break; default: /* Should not get here */ + break; } } @@ -2707,8 +2932,74 @@ static void up_pm_notify(struct pm_callback_s *cb, int domain, static int up_pm_prepare(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate) { + int n; + /* Logic to prepare for a reduced power state goes here. */ + switch (pmstate) + { + case PM_NORMAL: + case PM_IDLE: + break; + + case PM_STANDBY: + case PM_SLEEP: + +#ifdef SERIAL_HAVE_DMA + /* Flush Rx DMA buffers before checking state of serial device + * buffers. + */ + + stm32_serial_dma_poll(); +#endif + + /* Check if any of the active ports have data pending on Tx/Rx + * buffers. + */ + + for (n = 0; n < STM32F7_NUSART + STM32F7_NUART; n++) + { + struct up_dev_s *priv = g_uart_devs[n]; + + if (!priv || !priv->initialized) + { + /* Not active, skip. */ + + continue; + } + + if (priv->suspended) + { + /* Port already suspended, skip. */ + + continue; + } + + if (priv->dev.isconsole) + { + /* Allow losing some debug traces. */ + + continue; + } + + /* Check if port has data pending (Rx & Tx). */ + + if (priv->dev.xmit.head != priv->dev.xmit.tail) + { + return ERROR; + } + if (priv->dev.recv.head != priv->dev.recv.tail) + { + return ERROR; + } + } + break; + + default: + /* Should not get here */ + break; + } + return OK; } #endif @@ -2721,31 +3012,6 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain, #ifdef USE_SERIALDRIVER -/**************************************************************************** - * Name: stm32_serial_get_uart - * - * Description: - * Get serial driver structure for STM32 USART - * - ****************************************************************************/ - -FAR uart_dev_t *stm32_serial_get_uart(int uart_num) -{ - int uart_idx = uart_num - 1; - - if (uart_idx < 0 || uart_idx >= STM32_NSERIAL || !g_uart_devs[uart_idx]) - { - return NULL; - } - - if (!g_uart_devs[uart_idx]->initialized) - { - return NULL; - } - - return &g_uart_devs[uart_idx]->dev; -} - /**************************************************************************** * Name: up_earlyserialinit * @@ -2800,10 +3066,10 @@ void up_serialinit(void) int ret; #endif +#ifdef CONFIG_PM /* Register to receive power management callbacks */ -#ifdef CONFIG_PM - ret = pm_register(&g_serialcb); + ret = pm_register(&g_serialpm.pm_cb); DEBUGASSERT(ret == OK); UNUSED(ret); #endif @@ -2941,7 +3207,7 @@ void stm32_serial_dma_poll(void) * Name: up_putc * * Description: - * Provide priority, low-level access to support OS debug writes + * Provide priority, low-level access to support OS debug writes * ****************************************************************************/ @@ -2964,7 +3230,6 @@ int up_putc(int ch) up_lowputc(ch); up_restoreusartint(priv, ie); - #endif return ch; } diff --git a/arch/arm/src/stm32f7/stm32_spi.c b/arch/arm/src/stm32f7/stm32_spi.c index 4d433518ecf..fb366a5df62 100644 --- a/arch/arm/src/stm32f7/stm32_spi.c +++ b/arch/arm/src/stm32f7/stm32_spi.c @@ -1787,6 +1787,11 @@ static int spi_pm_prepare(FAR struct pm_callback_s *cb, int domain, } break; + + default: + /* Should not get here */ + + break; } return OK; diff --git a/arch/arm/src/stm32f7/stm32_uart.h b/arch/arm/src/stm32f7/stm32_uart.h index 496d7e9ea42..b53f532693d 100644 --- a/arch/arm/src/stm32f7/stm32_uart.h +++ b/arch/arm/src/stm32f7/stm32_uart.h @@ -320,16 +320,6 @@ extern "C" * Public Functions ************************************************************************************/ -/************************************************************************************ - * Name: stm32_serial_get_uart - * - * Description: - * Get serial driver structure for STM32 USART - * - ************************************************************************************/ - -FAR uart_dev_t *stm32_serial_get_uart(int uart_num); - /************************************************************************************ * Name: stm32_serial_dma_poll * diff --git a/arch/arm/src/stm32l4/stm32l4_1wire.c b/arch/arm/src/stm32l4/stm32l4_1wire.c index b884cd57b29..5be038beca2 100644 --- a/arch/arm/src/stm32l4/stm32l4_1wire.c +++ b/arch/arm/src/stm32l4/stm32l4_1wire.c @@ -1179,6 +1179,11 @@ static int stm32_1wire_pm_prepare(FAR struct pm_callback_s *cb, int domain, } break; + + default: + /* Should not get here */ + + break; } return OK; diff --git a/arch/arm/src/stm32l4/stm32l4_i2c.c b/arch/arm/src/stm32l4/stm32l4_i2c.c index fa1bd0ead97..8bce93048f9 100644 --- a/arch/arm/src/stm32l4/stm32l4_i2c.c +++ b/arch/arm/src/stm32l4/stm32l4_i2c.c @@ -2876,6 +2876,11 @@ static int stm32l4_i2c_pm_prepare(FAR struct pm_callback_s *cb, int domain, } break; + + default: + /* Should not get here */ + + break; } return OK; diff --git a/arch/arm/src/stm32l4/stm32l4_serial.c b/arch/arm/src/stm32l4/stm32l4_serial.c index 3db05181d28..fcb3414c54e 100644 --- a/arch/arm/src/stm32l4/stm32l4_serial.c +++ b/arch/arm/src/stm32l4/stm32l4_serial.c @@ -340,7 +340,7 @@ static int stm32l4serial_pmprepare(FAR struct pm_callback_s *cb, int domain, #endif /**************************************************************************** - * Private Variables + * Private Data ****************************************************************************/ #ifndef SERIAL_HAVE_ONLY_DMA @@ -1187,7 +1187,7 @@ static void stm32l4serial_setsuspend(struct uart_dev_s *dev, bool suspend) ****************************************************************************/ #ifdef CONFIG_PM -void stm32l4serial_pm_setsuspend(bool suspend) +static void stm32l4serial_pm_setsuspend(bool suspend) { int n; @@ -2613,6 +2613,7 @@ static void stm32l4serial_pmnotify(FAR struct pm_callback_s *cb, int domain, default: /* Should not get here */ + break; } } @@ -2709,9 +2710,13 @@ static int stm32l4serial_pmprepare(FAR struct pm_callback_s *cb, int domain, return ERROR; } } + break; + default: + /* Should not get here */ break; } + return OK; } #endif @@ -2731,7 +2736,7 @@ static int stm32l4serial_pmprepare(FAR struct pm_callback_s *cb, int domain, * Description: * Performs the low level USART initialization early in debug so that the * serial console will be available during bootup. This must be called - * before stm32l4serial_getregit. + * before up_serialinit. * ****************************************************************************/ @@ -2899,7 +2904,7 @@ void stm32l4_serial_dma_poll(void) * Name: up_putc * * Description: - * Provide priority, low-level access to support OS debug writes + * Provide priority, low-level access to support OS debug writes * ****************************************************************************/ diff --git a/arch/arm/src/stm32l4/stm32l4_spi.c b/arch/arm/src/stm32l4/stm32l4_spi.c index fa3fc299e5d..156143c9c0b 100644 --- a/arch/arm/src/stm32l4/stm32l4_spi.c +++ b/arch/arm/src/stm32l4/stm32l4_spi.c @@ -1560,6 +1560,11 @@ static int spi_pm_prepare(FAR struct pm_callback_s *cb, int domain, } break; + + default: + /* Should not get here */ + + break; } return OK; diff --git a/include/nuttx/power/pm.h b/include/nuttx/power/pm.h index 6b0fdb115af..0928264ba88 100644 --- a/include/nuttx/power/pm.h +++ b/include/nuttx/power/pm.h @@ -588,14 +588,14 @@ enum pm_state_e pm_querystate(int domain); */ # define pm_initialize() -# define pm_register(cb) (0) -# define pm_unregister(cb) (0) +# define pm_register(cb) (0) +# define pm_unregister(cb) (0) # define pm_activity(domain,prio) # define pm_stay(domain,state) # define pm_relax(domain,state) -# define pm_checkstate(domain) (0) -# define pm_changestate(domain,state) -# define pm_querystate(domain) (0) +# define pm_checkstate(domain) (0) +# define pm_changestate(domain,state) (0) +# define pm_querystate(domain) (0) #endif /* CONFIG_PM */ #endif /* __INCLUDE_NUTTX_POWER_PM_H */