From 904ea781d04ed73b115f1d83d198203894ddd705 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sat, 25 Oct 2014 17:15:16 -0600 Subject: EFM32: More fixes to DMA descriptor table logic --- nuttx/arch/arm/src/efm32/efm32_dma.c | 75 ++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/nuttx/arch/arm/src/efm32/efm32_dma.c b/nuttx/arch/arm/src/efm32/efm32_dma.c index 5425b58d8..c6081262d 100644 --- a/nuttx/arch/arm/src/efm32/efm32_dma.c +++ b/nuttx/arch/arm/src/efm32/efm32_dma.c @@ -71,10 +71,6 @@ struct dma_channel_s { uint8_t chan; /* DMA channel number (0-EFM32_DMA_NCHANNELS) */ bool inuse; /* TRUE: The DMA channel is in use */ - struct dma_descriptor_s *desc; /* Primary DMA descriptor for channel */ -#ifdef CONFIG_EFM32_DMA_ALTDSEC - struct dma_descriptor_s *alt; /* Alternate DMA descriptor for channel */ -#endif dma_config_t config; /* Current configuration */ dma_callback_t callback; /* Callback invoked when the DMA completes */ void *arg; /* Argument passed to callback function */ @@ -100,16 +96,40 @@ static struct dma_controller_s g_dmac; static struct dma_channel_s g_dmach[EFM32_DMA_NCHANNELS]; -/* This array describes the available channel control data structures. - * Each structure must be aligned to the size of the DMA descriptor. +/* This array describes the available channel control data structures. Each + * structure must be aligned to a 256 address boundary. The last 8 or 9 + * bits of address are provided by the DMA controller: + * + * 8-Channels: + * Bit 7: Selects the alternate descriptor list + * Bits 4-6: The DMA channel (0-7) + * Bits 2-3: Selects the descriptor field + * Bits 0-1: Always zero + * + * 12-Channels: + * Bit 8: Selects the alternate descriptor list + * Bits 4-7: The DMA channel (0-11) + * Bits 2-3: Selects the descriptor field + * Bits 0-1: Always zero + * + * So 256 byte alignment will work in either case (since the order in which + * the tables appear in physical memory is not important). */ -static struct dma_descriptor_s g_primary_descriptors[EFM32_DMA_NCHANNELS] - __attribute__((aligned(16))); +#if EFM32_DMA_NCHANNELS <= 8 +# define DESC_TABLE_SIZE 8 +#elif EFM32_DMA_NCHANNELS <= 16 +# define DESC_TABLE_SIZE 16 +#else +# error Unknown descriptor table size +#endif #ifdef CONFIG_EFM32_DMA_ALTDSEC -static struct dma_descriptor_s g_alt_descriptors[EFM32_DMA_NCHANNELS] - __attribute__((aligned(16))); +static struct dma_descriptor_s g_descriptors[DESC_TABLE_SIZE + EFM32_DMA_NCHANNELS] + __attribute__((aligned(256))); +#else +static struct dma_descriptor_s g_descriptors[EFM32_DMA_NCHANNELS] + __attribute__((aligned(256))); #endif /***************************************************************************** @@ -160,6 +180,23 @@ static inline unsigned int efm32_align_shift(dma_config_t config) return shift; } +/**************************************************************************** + * Name: efm32_get_descriptor + * + * Description: + * Get the address of the primary or alternate descriptor assigned to the + * DMA channel + * + ****************************************************************************/ + +static inline struct dma_descriptor_s * +efm32_get_descriptor(struct dma_channel_s *dmach, bool alt) +{ + uintptr_t base = alt ? getreg32(EFM32_DMA_ALTCTRLBASE) : + getreg32(EFM32_DMA_CTRLBASE); + return ((struct dma_descriptor_s *)base) + dmach->chan; +} + /**************************************************************************** * Name: efm32_dmac_interrupt * @@ -244,10 +281,6 @@ void weak_function up_dmainitialize(void) for (i = 0; i < EFM32_DMA_NCHANNELS; i++) { g_dmach[i].chan = i; - g_dmach[i].desc = &g_primary_descriptors[i]; -#ifdef CONFIG_EFM32_DMA_ALTDSEC - g_dmach[i].alt = &g_alt_descriptors[i]; -#endif } /* Enable clock to the DMA module. DMA is clocked by HFCORECLK. */ @@ -256,12 +289,12 @@ void weak_function up_dmainitialize(void) regval |= CMU_HFCORECLKEN0_DMA; putreg32(regval, EFM32_CMU_HFCORECLKEN0); - /* Set the primary and alternate control base addresses */ + /* Set the control base addresses. Note: EFM32_DMA_ALTCTRLBASE + * is a read-only register that provides the location where hardware + * will obtain the alternative descriptors. + */ - putreg32((uint32_t)g_primary_descriptors, EFM32_DMA_CTRLBASE); -#ifdef CONFIG_EFM32_DMA_ALTDSEC - putreg32((uint32_t)g_alt_descriptors, EFM32_DMA_ALTCTRLBASE); -#endif + putreg32((uint32_t)g_descriptors, EFM32_DMA_CTRLBASE); /* Attach DMA interrupt vector */ @@ -458,7 +491,7 @@ void efm32_rxdmasetup(DMA_HANDLE handle, uintptr_t paddr, uintptr_t maddr, /* Configure the primary channel descriptor */ - desc = dmach->desc; + desc = efm32_get_descriptor(dmach, false); desc->srcend = (uint32_t *)paddr; desc->dstend = (uint32_t *)(maddr + nbytes - xfersize); @@ -544,7 +577,7 @@ void efm32_txdmasetup(DMA_HANDLE handle, uintptr_t paddr, uintptr_t maddr, /* Configure the primary channel descriptor */ - desc = dmach->desc; + desc = efm32_get_descriptor(dmach, false); desc->srcend = (uint32_t *)(maddr + nbytes - xfersize); desc->dstend = (uint32_t *)paddr; -- cgit v1.2.3