From a0f46118f6ce904a80ab354d41517b908fcb2151 Mon Sep 17 00:00:00 2001 From: David Sidrane Date: Tue, 29 Oct 2019 09:57:11 -0600 Subject: [PATCH] arch/arm/src/stm32l4/stm32l4_sdmmc.c: SDMMC Fix system hang on card eject. --- arch/arm/src/stm32l4/stm32l4_sdmmc.c | 148 ++++++++++++++++++--------- 1 file changed, 97 insertions(+), 51 deletions(-) diff --git a/arch/arm/src/stm32l4/stm32l4_sdmmc.c b/arch/arm/src/stm32l4/stm32l4_sdmmc.c index a8d59a8d18..7a77d0300a 100644 --- a/arch/arm/src/stm32l4/stm32l4_sdmmc.c +++ b/arch/arm/src/stm32l4/stm32l4_sdmmc.c @@ -74,6 +74,7 @@ ****************************************************************************/ /* Configuration ************************************************************/ + /* Required system configuration options: * * CONFIG_ARCH_DMA - Enable architecture-specific DMA subsystem @@ -87,16 +88,17 @@ * CONFIG_SDIO_MUXBUS - Setting this configuration enables some locking * APIs to manage concurrent accesses on the SDMMC bus. This is not * needed for the simple case of a single SD card, for example. - * CONFIG_STM32L4_SDMMC_DMA - Enable SDMMC. This is a marginally optional. For - * most usages, SDMMC will cause data overruns if used without DMA. - * NOTE the above system DMA configuration options. + * CONFIG_STM32L4_SDMMC_DMA - Enable SDMMC. This is a marginally + * optional. For most usages, SDMMC will cause data overruns if used + * without DMA. NOTE the above system DMA configuration options. * CONFIG_SDMMC1/2_WIDTH_D1_ONLY - This may be selected to force the driver * operate with only a single data line (the default is to use all * 4 SD data lines). * CONFIG_SDMMMC_DMAPRIO - SDMMC DMA priority. This can be selecte if * CONFIG_STM32L4_SDMMC_DMA is enabled. - * CONFIG_CONFIG_STM32L4_SDMMC_XFRDEBUG - Enables some very low-level debug output - * This also requires CONFIG_DEBUG_FS and CONFIG_DEBUG_INFO + * CONFIG_CONFIG_STM32L4_SDMMC_XFRDEBUG - Enables some very low-level + * debug output. This also requires CONFIG_DEBUG_FS and + * CONFIG_DEBUG_INFO */ #ifndef CONFIG_STM32L4_SDMMC_DMA @@ -159,8 +161,9 @@ #endif /* Mode dependent settings. These depend on clock divisor settings that must - * be defined in the board-specific board.h header file: STM32_SDMMC_INIT_CLKDIV, - * STM32_SDMMC_MMCXFR_CLKDIV, and STM32_SDMMC_SDXFR_CLKDIV. + * be defined in the board-specific board.h header file: + * STM32_SDMMC_INIT_CLKDIV, STM32_SDMMC_MMCXFR_CLKDIV, and + * STM32_SDMMC_SDXFR_CLKDIV. */ #define STM32_CLCKCR_INIT (STM32_SDMMC_INIT_CLKDIV | \ @@ -329,6 +332,7 @@ struct stm32_dev_s struct sdio_dev_s dev; /* Standard, base SDIO interface */ /* STM32-specific extensions */ + uint32_t base; int nirq; #ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE @@ -402,7 +406,7 @@ struct stm32_sampleregs_s /* Low-level helpers ********************************************************/ -static inline void sdmmc_putreg32(struct stm32_dev_s *priv, uint32_t value,\ +static inline void sdmmc_putreg32(struct stm32_dev_s *priv, uint32_t value, int offset); static inline uint32_t sdmmc_getreg32(struct stm32_dev_s *priv, int offset); static void stm32_takesem(struct stm32_dev_s *priv); @@ -805,7 +809,7 @@ static void stm32_configwaitints(struct stm32_dev_s *priv, uint32_t waitmask, { /* Do not use this in STM32_SDMMC_MASK register */ - waitmask &= !SDIOWAIT_WRCOMPLETE; + waitmask &= ~SDIOWAIT_WRCOMPLETE; pinset = priv->d0_gpio & (GPIO_PORT_MASK | GPIO_PIN_MASK); pinset |= (GPIO_INPUT | GPIO_FLOAT | GPIO_EXTI); @@ -832,7 +836,8 @@ static void stm32_configwaitints(struct stm32_dev_s *priv, uint32_t waitmask, #ifdef CONFIG_STM32L4_SDMMC_DMA priv->xfrflags = 0; #endif - sdmmc_putreg32(priv, priv->xfrmask | priv->waitmask, STM32_SDMMC_MASK_OFFSET); + sdmmc_putreg32(priv, priv->xfrmask | priv->waitmask, + STM32_SDMMC_MASK_OFFSET); leave_critical_section(flags); } @@ -857,7 +862,8 @@ static void stm32_configxfrints(struct stm32_dev_s *priv, uint32_t xfrmask) flags = enter_critical_section(); priv->xfrmask = xfrmask; - sdmmc_putreg32(priv, priv->xfrmask | priv->waitmask, STM32_SDMMC_MASK_OFFSET); + sdmmc_putreg32(priv, priv->xfrmask | priv->waitmask, + STM32_SDMMC_MASK_OFFSET); leave_critical_section(flags); } @@ -1496,7 +1502,7 @@ static int stm32_sdmmc_rdyinterrupt(int irq, void *context, void *arg) static int stm32_sdmmc_interrupt(int irq, void *context, void *arg) { - struct stm32_dev_s *priv =(struct stm32_dev_s *)arg; + struct stm32_dev_s *priv = (struct stm32_dev_s *)arg; uint32_t enabled; uint32_t pending; @@ -1532,9 +1538,9 @@ static int stm32_sdmmc_interrupt(int irq, void *context, void *arg) stm32_recvfifo(priv); } - /* Otherwise, Is the transmit FIFO half empty or less? If so we must - * be processing a send transaction. NOTE: We can't be processing - * both! + /* Otherwise, Is the transmit FIFO half empty or less? If so we + * must be processing a send transaction. NOTE: We can't be + * processing both! */ else if ((pending & STM32_SDMMC_STA_TXFIFOHE) != 0) @@ -1599,7 +1605,8 @@ static int stm32_sdmmc_interrupt(int irq, void *context, void *arg) mcerr("ERROR: Data block CRC failure, remaining: %d\n", priv->remaining); - stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + stm32_endtransfer(priv, + SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); } /* Handle data timeout error */ @@ -1608,8 +1615,10 @@ static int stm32_sdmmc_interrupt(int irq, void *context, void *arg) { /* Terminate the transfer with an error */ - mcerr("ERROR: Data timeout, remaining: %d\n", priv->remaining); - stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT); + mcerr("ERROR: Data timeout, remaining: %d\n", + priv->remaining); + stm32_endtransfer(priv, + SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT); } /* Handle RX FIFO overrun error */ @@ -1618,8 +1627,10 @@ static int stm32_sdmmc_interrupt(int irq, void *context, void *arg) { /* Terminate the transfer with an error */ - mcerr("ERROR: RX FIFO overrun, remaining: %d\n", priv->remaining); - stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + mcerr("ERROR: RX FIFO overrun, remaining: %d\n", + priv->remaining); + stm32_endtransfer(priv, + SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); } /* Handle TX FIFO underrun error */ @@ -1628,8 +1639,10 @@ static int stm32_sdmmc_interrupt(int irq, void *context, void *arg) { /* Terminate the transfer with an error */ - mcerr("ERROR: TX FIFO underrun, remaining: %d\n", priv->remaining); - stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + mcerr("ERROR: TX FIFO underrun, remaining: %d\n", + priv->remaining); + stm32_endtransfer(priv, + SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); } } @@ -1649,7 +1662,8 @@ static int stm32_sdmmc_interrupt(int irq, void *context, void *arg) /* Yes.. wake the thread up */ sdmmc_putreg32(priv, STM32_SDMMC_RESPDONE_ICR | - STM32_SDMMC_CMDDONE_ICR, STM32_SDMMC_ICR_OFFSET); + STM32_SDMMC_CMDDONE_ICR, + STM32_SDMMC_ICR_OFFSET); stm32_endwait(priv, SDIOWAIT_RESPONSEDONE); } } @@ -1723,7 +1737,8 @@ static void stm32_reset(FAR struct sdio_dev_s *dev) /* Disable clocking */ flags = enter_critical_section(); - sdmmc_modifyreg32(priv, STM32_SDMMC_CLKCR_OFFSET, STM32_SDMMC_CLKCR_CLKEN, 0); + sdmmc_modifyreg32(priv, STM32_SDMMC_CLKCR_OFFSET, + STM32_SDMMC_CLKCR_CLKEN, 0); stm32_setpwrctrl(priv, STM32_SDMMC_POWER_PWRCTRL_OFF); /* Put SDIO registers in their default, reset state */ @@ -1926,13 +1941,14 @@ static int stm32_attach(FAR struct sdio_dev_s *dev) ret = irq_attach(priv->nirq, stm32_sdmmc_interrupt, priv); if (ret == OK) { - /* Disable all interrupts at the SDIO controller and clear static * interrupt flags */ - sdmmc_putreg32(priv, STM32_SDMMC_MASK_RESET, STM32_SDMMC_MASK_OFFSET); - sdmmc_putreg32(priv, STM32_SDMMC_ICR_STATICFLAGS, STM32_SDMMC_ICR_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_MASK_RESET, + STM32_SDMMC_MASK_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_ICR_STATICFLAGS, + STM32_SDMMC_ICR_OFFSET); /* Enable SDIO interrupts at the NVIC. They can now be enabled at * the SDIO controller as needed. @@ -1960,7 +1976,8 @@ static int stm32_attach(FAR struct sdio_dev_s *dev) * ****************************************************************************/ -static int stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t arg) +static int stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t arg) { struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; uint32_t regval; @@ -2023,8 +2040,9 @@ static int stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t arg) * (interrupt driven mode). This method will do whatever controller setup * is necessary. This would be called for SD memory just BEFORE sending * CMD13 (SEND_STATUS), CMD17 (READ_SINGLE_BLOCK), CMD18 - * (READ_MULTIPLE_BLOCKS), ACMD51 (SEND_SCR), etc. Normally, SDMMC_WAITEVENT - * will be called to receive the indication that the transfer is complete. + * (READ_MULTIPLE_BLOCKS), ACMD51 (SEND_SCR), etc. Normally, + * SDMMC_WAITEVENT will be called to receive the indication that the + * transfer is complete. * * Input Parameters: * dev - An instance of the SDIO device interface @@ -2076,9 +2094,9 @@ static int stm32_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, * Name: stm32_sendsetup * * Description: - * Setup hardware in preparation for data transfer from the card. This method - * will do whatever controller setup is necessary. This would be called - * for SD memory just AFTER sending CMD24 (WRITE_BLOCK), CMD25 + * Setup hardware in preparation for data transfer from the card. This + * method will do whatever controller setup is necessary. This would be + * called for SD memory just AFTER sending CMD24 (WRITE_BLOCK), CMD25 * (WRITE_MULTIPLE_BLOCK), ... and before SDMMC_SENDDATA is called. * * Input Parameters: @@ -2464,6 +2482,7 @@ static int stm32_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd, { *rshort = sdmmc_getreg32(priv, STM32_SDMMC_RESP1_OFFSET); } + return ret; } @@ -2585,7 +2604,21 @@ static sdio_eventset_t stm32_eventwait(FAR struct sdio_dev_s *dev, */ flags = enter_critical_section(); + +#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE) + /* A card ejected while in SDIOWAIT_WRCOMPLETE can lead to a + * condition where there is no waitevents set and no wkupevent + */ + + if (priv->waitevents == 0 && priv->wkupevent == 0) + { + wkupevent = SDIOWAIT_ERROR; + goto erroutdisable; + } + +#else DEBUGASSERT(priv->waitevents != 0 || priv->wkupevent != 0); +#endif /* Check if the timeout event is specified in the event set */ @@ -2639,16 +2672,17 @@ static sdio_eventset_t stm32_eventwait(FAR struct sdio_dev_s *dev, for (; ; ) { - /* Wait for an event in event set to occur. If this the event has already - * occurred, then the semaphore will already have been incremented and - * there will be no wait. + /* Wait for an event in event set to occur. If this the event has + * already occurred, then the semaphore will already have been + * incremented and there will be no wait. */ stm32_takesem(priv); wkupevent = priv->wkupevent; /* Check if the event has occurred. When the event has occurred, then - * evenset will be set to 0 and wkupevent will be set to a nonzero value. + * evenset will be set to 0 and wkupevent will be set to a nonzero + * value. */ if (wkupevent != 0) @@ -2661,6 +2695,10 @@ static sdio_eventset_t stm32_eventwait(FAR struct sdio_dev_s *dev, /* Disable event-related interrupts */ +#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE) +erroutdisable: +#endif + stm32_configwaitints(priv, 0, 0, 0); #ifdef CONFIG_STM32L4_SDMMC_DMA priv->xfrflags = 0; @@ -2833,10 +2871,11 @@ static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, stm32_configxfrints(priv, STM32_SDMMC_DMARECV_MASK); - sdmmc_modifyreg32(priv, STM32_SDMMC_DCTRL_OFFSET, 0, STM32_SDMMC_DCTRL_DMAEN); + sdmmc_modifyreg32(priv, STM32_SDMMC_DCTRL_OFFSET, 0, + STM32_SDMMC_DCTRL_DMAEN); stm32l4_dmasetup(priv->dma, priv->base + STM32_SDMMC_FIFO_OFFSET, - (uint32_t)buffer, (buflen + 3) >> 2, - SDMMC_RXDMA32_CONFIG | priv->dmapri); + (uint32_t)buffer, (buflen + 3) >> 2, + SDMMC_RXDMA32_CONFIG | priv->dmapri); /* Start the DMA */ @@ -2899,10 +2938,12 @@ static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev, /* Configure the TX DMA */ - stm32l4_dmasetup(priv->dma, priv->base + STM32_SDMMC_FIFO_OFFSET, (uint32_t)buffer, - (buflen + 3) >> 2, SDMMC_TXDMA32_CONFIG | priv->dmapri); + stm32l4_dmasetup(priv->dma, priv->base + STM32_SDMMC_FIFO_OFFSET, + (uint32_t)buffer, (buflen + 3) >> 2, + SDMMC_TXDMA32_CONFIG | priv->dmapri); - sdmmc_modifyreg32(priv, STM32_SDMMC_DCTRL_OFFSET, 0, STM32_SDMMC_DCTRL_DMAEN); + sdmmc_modifyreg32(priv, STM32_SDMMC_DCTRL_OFFSET, 0, + STM32_SDMMC_DCTRL_DMAEN); stm32_sample(priv, SAMPLENDX_BEFORE_ENABLE); /* Start the DMA */ @@ -2983,14 +3024,17 @@ static void stm32_callback(void *arg) { /* Yes.. queue it */ - mcinfo("Queuing callback to %p(%p)\n", priv->callback, priv->cbarg); - (void)work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0); + mcinfo("Queuing callback to %p(%p)\n", + priv->callback, priv->cbarg); + (void)work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback, + priv->cbarg, 0); } else { /* No.. then just call the callback here */ - mcinfo("Callback to %p(%p)\n", priv->callback, priv->cbarg); + mcinfo("Callback to %p(%p)\n", + priv->callback, priv->cbarg); priv->callback(priv->cbarg); } } @@ -3031,7 +3075,8 @@ static void stm32_default(struct stm32_dev_s *priv) * slotno - Not used. * * Returned Value: - * A reference to an SDIO interface structure. NULL is returned on failures. + * A reference to an SDIO interface structure. NULL is returned on + * failures. * ****************************************************************************/ @@ -3062,8 +3107,8 @@ FAR struct sdio_dev_s *sdio_initialize(int slotno) /* Configure GPIOs for 4-bit, wide-bus operation (the chip is capable of * 8-bit wide bus operation but D4-D7 are not configured). * - * If bus is multiplexed then there is a custom bus configuration utility - * in the scope of the board support package. + * If bus is multiplexed then there is a custom bus configuration + * utility in the scope of the board support package. */ #ifndef CONFIG_SDIO_MUXBUS stm32l4_configgpio(GPIO_SDMMC1_D0); @@ -3098,8 +3143,8 @@ FAR struct sdio_dev_s *sdio_initialize(int slotno) /* Configure GPIOs for 4-bit, wide-bus operation (the chip is capable of * 8-bit wide bus operation but D4-D7 are not configured). * - * If bus is multiplexed then there is a custom bus configuration utility - * in the scope of the board support package. + * If bus is multiplexed then there is a custom bus configuration + * utility in the scope of the board support package. */ #ifndef CONFIG_SDIO_MUXBUS @@ -3121,6 +3166,7 @@ FAR struct sdio_dev_s *sdio_initialize(int slotno) } /* Initialize the SDIO slot structure */ + /* Initialize semaphores */ nxsem_init(&priv->waitsem, 0, 0);