Squashed commit of the following:

arch/arm/src/imxrt:  Add structures to support list of TCDs for Scatter/Gather DMA.
    arch/arm/src/imxrt:  Add eDMA imxrt_dmach_initconfig().
    arch/arm/src/imxrt:  Add eDMA imxrt_tcd_instantiate().
    arch/arm/src/imxrt:  Replacing some of the logic cloned from SAMv7 XDMAC with eDMA logic from NXP sample code.  I am thinking that the eDMA is too complex to force into the same pattern as for other MCUs.
    arch/arms/src/imxrt/imxrt_edma.c:  Add support for in-memory TCDs.
    arch/arm/src/imxrt/chip:  Add an in-memory representation of the TCD in imxrt_edma.h
This commit is contained in:
Gregory Nutt 2018-05-21 11:46:16 -06:00
parent f837f2231f
commit 1cf676344e
5 changed files with 732 additions and 110 deletions

View File

@ -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

View File

@ -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 */

View File

@ -4,6 +4,13 @@
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <stdbool.h>
#include <string.h>
#include <semaphore.h>
#include <queue.h>
#include <debug.h>
#include <errno.h>
@ -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;

View File

@ -4,6 +4,13 @@
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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

View File

@ -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);
}
/****************************************************************************