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 d60e9e14f6
commit da4cebf572
11 changed files with 362 additions and 111 deletions

View file

@ -5332,4 +5332,10 @@
verified (with SPI) (2013-8-9).
* arch/arm/src/sama5/sam_memories.c and .h: Central logic for
conversions between physical and virtual addresses (2013-8-9).
* arch/arm/src/sama5/sam_hsmci.c and sam34/sam_hsmci.c: Correct a
race condition in the SAMA5 HSCMI driver: The tranfer done
interrupt was firing before the wait was started. Fix this and
also backported the changes to SAM3/4 (untested). Now HSCMI is
functional on the SAMA5 with DMA! (2013-8-10).
* arch/arm/src/sam34/sam3u_periphclks.h: Correct a typo in a register
name (2013-8-10).

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;
}

View file

@ -640,14 +640,44 @@ Configurations
CONFIG_DEBUG_VERBOSE=y : Enable verbose debug output
CONFIG_DEBUG_INPUT=y : Enable debug output from input devices
STATUS:
2013-6-28: The touchscreen is functional.
2013-6-29: Hmmm... but there appear to be conditions when the
touchscreen driver locks up. Looks like some issue with
managing the interrupts.
2013-6-30: Those lock-ups appear to be due to poorly placed
debug output statements. If you do not enable debug output,
the touchscreen is rock-solid.
3. Enabling HSMCI support. The SAM3U-KE provides a an SD memory card
slot. Support for the SD slot can be enabled with the following
settings:
System Type->ATSAM3/4 Peripheral Support
CONFIG_SAM34_HSMCI=y : Enable HSMCI support
CONFIG_SAM34_DMA=y : DMAC support is needed by HSMCI
System Type
CONFIG_SAM34_GPIO_IRQ=y : PIO interrupts needed
CONFIG_SAM34_GPIOA_IRQ=y : Card detect pin is on PIOA
Device Drivers -> MMC/SD Driver Support
CONFIG_MMCSD=y : Enable MMC/SD support
CONFIG_MMSCD_NSLOTS=1 : One slot per driver instance
CONFIG_MMCSD_HAVECARDDETECT=y : Supports card-detect PIOs
CONFIG_MMCSD_SDIO=y : SDIO-based MMC/SD support
CONFIG_SDIO_DMA=y : Use SDIO DMA
CONFIG_SDIO_BLOCKSETUP=y : Needs to know block sizes
Library Routines
CONFIG_SCHED_WORKQUEUE=y : Driver needs work queue support
Application Configuration -> NSH Library
CONFIG_NSH_ARCHINIT=y : NSH board-initialization
STATUS:
2013-6-28: The touchscreen is functional.
2013-6-29: Hmmm... but there appear to be conditions when the
touchscreen driver locks up. Looks like some issue with
managing the interrupts.
2013-6-30: Those lock-ups appear to be due to poorly placed
debug output statements. If you do not enable debug output,
the touchscreen is rock-solid.
2013-8-10: Added the comments above above enabling HSMCI memory
card support and verified that the configuration builds without
error. However, that configuration has not yet been tested (and
is may even be incomplete).
nx:
Configures to use examples/nx using the HX834x LCD hardware on

View file

@ -73,10 +73,17 @@ CONFIG_ARCH="arm"
# CONFIG_ARCH_CHIP_LPC31XX is not set
# CONFIG_ARCH_CHIP_LPC43XX is not set
# CONFIG_ARCH_CHIP_NUC1XX is not set
# CONFIG_ARCH_CHIP_SAMA5 is not set
CONFIG_ARCH_CHIP_SAM34=y
# CONFIG_ARCH_CHIP_STM32 is not set
# CONFIG_ARCH_CHIP_STR71X is not set
# CONFIG_ARCH_ARM7TDMI is not set
# CONFIG_ARCH_ARM926EJS is not set
# CONFIG_ARCH_ARM920T is not set
# CONFIG_ARCH_CORTEXM0 is not set
CONFIG_ARCH_CORTEXM3=y
# CONFIG_ARCH_CORTEXM4 is not set
# CONFIG_ARCH_CORTEXA5 is not set
CONFIG_ARCH_FAMILY="armv7-m"
CONFIG_ARCH_CHIP="sam34"
# CONFIG_ARMV7M_USEBASEPRI is not set
@ -93,6 +100,10 @@ CONFIG_ARMV7M_TOOLCHAIN_BUILDROOT=y
# CONFIG_ARMV7M_TOOLCHAIN_GNU_EABIL is not set
CONFIG_ARMV7M_OABI_TOOLCHAIN=y
# CONFIG_GPIO_IRQ is not set
CONFIG_ARCH_HAVE_EXTNAND=y
CONFIG_ARCH_HAVE_EXTNOR=y
CONFIG_ARCH_HAVE_EXTSRAM0=y
CONFIG_ARCH_HAVE_EXTSRAM1=y
#
# AT91SAM3/4 Configuration Options
@ -162,26 +173,18 @@ CONFIG_SAM34_UART0=y
# CONFIG_SAM34_WDT is not set
# CONFIG_SAM34_HSMCI is not set
#
# AT91SAM3/4 USART Configuration
#
#
# AT91SAM3/4 GPIO Interrupt Configuration
#
#
# External Memory Configuration
#
CONFIG_ARCH_HAVE_EXTNAND=y
CONFIG_ARCH_HAVE_EXTNOR=y
CONFIG_ARCH_HAVE_EXTSRAM0=y
CONFIG_ARCH_HAVE_EXTSRAM1=y
# CONFIG_SAM34_EXTNAND is not set
# CONFIG_SAM34_EXTNOR is not set
# CONFIG_SAM34_EXTSRAM0 is not set
# CONFIG_SAM34_EXTSRAM1 is not set
#
# AT91SAM3/4 GPIO Interrupt Configuration
#
#
# Architecture Options
#
@ -204,8 +207,6 @@ CONFIG_ARCH_HAVE_RAMVECTORS=y
#
CONFIG_BOARD_LOOPSPERMSEC=8720
# CONFIG_ARCH_CALIBRATION is not set
CONFIG_RAM_START=0x20000000
CONFIG_RAM_SIZE=32768
CONFIG_ARCH_HAVE_INTERRUPTSTACK=y
CONFIG_ARCH_INTERRUPTSTACK=0
@ -218,6 +219,12 @@ CONFIG_BOOT_RUNFROMFLASH=y
# CONFIG_BOOT_RUNFROMSDRAM is not set
# CONFIG_BOOT_COPYTORAM is not set
#
# Boot Memory Configuration
#
CONFIG_RAM_START=0x20000000
CONFIG_RAM_SIZE=32768
#
# Board Selection
#
@ -332,6 +339,10 @@ CONFIG_SERIAL=y
# CONFIG_DEV_LOWCONSOLE is not set
# CONFIG_16550_UART is not set
CONFIG_ARCH_HAVE_UART0=y
#
# USART Configuration
#
CONFIG_MCU_SERIAL=y
CONFIG_STANDARD_SERIAL=y
CONFIG_UART0_SERIAL_CONSOLE=y
@ -577,6 +588,7 @@ CONFIG_NSH_BUILTIN_APPS=y
# CONFIG_NSH_DISABLE_CAT is not set
# CONFIG_NSH_DISABLE_CD is not set
# CONFIG_NSH_DISABLE_CP is not set
# CONFIG_NSH_DISABLE_CMP is not set
# CONFIG_NSH_DISABLE_DD is not set
# CONFIG_NSH_DISABLE_ECHO is not set
# CONFIG_NSH_DISABLE_EXEC is not set
@ -693,3 +705,8 @@ CONFIG_READLINE_ECHO=y
#
# USB Monitor
#
#
# Zmodem Commands
#
# CONFIG_SYSTEM_ZMODEM is not set

View file

@ -55,12 +55,21 @@
* when the interrupt indicating that a card has been inserted or removed is received,
* this function must call sio_mediachange() to handle that event. See
* arch/arm/src/sam34/sam_hsmci.h for more information.
*
* Also see the SAMA5D3x-EK implementation of this same logic. The card detect
* interrupt handling should be a drop-in.
*/
#ifdef GPIO_MCI_CD
# warning "Card detect interrupt handling needed"
#endif
/* Usually defined in NuttX header files */
#ifndef OK
# define OK 0
#endif
/************************************************************************************
* Private Functions
************************************************************************************/

View file

@ -1027,10 +1027,11 @@ Configurations
CONFIG_SAMA5_DMAC0=y : DMAC0 is needed by HSMCI0
CONFIG_SAMA5_DMAC1=y : DMAC1 is needed by HSMCI1
System Type
CONFIG_SAMA5_PIO_IRQ=y : PIO interrupts needed
CONFIG_SAMA5_PIOD_IRQ=y : Card detect pins are on PIOD
Device Drivers ->
Device Drivers -> MMC/SD Driver Support
CONFIG_MMCSD=y : Enable MMC/SD support
CONFIG_MMSCD_NSLOTS=1 : One slot per driver instance
CONFIG_MMCSD_HAVECARDDETECT=y : Supports card-detect PIOs
@ -1074,9 +1075,12 @@ Configurations
where the memory test fails! No idea why.
2013-8-5: The AT25 configuration has been verified to be functional.
2013-9-9: The AT25 configuration has been verified with DMA
2013-8-9: The AT25 configuration has been verified with DMA
enabled.
2013-8-10: Basic HSCMI1 functionality (with DMA) has been verified.
Most testing is needed to assure that this is a stable solution.
ostest:
This configuration directory, performs a simple OS test using
examples/ostest.
@ -1133,7 +1137,7 @@ Configurations
configuration to start the program in NOR FLASH (see just above).
See "Creating and Using NORBOOT" above.
2013-7-31: The OS test configuration is basically functional, but
2013-7-31: The OS test configuration is basically functional, but
takes a very long time in the round-robin scheduler test computing
prime numbers. This test is supposed to be slow -- like several
seconds -- but not many minutes. No idea why yet. The best guess

View file

@ -63,10 +63,12 @@ endif
config ARCH_HAVE_SDIO
bool
default n
config MMCSD_SDIO
bool "MMC/SD SDIO transfer support"
default n
depends on ARCH_HAVE_SDIO
if MMCSD_SDIO

View file

@ -582,8 +582,19 @@
* Description:
* Enable/disable of a set of SDIO wait events. This is part of the
* the SDIO_WAITEVENT sequence. The set of to-be-waited-for events is
* configured before calling SDIO_EVENTWAIT. This is done in this way
* to help the driver to eliminate race conditions between the command
* 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) SDIO_WAITENABLE is called