Fix a problem with STM32 SDIO DMA.

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3793 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2011-07-17 17:39:40 +00:00
parent 80616e345a
commit c20c6dfe92

View File

@ -74,7 +74,6 @@
#if defined(CONFIG_SDIO_DMA) && !defined(CONFIG_STM32_DMA2)
# warning "CONFIG_SDIO_DMA support requires CONFIG_STM32_DMA2"
# undef CONFIG_SDIO_DMA
#endif
#ifndef CONFIG_SDIO_DMA
@ -174,10 +173,16 @@
#define SDIO_WAITALL_ICR (SDIO_CMDDONE_ICR|SDIO_RESPDONE_ICR|\
SDIO_XFRDONE_ICR)
/* Let's wait until we have both SDIO transfer complete and DMA complete. */
#define SDIO_XFRDONE_FLAG (1)
#define SDIO_DMADONE_FLAG (2)
#define SDIO_ALLDONE (3)
/* Register logging support */
#ifdef CONFIG_SDIO_XFRDEBUG
# if defined(CONFIG_DEBUG_DMA) && defined(CONFIG_SDIO_DMA)
# ifdef CONFIG_SDIO_DMA
# define SAMPLENDX_BEFORE_SETUP 0
# define SAMPLENDX_BEFORE_ENABLE 1
# define SAMPLENDX_AFTER_SETUP 2
@ -229,6 +234,7 @@ struct stm32_dev_s
bool widebus; /* Required for DMA support */
#ifdef CONFIG_SDIO_DMA
volatile uint8_t xfrflags; /* Used to synchronize SDIO and DMA completion events */
bool dmamode; /* true: DMA mode transfer */
DMA_HANDLE dma; /* Handle for DMA channel */
#endif
@ -276,7 +282,6 @@ static inline uint32_t stm32_getpwrctrl(void);
/* DMA Helpers **************************************************************/
#ifdef CONFIG_SDIO_XFRDEBUG
static void stm32_sampleinit(void);
static void stm32_sdiosample(struct stm32_sdioregs_s *regs);
@ -513,6 +518,9 @@ static void stm32_configwaitints(struct stm32_dev_s *priv, uint32_t waitmask,
priv->waitevents = waitevents;
priv->wkupevent = wkupevent;
priv->waitmask = waitmask;
#ifdef CONFIG_SDIO_DMA
priv->xfrflags = 0;
#endif
putreg32(priv->xfrmask | priv->waitmask, STM32_SDIO_MASK);
irqrestore(flags);
}
@ -737,18 +745,24 @@ static void stm32_dumpsamples(struct stm32_dev_s *priv)
#ifdef CONFIG_SDIO_DMA
static void stm32_dmacallback(DMA_HANDLE handle, uint8_t isr, void *arg)
{
/* FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)arg; */
FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)arg;
DEBUGASSERT(priv->dmamode);
/* We don't really do anything at the completion of DMA. The termination
* of the transfer is driven by the SDIO interrupts.
*
* In fact, we won't normally get the DMA callback at all! The SDIO
* appears to handle the End-Of-Transfer interrupt first and it will can
* stm32_dmastop() which will disable and clear the interrupt that performs
* this callback.
/* The SDIO appears to handle the End-Of-Transfer interrupt first with the
* End-Of-DMA event occurring significantly later.
*/
stm32_sample((struct stm32_dev_s*)arg, SAMPLENDX_DMA_CALLBACK);
/* Then terminate the transfer (we should already have the SDIO transfer
* done interrupt. If now, the transfer will appropriately time out.
*/
priv->xfrflags |= SDIO_DMADONE_FLAG;
if (priv->xfrflags == SDIO_ALLDONE)
{
stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
}
}
#endif
@ -1071,7 +1085,7 @@ static void stm32_endtransfer(struct stm32_dev_s *priv, sdio_eventset_t wkupeven
/* Make sure that the DMA is stopped (it will be stopped automatically
* on normal transfers, but not necessarily when the transfer terminates
* on an error condition.
* on an error condition).
*/
stm32_dmastop(priv->dma);
@ -1167,18 +1181,41 @@ static int stm32_interrupt(int irq, void *context)
* half-full interrupt will be received.
*/
/* Was this transfer performed in DMA mode? */
#ifdef CONFIG_SDIO_DMA
if (!priv->dmamode)
if (priv->dmamode)
{
/* Yes.. Terminate the transfers only if the DMA has also
* finished.
*/
priv->xfrflags |= SDIO_XFRDONE_FLAG;
if (priv->xfrflags == SDIO_ALLDONE)
{
stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
}
/* Otherwise, just disable futher transfer interrupts and
* wait for the DMA complete event.
*/
else
{
stm32_configxfrints(priv, 0);
}
}
else
#endif
{
/* Receive data from the RX FIFO */
stm32_recvfifo(priv);
/* Then terminate the transfer */
stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
}
/* Then terminate the transfer */
stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
}
/* Handle data block send/receive CRC failure */
@ -1309,6 +1346,10 @@ static void stm32_reset(FAR struct sdio_dev_s *dev)
priv->waitevents = 0; /* Set of events to be waited for */
priv->waitmask = 0; /* Interrupt enables for event waiting */
priv->wkupevent = 0; /* The event that caused the wakeup */
#ifdef CONFIG_SDIO_DMA
priv->xfrflags = 0; /* Used to synchronize SDIO and DMA completion events */
#endif
wd_cancel(priv->waitwdog); /* Cancel any timeouts */
/* Interrupt mode data transfer support */
@ -1860,7 +1901,7 @@ static int stm32_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t
}
else if ((regval & SDIO_STA_CCRCFAIL) != 0)
{
fdbg("ERROR: CRC failuret: %08x\n", regval);
fdbg("ERROR: CRC failure: %08x\n", regval);
ret = -EIO;
}
#ifdef CONFIG_DEBUG
@ -2147,7 +2188,10 @@ static sdio_eventset_t stm32_eventwait(FAR struct sdio_dev_s *dev,
/* Disable event-related interrupts */
stm32_configwaitints(priv, 0, 0, 0);
stm32_dumpsamples(priv);
#ifdef CONFIG_SDIO_DMA
priv->xfrflags = 0;
#endif
return wkupevent;
}