SAMA5: Fix HSMCI race condition. Now memory card interface is functional with DMA

This commit is contained in:
Gregory Nutt 2013-08-10 18:01:23 -06:00
parent 6622714c5d
commit 3c38992727
4 changed files with 255 additions and 83 deletions

View File

@ -56,6 +56,7 @@
#include "chip.h"
#include "sam_dmac.h"
#include "sam_periphclks.h"
#include "chip/sam3u_pmc.h"
#include "chip/sam3u_dmac.h"
@ -413,7 +414,7 @@ sam_txctrlabits(struct sam_dma_s *dmach)
*
****************************************************************************/
static size_t sam_maxtxtransfer(struct sam_dmach_s *dmach)
static size_t sam_maxtxtransfer(struct sam_dma_s *dmach)
{
unsigned int srcwidth;
size_t maxtransfer;
@ -558,7 +559,7 @@ static inline uint32_t sam_rxctrlabits(struct sam_dma_s *dmach)
*
****************************************************************************/
static size_t sam_maxrxtransfer(struct sam_dmach_s *dmach)
static size_t sam_maxrxtransfer(struct sam_dma_s *dmach)
{
unsigned int srcwidth;
size_t maxtransfer;
@ -1399,7 +1400,7 @@ DMA_HANDLE sam_dmachannel(uint32_t chflags)
void sam_dmaconfig(DMA_HANDLE handle, uint32_t chflags)
{
struct sam_dmach_s *dmach = (struct sam_dmach_s *)handle;
struct sam_dma_s *dmach = (struct sam_dma_s *)handle;
/* Set the new DMA channel flags. */

View File

@ -51,7 +51,7 @@
/* Helper macros */
#define sam_enableperipheral(s) putreg32((1 << (s)), SAM_PMC_PCER)
#define sam_disableperipheral(s) putreg32((1 << (s)), SAM_PMC_PDER)
#define sam_disableperipheral(s) putreg32((1 << (s)), SAM_PMC_PCDR)
#define sam_supc_enableclk() sam_enableperipheral(SAM_PID_SUPC)
#define sam_rstc_enableclk() sam_enableperipheral(SAM_PID_RSTC)

View File

@ -63,6 +63,7 @@
#include "sam_gpio.h"
#include "sam_dmac.h"
#include "sam_hsmci.h"
#include "sam_periphclks.h"
#include "chip/sam3u_dmac.h"
#include "chip/sam3u_pmc.h"
#include "chip/sam_hsmci.h"
@ -268,6 +269,7 @@ struct sam_dev_s
uint32_t cmdrmask; /* Interrupt enables for this particular cmd/response */
volatile sdio_eventset_t wkupevent; /* The event that caused the wakeup */
WDOG_ID waitwdog; /* Watchdog that handles event timeouts */
bool dmabusy; /* TRUE: DMA is in progress */
/* Callback support */
@ -329,11 +331,14 @@ struct sam_xfrregs_s
static void sam_takesem(struct sam_dev_s *priv);
#define sam_givesem(priv) (sem_post(&priv->waitsem))
static void sam_enablewaitints(struct sam_dev_s *priv, uint32_t waitmask,
static void sam_configwaitints(struct sam_dev_s *priv, uint32_t waitmask,
sdio_eventset_t waitevents);
static void sam_disablewaitints(struct sam_dev_s *priv, sdio_eventset_t wkupevents);
static void sam_enablexfrints(struct sam_dev_s *priv, uint32_t xfrmask);
static inline void sam_configxfrints(struct sam_dev_s *priv, uint32_t xfrmask);
static void sam_disablexfrints(struct sam_dev_s *priv);
static void sam_enableints(struct sam_dev_s *priv);
static inline void sam_disable(void);
static inline void sam_enable(void);
@ -522,10 +527,13 @@ static void sam_takesem(struct sam_dev_s *priv)
}
/****************************************************************************
* Name: sam_enablewaitints
* Name: sam_configwaitints
*
* Description:
* Enable HSMCI interrupts needed to suport the wait function
* Configure HSMCI interrupts needed to support the wait function. Wait
* interrupts are configured here, but not enabled until
* sam_enableints() is called. Why? Because the XFRDONE interrupt
* is always pending until start the data transfer.
*
* Input Parameters:
* priv - A reference to the HSMCI device state structure
@ -537,20 +545,17 @@ static void sam_takesem(struct sam_dev_s *priv)
*
****************************************************************************/
static void sam_enablewaitints(struct sam_dev_s *priv, uint32_t waitmask,
static void sam_configwaitints(struct sam_dev_s *priv, uint32_t waitmask,
sdio_eventset_t waitevents)
{
irqstate_t flags;
/* Save all of the data and set the new interrupt mask in one, atomic
* operation.
*/
/* Save all of the data in one, atomic operation. */
flags = irqsave();
priv->waitevents = waitevents;
priv->wkupevent = 0;
priv->waitmask = waitmask;
putreg32(priv->xfrmask | priv->waitmask, SAM_HSMCI_IER);
irqrestore(flags);
}
@ -587,10 +592,13 @@ static void sam_disablewaitints(struct sam_dev_s *priv,
}
/****************************************************************************
* Name: sam_enablexfrints
* Name: sam_configxfrints
*
* Description:
* Enable HSMCI interrupts needed to support the data transfer event
* Configure HSMCI interrupts needed to support the data transfer. Data
* transfer interrupts are configured here, but not enabled until
* sam_enableints() is called. Why? Because the XFRDONE interrupt
* is always pending until start the data transfer.
*
* Input Parameters:
* priv - A reference to the HSMCI device state structure
@ -601,12 +609,9 @@ static void sam_disablewaitints(struct sam_dev_s *priv,
*
****************************************************************************/
static void sam_enablexfrints(struct sam_dev_s *priv, uint32_t xfrmask)
static inline void sam_configxfrints(struct sam_dev_s *priv, uint32_t xfrmask)
{
irqstate_t flags = irqsave();
priv->xfrmask = xfrmask;
putreg32(priv->xfrmask | priv->waitmask, SAM_HSMCI_IER);
irqrestore(flags);
}
/****************************************************************************
@ -632,6 +637,28 @@ static void sam_disablexfrints(struct sam_dev_s *priv)
irqrestore(flags);
}
/****************************************************************************
* Name: sam_enableints
*
* Description:
* Enable the previously configured HSMCI interrupts needed to suport the
* wait and transfer functions.
*
* Input Parameters:
* priv - A reference to the HSMCI device state structure
*
* Returned Value:
* None
*
****************************************************************************/
static inline void sam_enableints(struct sam_dev_s *priv)
{
/* Enable all interrupts associated with the waited-for event */
putreg32(priv->xfrmask | priv->waitmask, SAM_HSMCI_IER);
}
/****************************************************************************
* Name: sam_disable
*
@ -903,10 +930,16 @@ static void sam_cmddump(void)
static void sam_dmacallback(DMA_HANDLE handle, void *arg, int result)
{
struct sam_dev_s *priv = (struct sam_dev_s *)arg;
/* We don't really do anything at the completion of DMA. The termination
* of the transfer is driven by the HSMCI interrupts.
*
* Mark the DMA not busy.
*/
priv->dmabusy = false;
sam_xfrsample((struct sam_dev_s*)arg, SAMPLENDX_DMA_CALLBACK);
}
@ -1025,6 +1058,7 @@ static void sam_endtransfer(struct sam_dev_s *priv,
*/
sam_dmastop(priv->dma);
priv->dmabusy = false;
/* Disable the DMA handshaking */
@ -1100,6 +1134,7 @@ static int sam_interrupt(int irq, void *context)
sr = getreg32(SAM_HSMCI_SR);
enabled = sr & getreg32(SAM_HSMCI_IMR);
if (enabled == 0)
{
break;
@ -1270,6 +1305,7 @@ static void sam_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 */
priv->dmabusy = false; /* No DMA in progress */
wd_cancel(priv->waitwdog); /* Cancel any timeouts */
/* Interrupt mode data transfer support */
@ -1620,8 +1656,8 @@ static void sam_blocksetup(FAR struct sdio_dev_s *dev, unsigned int blocklen,
* Name: sam_cancel
*
* Description:
* Cancel the data transfer setup of HSMCI_RECVSETUP, HSMCI_SENDSETUP,
* HSMCI_DMARECVSETUP or HSMCI_DMASENDSETUP. This must be called to cancel
* Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
* SDIO_DMARECVSETUP or SDIO_DMASENDSETUP. This must be called to cancel
* the data transfer setup if, for some reason, you cannot perform the
* transfer.
*
@ -1660,6 +1696,7 @@ static int sam_cancel(FAR struct sdio_dev_s *dev)
*/
sam_dmastop(priv->dma);
priv->dmabusy = false;
/* Disable the DMA handshaking */
@ -1947,13 +1984,24 @@ static int sam_recvnotimpl(FAR struct sdio_dev_s *dev,
*
* Description:
* Enable/disable of a set of SDIO wait events. This is part of the
* the HSMCI_WAITEVENT sequence. The set of to-be-waited-for events is
* configured before calling sam_eventwait. This is done in this way
* to help the driver to eliminate race conditions between the command
* the SDIO_WAITEVENT sequence. The set of to-be-waited-for events is
* configured before calling either calling SDIO_DMARECVSETUP,
* SDIO_DMASENDSETUP, or or SDIO_WAITEVENT. This is the recommended
* ordering:
*
* SDIO_WAITENABLE: Discard any pending interrupts, enable event(s)
* of interest
* SDIO_DMARECVSETUP/
* SDIO_DMASENDSETUP: Setup the logic that will trigger the event the
* event(s) of interest
* SDIO_WAITEVENT: Wait for the event of interest (which might
* already have occurred)
*
* This sequency should eliminate race conditions between the command/trasnfer
* setup and the subsequent events.
*
* The enabled events persist until either (1) HSMCI_WAITENABLE is called
* again specifying a different set of wait events, or (2) HSMCI_EVENTWAIT
* The enabled events persist until either (1) SDIO_WAITENABLE is called
* again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
* returns.
*
* Input Parameters:
@ -1988,10 +2036,20 @@ static void sam_waitenable(FAR struct sdio_dev_s *dev,
waitmask |= priv->cmdrmask;
}
/* Enable event-related interrupts */
/* Clear (most) pending interrupts by reading the status register.
* No interrupts should be lost (assuming that interrupts were enabled
* before sam_waitenable() was called). Any interrupts that become
* pending after this point must be valid event indications.
*/
(void)getreg32(SAM_HSMCI_SR);
sam_enablewaitints(priv, waitmask, eventset);
/* Wait interrupts are configured here, but not enabled until
* sam_eventwait() is called. Why? Because the XFRDONE interrupt is
* always pending until start the data transfer.
*/
sam_configwaitints(priv, waitmask, eventset);
}
/****************************************************************************
@ -1999,8 +2057,8 @@ static void sam_waitenable(FAR struct sdio_dev_s *dev,
*
* Description:
* Wait for one of the enabled events to occur (or a timeout). Note that
* all events enabled by HSMCI_WAITEVENTS are disabled when sam_eventwait
* returns. HSMCI_WAITEVENTS must be called again before sam_eventwait
* all events enabled by SDIO_WAITEVENTS are disabled when sam_eventwait
* returns. SDIO_WAITEVENTS must be called again before sam_eventwait
* can be used again.
*
* Input Parameters:
@ -2022,13 +2080,22 @@ static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev,
sdio_eventset_t wkupevent = 0;
int ret;
/* There is a race condition here... the event may have completed before
* we get here. In this case waitevents will be zero, but wkupevents will
* be non-zero (and, hopefully, the semaphore count will also be non-zero.
/* Since interrupts not been enabled to this point, any relevant events
* are pending and should not yet have occurred.
*/
DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
(priv->waitevents == 0 && priv->wkupevent != 0));
DEBUGASSERT(priv->waitevents != 0 && priv->wkupevent == 0);
/* Now enable event-related interrupts. If the events are pending, they
* may happen immediately here before entering the loop.
*/
sam_enableints(priv);
/* There is a race condition here... the event may have completed before
* we get here. In this case waitevents will be zero, but wkupevents will
* be non-zero (and, hopefully, the semaphore count will also be non-zero).
*/
/* Check if the timeout event is specified in the event set */
@ -2043,7 +2110,16 @@ static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev,
return SDIOWAIT_TIMEOUT;
}
/* Start the watchdog timer */
/* Start the watchdog timer. I am not sure why this is, but I am
* currently seeing some additional delays when DMA is used (On the
* SAMA5, might not be necessary for SAM3/4).
*/
#warning REVISIT: This should not be necessary
if (priv->dmabusy)
{
timeout += 500;
}
delay = (timeout + (MSEC_PER_TICK-1)) / MSEC_PER_TICK;
ret = wd_start(priv->waitwdog, delay, (wdentry_t)sam_eventtimeout,
@ -2099,7 +2175,7 @@ static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev,
*
* Events are automatically disabled once the callback is performed and no
* further callback events will occur until they are again enabled by
* calling this methos.
* calling this methods.
*
* Input Parameters:
* dev - An instance of the SDIO device interface
@ -2133,7 +2209,7 @@ static void sam_callbackenable(FAR struct sdio_dev_s *dev,
* thread.
*
* When this method is called, all callbacks should be disabled until they
* are enabled via a call to HSMCI_CALLBACKENABLE
* are enabled via a call to SDIO_CALLBACKENABLE.
*
* Input Parameters:
* dev - Device-specific state data
@ -2216,7 +2292,6 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
/* Configure the RX DMA */
sam_enablexfrints(priv, HSMCI_DMARECV_INTS);
sam_dmarxsetup(priv->dma, SAM_HSMCI_RDR, (uint32_t)buffer, buflen);
/* Enable DMA handshaking */
@ -2226,8 +2301,16 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
/* Start the DMA */
priv->dmabusy = true;
sam_dmastart(priv->dma, sam_dmacallback, priv);
/* Configure transfer-related interrupts. Transfer interrupts are not
* enabled until after the transfer is stard with an SD command (i.e.,
* at the beginning of sam_eventwait().
*/
sam_xfrsample(priv, SAMPLENDX_AFTER_SETUP);
sam_configxfrints(priv, HSMCI_DMARECV_INTS);
return OK;
}
@ -2274,12 +2357,16 @@ static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
/* Start the DMA */
priv->dmabusy = true;
sam_dmastart(priv->dma, sam_dmacallback, priv);
/* Configure transfer-related interrupts. Transfer interrupts are not
* enabled until after the transfer is stard with an SD command (i.e.,
* at the beginning of sam_eventwait().
*/
sam_xfrsample(priv, SAMPLENDX_AFTER_SETUP);
/* Enable TX interrrupts */
sam_enablexfrints(priv, HSMCI_DMASEND_INTS);
sam_configxfrints(priv, HSMCI_DMASEND_INTS);
return OK;
}

View File

@ -313,6 +313,7 @@ struct sam_dev_s
volatile sdio_eventset_t wkupevent; /* The event that caused the wakeup */
WDOG_ID waitwdog; /* Watchdog that handles event timeouts */
uint8_t hsmci; /* HSMCI (0, 1, or 2) */
bool dmabusy; /* TRUE: DMA is in progress */
/* Callback support */
@ -375,11 +376,12 @@ static inline uint32_t sam_getreg(struct sam_dev_s *priv,
static inline void sam_putreg(struct sam_dev_s *priv, uint32_t value,
unsigned int offset);
static void sam_enablewaitints(struct sam_dev_s *priv, uint32_t waitmask,
static inline void sam_configwaitints(struct sam_dev_s *priv, uint32_t waitmask,
sdio_eventset_t waitevents);
static void sam_disablewaitints(struct sam_dev_s *priv, sdio_eventset_t wkupevents);
static void sam_enablexfrints(struct sam_dev_s *priv, uint32_t xfrmask);
static inline void sam_configxfrints(struct sam_dev_s *priv, uint32_t xfrmask);
static void sam_disablexfrints(struct sam_dev_s *priv);
static inline void sam_enableints(struct sam_dev_s *priv);
static inline void sam_disable(struct sam_dev_s *priv);
static inline void sam_enable(struct sam_dev_s *priv);
@ -680,10 +682,13 @@ static inline void sam_putreg(struct sam_dev_s *priv, uint32_t value,
}
/****************************************************************************
* Name: sam_enablewaitints
* Name: sam_configwaitints
*
* Description:
* Enable HSMCI interrupts needed to suport the wait function
* Configure HSMCI interrupts needed to support the wait function. Wait
* interrupts are configured here, but not enabled until
* sam_enableints() is called. Why? Because the XFRDONE interrupt
* is always pending until start the data transfer.
*
* Input Parameters:
* priv - A reference to the HSMCI device state structure
@ -695,20 +700,18 @@ static inline void sam_putreg(struct sam_dev_s *priv, uint32_t value,
*
****************************************************************************/
static void sam_enablewaitints(struct sam_dev_s *priv, uint32_t waitmask,
sdio_eventset_t waitevents)
static inline void sam_configwaitints(struct sam_dev_s *priv,
uint32_t waitmask,
sdio_eventset_t waitevents)
{
irqstate_t flags;
/* Save all of the data and set the new interrupt mask in one, atomic
* operation.
*/
/* Save all of the data in one, atomic operation. */
flags = irqsave();
priv->waitevents = waitevents;
priv->wkupevent = 0;
priv->waitmask = waitmask;
sam_putreg(priv, priv->xfrmask | priv->waitmask, SAM_HSMCI_IER_OFFSET);
irqrestore(flags);
}
@ -745,10 +748,13 @@ static void sam_disablewaitints(struct sam_dev_s *priv,
}
/****************************************************************************
* Name: sam_enablexfrints
* Name: sam_configxfrints
*
* Description:
* Enable HSMCI interrupts needed to support the data transfer event
* Configure HSMCI interrupts needed to support the data transfer. Data
* transfer interrupts are configured here, but not enabled until
* sam_enableints() is called. Why? Because the XFRDONE interrupt
* is always pending until start the data transfer.
*
* Input Parameters:
* priv - A reference to the HSMCI device state structure
@ -759,12 +765,9 @@ static void sam_disablewaitints(struct sam_dev_s *priv,
*
****************************************************************************/
static void sam_enablexfrints(struct sam_dev_s *priv, uint32_t xfrmask)
static inline void sam_configxfrints(struct sam_dev_s *priv, uint32_t xfrmask)
{
irqstate_t flags = irqsave();
priv->xfrmask = xfrmask;
sam_putreg(priv, priv->xfrmask | priv->waitmask, SAM_HSMCI_IER_OFFSET);
irqrestore(flags);
}
/****************************************************************************
@ -790,6 +793,28 @@ static void sam_disablexfrints(struct sam_dev_s *priv)
irqrestore(flags);
}
/****************************************************************************
* Name: sam_enableints
*
* Description:
* Enable the previously configured HSMCI interrupts needed to suport the
* wait and transfer functions.
*
* Input Parameters:
* priv - A reference to the HSMCI device state structure
*
* Returned Value:
* None
*
****************************************************************************/
static inline void sam_enableints(struct sam_dev_s *priv)
{
/* Enable all interrupts associated with the waited-for event */
sam_putreg(priv, priv->xfrmask | priv->waitmask, SAM_HSMCI_IER_OFFSET);
}
/****************************************************************************
* Name: sam_disable
*
@ -1068,10 +1093,16 @@ static void sam_cmddump(struct sam_dev_s *priv)
static void sam_dmacallback(DMA_HANDLE handle, void *arg, int result)
{
struct sam_dev_s *priv = (struct sam_dev_s *)arg;
/* We don't really do anything at the completion of DMA. The termination
* of the transfer is driven by the HSMCI interrupts.
*
* Mark the DMA not busy.
*/
priv->dmabusy = false;
sam_xfrsample((struct sam_dev_s *)arg, SAMPLENDX_DMA_CALLBACK);
}
@ -1204,6 +1235,7 @@ static void sam_endtransfer(struct sam_dev_s *priv,
*/
sam_dmastop(priv->dma);
priv->dmabusy = false;
/* Disable the DMA handshaking */
@ -1287,6 +1319,7 @@ static int sam_hsmci_interrupt(struct sam_dev_s *priv)
sr = sam_getreg(priv, SAM_HSMCI_SR_OFFSET);
enabled = sr & sam_getreg(priv, SAM_HSMCI_IMR_OFFSET);
if (enabled == 0)
{
break;
@ -1491,6 +1524,7 @@ static void sam_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 */
priv->dmabusy = false; /* No DMA in progress */
wd_cancel(priv->waitwdog); /* Cancel any timeouts */
/* Interrupt mode data transfer support */
@ -1873,8 +1907,8 @@ static void sam_blocksetup(FAR struct sdio_dev_s *dev, unsigned int blocklen,
* Name: sam_cancel
*
* Description:
* Cancel the data transfer setup of HSMCI_RECVSETUP, HSMCI_SENDSETUP,
* HSMCI_DMARECVSETUP or HSMCI_DMASENDSETUP. This must be called to cancel
* Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
* SDIO_DMARECVSETUP or SDIO_DMASENDSETUP. This must be called to cancel
* the data transfer setup if, for some reason, you cannot perform the
* transfer.
*
@ -1913,6 +1947,7 @@ static int sam_cancel(FAR struct sdio_dev_s *dev)
*/
sam_dmastop(priv->dma);
priv->dmabusy = false;
/* Disable the DMA handshaking */
@ -2200,13 +2235,24 @@ static int sam_recvnotimpl(FAR struct sdio_dev_s *dev,
*
* Description:
* Enable/disable of a set of SDIO wait events. This is part of the
* the HSMCI_WAITEVENT sequence. The set of to-be-waited-for events is
* configured before calling sam_eventwait. This is done in this way
* to help the driver to eliminate race conditions between the command
* the SDIO_WAITEVENT sequence. The set of to-be-waited-for events is
* configured before calling either calling SDIO_DMARECVSETUP,
* SDIO_DMASENDSETUP, or or SDIO_WAITEVENT. This is the recommended
* ordering:
*
* SDIO_WAITENABLE: Discard any pending interrupts, enable event(s)
* of interest
* SDIO_DMARECVSETUP/
* SDIO_DMASENDSETUP: Setup the logic that will trigger the event the
* event(s) of interest
* SDIO_WAITEVENT: Wait for the event of interest (which might
* already have occurred)
*
* This sequency should eliminate race conditions between the command/trasnfer
* setup and the subsequent events.
*
* The enabled events persist until either (1) HSMCI_WAITENABLE is called
* again specifying a different set of wait events, or (2) HSMCI_EVENTWAIT
* The enabled events persist until either (1) SDIO_WAITENABLE is called
* again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
* returns.
*
* Input Parameters:
@ -2241,10 +2287,20 @@ static void sam_waitenable(FAR struct sdio_dev_s *dev,
waitmask |= priv->cmdrmask;
}
/* Enable event-related interrupts */
/* Clear (most) pending interrupts by reading the status register.
* No interrupts should be lost (assuming that interrupts were enabled
* before sam_waitenable() was called). Any interrupts that become
* pending after this point must be valid event indications.
*/
(void)sam_getreg(priv, SAM_HSMCI_SR_OFFSET);
sam_enablewaitints(priv, waitmask, eventset);
/* Wait interrupts are configured here, but not enabled until
* sam_eventwait() is called. Why? Because the XFRDONE interrupt is
* always pending until start the data transfer.
*/
sam_configwaitints(priv, waitmask, eventset);
}
/****************************************************************************
@ -2252,8 +2308,8 @@ static void sam_waitenable(FAR struct sdio_dev_s *dev,
*
* Description:
* Wait for one of the enabled events to occur (or a timeout). Note that
* all events enabled by HSMCI_WAITEVENTS are disabled when sam_eventwait
* returns. HSMCI_WAITEVENTS must be called again before sam_eventwait
* all events enabled by SDIO_WAITEVENTS are disabled when sam_eventwait
* returns. SDIO_WAITEVENTS must be called again before sam_eventwait
* can be used again.
*
* Input Parameters:
@ -2275,13 +2331,22 @@ static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev,
sdio_eventset_t wkupevent = 0;
int ret;
/* There is a race condition here... the event may have completed before
* we get here. In this case waitevents will be zero, but wkupevents will
* be non-zero (and, hopefully, the semaphore count will also be non-zero.
/* Since interrupts not been enabled to this point, any relevant events
* are pending and should not yet have occurred.
*/
DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
(priv->waitevents == 0 && priv->wkupevent != 0));
DEBUGASSERT(priv->waitevents != 0 && priv->wkupevent == 0);
/* Now enable event-related interrupts. If the events are pending, they
* may happen immediately here before entering the loop.
*/
sam_enableints(priv);
/* There is a race condition here... the event may have completed before
* we get here. In this case waitevents will be zero, but wkupevents will
* be non-zero (and, hopefully, the semaphore count will also be non-zero).
*/
/* Check if the timeout event is specified in the event set */
@ -2296,7 +2361,15 @@ static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev,
return SDIOWAIT_TIMEOUT;
}
/* Start the watchdog timer */
/* Start the watchdog timer. I am not sure why this is, but I am\
* currently seeing some additional delays when DMA is used.
*/
#warning REVISIT: This should not be necessary
if (priv->dmabusy)
{
timeout += 500;
}
delay = (timeout + (MSEC_PER_TICK-1)) / MSEC_PER_TICK;
ret = wd_start(priv->waitwdog, delay, (wdentry_t)sam_eventtimeout,
@ -2352,7 +2425,7 @@ static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev,
*
* Events are automatically disabled once the callback is performed and no
* further callback events will occur until they are again enabled by
* calling this methos.
* calling this methods.
*
* Input Parameters:
* dev - An instance of the SDIO device interface
@ -2386,7 +2459,7 @@ static void sam_callbackenable(FAR struct sdio_dev_s *dev,
* thread.
*
* When this method is called, all callbacks should be disabled until they
* are enabled via a call to HSMCI_CALLBACKENABLE
* are enabled via a call to SDIO_CALLBACKENABLE.
*
* Input Parameters:
* dev - Device-specific state data
@ -2478,7 +2551,6 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
/* Configure the RX DMA */
sam_enablexfrints(priv, HSMCI_DMARECV_INTS);
sam_dmarxsetup(priv->dma, paddr, maddr, buflen);
/* Enable DMA handshaking */
@ -2488,8 +2560,16 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
/* Start the DMA */
priv->dmabusy = true;
sam_dmastart(priv->dma, sam_dmacallback, priv);
/* Configure transfer-related interrupts. Transfer interrupts are not
* enabled until after the transfer is stard with an SD command (i.e.,
* at the beginning of sam_eventwait().
*/
sam_xfrsample(priv, SAMPLENDX_AFTER_SETUP);
sam_configxfrints(priv, HSMCI_DMARECV_INTS);
return OK;
}
@ -2543,12 +2623,16 @@ static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
/* Start the DMA */
priv->dmabusy = true;
sam_dmastart(priv->dma, sam_dmacallback, priv);
/* Configure transfer-related interrupts. Transfer interrupts are not
* enabled until after the transfer is stard with an SD command (i.e.,
* at the beginning of sam_eventwait().
*/
sam_xfrsample(priv, SAMPLENDX_AFTER_SETUP);
/* Enable TX interrrupts */
sam_enablexfrints(priv, HSMCI_DMASEND_INTS);
sam_configxfrints(priv, HSMCI_DMARECV_INTS);
return OK;
}