Add some logic to EFM32 DMA module. Still incomplete
This commit is contained in:
parent
6c044cbf9b
commit
f79e803e89
@ -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
|
||||
|
@ -42,12 +42,65 @@
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <semaphore.h>
|
||||
#include <queue.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user