arch/arm/src/stm32f7/stm32_sdmmc.c: SDMMC Fix system hang on card eject.

This commit is contained in:
David Sidrane
2019-10-29 09:38:02 -06:00
committed by Gregory Nutt
parent 5d095e00b3
commit 24f646a417
+77 -35
View File
@@ -1752,7 +1752,8 @@ static int stm32_sdmmc_interrupt(int irq, void *context, void *arg)
mcerr("ERROR: Data block CRC failure, remaining: %d\n", mcerr("ERROR: Data block CRC failure, remaining: %d\n",
priv->remaining); priv->remaining);
stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); stm32_endtransfer(priv,
SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
} }
/* Handle data timeout error */ /* Handle data timeout error */
@@ -1761,8 +1762,10 @@ static int stm32_sdmmc_interrupt(int irq, void *context, void *arg)
{ {
/* Terminate the transfer with an error */ /* Terminate the transfer with an error */
mcerr("ERROR: Data timeout, remaining: %d\n", priv->remaining); mcerr("ERROR: Data timeout, remaining: %d\n",
stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT); priv->remaining);
stm32_endtransfer(priv,
SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT);
} }
/* Handle RX FIFO overrun error */ /* Handle RX FIFO overrun error */
@@ -1771,8 +1774,10 @@ static int stm32_sdmmc_interrupt(int irq, void *context, void *arg)
{ {
/* Terminate the transfer with an error */ /* Terminate the transfer with an error */
mcerr("ERROR: RX FIFO overrun, remaining: %d\n", priv->remaining); mcerr("ERROR: RX FIFO overrun, remaining: %d\n"
stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); priv->remaining);
stm32_endtransfer(priv,
SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
} }
/* Handle TX FIFO underrun error */ /* Handle TX FIFO underrun error */
@@ -1783,7 +1788,8 @@ static int stm32_sdmmc_interrupt(int irq, void *context, void *arg)
mcerr("ERROR: TX FIFO underrun, remaining: %d\n", mcerr("ERROR: TX FIFO underrun, remaining: %d\n",
priv->remaining); priv->remaining);
stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); stm32_endtransfer(priv,
SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
} }
/* Handle start bit error */ /* Handle start bit error */
@@ -1792,8 +1798,10 @@ static int stm32_sdmmc_interrupt(int irq, void *context, void *arg)
{ {
/* Terminate the transfer with an error */ /* Terminate the transfer with an error */
mcerr("ERROR: Start bit, remaining: %d\n", priv->remaining); mcerr("ERROR: Start bit, remaining: %d\n",
stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); priv->remaining);
stm32_endtransfer(priv,
SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
} }
} }
@@ -1920,7 +1928,8 @@ static void stm32_reset(FAR struct sdio_dev_s *dev)
/* Disable clocking */ /* Disable clocking */
flags = enter_critical_section(); 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); stm32_setpwrctrl(priv, STM32_SDMMC_POWER_PWRCTRL_OFF);
/* Put SDIO registers in their default, reset state */ /* Put SDIO registers in their default, reset state */
@@ -2132,8 +2141,10 @@ static int stm32_attach(FAR struct sdio_dev_s *dev)
* interrupt flags * interrupt flags
*/ */
sdmmc_putreg32(priv, STM32_SDMMC_MASK_RESET, STM32_SDMMC_MASK_OFFSET); sdmmc_putreg32(priv, STM32_SDMMC_MASK_RESET,
sdmmc_putreg32(priv, STM32_SDMMC_ICR_STATICFLAGS, STM32_SDMMC_ICR_OFFSET); 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 /* Enable SDIO interrupts at the NVIC. They can now be enabled at
* the SDIO controller as needed. * the SDIO controller as needed.
@@ -2161,7 +2172,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; struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
uint32_t regval; uint32_t regval;
@@ -2232,8 +2244,8 @@ static int stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t arg)
* *
****************************************************************************/ ****************************************************************************/
static void stm32_blocksetup(FAR struct sdio_dev_s *dev, unsigned int blocksize, static void stm32_blocksetup(FAR struct sdio_dev_s *dev,
unsigned int nblocks) unsigned int blocksize, unsigned int nblocks)
{ {
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
@@ -2248,8 +2260,9 @@ static void stm32_blocksetup(FAR struct sdio_dev_s *dev, unsigned int blocksize,
* (interrupt driven mode). This method will do whatever controller setup * (interrupt driven mode). This method will do whatever controller setup
* is necessary. This would be called for SD memory just BEFORE sending * is necessary. This would be called for SD memory just BEFORE sending
* CMD13 (SEND_STATUS), CMD17 (READ_SINGLE_BLOCK), CMD18 * CMD13 (SEND_STATUS), CMD17 (READ_SINGLE_BLOCK), CMD18
* (READ_MULTIPLE_BLOCKS), ACMD51 (SEND_SCR), etc. Normally, SDMMC_WAITEVENT * (READ_MULTIPLE_BLOCKS), ACMD51 (SEND_SCR), etc. Normally,
* will be called to receive the indication that the transfer is complete. * SDMMC_WAITEVENT will be called to receive the indication that the
* transfer is complete.
* *
* Input Parameters: * Input Parameters:
* dev - An instance of the SDIO device interface * dev - An instance of the SDIO device interface
@@ -2286,7 +2299,8 @@ static int stm32_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
/* Then set up the SDIO data path */ /* Then set up the SDIO data path */
dblksize = stm32_log2(priv->blocksize) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT; dblksize = stm32_log2(priv->blocksize) <<
STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT * ((nbytes + 511) >> 9), stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT * ((nbytes + 511) >> 9),
nbytes, dblksize | STM32_SDMMC_DCTRL_DTDIR); nbytes, dblksize | STM32_SDMMC_DCTRL_DTDIR);
@@ -2301,9 +2315,9 @@ static int stm32_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
* Name: stm32_sendsetup * Name: stm32_sendsetup
* *
* Description: * Description:
* Setup hardware in preparation for data transfer from the card. This method * Setup hardware in preparation for data transfer from the card. This
* will do whatever controller setup is necessary. This would be called * method will do whatever controller setup is necessary. This would be
* for SD memory just AFTER sending CMD24 (WRITE_BLOCK), CMD25 * called for SD memory just AFTER sending CMD24 (WRITE_BLOCK), CMD25
* (WRITE_MULTIPLE_BLOCK), ... and before SDMMC_SENDDATA is called. * (WRITE_MULTIPLE_BLOCK), ... and before SDMMC_SENDDATA is called.
* *
* Input Parameters: * Input Parameters:
@@ -2341,7 +2355,8 @@ static int stm32_sendsetup(FAR struct sdio_dev_s *dev, FAR const
/* Then set up the SDIO data path */ /* Then set up the SDIO data path */
dblksize = stm32_log2(priv->blocksize) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT; dblksize = stm32_log2(priv->blocksize) <<
STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT * ((nbytes + 511) >> 9), stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT * ((nbytes + 511) >> 9),
nbytes, dblksize); nbytes, dblksize);
@@ -2637,6 +2652,7 @@ static int stm32_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
rlong[2] = sdmmc_getreg32(priv, STM32_SDMMC_RESP3_OFFSET); rlong[2] = sdmmc_getreg32(priv, STM32_SDMMC_RESP3_OFFSET);
rlong[3] = sdmmc_getreg32(priv, STM32_SDMMC_RESP4_OFFSET); rlong[3] = sdmmc_getreg32(priv, STM32_SDMMC_RESP4_OFFSET);
} }
return ret; return ret;
} }
@@ -2687,6 +2703,7 @@ static int stm32_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
{ {
*rshort = sdmmc_getreg32(priv, STM32_SDMMC_RESP1_OFFSET); *rshort = sdmmc_getreg32(priv, STM32_SDMMC_RESP1_OFFSET);
} }
return ret; return ret;
} }
@@ -2797,7 +2814,21 @@ static sdio_eventset_t stm32_eventwait(FAR struct sdio_dev_s *dev,
*/ */
flags = enter_critical_section(); 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); DEBUGASSERT(priv->waitevents != 0 || priv->wkupevent != 0);
#endif
/* Check if the timeout event is specified in the event set */ /* Check if the timeout event is specified in the event set */
@@ -2851,16 +2882,17 @@ static sdio_eventset_t stm32_eventwait(FAR struct sdio_dev_s *dev,
for (; ; ) for (; ; )
{ {
/* Wait for an event in event set to occur. If this the event has already /* Wait for an event in event set to occur. If this the event has
* occurred, then the semaphore will already have been incremented and * already occurred, then the semaphore will already have been
* there will be no wait. * incremented and there will be no wait.
*/ */
stm32_takesem(priv); stm32_takesem(priv);
wkupevent = priv->wkupevent; wkupevent = priv->wkupevent;
/* Check if the event has occurred. When the event has occurred, then /* 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) if (wkupevent != 0)
@@ -2873,6 +2905,10 @@ static sdio_eventset_t stm32_eventwait(FAR struct sdio_dev_s *dev,
/* Disable event-related interrupts */ /* Disable event-related interrupts */
#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE)
erroutdisable:
#endif
stm32_configwaitints(priv, 0, 0, 0); stm32_configwaitints(priv, 0, 0, 0);
#ifdef CONFIG_STM32F7_SDMMC_DMA #ifdef CONFIG_STM32F7_SDMMC_DMA
priv->xfrflags = 0; priv->xfrflags = 0;
@@ -3058,7 +3094,8 @@ static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
/* Then set up the SDIO data path */ /* Then set up the SDIO data path */
dblksize = stm32_log2(priv->blocksize) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT; dblksize = stm32_log2(priv->blocksize) <<
STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT * ((buflen + 511) >> 9), stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT * ((buflen + 511) >> 9),
buflen, dblksize | STM32_SDMMC_DCTRL_DTDIR); buflen, dblksize | STM32_SDMMC_DCTRL_DTDIR);
@@ -3066,7 +3103,8 @@ static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
stm32_configxfrints(priv, STM32_SDMMC_DMARECV_MASK); 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);
stm32_dmasetup(priv->dma, priv->base + STM32_SDMMC_FIFO_OFFSET, stm32_dmasetup(priv->dma, priv->base + STM32_SDMMC_FIFO_OFFSET,
(uint32_t)buffer, (buflen + 3) >> 2, (uint32_t)buffer, (buflen + 3) >> 2,
SDMMC_RXDMA32_CONFIG | priv->dmapri); SDMMC_RXDMA32_CONFIG | priv->dmapri);
@@ -3164,7 +3202,8 @@ static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev,
/* Then set up the SDIO data path */ /* Then set up the SDIO data path */
dblksize = stm32_log2(priv->blocksize) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT; dblksize = stm32_log2(priv->blocksize) <<
STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT * ((buflen + 511) >> 9), stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT * ((buflen + 511) >> 9),
buflen, dblksize); buflen, dblksize);
@@ -3174,7 +3213,8 @@ static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev,
(uint32_t)buffer, (buflen + 3) >> 2, (uint32_t)buffer, (buflen + 3) >> 2,
SDMMC_TXDMA32_CONFIG | priv->dmapri); 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); stm32_sample(priv, SAMPLENDX_BEFORE_ENABLE);
/* Start the DMA */ /* Start the DMA */
@@ -3297,7 +3337,8 @@ static void stm32_callback(void *arg)
{ {
/* Yes.. queue it */ /* Yes.. queue it */
mcinfo("Queuing callback to %p(%p)\n", priv->callback, priv->cbarg); mcinfo("Queuing callback to %p(%p)\n",
priv->callback, priv->cbarg);
(void)work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback, (void)work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback,
priv->cbarg, 0); priv->cbarg, 0);
} }
@@ -3346,7 +3387,8 @@ static void stm32_default(struct stm32_dev_s *priv)
* slotno - Not used. * slotno - Not used.
* *
* Returned Value: * 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.
* *
****************************************************************************/ ****************************************************************************/
@@ -3377,8 +3419,8 @@ FAR struct sdio_dev_s *sdio_initialize(int slotno)
/* Configure GPIOs for 4-bit, wide-bus operation (the chip is capable of /* Configure GPIOs for 4-bit, wide-bus operation (the chip is capable of
* 8-bit wide bus operation but D4-D7 are not configured). * 8-bit wide bus operation but D4-D7 are not configured).
* *
* If bus is multiplexed then there is a custom bus configuration utility * If bus is multiplexed then there is a custom bus configuration
* in the scope of the board support package. * utility in the scope of the board support package.
*/ */
# ifndef CONFIG_SDIO_MUXBUS # ifndef CONFIG_SDIO_MUXBUS
@@ -3414,8 +3456,8 @@ FAR struct sdio_dev_s *sdio_initialize(int slotno)
/* Configure GPIOs for 4-bit, wide-bus operation (the chip is capable of /* Configure GPIOs for 4-bit, wide-bus operation (the chip is capable of
* 8-bit wide bus operation but D4-D7 are not configured). * 8-bit wide bus operation but D4-D7 are not configured).
* *
* If bus is multiplexed then there is a custom bus configuration utility * If bus is multiplexed then there is a custom bus configuration
* in the scope of the board support package. * utility in the scope of the board support package.
*/ */
# ifndef CONFIG_SDIO_MUXBUS # ifndef CONFIG_SDIO_MUXBUS