From f79e803e892524d79347c65f926545b4840efc97 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sat, 25 Oct 2014 08:09:19 -0600 Subject: [PATCH] Add some logic to EFM32 DMA module. Still incomplete --- arch/arm/src/efm32/Kconfig | 12 ++ arch/arm/src/efm32/efm32_dma.c | 305 ++++++++++++++++++++++++++++++--- arch/arm/src/efm32/efm32_dma.h | 30 +--- arch/arm/src/sama5/sam_xdmac.c | 2 +- 4 files changed, 303 insertions(+), 46 deletions(-) diff --git a/arch/arm/src/efm32/Kconfig b/arch/arm/src/efm32/Kconfig index 32d9b160dd2..de034a53168 100644 --- a/arch/arm/src/efm32/Kconfig +++ b/arch/arm/src/efm32/Kconfig @@ -163,6 +163,18 @@ config EFM32_GPIO_IRQ ---help--- Enable support for interrupting GPIO pins +if EFM32_DMA + +config EFM32_DMA_NDESCR + int "Pre-allocated Control Structures" + default 8 + ---help--- + DMA transfers require DMA channel control descriptors. These + descriptors are pre-allocated and this setting provides the number + of descriptors that will be pre-allocated. + +endif + choice prompt "USART0 Mode" default EFM32_USART0_ISUART diff --git a/arch/arm/src/efm32/efm32_dma.c b/arch/arm/src/efm32/efm32_dma.c index 94272001c94..0b6263bf18e 100644 --- a/arch/arm/src/efm32/efm32_dma.c +++ b/arch/arm/src/efm32/efm32_dma.c @@ -42,12 +42,65 @@ #include #include #include +#include +#include +#include +#include + +#include #include "efm32_dma.h" /***************************************************************************** * Public Types *****************************************************************************/ +/* This union allows us to keep free DMA descriptors in in a list */ + +union dma_descriptor_u +{ + sq_entry_t link; + struct dma_desriptor_s desc; +}; + +/* This structure describes one DMA channel */ + +struct dma_channel_s +{ + uint8_t chan; /* DMA channel number (0-EFM_DMA_NCHANNELS) */ + bool inuse; /* TRUE: The DMA channel is in use */ + struct dms_descriptor_s *desc; /* DMA descriptor assigned to the channel */ + dma_callback_t callback; /* Callback invoked when the DMA completes */ + void *arg; /* Argument passed to callback function */ +}; + +/* This structure describes the state of the DMA controller */ + +struct dma_controller_s +{ + sem_t exclsem; /* Protects channel table */ + sem_t chansem; /* Count of free channels */ + sem_t freesem; /* Count of free descriptors */ + sq_queue_t freedesc; /* List of free DMA descriptors */ +}; + +/***************************************************************************** + * Private Data + *****************************************************************************/ + +/* This is the overall state of the DMA controller */ + +static struct dma_controller_s g_dmac; + +/* This is the array of all DMA channels */ + +static struct dma_channel_s g_dmach[EFM_DMA_NCHANNELS]; + +/* This array describes the available channel control data structures. + * Each structure must be aligned to the size of the DMA descriptor. + */ + +static union dma_descriptor_u g_descriptors[CONFIG_EFM32_DMA_NDESCR] + __attribute__((aligned(16))); /***************************************************************************** * Public Data @@ -57,49 +110,235 @@ * Private Functions *****************************************************************************/ +/**************************************************************************** + * Name: efm32_alloc_descriptor + * + * Description: + * Allocate one DMA descriptor by removing it from the free list. + * + * Returned Value: + * The allocated DMA descriptor. If no DMA descriptors are available, this + * function will wait until one is returned to the freelist. + * + ****************************************************************************/ + +static struct dma_descriptor_s *efm32_alloc_descriptor(void) +{ + struct dma_descriptor_s *desc; + irqstate_t flags; + + /* Take a count from from the descriptor counting semaphore. We may block + * if there are no free descriptors. When we get the count, then we can + * be assured that a DMA descriptor is available in the free list and is + * reserved for us. + */ + + flags = irqsave(); + while (sem_wait(&g_dmac.freesem) < 0) + { + /* sem_wait should fail only if it is awakened by a a signal */ + + DEBUGASSERT(errno == EINTR); + } + + /* Get our descriptor to the free list. Interrupts mus be disabled here + * because the free list may also be accessed from the DMA interrupt + * handler. + */ + + desc = (struct dma_descriptor_s *)sq_remfirst(&g_dmac.freedesc); + irqrestore(flags); + + /* And return the descriptor */ + + DEBUGASSERT(desc && ((uintptr_t)desc & ~15) == 0); + return desc; +} + +/**************************************************************************** + * Name: efm32_free_descriptor + * + * Description: + * Free one DMA descriptor by returning it to the free list. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void efm32_free_descriptor(struct dma_descriptor_s *desc) +{ + union dma_descriptor_u *udesc = (union dma_descriptor_u *)desc; + irqstate_t flags; + + /* Return the descriptor to the free list. This may be called from the + * the DMA completion interrupt handler. + */ + + flags = irqsave(); + sq_addlast(&udesc->link, &g_dmac.freedesc); + irqrestore(flags); + + /* Notify any waiters that a new DMA descriptor is available in the free + * list. + */ + + sem_post(&g_dmac.freesem); +} + +/**************************************************************************** + * Name: efm32_dmac_interrupt + * + * Description: + * DMA interrupt handler + * + ****************************************************************************/ + +static int efm32_dmac_interrupt(int irq, void *context) +{ +#warning Missing logic + return OK; +} + /***************************************************************************** * Public Functions *****************************************************************************/ -/***************************************************************************** +/**************************************************************************** + * Name: up_dmainitialize + * + * Description: + * Initialize the DMA subsystem + * + * Returned Value: + * None + * + ****************************************************************************/ + +void weak_function up_dmainitialize(void) +{ + int i; + + dmallvdbg("Initialize XDMAC0\n"); + + /* Add the pre-allocated DMA descriptors to the free list */ + + sq_init(&g_dmac.freedesc); + sem_init(&g_dmac.freesem, 0, CONFIG_EFM32_DMA_NDESCR); + + for (i = 0; i < CONFIG_EFM32_DMA_NDESCR; i++) + { + sq_addlast(&g_descriptors[i].link, &g_dmac.freedesc); + } + + /* Initialize the channel list */ + + sem_init(&g_dmac.exclsem, 0, 1); + sem_init(&g_dmac.chansem, 0, EFM_DMA_NCHANNELS); + + for (i = 0; i < EFM_DMA_NCHANNELS; i++) + { + g_dmach[i].chan = i; + } + + /* Enable clock to the DMA module */ +#warning Missing logic + + /* Attach DMA interrupt vector */ + + (void)irq_attach(EFM32_IRQ_DMA, efm32_dmac_interrupt); + + /* Initialize the controller */ +#warning Missing logic + + /* Enable the IRQ at the AIC (still disabled at the DMA controller) */ + + up_enable_irq(EFM32_IRQ_DMA); +} + +/**************************************************************************** * Name: efm32_dmachannel * * Description: * Allocate a DMA channel. This function gives the caller mutually - * exclusive access to the DMA channel specified by the 'chan' argument. - * DMA channels are shared on the EFM32: Devices sharing the same DMA - * channel cannot do DMA concurrently! See the DMACHAN_* definitions in - * efm32_dma.h. + * exclusive access to a DMA channel. * - * If the DMA channel is not available, then efm32_dmachannel() will wait - * until the holder of the channel relinquishes the channel by calling - * efm32_dmafree(). WARNING: If you have two devices sharing a DMA - * channel and the code never releases the channel, the efm32_dmachannel - * call for the other will hang forever in this function! Don't let your - * design do that! + * If no DMA channel is available, then efm32_dmachannel() will wait + * until the holder of a channel relinquishes the channel by calling + * efm32_dmafree(). * - * Hmm.. I suppose this interface could be extended to make a non-blocking - * version. Feel free to do that if that is what you need. - * - * Input parameter: - * chan - Identifies the channel resource + * Input parameters: + * None * * Returned Value: - * Provided that 'chan' is valid, this function ALWAYS returns a non-NULL, - * void* DMA channel handle. (If 'chan' is invalid, the function will - * assert if debug is enabled or do something ignorant otherwise). + * This function ALWAYS returns a non-NULL, void* DMA channel handle. * * Assumptions: - * - The caller does not hold he DMA channel. - * - The caller can wait for the DMA channel to be freed if it is no + * - The caller can wait for a DMA channel to be freed if it is not * available. * - *****************************************************************************/ + ****************************************************************************/ -DMA_HANDLE efm32_dmachannel(unsigned int chan) +DMA_HANDLE efm32_dmachannel(void) { + struct dma_channel_s *dmach; + unsigned int chndx; + + /* Take a count from from the channel counting semaphore. We may block + * if there are no free channels. When we get the count, then we can + * be assured that a channel is available in the channel list and is + * reserved for us. + */ + + while (sem_wait(&g_dmac.chansem) < 0) + { + /* sem_wait should fail only if it is awakened by a a signal */ + + DEBUGASSERT(errno == EINTR); + } + + /* Get exclusive access to the DMA channel list */ + + while (sem_wait(&g_dmac.exclsem) < 0) + { + /* sem_wait should fail only if it is awakened by a a signal */ + + DEBUGASSERT(errno == EINTR); + } + + /* Search for an available DMA channel */ + + for (chndx = 0, dmach = NULL; chndx < EFM_DMA_NCHANNELS; chndx++) + { + struct dma_channel_s *candidate = &g_dmach[chndx]; + if (!candidate->inuse) + { + dmach = candidate; + dmach->inuse = true; + + /* Clear any pending channel interrupts */ #warning Missing logic - return NULL; + + /* Disable the channel */ +#warning Missing logic + break; + } + } + + sem_post(&g_dmac.exclsem); + + /* Show the result of the allocation */ + + if (dmach) + { + dmavdbg("Allocated DMA channel %d\n", dmach->chan); + } + else + { + dmadbg("ERROR: Failed allocate DMA channel\n"); + } + + return (DMA_HANDLE)dmach; } /**************************************************************************** @@ -123,7 +362,25 @@ DMA_HANDLE efm32_dmachannel(unsigned int chan) void efm32_dmafree(DMA_HANDLE handle) { + struct dma_channel_s *dmach = (struct dma_channel_s *)handle; + + DEBUGASSERT(dmach != NULL && dmach->inuse); + dmavdbg("DMA channel %d\n", dmach->chan); + + /* Disable the channel */ #warning Missing logic + + /* Mark the channel no longer in use. Clearing the inuse flag is an atomic + * operation and so should be safe. + */ + + dmach->inuse = false; + + /* And increment the count of free channels... possibly waking up a + * thread that may be waiting for a channel. + */ + + sem_post(&g_dmac.chansem); } /**************************************************************************** diff --git a/arch/arm/src/efm32/efm32_dma.h b/arch/arm/src/efm32/efm32_dma.h index 7b65876a0b3..8d0262caf1d 100644 --- a/arch/arm/src/efm32/efm32_dma.h +++ b/arch/arm/src/efm32/efm32_dma.h @@ -125,37 +125,25 @@ extern "C" * * Description: * Allocate a DMA channel. This function gives the caller mutually - * exclusive access to the DMA channel specified by the 'chan' argument. - * DMA channels are shared on the EFM32: Devices sharing the same DMA - * channel cannot do DMA concurrently! See the DMACHAN_* definitions in - * efm32_dma.h. + * exclusive access to a DMA channel. * - * If the DMA channel is not available, then efm32_dmachannel() will wait - * until the holder of the channel relinquishes the channel by calling - * efm32_dmafree(). WARNING: If you have two devices sharing a DMA - * channel and the code never releases the channel, the efm32_dmachannel - * call for the other will hang forever in this function! Don't let your - * design do that! + * If no DMA channel is available, then efm32_dmachannel() will wait + * until the holder of a channel relinquishes the channel by calling + * efm32_dmafree(). * - * Hmm.. I suppose this interface could be extended to make a non-blocking - * version. Feel free to do that if that is what you need. - * - * Input parameter: - * chan - Identifies the channel resource + * Input parameters: + * None * * Returned Value: - * Provided that 'chan' is valid, this function ALWAYS returns a non-NULL, - * void* DMA channel handle. (If 'chan' is invalid, the function will - * assert if debug is enabled or do something ignorant otherwise). + * This function ALWAYS returns a non-NULL, void* DMA channel handle. * * Assumptions: - * - The caller does not hold he DMA channel. - * - The caller can wait for the DMA channel to be freed if it is no + * - The caller can wait for a DMA channel to be freed if it is not * available. * ****************************************************************************/ -DMA_HANDLE efm32_dmachannel(unsigned int chan); +DMA_HANDLE efm32_dmachannel(void); /**************************************************************************** * Name: efm32_dmafree diff --git a/arch/arm/src/sama5/sam_xdmac.c b/arch/arm/src/sama5/sam_xdmac.c index d91f6f72159..98c973efaeb 100644 --- a/arch/arm/src/sama5/sam_xdmac.c +++ b/arch/arm/src/sama5/sam_xdmac.c @@ -136,7 +136,7 @@ struct sam_xdmach_s struct chnext_view1_s *lltail; /* DMA link list head */ }; -/* This structure describes the stae of one DMA controller */ +/* This structure describes the state of one DMA controller */ struct sam_xdmac_s {