From 49c7861f0182a9cc9ced8426ddd600d3e218e899 Mon Sep 17 00:00:00 2001 From: patacongo Date: Mon, 16 Nov 2009 15:37:11 +0000 Subject: Redesign SDIO wait logic; add SDIO DMA logic git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2261 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/arch/arm/src/stm32/stm32_sdio.c | 467 ++++++++++++++++++++++++---------- 1 file changed, 331 insertions(+), 136 deletions(-) (limited to 'nuttx/arch/arm/src') diff --git a/nuttx/arch/arm/src/stm32/stm32_sdio.c b/nuttx/arch/arm/src/stm32/stm32_sdio.c index 85db660ea..d5a73ce67 100644 --- a/nuttx/arch/arm/src/stm32/stm32_sdio.c +++ b/nuttx/arch/arm/src/stm32/stm32_sdio.c @@ -48,7 +48,8 @@ #include "chip.h" #include "up_arch.h" -#include "stm32_internal.h" +#include "stm32_internal.h" +#include "stm32_dma.h" #include "stm32_sdio.h" #if CONFIG_STM32_SDIO @@ -62,6 +63,10 @@ #if defined(CONFIG_SDIO_DMA) && !defined(CONFIG_STM32_DMA2) # warning "CONFIG_SDIO_DMA support requires CONFIG_STM32_DMA2" # undef CONFIG_SDIO_DMA +#endif + +#ifndef CONFIG_SDIO_DMAPRIO +# define CONFIG_SDIO_DMAPRIO DMA_CCR_PRIMED #endif /* Friendly CLKCR bit re-definitions ****************************************/ @@ -87,6 +92,13 @@ #define SDIO_CMDTIMEOUT 100000 #define SDIO_LONGTIMEOUT 0x7fffffff + +/* DMA CCR register settings */ + +#define SDIO_RXDMA16_CONFIG (CONFIG_SDIO_DMAPRIO|DMA_CCR_MSIZE_16BITS|\ + DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC) +#define SDIO_TXDMA16_CONFIG (CONFIG_SDIO_DMAPRIO|DMA_CCR_MSIZE_16BITS|\ + DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR) /**************************************************************************** * Private Types @@ -96,11 +108,21 @@ struct stm32_dev_s { - struct sdio_dev_s dev; /* Standard, base MMC/SD interface */ + struct sdio_dev_s dev; /* Standard, base MMC/SD interface */ /* STM32-specific extensions */ - - ubyte type; /* Card type (see MMCSD_CARDTYPE_ definitions) */ + + sem_t eventsem; /* Implements event waiting */ + sdio_event_t waitevents; /* Set of events to be waited for */ + volatile sdio_event_t wkupevents; /* Set of events that caused the wakeup */ + sdio_event_t cbevents; /* Set of events to be cause callbacks */ + sdio_mediachange_t callback; /* Registered callback function */ + + /* DMA support */ + +#ifdef CONFIG_SDIO_DMA + DMA_HANDLE dma; /* Handle for DMA channel */ +#endif }; /**************************************************************************** @@ -109,6 +131,8 @@ struct stm32_dev_s /* Low-level helpers ********************************************************/ +static void stm32_takesem(struct stm32_dev_s *priv); +#define stm32_givesem(priv) (sem_post(&priv->waitsem)) static inline void stm32_setclkcr(uint32 clkcr); static inline void stm32_enableint(uint32 bitset); static inline void stm32_disableint(uint32 bitset); @@ -117,9 +141,11 @@ static inline uint32 stm32_getpwrctrl(void); static inline void stm32_clkenable(void) static inline void stm32_clkdisable(void) -/* DMA Helpers **************************************************************/ - -static inline void stm32_dmaenable(void); +/* DMA Helpers **************************************************************/ + +#ifdef CONFIG_SDIO_DMA +static void stm32_dmacallback(DMA_HANDLE handle, ubyte isr, void *arg); +#endif /* Data Transfer Helpers ****************************************************/ @@ -155,10 +181,11 @@ static int stm32_recvdata(FAR struct sdio_dev_s *dev, FAR ubyte *buffer); /* EVENT handler */ -static void stm32_eventenable(FAR struct sdio_dev_s *dev, sdio_event_t eventset, +static void stm32_waitenable(FAR struct sdio_dev_s *dev, sdio_event_t eventset, boolean enable); static ubyte stm32_eventwait(FAR struct sdio_dev_s *dev, uint32 timeout); static ubyte stm32_events(FAR struct sdio_dev_s *dev); +static void stm32_callbackenable(FAR struct sdio_dev_s *dev, sdio_event_t eventset); static int stm32_registercallback(FAR struct sdio_dev_s *dev, sdio_mediachange_t callback, void *arg) @@ -166,23 +193,18 @@ static int stm32_registercallback(FAR struct sdio_dev_s *dev, #ifdef CONFIG_SDIO_DMA static boolean stm32_dmasupported(FAR struct sdio_dev_s *dev); -#ifdef CONFIG_DATA_CACHE -static void stm32_coherent(FAR struct sdio_dev_s *dev, FAR void *addr, - size_t len, boolean write); -#endif static int stm32_dmareadsetup(FAR struct sdio_dev_s *dev, - FAR ubyte *buffer); + FAR ubyte *buffer, size_t buflen); static int stm32_dmawritesetup(FAR struct sdio_dev_s *dev, - FAR const ubyte *buffer); -static int stm32_dmaenable(FAR struct sdio_dev_s *dev); + FAR const ubyte *buffer, size_t buflen); static int stm32_dmastart(FAR struct sdio_dev_s *dev); -static int stm32_dmastop(FAR struct sdio_dev_s *dev); static int stm32_dmastatus(FAR struct sdio_dev_s *dev, size_t *remaining); #endif /* Initialization/uninitialization/reset ************************************/ - + +static void stm32_callback(void *arg); static void stm32_default(void); /**************************************************************************** @@ -193,38 +215,35 @@ struct stm32_dev_s g_mmcsd = { .dev = { - .reset = stm32_reset, - .status = stm32_status, - .widebus = stm32_widebus, - .clock = stm32_clock, - .attach = stm32_attach, - .sendcmd = stm32_sendcmd, - .sendsetup = stm32_sendsetup, - .senddata = stm32_senddata, - .waitresponse = stm32_waitresponse, - .recvR1 = stm32_recvshortcrc, - .recvR2 = stm32_recvlong, - .recvR3 = stm32_recvshort, - .recvR4 = stm32_recvnotimpl, - .recvR5 = stm32_recvnotimpl, - .recvR6 = stm32_recvshortcrc, - .recvR7 = stm32_recvshort, - .recvsetup = stm32_recvsetup, - .recvdata = stm32_recvdata, - .eventenable = stm32_eventenable, - .eventwait = stm32_eventwait, - .events = stm32_events, + .reset = stm32_reset, + .status = stm32_status, + .widebus = stm32_widebus, + .clock = stm32_clock, + .attach = stm32_attach, + .sendcmd = stm32_sendcmd, + .sendsetup = stm32_sendsetup, + .senddata = stm32_senddata, + .waitresponse = stm32_waitresponse, + .recvR1 = stm32_recvshortcrc, + .recvR2 = stm32_recvlong, + .recvR3 = stm32_recvshort, + .recvR4 = stm32_recvnotimpl, + .recvR5 = stm32_recvnotimpl, + .recvR6 = stm32_recvshortcrc, + .recvR7 = stm32_recvshort, + .recvsetup = stm32_recvsetup, + .recvdata = stm32_recvdata, + .waitenable = stm32_waitenable, + .eventwait = stm32_eventwait, + .events = stm32_events, + .callbackenable = stm32_callbackenable, + .registercallback = stm32_registercallback, #ifdef CONFIG_SDIO_DMA - .dmasupported = stm32_dmasupported, -#ifdef CONFIG_DATA_CACHE - .coherent = stm32_coherent, -#endif - .dmareadsetup = stm32_dmareadsetup, - .dmawritesetup = stm32_dmawritesetup, - .dmaenable = stm32_dmaenable, - .dmastart = stm32_dmastart, - .dmastop = stm32_dmastop, - .dmastatus = stm32_dmastatus, + .dmasupported = stm32_dmasupported, + .dmareadsetup = stm32_dmareadsetup, + .dmawritesetup = stm32_dmawritesetup, + .dmastart = stm32_dmastart, + .dmastatus = stm32_dmastatus, #endif }, }; @@ -236,6 +255,35 @@ struct stm32_dev_s g_mmcsd = /**************************************************************************** * Low-level Helpers ****************************************************************************/ +/**************************************************************************** + * Name: stm32_takesem + * + * Description: + * Take the wait semaphore (handling false alarm wakeups due to the receipt + * of signals). + * + * Input Parameters: + * dev - Instance of the SDIO device driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_takesem(struct stm32_dev_s *priv) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&priv->waitsem) != 0) + { + /* The only case that an error should occr here is if the wait was + * awakened by a signal. + */ + + ASSERT(errno == EINTR); + } +} + /**************************************************************************** * Name: stm32_setclkcr * @@ -373,11 +421,32 @@ static inline void stm32_clkdisable(void) /**************************************************************************** * DMA Helpers - ****************************************************************************/ -static inline void stm32_dmaenable(void) -{ - putreg32(1, SDIO_DCTRL_DMAEN_BB); -} + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_dmacallback + * + * Description: + * Called when SDIO DMA completes + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_DMA +static void stm32_dmacallback(DMA_HANDLE handle, ubyte isr, void *arg) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)arg; + + /* Is a data transfer complete event expected? */ + + if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0) + { + /* Yes, wake up the waiting thread */ + + priv->wkupevent = SDIOWAIT_TRANSFERDONE; + sdio_semgive(priv); + } +} +#endif /**************************************************************************** * Data Transfer Helpers @@ -1008,14 +1077,22 @@ static int stm32_recvdata(FAR struct sdio_dev_s *dev, FAR ubyte *buffer) } /**************************************************************************** - * Name: stm32_eventenable + * Name: stm32_waitenable * * Description: - * Enable/disable notification of a set of MMC/SD events - * + * Enable/disable of a set of MMC/SD wait events. This is part of the + * the SDIO_WAITEVENT sequence. The set of to-be-waited-for events is + * configured before calling stm32_waitevent. This is done in this way + * to help the driver to eliminate race conditions between the command + * setup and the subsequent events. + * + * 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: * dev - An instance of the MMC/SD device interface - * eventset - A bitset of events to enable or disable (see MMCSDEVENT_* + * eventset - A bitset of events to enable or disable (see SDIOWAIT_* * definitions). 0=disable; 1=enable. * * Returned Value: @@ -1023,15 +1100,26 @@ static int stm32_recvdata(FAR struct sdio_dev_s *dev, FAR ubyte *buffer) * ****************************************************************************/ -static void stm32_eventenable(FAR struct sdio_dev_s *dev, sdio_event_t eventset) -{ +static void stm32_waitenable(FAR struct sdio_dev_s *dev, sdio_event_t eventset) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s*)dev; + + /* This odd sequence avoids race conditions */ + + DEBUGASSERT(priv != NULL); + priv->waitevents = 0; + priv->wkupevents = 0; + priv->waitevents = eventset; } /**************************************************************************** * Name: stm32_eventwait * * Description: - * Wait for one of the enabled events to occur (or a timeout) + * Wait for one of the enabled events to occur (or a timeout). Note that + * all events enabled by SDIO_WAITEVENTS are disabled when stm32_eventwait + * returns. SDIO_WAITEVENTS must be called again before stm32_eventwait + * can be used again. * * Input Parameters: * dev - An instance of the MMC/SD device interface @@ -1045,8 +1133,42 @@ static void stm32_eventenable(FAR struct sdio_dev_s *dev, sdio_event_t eventset) ****************************************************************************/ static ubyte stm32_eventwait(FAR struct sdio_dev_s *dev, uint32 timeout) -{ - return 0; +{ + ubyte wkupevents = 0; + + DEBUGASSERT(priv->waitevents != 0); + + /* Loop until the event (or the timeout occurs). Race conditions are avoided + * by calling stm32_waitenable prior to triggering the logic that will cause + * the wait to terminate. Under certain race conditions, the waited-for + * may have already occurred before this function was called! + */ +#warning "Timeout logic not implemented" + for (;;) + { + /* Wait for an event in event set to occur. If this the event has already + * occurred, then the semaphore will already have been incremented and + * there will be no wait. + */ + + stm32_takesem(priv); + + /* Check if the event has occurred. */ + + wkupevents = (ubyte)(priv->wkupevents & priv->waitevents) + if (wkupevents != 0) + { + /* Yes... break out of the loop with wkupevents non-zero */ + + break; + } + } + + /* Clear all enabled wait events before returning */ + + priv->waitevents = 0; + priv->wkupevents = 0; + return wkupevents; } /**************************************************************************** @@ -1070,6 +1192,36 @@ static ubyte stm32_events(FAR struct sdio_dev_s *dev) return 0; } +/**************************************************************************** + * Name: stm32_callbackenable + * + * Description: + * Enable/disable of a set of MMC/SD callback events. This is part of the + * the SDIO callback sequence. The set of events is configured to enabled + * callbacks to the function provided in stm32_registercallback. + * + * 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. + * + * Input Parameters: + * dev - An instance of the MMC/SD device interface + * eventset - A bitset of events to enable or disable (see SDIOMEDIA_* + * definitions). 0=disable; 1=enable. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_callbackenable(FAR struct sdio_dev_s *dev, sdio_event_t eventset) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s*)dev; + DEBUGASSERT(priv != NULL); + priv->cbevents = eventset; + stm32_callback(priv); +} + /**************************************************************************** * Name: stm32_registercallback * @@ -1079,6 +1231,9 @@ static ubyte stm32_events(FAR struct sdio_dev_s *dev) * interrupt level events should be handled by calling back on the work * thread. * + * When this method is called, all callbacks should be disabled until they + * are enabled via a call to SDIO_CALLBACKENABLE + * * Input Parameters: * dev - Device-specific state data * callback - The funtion to call on the media change @@ -1092,7 +1247,15 @@ static ubyte stm32_events(FAR struct sdio_dev_s *dev) static int stm32_registercallback(FAR struct sdio_dev_s *dev, sdio_mediachange_t callback, void *arg) { - return -ENOSYS; + struct stm32_dev_s *priv = (struct stm32_dev_s*)dev; + + /* Disable callbacks and register this callback and is argument */ + + DEBUGASSERT(priv != NULL); + priv->cbevents = 0; + priv->cbarg = arg; + priv->callback = callback; + return OK; } /**************************************************************************** @@ -1116,44 +1279,19 @@ static boolean stm32_dmasupported(FAR struct sdio_dev_s *dev) } #endif -/**************************************************************************** - * Name: stm32_coherent - * - * Description: - * If the processor supports a data cache, then this method will make sure - * that the contents of the DMA memory and the data cache are coherent in - * preparation for the DMA transfer. For write transfers, this may mean - * flushing the data cache, for read transfers this may mean invalidating - * the data cache. - * - * Input Parameters: - * dev - An instance of the MMC/SD device interface - * addr - The beginning address of the DMA - * len - The length of the DMA - * write - TRUE: A write DMA will be performed; FALSE: a read DMA will be - * performed. - * - * Returned Value: - * None - * - ****************************************************************************/ - -#if defined(CONFIG_SDIO_DMA) && defined(CONFIG_DATA_CACHE) -static void stm32_coherent(FAR struct sdio_dev_s *dev, FAR void *addr, - size_t len, boolean write) -{ -} -#endif - /**************************************************************************** * Name: stm32_dmareadsetup * * Description: - * Setup to perform a read DMA + * Setup to perform a read DMA. If the processor supports a data cache, + * then this method will also make sure that the contents of the DMA memory + * and the data cache are coherent. For read transfers this may mean + * invalidating the data cache. * * Input Parameters: * dev - An instance of the MMC/SD device interface * buffer - The memory to DMA from + * buflen - The size of the DMA transfer in bytes * * Returned Value: * OK on success; a negated errno on failure @@ -1161,9 +1299,12 @@ static void stm32_coherent(FAR struct sdio_dev_s *dev, FAR void *addr, ****************************************************************************/ #ifdef CONFIG_SDIO_DMA -static int tm32_dmareadsetup(FAR struct sdio_dev_s *dev, FAR ubyte *buffer) +static int tm32_dmareadsetup(FAR struct sdio_dev_s *dev, FAR ubyte *buffer, size_t buflen) { - return -ENOSYS; + /* Configure the RX DMA */ + + stm32_dmasetup(priv->dma, STM32_SDIO_FIFO, (uint32)buffer, + (buflen + 3) >> 2, SDIO_RXDMA16_CONFIG); } #endif @@ -1171,11 +1312,15 @@ static int tm32_dmareadsetup(FAR struct sdio_dev_s *dev, FAR ubyte *buffer) * Name: stm32_dmawritesetup * * Description: - * Setup to perform a write DMA + * Setup to perform a write DMA. If the processor supports a data cache, + * then this method will also make sure that the contents of the DMA memory + * and the data cache are coherent. For write transfers, this may mean + * flushing the data cache. * * Input Parameters: * dev - An instance of the MMC/SD device interface * buffer - The memory to DMA into + * buflen - The size of the DMA transfer in bytes * * Returned Value: * OK on success; a negated errno on failure @@ -1184,9 +1329,12 @@ static int tm32_dmareadsetup(FAR struct sdio_dev_s *dev, FAR ubyte *buffer) #ifdef CONFIG_SDIO_DMA static int stm32_dmawritesetup(FAR struct sdio_dev_s *dev, - FAR const ubyte *buffer) + FAR const ubyte *buffer, size_t buflen) { - return -ENOSYS; + /* Configure the RX DMA */ + + stm32_dmasetup(priv->dma, STM32_SDIO_FIFO, (uint32)buffer, + (buflen + 3) >> 2, SDIO_TXDMA16_CONFIG); } #endif @@ -1206,29 +1354,8 @@ static int stm32_dmawritesetup(FAR struct sdio_dev_s *dev, #ifdef CONFIG_SDIO_DMA static int stm32_dmastart(FAR struct sdio_dev_s *dev) -{ - return -ENOSYS; -} -#endif - -/**************************************************************************** - * Name: stm32_dmastop - * - * Description: - * Stop the DMA - * - * Input Parameters: - * dev - An instance of the MMC/SD device interface - * - * Returned Value: - * OK on success; a negated errno on failure - * - ****************************************************************************/ - -#ifdef CONFIG_SDIO_DMA -static int stm32_dmastop(FAR struct sdio_dev_s *dev) -{ - return -ENOSYS; +{ + stm32_dmastart(priv->dma, sdio_dmacallback, priv, FALSE); } #endif @@ -1236,7 +1363,7 @@ static int stm32_dmastop(FAR struct sdio_dev_s *dev) * Name: stm32_dmastatus * * Description: - * Returnt the number of bytes remaining in the DMA transfer + * Return the number of bytes remaining in the DMA transfer * * Input Parameters: * dev - An instance of the MMC/SD device interface @@ -1268,6 +1395,63 @@ static int stm32_dmastatus(FAR struct sdio_dev_s *dev, size_t *remaining) /**************************************************************************** * Initialization/uninitialization/reset ****************************************************************************/ +/**************************************************************************** + * Name: stm32_callback + * + * Description: + * Perform callback. + * + * Assumptions: + * This function does not execute in the context of an interrupt handler. + * It may be invoked on any user thread or scheduled on the work thread + * from an interrupt handler. + * + ****************************************************************************/ + +static void stm32_callback(void *arg) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s*)arg; + + /* Is a callback registered? */ + + DEBUGASSERT(priv != NULL); + if (priv->callback) + { + /* Yes.. Check for enabled callback events */ + + if ((stm32_status(&priv->dev) & SDIO_STATUS_PRESENT) != 0) + { + /* Media is present. Is the media inserted event enabled? */ + + if ((priv->cbevents & SDIOMEDIA_INSERTED) == 0) + { + /* No... return without performing the callback */ + + return; + } + } + else + { + /* Media is not present. Is the media eject event enabled? */ + + if ((priv->cbevents & SDIOMEDIA_EJECTED) == 0) + { + /* No... return without performing the callback */ + + return; + } + } + + /* Perform the callback, disabling further callbacks. Of course, the + * the callback can (and probably should) re-enable callbacks. + */ + + priv->cbevents = 0; + priv->callback(priv->cbarg); + + } +} + /**************************************************************************** * Name: stm32_default * @@ -1294,24 +1478,35 @@ static void stm32_default(void) ****************************************************************************/ /**************************************************************************** - * Name: mmcsd_slotinitialize + * Name: sdio_initialize * * Description: - * Initialize one slot for operation using the MMC/SD interface + * Initialize SDIO for operation. * - * Input Parameters: - * minor - The MMC/SD minor device number. The MMC/SD device will be - * registered as /dev/mmcsdN where N is the minor number - * slotno - The slot number to use. This is only meaningful for architectures - * that support multiple MMC/SD slots. This value must be in the range - * {0, ..., CONFIG_MMCSD_NSLOTS}. - * dev - And instance of an MMC/SD interface. The MMC/SD hardware should - * be initialized and ready to use. + * Input Parameters: + * slotno - Not used. + * + * Returned Values: + * A reference to an SDIO interface structure. NULL is returned on failures. * ****************************************************************************/ -int mmcsd_slotinitialize(int minor, int slotno, FAR struct sdio_dev_s *dev) -{ +FAR sdio_dev_s *mmcsd_slotinitialize(int slotno) +{ + /* There is only one, slot */ + + struct stm32_dev_s *priv = &g_sdiodev; + + /* Initialize the SDIO slot structure */ + + sem_init(&priv->eventsem, 0, 0); + + /* Allocate a DMA channel */ + +#ifdef CONFIG_SDIO_DMA + priv->dma = stm32_dmachannel(DMACHAN_SDIO); +#endif + /* Configure GPIOs for 4-bit, wide-bus operation (the chip is capable of * 8-bit wide bus operation but D4-D7 are not configured). */ @@ -1332,8 +1527,8 @@ int mmcsd_slotinitialize(int minor, int slotno, FAR struct sdio_dev_s *dev) stm32_setclkcr(STM32_CLCKCR_INIT); stm32_setpwrctrl(SDIO_POWER_PWRCTRL_ON); stm32_clkenable(ENABLE); - - return -ENOSYS; + + return &g_sdiodev.dev; } #endif /* CONFIG_STM32_SDIO */ -- cgit v1.2.3