mirror of
https://github.com/apache/nuttx.git
synced 2026-06-04 23:03:27 +08:00
Fixes two bugs in multi-block SD-card operations on the STM32F7 platform. arch/arm/src/stm32f7: DBLOCKSIZE must be the size of SD-card block, not the total amount of transferred bytes. drivers/mmcsd: respect SDIO_CAPS_DMABEFOREWRITE on CMD25.
This commit is contained in:
committed by
Gregory Nutt
parent
c67de1f114
commit
7d36a81806
@@ -1430,6 +1430,7 @@ config STM32F7_SDMMC1
|
|||||||
select ARCH_HAVE_SDIO
|
select ARCH_HAVE_SDIO
|
||||||
select ARCH_HAVE_SDIOWAIT_WRCOMPLETE
|
select ARCH_HAVE_SDIOWAIT_WRCOMPLETE
|
||||||
select SDIO_PREFLIGHT
|
select SDIO_PREFLIGHT
|
||||||
|
select SDIO_BLOCKSETUP
|
||||||
|
|
||||||
config STM32F7_SDMMC2
|
config STM32F7_SDMMC2
|
||||||
bool "SDMMC2"
|
bool "SDMMC2"
|
||||||
@@ -1439,6 +1440,7 @@ config STM32F7_SDMMC2
|
|||||||
select ARCH_HAVE_SDIO
|
select ARCH_HAVE_SDIO
|
||||||
select ARCH_HAVE_SDIOWAIT_WRCOMPLETE
|
select ARCH_HAVE_SDIOWAIT_WRCOMPLETE
|
||||||
select SDIO_PREFLIGHT
|
select SDIO_PREFLIGHT
|
||||||
|
select SDIO_BLOCKSETUP
|
||||||
|
|
||||||
config STM32F7_SPDIFRX
|
config STM32F7_SPDIFRX
|
||||||
bool "SPDIFRX"
|
bool "SPDIFRX"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* arch/arm/src/stm32f7/stm32_sdmmc.c
|
* arch/arm/src/stm32f7/stm32_sdmmc.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009, 2011-2017 Gregory Nutt. All rights reserved.
|
* Copyright (C) 2009, 2011-2018 Gregory Nutt. All rights reserved.
|
||||||
* Authors: Gregory Nutt <gnutt@nuttx.org>
|
* Authors: Gregory Nutt <gnutt@nuttx.org>
|
||||||
* David Sidrane <david_s5@nscdg.com>
|
* David Sidrane <david_s5@nscdg.com>
|
||||||
*
|
*
|
||||||
@@ -388,6 +388,10 @@ struct stm32_dev_s
|
|||||||
bool dmamode; /* true: DMA mode transfer */
|
bool dmamode; /* true: DMA mode transfer */
|
||||||
DMA_HANDLE dma; /* Handle for DMA channel */
|
DMA_HANDLE dma; /* Handle for DMA channel */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Misc */
|
||||||
|
|
||||||
|
uint32_t blocksize; /* Current block size */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Register logging support */
|
/* Register logging support */
|
||||||
@@ -421,7 +425,7 @@ struct stm32_sampleregs_s
|
|||||||
|
|
||||||
/* Low-level helpers ********************************************************/
|
/* 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);
|
int offset);
|
||||||
static inline uint32_t sdmmc_getreg32(struct stm32_dev_s *priv, int offset);
|
static inline uint32_t sdmmc_getreg32(struct stm32_dev_s *priv, int offset);
|
||||||
static void stm32_takesem(struct stm32_dev_s *priv);
|
static void stm32_takesem(struct stm32_dev_s *priv);
|
||||||
@@ -463,8 +467,10 @@ static void stm32_datadisable(struct stm32_dev_s *priv);
|
|||||||
static void stm32_sendfifo(struct stm32_dev_s *priv);
|
static void stm32_sendfifo(struct stm32_dev_s *priv);
|
||||||
static void stm32_recvfifo(struct stm32_dev_s *priv);
|
static void stm32_recvfifo(struct stm32_dev_s *priv);
|
||||||
static void stm32_eventtimeout(int argc, uint32_t arg);
|
static void stm32_eventtimeout(int argc, uint32_t arg);
|
||||||
static void stm32_endwait(struct stm32_dev_s *priv, sdio_eventset_t wkupevent);
|
static void stm32_endwait(struct stm32_dev_s *priv,
|
||||||
static void stm32_endtransfer(struct stm32_dev_s *priv, sdio_eventset_t wkupevent);
|
sdio_eventset_t wkupevent);
|
||||||
|
static void stm32_endtransfer(struct stm32_dev_s *priv,
|
||||||
|
sdio_eventset_t wkupevent);
|
||||||
|
|
||||||
/* Interrupt Handling *******************************************************/
|
/* Interrupt Handling *******************************************************/
|
||||||
|
|
||||||
@@ -495,6 +501,8 @@ static int stm32_attach(FAR struct sdio_dev_s *dev);
|
|||||||
|
|
||||||
static int stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
|
static int stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
|
||||||
uint32_t arg);
|
uint32_t arg);
|
||||||
|
static void stm32_blocksetup(FAR struct sdio_dev_s *dev,
|
||||||
|
unsigned int blocksize, unsigned int nblocks);
|
||||||
static int stm32_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
|
static int stm32_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
|
||||||
size_t nbytes);
|
size_t nbytes);
|
||||||
static int stm32_sendsetup(FAR struct sdio_dev_s *dev,
|
static int stm32_sendsetup(FAR struct sdio_dev_s *dev,
|
||||||
@@ -543,6 +551,7 @@ static void stm32_default(struct stm32_dev_s *priv);
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private Data
|
* Private Data
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#ifdef CONFIG_STM32F7_SDMMC1
|
#ifdef CONFIG_STM32F7_SDMMC1
|
||||||
struct stm32_dev_s g_sdmmcdev1 =
|
struct stm32_dev_s g_sdmmcdev1 =
|
||||||
{
|
{
|
||||||
@@ -558,9 +567,7 @@ struct stm32_dev_s g_sdmmcdev1 =
|
|||||||
.clock = stm32_clock,
|
.clock = stm32_clock,
|
||||||
.attach = stm32_attach,
|
.attach = stm32_attach,
|
||||||
.sendcmd = stm32_sendcmd,
|
.sendcmd = stm32_sendcmd,
|
||||||
#ifdef CONFIG_SDIO_BLOCKSETUP
|
.blocksetup = stm32_blocksetup,
|
||||||
.blocksetup = stm32_blocksetup, /* Not implemented yet */
|
|
||||||
#endif
|
|
||||||
.recvsetup = stm32_recvsetup,
|
.recvsetup = stm32_recvsetup,
|
||||||
.sendsetup = stm32_sendsetup,
|
.sendsetup = stm32_sendsetup,
|
||||||
.cancel = stm32_cancel,
|
.cancel = stm32_cancel,
|
||||||
@@ -620,9 +627,7 @@ struct stm32_dev_s g_sdmmcdev2 =
|
|||||||
.clock = stm32_clock,
|
.clock = stm32_clock,
|
||||||
.attach = stm32_attach,
|
.attach = stm32_attach,
|
||||||
.sendcmd = stm32_sendcmd,
|
.sendcmd = stm32_sendcmd,
|
||||||
#ifdef CONFIG_SDIO_BLOCKSETUP
|
.blocksetup = stm32_blocksetup,
|
||||||
.blocksetup = stm32_blocksetup, /* Not implemented yet */
|
|
||||||
#endif
|
|
||||||
.recvsetup = stm32_recvsetup,
|
.recvsetup = stm32_recvsetup,
|
||||||
.sendsetup = stm32_sendsetup,
|
.sendsetup = stm32_sendsetup,
|
||||||
.cancel = stm32_cancel,
|
.cancel = stm32_cancel,
|
||||||
@@ -1433,6 +1438,19 @@ static void stm32_endwait(struct stm32_dev_s *priv, sdio_eventset_t wkupevent)
|
|||||||
static void stm32_endtransfer(struct stm32_dev_s *priv,
|
static void stm32_endtransfer(struct stm32_dev_s *priv,
|
||||||
sdio_eventset_t wkupevent)
|
sdio_eventset_t wkupevent)
|
||||||
{
|
{
|
||||||
|
/* Disable the DTEN bit (it should not be left set after previous read when
|
||||||
|
* the next write initialization starts).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
sdmmc_putreg32(priv,
|
||||||
|
sdmmc_getreg32(priv, STM32_SDMMC_DCTRL_OFFSET) &
|
||||||
|
~STM32_SDMMC_DCTRL_DTEN,
|
||||||
|
STM32_SDMMC_DCTRL_OFFSET);
|
||||||
|
#else
|
||||||
|
stm32_datadisable(priv);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Disable all transfer related interrupts */
|
/* Disable all transfer related interrupts */
|
||||||
|
|
||||||
stm32_configxfrints(priv, 0);
|
stm32_configxfrints(priv, 0);
|
||||||
@@ -2057,6 +2075,30 @@ static int stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t arg)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: stm32_blocksetup
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Configure block size and the number of blocks for next transfer.
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* dev - An instance of the SDIO device interface.
|
||||||
|
* blocksize - The selected block size.
|
||||||
|
* nblocks - The number of blocks to transfer.
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static void stm32_blocksetup(FAR struct sdio_dev_s *dev, unsigned int blocksize,
|
||||||
|
unsigned int nblocks)
|
||||||
|
{
|
||||||
|
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
|
||||||
|
|
||||||
|
priv->blocksize = blocksize;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: stm32_recvsetup
|
* Name: stm32_recvsetup
|
||||||
*
|
*
|
||||||
@@ -2103,7 +2145,7 @@ 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 */
|
||||||
|
|
||||||
dblocksize = stm32_log2(nbytes) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
|
dblocksize = stm32_log2(priv->blocksize) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
|
||||||
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT, nbytes, dblocksize |
|
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT, nbytes, dblocksize |
|
||||||
STM32_SDMMC_DCTRL_DTDIR);
|
STM32_SDMMC_DCTRL_DTDIR);
|
||||||
|
|
||||||
@@ -2158,7 +2200,7 @@ 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 */
|
||||||
|
|
||||||
dblocksize = stm32_log2(nbytes) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
|
dblocksize = stm32_log2(priv->blocksize) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
|
||||||
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT, nbytes, dblocksize);
|
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT, nbytes, dblocksize);
|
||||||
|
|
||||||
/* Enable TX interrupts */
|
/* Enable TX interrupts */
|
||||||
@@ -2882,7 +2924,7 @@ 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 */
|
||||||
|
|
||||||
dblocksize = stm32_log2(buflen) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
|
dblocksize = stm32_log2(priv->blocksize) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
|
||||||
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT, buflen, dblocksize |
|
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT, buflen, dblocksize |
|
||||||
STM32_SDMMC_DCTRL_DTDIR);
|
STM32_SDMMC_DCTRL_DTDIR);
|
||||||
|
|
||||||
@@ -2975,7 +3017,7 @@ static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev,
|
|||||||
|
|
||||||
/* Then set up the SDIO data path */
|
/* Then set up the SDIO data path */
|
||||||
|
|
||||||
dblocksize = stm32_log2(buflen) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
|
dblocksize = stm32_log2(priv->blocksize) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT;
|
||||||
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT, buflen, dblocksize);
|
stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT, buflen, dblocksize);
|
||||||
|
|
||||||
/* Configure the TX DMA */
|
/* Configure the TX DMA */
|
||||||
|
|||||||
@@ -1895,6 +1895,25 @@ static ssize_t mmcsd_writemultiple(FAR struct mmcsd_state_s *priv,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If Controller does not need DMA setup before the write then send CMD25
|
||||||
|
* now.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((priv->caps & SDIO_CAPS_DMABEFOREWRITE) == 0)
|
||||||
|
{
|
||||||
|
/* Send CMD25, WRITE_MULTIPLE_BLOCK, and verify that good R1 status
|
||||||
|
* is returned
|
||||||
|
*/
|
||||||
|
|
||||||
|
mmcsd_sendcmdpoll(priv, MMCSD_CMD25, offset);
|
||||||
|
ret = mmcsd_recvR1(priv, MMCSD_CMD25);
|
||||||
|
if (ret != OK)
|
||||||
|
{
|
||||||
|
ferr("ERROR: mmcsd_recvR1 for CMD25 failed: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Configure SDIO controller hardware for the write transfer */
|
/* Configure SDIO controller hardware for the write transfer */
|
||||||
|
|
||||||
SDIO_BLOCKSETUP(priv->dev, priv->blocksize, nblocks);
|
SDIO_BLOCKSETUP(priv->dev, priv->blocksize, nblocks);
|
||||||
@@ -1923,6 +1942,10 @@ static ssize_t mmcsd_writemultiple(FAR struct mmcsd_state_s *priv,
|
|||||||
|
|
||||||
priv->wrbusy = true;
|
priv->wrbusy = true;
|
||||||
|
|
||||||
|
/* If Controller needs DMA setup before write then only send CMD25 now. */
|
||||||
|
|
||||||
|
if ((priv->caps & SDIO_CAPS_DMABEFOREWRITE) != 0)
|
||||||
|
{
|
||||||
/* Send CMD25, WRITE_MULTIPLE_BLOCK, and verify that good R1 status
|
/* Send CMD25, WRITE_MULTIPLE_BLOCK, and verify that good R1 status
|
||||||
* is returned
|
* is returned
|
||||||
*/
|
*/
|
||||||
@@ -1934,13 +1957,15 @@ static ssize_t mmcsd_writemultiple(FAR struct mmcsd_state_s *priv,
|
|||||||
ferr("ERROR: mmcsd_recvR1 for CMD25 failed: %d\n", ret);
|
ferr("ERROR: mmcsd_recvR1 for CMD25 failed: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Wait for the transfer to complete */
|
/* Wait for the transfer to complete */
|
||||||
|
|
||||||
ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR, nblocks * MMCSD_BLOCK_WDATADELAY);
|
ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR,
|
||||||
|
nblocks * MMCSD_BLOCK_WDATADELAY);
|
||||||
if (ret != OK)
|
if (ret != OK)
|
||||||
{
|
{
|
||||||
ferr("ERROR: CMD18 transfer failed: %d\n", ret);
|
ferr("ERROR: CMD25 transfer failed: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user