diff options
Diffstat (limited to 'nuttx')
-rw-r--r-- | nuttx/ChangeLog | 8 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sam34/sam3u_dmac.c | 7 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sam34/sam3u_periphclks.h | 2 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sam34/sam_hsmci.c | 161 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sama5/sam_hsmci.c | 160 | ||||
-rw-r--r-- | nuttx/configs/sam3u-ek/README.txt | 46 | ||||
-rw-r--r-- | nuttx/configs/sam3u-ek/nsh/defconfig | 45 | ||||
-rw-r--r-- | nuttx/configs/sam3u-ek/src/up_mmcsd.c | 9 | ||||
-rw-r--r-- | nuttx/configs/sama5d3x-ek/README.txt | 10 | ||||
-rw-r--r-- | nuttx/drivers/mmcsd/Kconfig | 2 | ||||
-rw-r--r-- | nuttx/include/nuttx/sdio.h | 15 |
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 |