diff --git a/arch/arm/src/sam34/sam3u_dmac.c b/arch/arm/src/sam34/sam3u_dmac.c index 4c568ba5be..32a693d4d1 100644 --- a/arch/arm/src/sam34/sam3u_dmac.c +++ b/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/arch/arm/src/sam34/sam3u_periphclks.h b/arch/arm/src/sam34/sam3u_periphclks.h index 5152297052..309454a026 100644 --- a/arch/arm/src/sam34/sam3u_periphclks.h +++ b/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/arch/arm/src/sam34/sam_hsmci.c b/arch/arm/src/sam34/sam_hsmci.c index e5fca7f0d1..b9c4e883aa 100644 --- a/arch/arm/src/sam34/sam_hsmci.c +++ b/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); } /**************************************************************************** @@ -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; } diff --git a/arch/arm/src/sama5/sam_hsmci.c b/arch/arm/src/sama5/sam_hsmci.c index ad73e1434d..3776f53c71 100644 --- a/arch/arm/src/sama5/sam_hsmci.c +++ b/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); } /**************************************************************************** @@ -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; }