diff --git a/arch/arm/src/kinetis/hardware/kinetis_edma.h b/arch/arm/src/kinetis/hardware/kinetis_edma.h index c21c0d9f01..0c1f1a1fd8 100644 --- a/arch/arm/src/kinetis/hardware/kinetis_edma.h +++ b/arch/arm/src/kinetis/hardware/kinetis_edma.h @@ -831,14 +831,12 @@ * Public Types ****************************************************************************/ -/* In-memory representation of the 32-byte Transfer Control Descriptor - * (TCD) +/* Hardware representation of the 32-byte Transfer + * Control Descriptor (TCD) */ struct kinetis_edmatcd_s { - sq_entry_t node; - uint8_t flags; /* See EDMA_CONFIG_* definitions */ uint32_t saddr; /* Offset: 0x0000 TCD Source Address */ uint16_t soff; /* Offset: 0x0004 TCD Signed Source Address Offset */ uint16_t attr; /* Offset: 0x0006 TCD Transfer Attributes */ diff --git a/arch/arm/src/kinetis/kinetis_edma.c b/arch/arm/src/kinetis/kinetis_edma.c index d8925b067c..5a57f62f54 100644 --- a/arch/arm/src/kinetis/kinetis_edma.c +++ b/arch/arm/src/kinetis/kinetis_edma.c @@ -88,22 +88,16 @@ */ #ifdef CONFIG_ARMV7M_DCACHE -/* Align to the cache line size which we assume is >= 8 */ - -# define EDMA_ALIGN ARMV7M_DCACHE_LINESIZE -# define EDMA_ALIGN_MASK (EDMA_ALIGN-1) -# define EDMA_ALIGN_UP(n) (((n) + EDMA_ALIGN_MASK) & ~EDMA_ALIGN_MASK) - +# define EDMA_ALIGN ARMV7M_DCACHE_LINESIZE #else -/* Special alignment is not required in this case, - * but we will align to 8-bytes - */ +/* 32 byte alignment for TCDs is required for scatter gather */ -# define EDMA_ALIGN 8 -# define EDMA_ALIGN_MASK 7 -# define EDMA_ALIGN_UP(n) (((n) + 7) & ~7) +#define EDMA_ALIGN 32 #endif +#define EDMA_ALIGN_MASK (EDMA_ALIGN - 1) +#define EDMA_ALIGN_UP(n) (((n) + EDMA_ALIGN_MASK) & ~EDMA_ALIGN_MASK) + /**************************************************************************** * Private Types ****************************************************************************/ @@ -121,11 +115,10 @@ enum kinetis_dmastate_e struct kinetis_dmach_s { - uint8_t chan; /* DMA channel number (0-KINETIS_EDMA_NCHANNELS) */ - bool inuse; /* true: The DMA channel is in use */ - uint8_t dmamux; /* The DMAMUX channel selection */ - uint8_t ttype; /* Transfer type: M2M, M2P, P2M, or P2P */ - uint8_t state; /* Channel state. See enum kinetis_dmastate_e */ + uint8_t chan; /* DMA channel number (0-KINETIS_EDMA_NCHANNELS) */ + bool inuse; /* true: The DMA channel is in use */ + uint8_t state; /* Channel state. See enum kinetis_dmastate_e */ + uint8_t dmamux; /* The DMAMUX channel selection */ uint32_t flags; /* DMA channel flags */ edma_callback_t callback; /* Callback invoked when the DMA completes */ void *arg; /* Argument passed to callback function */ @@ -341,18 +334,13 @@ static inline void kinetis_tcd_chanlink(uint8_t flags, if (linkch == NULL || flags == EDMA_CONFIG_LINKTYPE_LINKNONE) { -#if 0 /* Already done */ /* No link or no link channel provided */ - /* Disable minor links */ - - tcd->citer &= ~EDMA_TCD_CITER_ELINK; - tcd->biter &= ~EDMA_TCD_BITER_ELINK; + /* Disable minor links is done in kinetis_tcd_configure */ /* Disable major link */ tcd->csr &= ~EDMA_TCD_CSR_MAJORELINK; -#endif } else if (flags == EDMA_CONFIG_LINKTYPE_MINORLINK) /* Minor link config */ { @@ -403,22 +391,21 @@ static inline void kinetis_tcd_chanlink(uint8_t flags, static inline void kinetis_tcd_configure(struct kinetis_edmatcd_s *tcd, const struct kinetis_edma_xfrconfig_s *config) { - tcd->flags = config->flags; tcd->saddr = config->saddr; tcd->soff = config->soff; tcd->attr = EDMA_TCD_ATTR_SSIZE(config->ssize) | /* Transfer Attributes */ EDMA_TCD_ATTR_DSIZE(config->dsize); tcd->nbytes = config->nbytes; - tcd->slast = config->flags & EDMA_CONFIG_LOOPSRC ? -config->iter : 0; + tcd->slast = config->flags & EDMA_CONFIG_LOOPSRC ? -config->iter : 0; tcd->daddr = config->daddr; tcd->doff = config->doff; tcd->citer = config->iter & EDMA_TCD_CITER_CITER_MASK; tcd->biter = config->iter & EDMA_TCD_BITER_BITER_MASK; - tcd->csr = config->flags & EDMA_CONFIG_LOOPDEST ? + tcd->csr = config->flags & EDMA_CONFIG_LOOP_MASK ? 0 : EDMA_TCD_CSR_DREQ; - tcd->csr |= config->flags & EDMA_CONFIG_INTHALF ? + tcd->csr |= config->flags & EDMA_CONFIG_INTHALF ? EDMA_TCD_CSR_INTHALF : 0; - tcd->dlastsga = config->flags & EDMA_CONFIG_LOOPDEST ? -config->iter : 0; + tcd->dlastsga = config->flags & EDMA_CONFIG_LOOPDEST ? -config->iter : 0; /* And special case flags */ @@ -447,6 +434,10 @@ static void kinetis_tcd_instantiate(struct kinetis_dmach_s *dmach, /* Push tcd into hardware TCD register */ + /* Clear DONE bit first, otherwise ESG cannot be set */ + + putreg16(0, base + KINETIS_EDMA_TCD_CSR_OFFSET); + putreg32(tcd->saddr, base + KINETIS_EDMA_TCD_SADDR_OFFSET); putreg16(tcd->soff, base + KINETIS_EDMA_TCD_SOFF_OFFSET); putreg16(tcd->attr, base + KINETIS_EDMA_TCD_ATTR_OFFSET); @@ -457,9 +448,6 @@ static void kinetis_tcd_instantiate(struct kinetis_dmach_s *dmach, putreg16(tcd->citer, base + KINETIS_EDMA_TCD_CITER_ELINK_OFFSET); putreg32(tcd->dlastsga, base + KINETIS_EDMA_TCD_DLASTSGA_OFFSET); - /* Clear DONE bit first, otherwise ESG cannot be set */ - - putreg16(0, base + KINETIS_EDMA_TCD_CSR_OFFSET); putreg16(tcd->csr, base + KINETIS_EDMA_TCD_CSR_OFFSET); putreg16(tcd->biter, base + KINETIS_EDMA_TCD_BITER_ELINK_OFFSET); @@ -495,34 +483,13 @@ static void kinetis_dmaterminate(struct kinetis_dmach_s *dmach, int result) regval8 = EDMA_CERQ(chan); putreg8(regval8, KINETIS_EDMA_CERQ); - /* Check for an Rx (memory-to-peripheral/memory-to-memory) DMA transfer */ - - if (dmach->ttype == EDMA_MEM2MEM || dmach->ttype == EDMA_PERIPH2MEM) - { - /* Invalidate the cache to force reloads from memory. */ - -#warning Missing logic - } - - /* Perform the DMA complete callback */ - - if (dmach->callback) - { - dmach->callback((DMACH_HANDLE)dmach, dmach->arg, true, result); - } - - /* Clear CSR to disable channel. Because if the given channel started, - * transfer CSR will be not zero. Because if it is the last transfer, DREQ - * will be set. If not, ESG will be set. - */ - regaddr = KINETIS_EDMA_TCD_CSR(chan); putreg16(0, regaddr); /* Cancel next TCD transfer. */ regaddr = KINETIS_EDMA_TCD_DLASTSGA(chan); - putreg16(0, regaddr); + putreg32(0, regaddr); #if CONFIG_KINETIS_EDMA_NTCD > 0 /* Return all allocated TCDs to the free list */ @@ -533,7 +500,7 @@ static void kinetis_dmaterminate(struct kinetis_dmach_s *dmach, int result) * if not continue to free tcds in chain */ - next = tcd->flags & EDMA_CONFIG_LOOPDEST ? + next = dmach->flags & EDMA_CONFIG_LOOPDEST ? NULL : (struct kinetis_edmatcd_s *)tcd->dlastsga; kinetis_tcd_free(tcd); @@ -543,6 +510,13 @@ static void kinetis_dmaterminate(struct kinetis_dmach_s *dmach, int result) dmach->tail = NULL; #endif + /* Perform the DMA complete callback */ + + if (dmach->callback) + { + dmach->callback((DMACH_HANDLE)dmach, dmach->arg, true, result); + } + dmach->callback = NULL; dmach->arg = NULL; dmach->state = KINETIS_DMA_IDLE; @@ -569,13 +543,13 @@ static int kinetis_edma_interrupt(int irq, void *context, void *arg) { struct kinetis_dmach_s *dmach; uintptr_t regaddr; - uint8_t regval8; - uint16_t regval16; uint32_t regval32; + uint16_t regval16; + uint8_t regval8; uint8_t chan; int result; - /* 'arg' should the DMA channel instance. */ + /* 'arg' should be the DMA channel instance. */ dmach = (struct kinetis_dmach_s *)arg; DEBUGASSERT(dmach != NULL); @@ -617,12 +591,12 @@ static int kinetis_edma_interrupt(int irq, void *context, void *arg) else { #if CONFIG_KINETIS_EDMA_NTCD > 0 - /* Perform the end-of-major-cycle DMA callback */ + /* Perform the half or end-of-major-cycle DMA callback */ if (dmach->callback != NULL) { dmach->callback((DMACH_HANDLE)dmach, dmach->arg, - false, 0); + false, OK); } return OK; @@ -636,7 +610,15 @@ static int kinetis_edma_interrupt(int irq, void *context, void *arg) /* Terminate the transfer when it is done. */ - kinetis_dmaterminate(dmach, result); + if ((dmach->flags & EDMA_CONFIG_LOOP_MASK) == 0) + { + kinetis_dmaterminate(dmach, result); + } + else if (dmach->callback != NULL) + { + dmach->callback((DMACH_HANDLE)dmach, dmach->arg, + true, result); + } } return OK; @@ -805,6 +787,14 @@ void weak_function arm_dma_initialize(void) regaddr = KINETIS_EDMA_TCD_CSR(i); putreg16(0, regaddr); + + /* Set all TCD entries to 0 so that biter and citer + * will be 0 when DONE is not set so that kinetis_dmach_getcount + * reports 0. + */ + + memset((void *)KINETIS_EDMA_TCD_BASE(i), 0, + sizeof(struct kinetis_edmatcd_s)); } /* Clear all pending DMA channel interrupts */ @@ -993,6 +983,8 @@ int kinetis_dmach_xfrsetup(DMACH_HANDLE *handle, #if CONFIG_KINETIS_EDMA_NTCD > 0 struct kinetis_edmatcd_s *tcd; struct kinetis_edmatcd_s *prev; + uint16_t mask = config->flags & EDMA_CONFIG_INTMAJOR ? 0 : + EDMA_TCD_CSR_INTMAJOR; #endif uintptr_t regaddr; uint16_t regval16; @@ -1000,6 +992,8 @@ int kinetis_dmach_xfrsetup(DMACH_HANDLE *handle, DEBUGASSERT(dmach != NULL); dmainfo("dmach%u: %p config: %p\n", dmach->chan, dmach, config); + dmach->flags = config->flags; + #if CONFIG_KINETIS_EDMA_NTCD > 0 /* Scatter/gather DMA is supported */ @@ -1019,20 +1013,6 @@ int kinetis_dmach_xfrsetup(DMACH_HANDLE *handle, tcd->csr |= EDMA_TCD_CSR_INTMAJOR; - /* Is looped to it's self? */ - - if (config->flags & EDMA_CONFIG_LOOP_MASK) - { - /* Enable major link */ - - tcd->csr |= EDMA_TCD_CSR_MAJORELINK; - - /* Set major linked channel back to this one */ - - tcd->csr &= ~EDMA_TCD_CSR_MAJORLINKCH_MASK; - tcd->csr |= EDMA_TCD_CSR_MAJORLINKCH(dmach->chan); - } - /* Is this the first descriptor in the list? */ if (dmach->head == NULL) @@ -1041,7 +1021,6 @@ int kinetis_dmach_xfrsetup(DMACH_HANDLE *handle, dmach->head = tcd; dmach->tail = tcd; - dmach->ttype = config->ttype; /* And instantiate the first TCD in the DMA channel TCD registers. */ @@ -1049,12 +1028,9 @@ int kinetis_dmach_xfrsetup(DMACH_HANDLE *handle, } else { - /* Cannot mix transfer types (only because of cache-related operations. - * this restriction could be removed with some effort). - */ + /* Cannot mix transfer types */ - if (dmach->ttype != config->ttype || - dmach->flags & EDMA_CONFIG_LOOPDEST) + if (dmach->flags & EDMA_CONFIG_LOOP_MASK) { kinetis_tcd_free(tcd); return -EINVAL; @@ -1066,8 +1042,9 @@ int kinetis_dmach_xfrsetup(DMACH_HANDLE *handle, prev = dmach->tail; regval16 = prev->csr; - regval16 &= ~EDMA_TCD_CSR_DREQ; + regval16 &= ~(EDMA_TCD_CSR_DREQ | mask); regval16 |= EDMA_TCD_CSR_ESG; + prev->csr = regval16; prev->dlastsga = (uint32_t)tcd; @@ -1089,7 +1066,7 @@ int kinetis_dmach_xfrsetup(DMACH_HANDLE *handle, regaddr = KINETIS_EDMA_TCD_CSR(dmach->chan); regval16 = getreg16(regaddr); - regval16 &= ~EDMA_TCD_CSR_DREQ; + regval16 &= ~(EDMA_TCD_CSR_DREQ | mask); regval16 |= EDMA_TCD_CSR_ESG; putreg16(regval16, regaddr); @@ -1128,35 +1105,6 @@ int kinetis_dmach_xfrsetup(DMACH_HANDLE *handle, modifyreg16(regaddr, 0, EDMA_TCD_CSR_INTMAJOR); #endif - /* Check for an Rx (memory-to-peripheral/memory-to-memory) DMA transfer */ - - if (dmach->ttype == EDMA_MEM2MEM || dmach->ttype == EDMA_PERIPH2MEM) - { - /* Invalidate caches associated with the destination DMA memory. - * REVISIT: nbytes is the number of bytes transferred on each - * minor loop. The following is only valid when the major loop - * is one. - */ - - up_invalidate_dcache((uintptr_t)config->daddr, - (uintptr_t)config->daddr + config->nbytes); - } - - /* Check for an Tx (peripheral-to-memory/memory-to-memory) DMA transfer */ - - if (dmach->ttype == EDMA_MEM2MEM || dmach->ttype == EDMA_MEM2PERIPH) - { - /* Clean caches associated with the source DMA memory. - * REVISIT: nbytes is the number of bytes transferred on each - * minor loop. The following is only valid when the major loop - * is one. - */ -#warning Missing logic - - up_clean_dcache((uintptr_t)config->saddr, - (uintptr_t)config->saddr + config->nbytes); - } - /* Set the DMAMUX source and enable and optional trigger */ putreg8(dmach->dmamux, KINETIS_DMAMUX_CHCFG(dmach->chan)); @@ -1172,10 +1120,10 @@ int kinetis_dmach_xfrsetup(DMACH_HANDLE *handle, * Start the DMA transfer. This function should be called after the final * call to kinetis_dmach_xfrsetup() in order to avoid race conditions. * - * At the conclusion of each major DMA loop, a callback to the user - * provided function is made: |For "normal" DMAs, this will correspond to - * the DMA DONE interrupt; for scatter gather DMAs, multiple interrupts - * will be generated with the final being the DONE interrupt. + * At the conclusion of each major DMA loop, a callback to + * the user-provided function is made: For "normal" DMAs, this will + * correspond to the DMA DONE interrupt; for scatter gather DMAs, + * this will be generated with the final TCD. * * At the conclusion of the DMA, the DMA channel is reset, all TCDs are * freed, and the callback function is called with the the success/fail diff --git a/arch/arm/src/kinetis/kinetis_edma.h b/arch/arm/src/kinetis/kinetis_edma.h index f12ac414e5..f5a9909edb 100644 --- a/arch/arm/src/kinetis/kinetis_edma.h +++ b/arch/arm/src/kinetis/kinetis_edma.h @@ -122,13 +122,17 @@ # define EDMA_CONFIG_LINKTYPE_MINORLINK (1 << EDMA_CONFIG_LINKTYPE_SHIFT) /* Channel link after each minor loop */ # define EDMA_CONFIG_LINKTYPE_MAJORLINK (2 << EDMA_CONFIG_LINKTYPE_SHIFT) /* Channel link when major loop count exhausted */ -#define EDMA_CONFIG_LOOP_SHIFT (2) /* Bits 2: Loop type */ +#define EDMA_CONFIG_LOOP_SHIFT (2) /* Bits 2-3: Loop type */ #define EDMA_CONFIG_LOOP_MASK (3 << EDMA_CONFIG_LOOP_SHIFT) # define EDMA_CONFIG_LOOPNONE (0 << EDMA_CONFIG_LOOP_SHIFT) /* No looping */ # define EDMA_CONFIG_LOOPSRC (1 << EDMA_CONFIG_LOOP_SHIFT) /* Source looping */ # define EDMA_CONFIG_LOOPDEST (2 << EDMA_CONFIG_LOOP_SHIFT) /* Dest looping */ -#define EDMA_CONFIG_INTHALF (1 << 3) /* Bits 3: Int on HALF */ +#define EDMA_CONFIG_INTHALF (1 << 4) /* Bits 4: Int on HALF */ +#define EDMA_CONFIG_INTMAJOR (1 << 5) /* Bits 5: Int on all Major completion + * Default is only on last completion + * if using scatter gather + */ /**************************************************************************** * Public Types @@ -138,16 +142,7 @@ typedef void *DMACH_HANDLE; typedef void (*edma_callback_t)(DMACH_HANDLE handle, void *arg, bool done, int result); -/* eDMA transfer type */ - -enum kinetis_edma_xfrtype_e -{ - EDMA_MEM2MEM = 0, /* Transfer from memory to memory */ - EDMA_PERIPH2MEM, /* Transfer from peripheral to memory */ - EDMA_MEM2PERIPH, /* Transfer from memory to peripheral */ -}; - -/* eDMA transfer sises */ +/* eDMA transfer sizes */ enum kinetis_edma_sizes_e { @@ -170,7 +165,6 @@ struct kinetis_edma_xfrconfig_s uint8_t flags; /* See EDMA_CONFIG_* definitions */ uint8_t ssize; /* Source data transfer size (see TCD_ATTR_SIZE_* definitions in rdware/. */ uint8_t dsize; /* Destination data transfer size. */ - uint8_t ttype; /* Transfer type (see enum kinetis_edma_xfrtype_e). */ #ifdef CONFIG_KINETIS_EDMA_EMLIM uint16_t nbytes; /* Bytes to transfer in a minor loop */ #else