summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-08-10 18:01:23 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-08-10 18:01:23 -0600
commit11719aa69e37addcdd96215953844966c32149d7 (patch)
tree59fa87a155bb304aa2c9d3791db5022f8d0cfe26
parentf143cb2eb878d52b65ee1287a2c2a11a1f3392bf (diff)
downloadnuttx-11719aa69e37addcdd96215953844966c32149d7.tar.gz
nuttx-11719aa69e37addcdd96215953844966c32149d7.tar.bz2
nuttx-11719aa69e37addcdd96215953844966c32149d7.zip
SAMA5: Fix HSMCI race condition. Now memory card interface is functional with DMA
-rw-r--r--nuttx/ChangeLog8
-rw-r--r--nuttx/arch/arm/src/sam34/sam3u_dmac.c7
-rw-r--r--nuttx/arch/arm/src/sam34/sam3u_periphclks.h2
-rw-r--r--nuttx/arch/arm/src/sam34/sam_hsmci.c161
-rw-r--r--nuttx/arch/arm/src/sama5/sam_hsmci.c160
-rw-r--r--nuttx/configs/sam3u-ek/README.txt46
-rw-r--r--nuttx/configs/sam3u-ek/nsh/defconfig45
-rw-r--r--nuttx/configs/sam3u-ek/src/up_mmcsd.c9
-rw-r--r--nuttx/configs/sama5d3x-ek/README.txt10
-rw-r--r--nuttx/drivers/mmcsd/Kconfig2
-rw-r--r--nuttx/include/nuttx/sdio.h15
11 files changed, 358 insertions, 107 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index fd40029e7..a08d71418 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -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).
diff --git a/nuttx/arch/arm/src/sam34/sam3u_dmac.c b/nuttx/arch/arm/src/sam34/sam3u_dmac.c
index 4c568ba5b..32a693d4d 100644
--- a/nuttx/arch/arm/src/sam34/sam3u_dmac.c
+++ b/nuttx/arch/arm/src/sam34/sam3u_dmac.c
@@ -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. */
diff --git a/nuttx/arch/arm/src/sam34/sam3u_periphclks.h b/nuttx/arch/arm/src/sam34/sam3u_periphclks.h
index 515229705..309454a02 100644
--- a/nuttx/arch/arm/src/sam34/sam3u_periphclks.h
+++ b/nuttx/arch/arm/src/sam34/sam3u_periphclks.h
@@ -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)
diff --git a/nuttx/arch/arm/src/sam34/sam_hsmci.c b/nuttx/arch/arm/src/sam34/sam_hsmci.c
index e5fca7f0d..b9c4e883a 100644
--- a/nuttx/arch/arm/src/sam34/sam_hsmci.c
+++ b/nuttx/arch/arm/src/sam34/sam_hsmci.c
@@ -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);
}
/****************************************************************************
@@ -633,6 +638,28 @@ static void sam_disablexfrints(struct sam_dev_s *priv)
}
/****************************************************************************
+ * 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
*
* Description:
@@ -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,14 +2080,23 @@ static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev,
sdio_eventset_t wkupevent = 0;
int ret;
+ /* 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);
+
+ /* 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.
+ * be non-zero (and, hopefully, the semaphore count will also be non-zero).
*/
- DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
- (priv->waitevents == 0 && priv->wkupevent != 0));
-
/* Check if the timeout event is specified in the event set */
if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
@@ -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);
- sam_xfrsample(priv, SAMPLENDX_AFTER_SETUP);
- /* Enable TX interrrupts */
+ /* 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_enablexfrints(priv, HSMCI_DMASEND_INTS);
+ sam_xfrsample(priv, SAMPLENDX_AFTER_SETUP);
+ sam_configxfrints(priv, HSMCI_DMASEND_INTS);
return OK;
}
diff --git a/nuttx/arch/arm/src/sama5/sam_hsmci.c b/nuttx/arch/arm/src/sama5/sam_hsmci.c
index ad73e1434..3776f53c7 100644
--- a/nuttx/arch/arm/src/sama5/sam_hsmci.c
+++ b/nuttx/arch/arm/src/sama5/sam_hsmci.c
@@ -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);
}
/****************************************************************************
@@ -791,6 +794,28 @@ static void sam_disablexfrints(struct sam_dev_s *priv)
}
/****************************************************************************
+ * 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
*
* Description:
@@ -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,14 +2331,23 @@ static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev,
sdio_eventset_t wkupevent = 0;
int ret;
+ /* 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);
+
+ /* 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.
+ * be non-zero (and, hopefully, the semaphore count will also be non-zero).
*/
- DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
- (priv->waitevents == 0 && priv->wkupevent != 0));
-
/* Check if the timeout event is specified in the event set */
if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
@@ -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);
- sam_xfrsample(priv, SAMPLENDX_AFTER_SETUP);
- /* Enable TX interrrupts */
+ /* 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_enablexfrints(priv, HSMCI_DMASEND_INTS);
+ sam_xfrsample(priv, SAMPLENDX_AFTER_SETUP);
+ sam_configxfrints(priv, HSMCI_DMARECV_INTS);
return OK;
}
diff --git a/nuttx/configs/sam3u-ek/README.txt b/nuttx/configs/sam3u-ek/README.txt
index f4eff1f49..b178f0682 100644
--- a/nuttx/configs/sam3u-ek/README.txt
+++ b/nuttx/configs/sam3u-ek/README.txt
@@ -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
diff --git a/nuttx/configs/sam3u-ek/nsh/defconfig b/nuttx/configs/sam3u-ek/nsh/defconfig
index 289c3417b..43b8295d9 100644
--- a/nuttx/configs/sam3u-ek/nsh/defconfig
+++ b/nuttx/configs/sam3u-ek/nsh/defconfig
@@ -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
@@ -163,26 +174,18 @@ CONFIG_SAM34_UART0=y
# 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
#
# CONFIG_ARCH_NOINTC is not set
@@ -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
@@ -219,6 +220,12 @@ CONFIG_BOOT_RUNFROMFLASH=y
# CONFIG_BOOT_COPYTORAM is not set
#
+# Boot Memory Configuration
+#
+CONFIG_RAM_START=0x20000000
+CONFIG_RAM_SIZE=32768
+
+#
# Board Selection
#
CONFIG_ARCH_BOARD_SAM3UEK=y
@@ -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
diff --git a/nuttx/configs/sam3u-ek/src/up_mmcsd.c b/nuttx/configs/sam3u-ek/src/up_mmcsd.c
index 0c1c5a9ad..c03621522 100644
--- a/nuttx/configs/sam3u-ek/src/up_mmcsd.c
+++ b/nuttx/configs/sam3u-ek/src/up_mmcsd.c
@@ -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
************************************************************************************/
diff --git a/nuttx/configs/sama5d3x-ek/README.txt b/nuttx/configs/sama5d3x-ek/README.txt
index 2d90c2c48..0bb288cca 100644
--- a/nuttx/configs/sama5d3x-ek/README.txt
+++ b/nuttx/configs/sama5d3x-ek/README.txt
@@ -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
diff --git a/nuttx/drivers/mmcsd/Kconfig b/nuttx/drivers/mmcsd/Kconfig
index b518ea482..84037707c 100644
--- a/nuttx/drivers/mmcsd/Kconfig
+++ b/nuttx/drivers/mmcsd/Kconfig
@@ -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
diff --git a/nuttx/include/nuttx/sdio.h b/nuttx/include/nuttx/sdio.h
index d5b15370d..186736e86 100644
--- a/nuttx/include/nuttx/sdio.h
+++ b/nuttx/include/nuttx/sdio.h
@@ -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