diff --git a/arch/arm/src/imxrt/Kconfig b/arch/arm/src/imxrt/Kconfig index f87cb57769..8e09492a7a 100644 --- a/arch/arm/src/imxrt/Kconfig +++ b/arch/arm/src/imxrt/Kconfig @@ -337,25 +337,61 @@ endmenu # Memory Configuration menu "eDMA Configuration" depends on IMXRT_EDMA +config IMXRT_EDMA_NTCD + int "Number of transfer descriptors" + default 0 + ---help--- + Number of pre-allocated transfer descriptors. Needed for scatter- + gather DMA. Make to be set to zero to disable in-memory TCDs in + which case only the TCD channel registers will be used and scatter- + will not be supported. + config IMXRT_EDMA_ERCA bool "Round Robin Channel Arbitration" default n + ---help--- + Normally, a fixed priority arbitration is used for channel + selection. If this option is selected, round robin arbitration is + used for channel selection. config IMXRT_EDMA_HOE bool "Halt On Error" default y + ---help--- + Any error causes the HALT bit to set. Subsequently, all service + requests are ignored until the HALT bit is cleared. config IMXRT_EDMA_CLM bool "Continuous Link Mode" default n + ---help--- + By default, A minor loop channel link made to itself goes through + channel arbitration before being activated again. If this option is + selected, a minor loop channel link made to itself does not go + through channel arbitration before being activated again. Upon minor + loop completion, the channel activates again if that channel has a + minor loop channel link enabled and the link channel is itself. This + effectively applies the minor loop offsets and restarts the next + minor loop. config IMXRT_EDMA_EMLIM bool "Minor Loop Mapping" default n + ---help--- + Normally TCD word 2 is a 32-bit NBYTES field. When this option is + enabled, TCD word 2 is redefined to include individual enable fields, + an offset field, and the NBYTES field. The individual enable fields + allow the minor loop offset to be applied to the source address, the + destination address, or both. The NBYTES field is reduced when either + offset is enabled. config IMXRT_EDMA_EDBG bool "Enable Debug" default n + ---help--- + When in debug mode, the DMA stalls the start of a new channel. Executing + channels are allowed to complete. Channel execution resumes when the + system exits debug mode or the EDBG bit is cleared endmenu # eDMA Global Configuration endif # ARCH_CHIP_IMXRT diff --git a/arch/arm/src/imxrt/chip/imxrt_edma.h b/arch/arm/src/imxrt/chip/imxrt_edma.h index a7b5702368..356d4a3cc4 100644 --- a/arch/arm/src/imxrt/chip/imxrt_edma.h +++ b/arch/arm/src/imxrt/chip/imxrt_edma.h @@ -44,7 +44,7 @@ #include "chip/imxrt_memorymap.h" /**************************************************************************************************** - * Pre-processor definitions + * Pre-processor Definitions ****************************************************************************************************/ #define IMXRT_EDMA_NCHANNELS 32 @@ -103,6 +103,8 @@ #define IMXRT_EDMA_DCHPRI29_OFFSET 0x011e /* Channel 0 Priority */ #define IMXRT_EDMA_DCHPRI28_OFFSET 0x011f /* Channel 1 Priority */ +/* Transfer Control Descriptor (TCD) */ + #define IMXRT_EDMA_TCD_OFFSET(n) (0x1000 + ((n) << 5)) #define IMXRT_EDMA_TCD_SADDR_OFFSET 0x0000 /* TCD Source Address */ #define IMXRT_EDMA_TCD_SOFF_OFFSET 0x0004 /* TCD Signed Source Address Offset */ @@ -566,6 +568,8 @@ #define IMXRT_EDMA_DCHPRI29 (IMXRT_EDMA_BASE + IMXRT_EDMA_DCHPRI2_OFFSET) #define IMXRT_EDMA_DCHPRI28 (IMXRT_EDMA_BASE + IMXRT_EDMA_DCHPRI3_OFFSET) +/* Transfer Control Descriptor (TCD) */ + #define IMXRT_EDMA_TCD_BASE(n) (IMXRT_EDMA_BASE + IMXRT_EDMA_TCD_OFFSET(n)) #define IMXRT_EDMA_TCD_SADDR(n) (IMXRT_EDMA_TCD_BASE(n) + IMXRT_EDMA_TCD_SADDR_OFFSET) #define IMXRT_EDMA_TCD_SOFF(n) (IMXRT_EDMA_TCD_BASE(n) + IMXRT_EDMA_TCD_SOFF_OFFSET) @@ -1125,6 +1129,7 @@ #define EDMA_TCD_ATTR_DSIZE_SHIFT (0) /* Bits 0-2: Destination data transfer size */ #define EDMA_TCD_ATTR_DSIZE_MASK (7 << EDMA_TCD_ATTR_DSIZE_SHIFT) +# define EDMA_TCD_ATTR_DSIZE(n) ((uint32_t)(n) << EDMA_TCD_ATTR_DSIZE_SHIFT) /* 8-bit */ # define EDMA_TCD_ATTR_DSIZE_8BIT (TCD_ATTR_SIZE_8BIT << EDMA_TCD_ATTR_DSIZE_SHIFT) /* 8-bit */ # define EDMA_TCD_ATTR_DSIZE_16BIT (TCD_ATTR_SIZE_16BIT << EDMA_TCD_ATTR_DSIZE_SHIFT) /* 16-bit */ # define EDMA_TCD_ATTR_DSIZE_32BIT (TCD_ATTR_SIZE_32BIT << EDMA_TCD_ATTR_DSIZE_SHIFT) /* 32-bit */ @@ -1135,6 +1140,7 @@ # define EDMA_TCD_ATTR_DMOD(n) ((uint32_t)(n) << EDMA_TCD_ATTR_DMOD_SHIFT) #define EDMA_TCD_ATTR_SSIZE_SHIFT (8) /* Bits 8-10: Source data transfer size */ #define EDMA_TCD_ATTR_SSIZE_MASK (7 << EDMA_TCD_ATTR_SSIZE_SHIFT) +# define EDMA_TCD_ATTR_SSIZE(n) ((uint32_t)(n) << EDMA_TCD_ATTR_SSIZE_SHIFT) /* 8-bit */ # define EDMA_TCD_ATTR_SSIZE_8BIT (TCD_ATTR_SIZE_8BIT << EDMA_TCD_ATTR_SSIZE_SHIFT) /* 8-bit */ # define EDMA_TCD_ATTR_SSIZE_16BIT (TCD_ATTR_SIZE_16BIT << EDMA_TCD_ATTR_SSIZE_SHIFT) /* 16-bit */ # define EDMA_TCD_ATTR_SSIZE_32BIT (TCD_ATTR_SIZE_32BIT << EDMA_TCD_ATTR_SSIZE_SHIFT) /* 32-bit */ @@ -1237,4 +1243,25 @@ #define EDMA_TCD_BITER_ELINK_ELINK (1 << 15) /* Bit 15: Enable channel-to-channel linking * on minor-loop complete */ +/**************************************************************************************************** + * Public Types + ****************************************************************************************************/ + +/* In-memory representation of the 32-byte Transfer Control Descriptor (TCD) */ + +struct imxrt_edmatcd_s +{ + 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 */ + uint32_t nbytes; /* Offset: 0x0008 TCD Signed Minor Loop Offset / Byte Count */ + uint32_t slast; /* Offset: 0x000c TCD Last Source Address Adjustment */ + uint32_t daddr; /* Offset: 0x0010 TCD Destination Address */ + uint16_t doff; /* Offset: 0x0014 TCD Signed Destination Address Offset */ + uint16_t citer; /* Offset: 0x0016 TCD Current Minor Loop Link, Major Loop Count */ + uint32_t dlastsga; /* Offset: 0x0018 TCD Last Destination Address Adjustment/Scatter Gather Address */ + uint16_t csr; /* Offset: 0x001c TCD Control and Status */ + uint16_t biter; /* Offset: 0x001e TCD Beginning Minor Loop Link, Major Loop Count */ +}; + #endif /* __ARCH_ARM_SRC_IMXRT_CHIP_IMXRT_EDMA_H */ diff --git a/arch/arm/src/imxrt/imxrt_edma.c b/arch/arm/src/imxrt/imxrt_edma.c index 96b4692f83..1806b41a7d 100644 --- a/arch/arm/src/imxrt/imxrt_edma.c +++ b/arch/arm/src/imxrt/imxrt_edma.c @@ -4,6 +4,13 @@ * Copyright (C) 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * + * Portions of the eDMA logic derive from NXP sample code which has a compatible + * BSD 3-clause license: + * + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2017 NXP + * All rights reserved + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -43,6 +50,7 @@ #include #include #include +#include #include #include @@ -66,6 +74,32 @@ * Pre-processor Definitions ****************************************************************************/ +/* TCD Alignment. + * + * eDMA TCDs must be aligned with the D-Cache line boundaries to facilitate + * cache operations on the TCDs when the D-Cache is enabled. + * + * NOTE: The TCDs are 32-bytes in length. We implicitly assume that the + * D-Cache line size is also 32-bits. Otherwise, padding would be required + * at the ends of the TCDS and buffers to protect data after the end of from + * invalidation. + */ + +#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) + +#else +/* Special alignment is not required in this case, but we will align to 8-bytes */ + +# define EDMA_ALIGN 8 +# define EDMA_ALIGN_MASK 7 +# define EDMA_ALIGN_UP(n) (((n) + 7) & ~7) +#endif + /**************************************************************************** * Private Types ****************************************************************************/ @@ -92,6 +126,16 @@ struct imxrt_dmach_s void *arg; /* Argument passed to callback function */ uint32_t rxaddr; /* RX memory address */ size_t rxsize; /* Size of RX memory region */ + +#if CONFIG_IMXRT_EDMA_NTCD > 0 + /* That TCD list is linked through the DLAST SGA field. The first transfer + * to be performed is at the head of the list. Subsequent TCDs are added at + * the tail of the list. + */ + + struct imxrt_edmatcd_s *head; /* First TCD in the list */ + struct imxrt_edmatcd_s *tail; /* Last TCD in the list */ +#endif }; /* This structure describes the state of the eDMA controller */ @@ -101,7 +145,9 @@ struct imxrt_edma_s /* These semaphores protect the DMA channel and descriptor tables */ sem_t chsem; /* Protects channel table */ - sem_t dsem; /* Protects descriptor table */ +#if CONFIG_IMXRT_EDMA_NTCD > 0 + sem_t dsem; /* Supports wait for free descriptors */ +#endif /* This array describes each DMA channel */ @@ -116,6 +162,17 @@ struct imxrt_edma_s static struct imxrt_edma_s g_edma; +#if CONFIG_IMXRT_EDMA_NTCD > 0 +/* This is a singly-linked list of free TCDs */ + +static sq_queue_t g_tcd_free; + +/* This is a pool of pre-allocated TCDs */ + +static struct imxrt_edmatcd_s g_tcd_pool[CONFIG_IMXRT_EDMA_NTCD] + __attribute__((aligned(EDMA_ALIGN))); +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -124,7 +181,8 @@ static struct imxrt_edma_s g_edma; * Name: imxrt_takechsem() and imxrt_givechsem() * * Description: - * Used to get exclusive access to the DMA channel table + * Used to get exclusive access to the DMA channel table for channel + * allocation. * ****************************************************************************/ @@ -160,6 +218,7 @@ static inline void imxrt_givechsem(void) * ****************************************************************************/ +#if CONFIG_IMXRT_EDMA_NTCD > 0 static void imxrt_takedsem(void) { int ret; @@ -183,6 +242,36 @@ static inline void imxrt_givedsem(void) { (void)nxsem_post(&g_edma.dsem); } +#endif + +/**************************************************************************** + * Name: imxrt_tcd_initialize() + * + * Description: + * Initialize the TCD free list from the pool of pre-allocated TCDs. + * + * Assumptions: + * Called early in the initialization sequence so no special protection is + * necessary. + * + ****************************************************************************/ + +#if CONFIG_IMXRT_EDMA_NTCD > 0 +static inline void imxrt_tcd_initialize(void) +{ + sq_entry_t *tcd; + int i; + + /* Add each pre-allocated TCD to the tail of the TCD free list */ + + sq_init(&g_tcd_free); + for (i = 0; i < CONFIG_IMXRT_EDMA_NTCD; i++) + { + tcd = (sq_entry_t *)&g_tcd_poll[i]; + sq_add_last(tcd, &g_tcd_free); + } +} +#endif /**************************************************************************** * Name: imxrt_getdmach @@ -212,6 +301,130 @@ static inline void imxrt_putdmach(struct imxrt_dmach_s *dmach, uint32_t value, putreg32(value, IMXRT_EDMA_TCD_BASE(dmach->chan) + offset); } +/**************************************************************************** + * Name: imxrt_tcd_reset + * + * Description: + * Reset all TCD registers to default values. 'tcd' is an 'overlay' that + * may refer either to either the TCD register set or to an in-memory TCD + * structure. + * + ****************************************************************************/ + +static void imxrt_tcd_reset(struct imxrt_edmatcd_s *tcd) +{ + /* Reset channel TCD */ + + tcd->saddr = 0; + tcd->soff = 0; + tcd->attr = 0; + tcd->nbytes = 0; + tcd->slast = 0; + tcd->daddr = 0; + tcd->doff = 0; + tcd->citer = 0; + tcd->dlastsga = 0; + + /* Enable auto disable request feature */ + + tcd->csr = EDMA_TCD_CSR_DREQ; + tcd->biter = 0; +} + +/**************************************************************************** + * Name: imxrt_tcd_configure + * + * Description: + * Configure all TCD registers to the specified values. 'tcd' is an + * 'overlay' that may refer either to either the TCD register set or to an + * in-memory TCD structure. + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EDMA_NTCD > 0 +void imxrt_tcd_configure(struct imxrt_edmatcd_s *tcd, + const struct imxrt_edma_xfrconfig_s *config, + struct imxrt_edmatcd_s *next) +#else +void imxrt_tcd_configure(struct imxrt_edmatcd_s *tcd, + const struct imxrt_edma_xfrconfig_s *config) +#endif +{ + tcd->saddr = config->saddr; /* Source Address */ + tcd->soff = config->soff; /* Signed Source Address Offset */ + tcd->attr = EDMA_TCD_ATTR_SSIZE(config->ssize) | /* Transfer Attributes */ + EDMA_TCD_ATTR_DSIZE(config->destTransferSize); + tcd->nbytes = config->nbytes; /* Signed Minor Loop Offset / Byte Count */ + tcd->slast = tcd->slast; /* Last Source Address Adjustment */ + tcd->daddr = config->daddr; /* Destination Address */ + tcd->doff = config->doff; /* Signed Destination Address Offset */ + tcd->citer = config->iter; /* Current Minor Loop Link, Major Loop Count */ + tcd->biter = config->iter; /* Beginning Minor Loop Link, Major Loop Count */ + +#ifdef CONFIG_IMXRT_EDMA_NTCD > 0 + /* Enable scatter/gather processing */ + + if (next != NULL) + { + uint16_t regval16; + + /* Set the next TCD address */ + + tcd->dlastsga = (uint32_t)next; + + /* Before calling imxrt_tcd_configure or imxrt_dmach_setconfig, the + * user must call imxrt_tcd_reset or imxrt_dmach_reset which will set + * DREQ, so must use "|" or "&" rather than "=". + * + * Clear the DREQ bit because scatter gather has been enabled, so the + * previous transfer is not the last transfer, and channel request should + * be enabled at the next transfer(the next TCD). + */ + + regval16 = tcd->csr; + regval16 &= ~EDMA_TCD_CSR_DREQ; + regval16 |= EDMA_TCD_CSR_ESG + tcd->csr = regval16; + } +#endif +} + +/**************************************************************************** + * Name: imxrt_tcd_instantiate + * + * Description: + * Copy an in-memory TCD into eDMA channel TCD registers + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EDMA_NTCD > 0 +static void imxrt_tcd_instantiate(struct imxrt_dmach_s *dmach, + const struct imxrt_edmatcd_s *tcd) +{ + uintptr_t base = IMXRT_EDMA_TCD_BASE(dmach->chan); + + /* Push tcd into hardware TCD register */ + + putreg32(tcd->saddr, base + IMXRT_EDMA_TCD_SADDR_OFFSET); + putreg16(tcd->soff, base + IMXRT_EDMA_TCD_SOFF_OFFSET); + putreg16(tcd->attr, base + IMXRT_EDMA_TCD_ATTR_OFFSET); + putreg32(tcd->nbytes, base + IMXRT_EDMA_TCD_NBYTES_ML_OFFSET); + putreg32(tcd->slast, base + IMXRT_EDMA_TCD_SLAST_OFFSET); + putreg32(tcd->daddr, base + IMXRT_EDMA_TCD_DADDR_OFFSET); + putreg16(tcd->doff, base + IMXRT_EDMA_TCD_DOFF_OFFSET); + putreg16(tcd->citer, base + IMXRT_EDMA_TCD_CITER_ELINK_OFFSET); + putreg32(tcd->dlastsga, base + IMXRT_EDMA_TCD_DLASTSGA_OFFSET); + + /* Clear DONE bit first, otherwise ESG cannot be set */ + + putreg16(0, base + IMXRT_EDMA_TCD_CSR_OFFSET); + putreg16(tcd->csr, base + IMXRT_EDMA_TCD_CSR_OFFSET); + + putreg16(tcd->biter, base + IMXRT_EDMA_TCD_BITER_ELINK_OFFSET); + base->TCD[channel].BITER_ELINKNO = tcd->biter; +} +#endif + /**************************************************************************** * Name: imxrt_dmaterminate * @@ -224,20 +437,35 @@ static void imxrt_dmaterminate(struct imxrt_dmach_s *dmach, int result) { struct imxrt_edma_s *dmac = imxrt_controller(dmach); - /* Disable all channel interrupts */ /* Disable channel ERROR interrupts */ regval8 = EDMA_CEEI(chan); putreg8(regval8, IMXRT_EDMA_CEEI); - /* Disable the DONE interrupt when the major iteration count completes. */ + /* Disable channel IRQ requests */ + + regval8 = EDMA_CERQ(chan); + putreg8(regval8, IMXRT_EDMA_CERQ); + + /* 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 = IMXRT_EDMA_TCD_CSR(chan); - modifyreg16(regaddr, EDMA_TCD_CSR_INTMAJOR | EDMA_TCD_CSR_INTHALF, 0); + putreg16(0, regaddr); - /* Disable the channel. */ + /* Cancel all next TCD transfer. */ + + regaddr = IMXRT_EDMA_TCD_DLASTSGA(chan); + putreg16(0, regaddr); + +#if CONFIG_IMXRT_EDMA_NTCD > 0 + /* Return all allocated TCDs to the free list */ #warning Missing logic +#endif + /* If this was an RX DMA (peripheral-to-memory), then invalidate the cache * to force reloads from memory. */ @@ -251,7 +479,7 @@ static void imxrt_dmaterminate(struct imxrt_dmach_s *dmach, int result) if (dmach->callback) { - dmach->callback((DMA_HANDLE)dmach, dmach->arg, result); + dmach->callback((DMACH_HANDLE)dmach, dmach->arg, result); } dmach->callback = NULL; @@ -434,7 +662,19 @@ void weak_function up_dmainitialize(void) /* Initialize semaphores */ nxsem_init(&g_edma.chsem, 0, 1); - nxsem_init(&g_edma.dsem, 0, SAM_NDMACHAN); +#if CONFIG_IMXRT_EDMA_NTCD > 0 + nxsem_init(&g_edma.dsem, 0, CONFIG_IMXRT_EDMA_NTCD); + + /* The 'dsem' is used for signaling rather than mutual exclusion and, + * hence, should not have priority inheritance enabled. + */ + + nxsem_setprotocol(&g_edma.dsem, SEM_PRIO_NONE); + + /* Initialize the list of of free TCDs from the pool of pre-allocated TCDs. */ + + imxrt_tcd_initialize(); +#endif /* Attach DMA interrupt vectors. * @@ -514,7 +754,7 @@ void weak_function up_dmainitialize(void) * ****************************************************************************/ -DMA_HANDLE imxrt_dmachannel(uint32_t dmamux) +DMACH_HANDLE imxrt_dmachannel(uint32_t dmamux) { struct imxrt_dmach_s *dmach; unsigned int chndx; @@ -566,7 +806,7 @@ DMA_HANDLE imxrt_dmachannel(uint32_t dmamux) dmaerr("ERROR: Failed allocate eDMA channel\n"); } - return (DMA_HANDLE)dmach; + return (DMACH_HANDLE)dmach; } /**************************************************************************** @@ -582,7 +822,7 @@ DMA_HANDLE imxrt_dmachannel(uint32_t dmamux) * ****************************************************************************/ -void imxrt_dmafree(DMA_HANDLE handle) +void imxrt_dmafree(DMACH_HANDLE handle) { struct imxrt_dmach_s *dmach = (struct imxrt_dmach_s *)handle; uintptr_t regaddr; @@ -610,6 +850,253 @@ void imxrt_dmafree(DMA_HANDLE handle) putreg32(0, regaddr); } +/**************************************************************************** + * Name: imxrt_tcd_alloc + * + * Description: + * Allocate an in-memory, TCD + * + ****************************************************************************/ + +#if CONFIG_IMXRT_EDMA_NTCD > 0 +struct imxrt_edmatcd_s *imxrt_tcd_alloc(void) +{ + struct imxrt_edmatcd_s *tcd; + irqstate_t flags; + + /* Take the 'dsem'. When we hold the the 'dsem', then we know that one + * TCD is reserved for us in the free list. + * + * NOTE: We use a critical section here because we may block waiting for + * the 'dsem'. The critical section will be suspended while we are + * waiting. + */ + + flags = enter_critical_section(); + imxrt_takedsem(); + + /* Now there should be a TCD in the free list reserved just for us */ + + tcd = (struct imxrt_edmatcd_s *)sq_remfirst(&g_tcd_free); + DEBUGASSERT(tcd != NULL); + + leave_critical_section(flags); + return tcd; +} +#endif + +/**************************************************************************** + * Name: imxrt_tcd_free() + * + * Description: + * Free an in-memory, TCD + * + ****************************************************************************/ + +#if CONFIG_IMXRT_EDMA_NTCD > 0 +void imxrt_tcd_free(struct imxrt_edmatcd_s *tcd) +{ + irqstate_t flags; + + /* Add the the TCD to the end of the free list and post the 'dsem', + * possibly waking up another thread that might be waiting for + * a TCD. + */ + + flags = spin_lock_irqsave(); + sq_add_last((sq_entry_t *)tcd, &g_tcd_free); + (void)imxrt_givedsem(); + spin_unlock_irqrestore(flags); +} +#endif + +/************************************************************************************ + * Name: imxrt_dmach_reset + * + * Description: + * Sets all TCD registers to default values.. + * + * NOTE: This function enables the auto stop request feature. + * + ************************************************************************************/ + +void imxrt_dmach_reset(DMACH_HANDLE handle) +{ + struct imxrt_dmach_s *dmach = (struct imxrt_dmach_s *)handle; + + dmainfo("dmach: %p\n", dmach); + DEBUGASSERT(dmach != NULL && dmach->inuse && dmach->state != IMXRT_DMA_ACTIVE); + + imxrt_tcd_reset((struct imxrt_edmatcd_s *)IMXRT_EDMA_TCD_BASE(dmach->chan)); +} + +/******************************************************************************* + * Name: imxrt_dmach_initconfig + * + * Description: + * This function initializes the transfer configuration structure according + * to the user-provided input configuration. + * + * Input Parameters: + * saddr - eDMA transfer source address. + * srcwidth - eDMA transfer source address width(bytes). + * daddr - eDMA transfer destination address. + * destwidth - eDMA transfer destination address width(bytes). + * reqsize - eDMA transfer bytes per channel request. + * nbytes - eDMA transfer bytes to be transferred. + * type - eDMA transfer type. + * config - The user configuration structure of type struct + * imxrt_edma_xfrconfig_s. + * + * NOTE: The data address and the data width must be consistent. For example, + * if the SRC is 4 bytes, the source address must be 4 bytes aligned, or it + * results in source address error (SAE). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ******************************************************************************/ + +int imxrt_dmach_initconfig(void *saddr, uint32_t srcwidth, + void *daddr, uint32_t destwidth, + uint32_t reqsize, uint32_t nbytes, + edma_transfer_type_t type, + struct imxrt_edma_xfrconfig_s *config) +{ + config->daddr = (uint32_t)daddr; + config->saddr = (uint32_t)saddr; + config->nbytes = reqsize; + config->iter = nbytes / reqsize; + + switch (srcwidth) + { + case 1: + config->ssize = TCD_ATTR_SIZE_8BIT; + break; + + case 2: + config->ssize = TCD_ATTR_SIZE_16BIT; + break; + + case 4: + config->ssize = TCD_ATTR_SIZE_32BIT; + break; + + case 8: + config->ssize = TCD_ATTR_SIZE_64BIT; + break; + + case 32: + config->ssize = TCD_ATTR_SIZE_256BIT; + break; + + default: + return -EINVAL; + } + + switch (destwidth) + { + case 1: + config->dsize = TCD_ATTR_SIZE_8BIT; + break; + + case 2: + config->dsize = TCD_ATTR_SIZE_16BIT; + break; + + case 4U + config->dsize = TCD_ATTR_SIZE_32BIT; + break; + + case 8: + config->dsize = TCD_ATTR_SIZE_64BIT; + break; + + case 32: + config->dsize = TCD_ATTR_SIZE_256BIT; + break; + + default: + return -EINVAL; + } + + switch (type) + { + case kEDMA_MemoryToMemory: + config->doff = destwidth; + config->soff = srcwidth; + break; + + case kEDMA_MemoryToPeripheral: + config->doff = 0; + config->soff = srcwidth; + break; + + case kEDMA_PeripheralToMemory: + config->doff = destwidth; + config->soff = 0; + break; + + default: + return -EINVAL; + } + + return OK; +} + +/******************************************************************************* + * Name: imxrt_dmach_setconfig + * + * Description: + * This function configures the transfer attribute, including source address, + * destination address, transfer size, address offset, and so on. It also + * configures the scatter gather feature if the user supplies the TCD address. + * + * Example: + * + * edma_transfer_t config; + * struct imxrt_edmatcd_s tcd; + * config.saddr = ..; + * config.daddr = ..; + * ... + * dmach = imxrt_dmachannel(dmamux); + * ... + * tcd = imxrt_tcd_alloc(dmach) + * ... + * imxrt_dmach_setconfig(dmach, &config, &tcd); + * + * Input Parameters: + * handle - DMA channel handle created by imxrt_dmachannel() + * channel - eDMA channel number. + * config - Pointer to eDMA transfer configuration structure. + * next - Points to a TCD structure previously allocated via + * imxrt_tcd_alloc(). 'next' can be NULL if the caller does not + * wish to enable scatter/gather feature. + * + * NOTE: If 'next' is not NULL, it means scatter gather feature is enabled + * and DREQ bit is cleared in the previous transfer configuration. + * That bit was set in imxrt_dmach_reset(). + * + ******************************************************************************/ + +#ifdef CONFIG_IMXRT_EDMA_NTCD > 0 +void imxrt_dmach_setconfig(DMACH_HANDLE handle, + const struct imxrt_edma_xfrconfig_s *config, + struct imxrt_edmatcd_s *next) +{ + imxrt_tcd_configure((struct imxrt_edmatcd_s *)IMXRT_EDMA_TCD_BASE(dmach->chan)), + config, next); +} +#else +void imxrt_dmach_setconfig(DMACH_HANDLE handle, + const struct imxrt_edma_xfrconfig_s *config) +{ + imxrt_tcd_configure((struct imxrt_edmatcd_s *)IMXRT_EDMA_TCD_BASE(dmach->chan)), + config); +} +#endif + /************************************************************************************ * Name: imxrt_dmasetup * @@ -622,8 +1109,8 @@ void imxrt_dmafree(DMA_HANDLE handle) * ************************************************************************************/ -int imxrt_dmasetup(DMA_HANDLE handle, uint32_t saddr, uint32_t daddr, size_t nbytes, - uint32_t chflags); +int imxrt_dmasetup(DMACH_HANDLE handle, uint32_t saddr, uint32_t daddr, + size_t nbytes, uint32_t chflags); { struct imxrt_dmach_s *dmach = (struct imxrt_dmach_s *)handle; int ret = OK; @@ -697,7 +1184,7 @@ int imxrt_dmasetup(DMA_HANDLE handle, uint32_t saddr, uint32_t daddr, size_t nby * ****************************************************************************/ -int imxrt_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg) +int imxrt_dmastart(DMACH_HANDLE handle, dma_callback_t callback, void *arg) { struct imxrt_dmach_s *dmach = (struct imxrt_dmach_s *)handle; irqstate_t flags; @@ -764,7 +1251,7 @@ int imxrt_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg) * ****************************************************************************/ -void imxrt_dmastop(DMA_HANDLE handle) +void imxrt_dmastop(DMACH_HANDLE handle) { struct imxrt_dmach_s *dmach = (struct imxrt_dmach_s *)handle; irqstate_t flags; @@ -789,7 +1276,7 @@ void imxrt_dmastop(DMA_HANDLE handle) ****************************************************************************/ #ifdef CONFIG_DEBUG_DMA -void imxrt_dmasample(DMA_HANDLE handle, struct imxrt_dmaregs_s *regs) +void imxrt_dmasample(DMACH_HANDLE handle, struct imxrt_dmaregs_s *regs) { struct imxrt_dmach_s *dmach = (struct imxrt_dmach_s *)handle; uintptr_t regaddr; diff --git a/arch/arm/src/imxrt/imxrt_edma.h b/arch/arm/src/imxrt/imxrt_edma.h index e82e62f61b..bffda579b6 100644 --- a/arch/arm/src/imxrt/imxrt_edma.h +++ b/arch/arm/src/imxrt/imxrt_edma.h @@ -4,6 +4,13 @@ * Copyright (C) 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * + * Portions of the eDMA logic derive from NXP sample code which has a compatible + * BSD 3-clause license: + * + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2017 NXP + * All rights reserved + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -49,94 +56,39 @@ * Pre-processor Definitions ************************************************************************************/ -/* eDMA Configuration ***************************************************************/ - -/* Flags used to characterize the DMA channel. The naming convention is that one - * side is the peripheral and the other is memory (however, the interface could still - * be used if, for example, both sides were memory although the naming would be - * awkward) - * - * .... .... .... .... CCCC GGBA DDSS ..TT - * - * REVISIT: Initially, only vanilla Rx/Tx DMA block transfers are supported. - */ - -/* Transfer type: - * - * .... .... .... .... .... .... .... ..TT - */ - -#define TTYPE_M2M (0) /* Memory-to-memory (not supported) */ -#define TTYPE_M2P (1) /* Memory-to-peripheral (normal Tx) */ -#define TTYPE_P2M (2) /* Peripheral-to-memory (normal Rx) */ -#define TTYPE_P2P (3) /* Peripheral-to-peripheral (not supported) */ - -#define TTYPE_2P_MASK (1) /* Transfer to peripheral (M2P or P2P) */ -#define TTYPE_P2_MASK (2) /* Transfer from peripheral (P2M or P2P) */ - -#define DMACH_FLAG_TTYPE_SHIFT (0) /* Bits 0-1 Destination transfer size */ -#define DMACH_FLAG_TTYPE_MASK (3 << DMACH_FLAG_TTYPE_SHIFT) -# define DMACH_FLAG_TTYPE_M2M (TTYPE_M2M << DMACH_FLAG_TTYPE_SHIFT) /* Memory-to-memory */ -# define DMACH_FLAG_TTYPE_M2P (TTYPE_M2P << DMACH_FLAG_TTYPE_SHIFT) /* Memory-to-peripheral */ -# define DMACH_FLAG_TTYPE_P2M (TTYPE_P2M << DMACH_FLAG_TTYPE_SHIFT) /* Peripheral-to-memory */ -# define DMACH_FLAG_TTYPE_P2P (TTYPE_P2P << DMACH_FLAG_TTYPE_SHIFT) /* Peripheral-to-peripheral */ - -# define DMACH_FLAG_TTYPE_2P_MASK (TTYPE_2P_MASK << DMACH_FLAG_TTYPE_SHIFT) /* Transfer to peripheral */ -# define DMACH_FLAG_TTYPE_P2_MASK (TTYPE_P2_MASK << DMACH_FLAG_TTYPE_SHIFT) /* Transfer from peripheral */ - -/* Source transfer size: - * - * .... .... .... .... .... .... ..SS .... - */ - -#define DMACH_FLAG_SSIZE_SHIFT (4) /* Bits 4-5: Source transfer size */ -#define DMACH_FLAG_SSIZE_MASK (7 << DMACH_FLAG_SSIZE_SHIFT) -# define DMACH_FLAG_SSIZE_8BIT (TCD_ATTR_SIZE_8BIT << DMACH_FLAG_SSIZE_SHIFT) /* 8-bit */ -# define DMACH_FLAG_SSIZE_16BIT (TCD_ATTR_SIZE_16BIT << DMACH_FLAG_SSIZE_SHIFT) /* 16-bit */ -# define DMACH_FLAG_SSIZE_32BIT (TCD_ATTR_SIZE_32BIT << DMACH_FLAG_SSIZE_SHIFT) /* 32-bit */ -# define DMACH_FLAG_SSIZE_64BIT (TCD_ATTR_SIZE_64BIT << DMACH_FLAG_SSIZE_SHIFT) /* 64-bit */ -# define DMACH_FLAG_SSIZE_256BIT (TCD_ATTR_SIZE_256BIT << DMACH_FLAG_SSIZE_SHIFT) /* 32-byte burst */ - -/* Destination transfer size: - * - * .... .... .... .... .... .... DD.. .... - */ - -#define DMACH_FLAG_DSIZE_SHIFT (6) /* Bits 6-7: Destination transfer size */ -#define DMACH_FLAG_DSIZE_MASK (7 << DMACH_FLAG_DSIZE_SHIFT) -# define DMACH_FLAG_DSIZE_8BIT (TCD_ATTR_SIZE_8BIT << DMACH_FLAG_DSIZE_SHIFT) /* 8-bit */ -# define DMACH_FLAG_DSIZE_16BIT (TCD_ATTR_SIZE_16BIT << DMACH_FLAG_DSIZE_SHIFT) /* 16-bit */ -# define DMACH_FLAG_DSIZE_32BIT (TCD_ATTR_SIZE_32BIT << DMACH_FLAG_DSIZE_SHIFT) /* 32-bit */ -# define DMACH_FLAG_DSIZE_64BIT (TCD_ATTR_SIZE_64BIT << DMACH_FLAG_DSIZE_SHIFT) /* 64-bit */ -# define DMACH_FLAG_DSIZE_256BIT (TCD_ATTR_SIZE_256BIT << DMACH_FLAG_DSIZE_SHIFT) /* 32-byte burst */ - -/* Arbitration: - * - * .... .... .... .... .... ..BA .... .... - */ - -#define DMACH_FLAG_CHRR (1 << 8) /* Bit 8: Round Robin Channel Arbitration */ -#define DMACH_FLAG_GRPRR (1 << 8) /* Bit 9: Round Robin Group Arbitration */ - -/* DMA Priorities: - * - * .... .... .... .... CCCC GG.. .... .... - */ - -#define DMACH_FLAG_GPPRI_SHIFT (10) /* Bits 10-11: Channel Group Priority */ -#define DMACH_FLAG_GRPPRI_MASK (3 << DMACH_FLAG_GPPRI_SHIFT) -# define DMACH_FLAG_GRPPRI(n) ((uint32_t)(n) << DMACH_FLAG_GPPRI_SHIFT) - -#define DMACH_FLAG_CHPRI_SHIFT (12) /* Bits 12-15: Channel Arbitration Priority */ -#define DMACH_FLAG_CHPRI_MASK (15 << DMACH_FLAG_CHPRI_SHIFT) -# define DMACH_FLAG_CHPRI(n) ((uint32_t)(n) << DMACH_FLAG_CHPRI_SHIFT) - /************************************************************************************ * Public Types ************************************************************************************/ -typedef FAR void *DMA_HANDLE; -typedef void (*dma_callback_t)(DMA_HANDLE handle, void *arg, int result); +typedef FAR void *DMACH_HANDLE; +typedef void (*dma_callback_t)(DMACH_HANDLE handle, void *arg, int result); + +/* eDMA transfer type */ + +enum imxrt_edma_xfrtype_e +{ + eDMA_MEMORY2MEMORY = 0, /* Transfer from memory to memory */ + eDMA_PERIPH2MEMORY, /* Transfer from peripheral to memory */ + eDMA_MEMORY2PERIPH, /* Transfer from memory to peripheral */ +}; + +/* This structure holds the source/destination transfer attribute configuration. */ + +struct imxrt_edma_xfrconfig_s +{ + uint32_t saddr; /* Source data address. */ + uint32_t daddr; /* Destination data address. */ + uint8_t ssize; /* Source data transfer size (see TCD_ATTR_SIZE_* definitions in chip/. */ + uint8_t dsize; /* Destination data transfer size. */ + int16_t soff; /* Sign-extended offset for current source address. */ + int16_t doff; /* Sign-extended offset for current destination address. */ + uint16_t iter; /* Major loop iteration count. */ +#ifdef CONFIG_IMXRT_EDMA_EMLIM + uint16_t nbytes; /* Bytes to transfer in a minor loop */ +#else + uint32_t nbytes; /* Bytes to transfer in a minor loop */ +#endif +}; /* The following is used for sampling DMA registers when CONFIG DEBUG_DMA is selected */ @@ -202,7 +154,7 @@ extern "C" * Public Function Prototypes ************************************************************************************/ -/**************************************************************************** +/************************************************************************************ * Name: imxrt_dmachannel * * Allocate a DMA channel. This function sets aside a DMA channel, @@ -224,9 +176,9 @@ extern "C" * If a DMA channel is available, this function returns a non-NULL, void* * DMA channel handle. NULL is returned on any failure. * - ****************************************************************************/ + ************************************************************************************/ -DMA_HANDLE imxrt_dmachannel(uint32_t dmamux); +DMACH_HANDLE imxrt_dmachannel(uint32_t dmamux); /************************************************************************************ * Name: imxrt_dmafree @@ -240,7 +192,121 @@ DMA_HANDLE imxrt_dmachannel(uint32_t dmamux); * ************************************************************************************/ -void imxrt_dmafree(DMA_HANDLE handle); +void imxrt_dmafree(DMACH_HANDLE handle); + +/**************************************************************************** + * Name: imxrt_tcd_alloc + * + * Description: + * Allocate an in-memory, TCD + * + ****************************************************************************/ + +#if CONFIG_IMXRT_EDMA_NTCD > 0 +struct imxrt_edmatcd_s *imxrt_tcd_alloc(void); +#endif + +/**************************************************************************** + * Name: imxrt_tcd_free() + * + * Description: + * Free an in-memory, TCD + * + ****************************************************************************/ + +#if CONFIG_IMXRT_EDMA_NTCD > 0 +void imxrt_tcd_free(struct imxrt_edmatcd_s *tcd); +#endif + +/************************************************************************************ + * Name: imxrt_dmach_reset + * + * Description: + * Sets all TCD registers to default values.. + * + * NOTE: This function enables the auto stop request feature. + * + ************************************************************************************/ + +void imxrt_dmach_reset(DMACH_HANDLE handle); + +/******************************************************************************* + * Name: imxrt_dmach_initconfig + * + * Description: + * This function initializes the transfer configuration structure according + * to the user-provided input configuration. + * + * Input Parameters: + * saddr - eDMA transfer source address. + * srcwidth - eDMA transfer source address width(bytes). + * daddr - eDMA transfer destination address. + * destwidth - eDMA transfer destination address width(bytes). + * reqsize - eDMA transfer bytes per channel request. + * nbytes - eDMA transfer bytes to be transferred. + * type - eDMA transfer type. + * config - The user configuration structure of type struct + * imxrt_edma_xfrconfig_s. + * + * NOTE: The data address and the data width must be consistent. For example, + * if the SRC is 4 bytes, the source address must be 4 bytes aligned, or it + * results in source address error (SAE). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ******************************************************************************/ + +int imxrt_dmach_initconfig(void *saddr, uint32_t srcwidth, + void *daddr, uint32_t destwidth, + uint32_t reqsize, uint32_t nbytes, + edma_transfer_type_t type, + struct imxrt_edma_xfrconfig_s *config); + +/******************************************************************************* + * Name: imxrt_dmach_setconfig + * + * Description: + * This function configures the transfer attribute, including source address, + * destination address, transfer size, address offset, and so on. It also + * configures the scatter gather feature if the user supplies the TCD address. + * + * Example: + * + * edma_transfer_t config; + * struct imxrt_edmatcd_s tcd; + * config.saddr = ..; + * config.daddr = ..; + * ... + * dmach = imxrt_dmachannel(dmamux); + * ... + * tcd = imxrt_tcd_alloc(dmach) + * ... + * imxrt_dmach_setconfig(dmach, &config, &tcd); + * + * Input Parameters: + * handle - DMA channel handle created by imxrt_dmachannel() + * channel - eDMA channel number. + * config - Pointer to eDMA transfer configuration structure. + * next - Points to a TCD structure previously allocated via + * imxrt_tcd_alloc(). 'next' can be NULL if the caller does not + * wish to enable scatter/gather feature. + * + * NOTE: If 'next' is not NULL, it means scatter gather feature is enabled + * and DREQ bit is cleared in the previous transfer configuration. + * That bit was set in imxrt_dmach_reset(). + * + ******************************************************************************/ + +#ifdef CONFIG_IMXRT_EDMA_NTCD > 0 +void imxrt_dmach_setconfig(DMACH_HANDLE handle, + const struct imxrt_edma_xfrconfig_s *config, + struct imxrt_edmatcd_s *next); +#else +void imxrt_dmach_setconfig(DMACH_HANDLE handle, + const struct imxrt_edma_xfrconfig_s *config); +#endif /************************************************************************************ * Name: imxrt_dmasetup @@ -254,8 +320,8 @@ void imxrt_dmafree(DMA_HANDLE handle); * ************************************************************************************/ -int imxrt_dmasetup(DMA_HANDLE handle, uint32_t saddr, uint32_t daddr, size_t nbytes, - uint32_t chflags); +int imxrt_dmasetup(DMACH_HANDLE handle, uint32_t saddr, uint32_t daddr, + size_t nbytes, uint32_t chflags); /************************************************************************************ * Name: imxrt_dmastart @@ -265,7 +331,7 @@ int imxrt_dmasetup(DMA_HANDLE handle, uint32_t saddr, uint32_t daddr, size_t nby * ************************************************************************************/ -int imxrt_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg); +int imxrt_dmastart(DMACH_HANDLE handle, dma_callback_t callback, void *arg); /************************************************************************************ * Name: imxrt_dmastop @@ -276,7 +342,7 @@ int imxrt_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg); * ************************************************************************************/ -void imxrt_dmastop(DMA_HANDLE handle); +void imxrt_dmastop(DMACH_HANDLE handle); /************************************************************************************ * Name: imxrt_dmasample @@ -287,7 +353,7 @@ void imxrt_dmastop(DMA_HANDLE handle); ************************************************************************************/ #ifdef CONFIG_DEBUG_DMA -void imxrt_dmasample(DMA_HANDLE handle, struct imxrt_dmaregs_s *regs); +void imxrt_dmasample(DMACH_HANDLE handle, struct imxrt_dmaregs_s *regs); #else # define imxrt_dmasample(handle,regs) #endif diff --git a/arch/arm/src/samv7/sam_xdmac.c b/arch/arm/src/samv7/sam_xdmac.c index 9e2e43ffea..1b2675d40b 100644 --- a/arch/arm/src/samv7/sam_xdmac.c +++ b/arch/arm/src/samv7/sam_xdmac.c @@ -1598,6 +1598,12 @@ void sam_dmainitialize(struct sam_xdmac_s *xdmac) nxsem_init(&xdmac->chsem, 0, 1); nxsem_init(&xdmac->dsem, 0, SAMV7_NDMACHAN); + + /* The 'dsem' is used for signaling rather than mutual exclusion and, + * hence, should not have priority inheritance enabled. + */ + + nxsem_setprotocol(&xdmac->dsem, SEM_PRIO_NONE); } /****************************************************************************