mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-01 13:42:49 +00:00
Multi channels share one IRQ, add channels-shared-irq-mask on RT1180 attribution to describe the channel shared status, and add code implementation to register the handler function for each channel in different interrupts. Fix legacy building warning issue Signed-off-by: Lucien Zhao <lucien.zhao@nxp.com>
997 lines
32 KiB
C
997 lines
32 KiB
C
/*
|
|
* Copyright 2020-2024 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @brief Common part of DMA drivers for imx rt series.
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT nxp_mcux_edma
|
|
|
|
#include <errno.h>
|
|
#include <soc.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/devicetree.h>
|
|
#include <zephyr/sys/atomic.h>
|
|
#include <zephyr/drivers/dma.h>
|
|
#include <zephyr/drivers/clock_control.h>
|
|
#include <zephyr/sys/barrier.h>
|
|
|
|
#include "dma_mcux_edma.h"
|
|
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/irq.h>
|
|
|
|
LOG_MODULE_REGISTER(dma_mcux_edma, CONFIG_DMA_LOG_LEVEL);
|
|
|
|
#define HAS_CHANNEL_GAP(n) DT_INST_NODE_HAS_PROP(n, channel_gap) ||
|
|
#define DMA_MCUX_HAS_CHANNEL_GAP (DT_INST_FOREACH_STATUS_OKAY(HAS_CHANNEL_GAP) 0)
|
|
|
|
struct dma_mcux_edma_config {
|
|
DMA_Type *base;
|
|
#if defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT
|
|
DMAMUX_Type **dmamux_base;
|
|
#endif
|
|
uint8_t channels_per_mux;
|
|
uint8_t dmamux_reg_offset;
|
|
int dma_requests;
|
|
int dma_channels; /* number of channels */
|
|
#if DMA_MCUX_HAS_CHANNEL_GAP
|
|
uint32_t channel_gap[2];
|
|
#endif
|
|
void (*irq_config_func)(const struct device *dev);
|
|
edma_tcd_t (*tcdpool)[CONFIG_DMA_TCD_QUEUE_SIZE];
|
|
};
|
|
|
|
|
|
#ifdef CONFIG_HAS_MCUX_CACHE
|
|
|
|
#ifdef CONFIG_DMA_MCUX_USE_DTCM_FOR_DMA_DESCRIPTORS
|
|
|
|
#if DT_NODE_HAS_STATUS_OKAY(DT_CHOSEN(zephyr_dtcm))
|
|
#define EDMA_TCDPOOL_CACHE_ATTR __dtcm_noinit_section
|
|
#else /* DT_NODE_HAS_STATUS_OKAY(DT_CHOSEN(zephyr_dtcm)) */
|
|
#error Selected DTCM for MCUX DMA descriptors but no DTCM section.
|
|
#endif /* DT_NODE_HAS_STATUS_OKAY(DT_CHOSEN(zephyr_dtcm)) */
|
|
|
|
#elif defined(CONFIG_NOCACHE_MEMORY)
|
|
#define EDMA_TCDPOOL_CACHE_ATTR __nocache
|
|
#else
|
|
/*
|
|
* Note: the TCD pool *must* be in non cacheable memory. All of the NXP SOCs
|
|
* that support caching memory have their default SRAM regions defined as a
|
|
* non cached memory region, but if the default SRAM region is changed EDMA
|
|
* TCD pools would be moved to cacheable memory, resulting in DMA cache
|
|
* coherency issues.
|
|
*/
|
|
|
|
#define EDMA_TCDPOOL_CACHE_ATTR
|
|
|
|
#endif /* CONFIG_DMA_MCUX_USE_DTCM_FOR_DMA_DESCRIPTORS */
|
|
|
|
#else /* CONFIG_HAS_MCUX_CACHE */
|
|
|
|
#define EDMA_TCDPOOL_CACHE_ATTR
|
|
|
|
#endif /* CONFIG_HAS_MCUX_CACHE */
|
|
|
|
struct dma_mcux_channel_transfer_edma_settings {
|
|
uint32_t source_data_size;
|
|
uint32_t dest_data_size;
|
|
uint32_t source_burst_length;
|
|
uint32_t dest_burst_length;
|
|
enum dma_channel_direction direction;
|
|
edma_transfer_type_t transfer_type;
|
|
bool valid;
|
|
/* This var indicate it is dynamic SG mode or loop SG mode. */
|
|
bool cyclic;
|
|
/* These parameters are for cyclic mode only.
|
|
* Next empty TCD idx which can be used for transfer
|
|
*/
|
|
volatile uint8_t write_idx;
|
|
/* How many TCDs in TCD pool is emtpy(can be used to write transfer parameters) */
|
|
volatile uint8_t empty_tcds;
|
|
};
|
|
|
|
|
|
struct call_back {
|
|
edma_transfer_config_t transferConfig;
|
|
edma_handle_t edma_handle;
|
|
const struct device *dev;
|
|
void *user_data;
|
|
dma_callback_t dma_callback;
|
|
struct dma_mcux_channel_transfer_edma_settings transfer_settings;
|
|
bool busy;
|
|
};
|
|
|
|
struct dma_mcux_edma_data {
|
|
struct dma_context dma_ctx;
|
|
struct call_back *data_cb;
|
|
atomic_t *channels_atomic;
|
|
};
|
|
|
|
#define DEV_CFG(dev) \
|
|
((const struct dma_mcux_edma_config *const)dev->config)
|
|
#define DEV_DATA(dev) ((struct dma_mcux_edma_data *)dev->data)
|
|
#define DEV_BASE(dev) ((DMA_Type *)DEV_CFG(dev)->base)
|
|
|
|
#define DEV_CHANNEL_DATA(dev, ch) \
|
|
((struct call_back *)(&(DEV_DATA(dev)->data_cb[ch])))
|
|
|
|
#define DEV_EDMA_HANDLE(dev, ch) \
|
|
((edma_handle_t *)(&(DEV_CHANNEL_DATA(dev, ch)->edma_handle)))
|
|
|
|
#if defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT
|
|
#define DEV_DMAMUX_BASE(dev, idx) ((DMAMUX_Type *)DEV_CFG(dev)->dmamux_base[idx])
|
|
#define DEV_DMAMUX_IDX(dev, ch) (ch / DEV_CFG(dev)->channels_per_mux)
|
|
|
|
#define DEV_DMAMUX_CHANNEL(dev, ch) \
|
|
(ch % DEV_CFG(dev)->channels_per_mux) ^ (DEV_CFG(dev)->dmamux_reg_offset)
|
|
#endif
|
|
|
|
/* Definations for SW TCD fields */
|
|
#if defined(CONFIG_DMA_MCUX_EDMA) || defined(CONFIG_DMA_MCUX_EDMA_V3)
|
|
#define EDMA_TCD_SADDR(tcd, flag) ((tcd)->SADDR)
|
|
#define EDMA_TCD_DADDR(tcd, flag) ((tcd)->DADDR)
|
|
#define EDMA_TCD_BITER(tcd, flag) ((tcd)->BITER)
|
|
#define EDMA_TCD_CITER(tcd, flag) ((tcd)->CITER)
|
|
#define EDMA_TCD_CSR(tcd, flag) ((tcd)->CSR)
|
|
#define EDMA_TCD_DLAST_SGA(tcd, flag) ((tcd)->DLAST_SGA)
|
|
#if defined(CONFIG_DMA_MCUX_EDMA_V3)
|
|
#define DMA_CSR_DREQ DMA_TCD_CSR_DREQ
|
|
#define EDMA_HW_TCD_CH_ACTIVE_MASK (DMA_CH_CSR_ACTIVE_MASK)
|
|
#else
|
|
#define EDMA_HW_TCD_CH_ACTIVE_MASK (DMA_CSR_ACTIVE_MASK)
|
|
#endif /* CONFIG_DMA_MCUX_EDMA_V3 */
|
|
#elif defined(CONFIG_DMA_MCUX_EDMA_V4)
|
|
/* Above macros have been defined in fsl_edma_core.h */
|
|
#define EDMA_HW_TCD_CH_ACTIVE_MASK (DMA_CH_CSR_ACTIVE_MASK)
|
|
#endif
|
|
|
|
/* Definations for HW TCD fields */
|
|
#ifdef CONFIG_DMA_MCUX_EDMA
|
|
#define EDMA_HW_TCD_SADDR(dev, ch) (DEV_BASE(dev)->TCD[ch].SADDR)
|
|
#define EDMA_HW_TCD_DADDR(dev, ch) (DEV_BASE(dev)->TCD[ch].DADDR)
|
|
#define EDMA_HW_TCD_BITER(dev, ch) (DEV_BASE(dev)->TCD[ch].BITER_ELINKNO)
|
|
#define EDMA_HW_TCD_CITER(dev, ch) (DEV_BASE(dev)->TCD[ch].CITER_ELINKNO)
|
|
#define EDMA_HW_TCD_CSR(dev, ch) (DEV_BASE(dev)->TCD[ch].CSR)
|
|
#elif defined(CONFIG_DMA_MCUX_EDMA_V3) || defined(CONFIG_DMA_MCUX_EDMA_V4)
|
|
#define EDMA_HW_TCD_SADDR(dev, ch) (DEV_BASE(dev)->CH[ch].TCD_SADDR)
|
|
#define EDMA_HW_TCD_DADDR(dev, ch) (DEV_BASE(dev)->CH[ch].TCD_DADDR)
|
|
#define EDMA_HW_TCD_BITER(dev, ch) (DEV_BASE(dev)->CH[ch].TCD_BITER_ELINKNO)
|
|
#define EDMA_HW_TCD_CITER(dev, ch) (DEV_BASE(dev)->CH[ch].TCD_CITER_ELINKNO)
|
|
#define EDMA_HW_TCD_CSR(dev, ch) (DEV_BASE(dev)->CH[ch].TCD_CSR)
|
|
#endif
|
|
|
|
/*
|
|
* The hardware channel (takes the gap into account) is used when access DMA registers.
|
|
* For data structures in the shim driver still use the primitive channel.
|
|
*/
|
|
static ALWAYS_INLINE uint32_t dma_mcux_edma_add_channel_gap(const struct device *dev,
|
|
uint32_t channel)
|
|
{
|
|
#if DMA_MCUX_HAS_CHANNEL_GAP
|
|
const struct dma_mcux_edma_config *config = DEV_CFG(dev);
|
|
|
|
return (channel < config->channel_gap[0]) ? channel :
|
|
(channel + 1 + config->channel_gap[1] - config->channel_gap[0]);
|
|
#else
|
|
ARG_UNUSED(dev);
|
|
return channel;
|
|
#endif
|
|
}
|
|
|
|
static ALWAYS_INLINE uint32_t dma_mcux_edma_remove_channel_gap(const struct device *dev,
|
|
uint32_t channel)
|
|
{
|
|
#if DMA_MCUX_HAS_CHANNEL_GAP
|
|
const struct dma_mcux_edma_config *config = DEV_CFG(dev);
|
|
|
|
return (channel < config->channel_gap[0]) ? channel :
|
|
(channel + config->channel_gap[0] - config->channel_gap[1] - 1);
|
|
#else
|
|
ARG_UNUSED(dev);
|
|
return channel;
|
|
#endif
|
|
}
|
|
|
|
static bool data_size_valid(const size_t data_size)
|
|
{
|
|
return (data_size == 4U || data_size == 2U ||
|
|
data_size == 1U || data_size == 8U ||
|
|
data_size == 16U || data_size == 32U
|
|
#if defined(CONFIG_DMA_MCUX_EDMA_V3) || defined(CONFIG_DMA_MCUX_EDMA_V4)
|
|
|| data_size == 64U
|
|
#endif
|
|
);
|
|
}
|
|
|
|
static void nxp_edma_callback(edma_handle_t *handle, void *param, bool transferDone,
|
|
uint32_t tcds)
|
|
{
|
|
int ret = -EIO;
|
|
struct call_back *data = (struct call_back *)param;
|
|
uint32_t channel = dma_mcux_edma_remove_channel_gap(data->dev, handle->channel);
|
|
|
|
if (data->transfer_settings.cyclic) {
|
|
data->transfer_settings.empty_tcds++;
|
|
/*In loop mode, DMA is always busy*/
|
|
data->busy = 1;
|
|
ret = DMA_STATUS_COMPLETE;
|
|
} else if (transferDone) {
|
|
/* DMA is no longer busy when there are no remaining TCDs to transfer */
|
|
data->busy = (handle->tcdPool != NULL) && (handle->tcdUsed > 0);
|
|
ret = DMA_STATUS_COMPLETE;
|
|
}
|
|
LOG_DBG("transfer %d", tcds);
|
|
data->dma_callback(data->dev, data->user_data, channel, ret);
|
|
}
|
|
|
|
static void dma_mcux_edma_irq_handler(const struct device *dev, uint32_t channel)
|
|
{
|
|
uint32_t hw_channel = dma_mcux_edma_add_channel_gap(dev, channel);
|
|
uint32_t flag = EDMA_GetChannelStatusFlags(DEV_BASE(dev), hw_channel);
|
|
|
|
if (flag & kEDMA_InterruptFlag) {
|
|
LOG_DBG("IRQ OCCURRED");
|
|
/* EDMA interrupt flag is cleared here */
|
|
EDMA_HandleIRQ(DEV_EDMA_HANDLE(dev, channel));
|
|
LOG_DBG("IRQ DONE");
|
|
}
|
|
|
|
#if DT_INST_PROP(0, no_error_irq)
|
|
/* Channel shares the same irq for error and transfer complete */
|
|
else if (flag & kEDMA_ErrorFlag) {
|
|
EDMA_ClearChannelStatusFlags(DEV_BASE(dev), channel, 0xFFFFFFFF);
|
|
EDMA_AbortTransfer(DEV_EDMA_HANDLE(dev, channel));
|
|
DEV_CHANNEL_DATA(dev, channel)->busy = false;
|
|
LOG_INF("channel %d error status is 0x%x", channel, flag);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !DT_INST_PROP(0, no_error_irq)
|
|
static void dma_mcux_edma_error_irq_handler(const struct device *dev)
|
|
{
|
|
int i = 0;
|
|
uint32_t flag = 0;
|
|
uint32_t hw_channel;
|
|
|
|
for (i = 0; i < DEV_CFG(dev)->dma_channels; i++) {
|
|
if (DEV_CHANNEL_DATA(dev, i)->busy) {
|
|
hw_channel = dma_mcux_edma_add_channel_gap(dev, i);
|
|
flag = EDMA_GetChannelStatusFlags(DEV_BASE(dev), hw_channel);
|
|
EDMA_ClearChannelStatusFlags(DEV_BASE(dev), hw_channel, 0xFFFFFFFF);
|
|
EDMA_AbortTransfer(DEV_EDMA_HANDLE(dev, i));
|
|
DEV_CHANNEL_DATA(dev, i)->busy = false;
|
|
LOG_INF("channel %d error status is 0x%x", hw_channel, flag);
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_CPU_CORTEX_M4)
|
|
barrier_dsync_fence_full();
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(channels_shared_irq_mask)
|
|
static void dma_mcux_edma_multi_channels_irq_handler(const struct device *dev, uint32_t idx,
|
|
uint32_t *buf, uint32_t mask_width)
|
|
{
|
|
uint32_t *num = &buf[mask_width * idx];
|
|
uint32_t count = 0;
|
|
|
|
for (int _i = 0; _i < mask_width; _i++) {
|
|
uint32_t value = (*num);
|
|
|
|
while (value > 0) {
|
|
if ((value & 0x1) == 1) {
|
|
dma_mcux_edma_irq_handler(dev, count);
|
|
}
|
|
value = value >> 1;
|
|
count++;
|
|
}
|
|
num++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Configure a channel */
|
|
static int dma_mcux_edma_configure(const struct device *dev, uint32_t channel,
|
|
struct dma_config *config)
|
|
{
|
|
/* Check for invalid parameters before dereferencing them. */
|
|
if (NULL == dev || NULL == config) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
edma_handle_t *p_handle = DEV_EDMA_HANDLE(dev, channel);
|
|
struct call_back *data = DEV_CHANNEL_DATA(dev, channel);
|
|
struct dma_block_config *block_config = config->head_block;
|
|
uint32_t slot = config->dma_slot;
|
|
uint32_t hw_channel;
|
|
edma_transfer_type_t transfer_type;
|
|
unsigned int key;
|
|
int ret = 0;
|
|
edma_tcd_t *tcd = NULL;
|
|
|
|
if (slot >= DEV_CFG(dev)->dma_requests) {
|
|
LOG_ERR("source number is out of scope %d", slot);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (channel >= DEV_CFG(dev)->dma_channels) {
|
|
LOG_ERR("out of DMA channel %d", channel);
|
|
return -EINVAL;
|
|
}
|
|
|
|
hw_channel = dma_mcux_edma_add_channel_gap(dev, channel);
|
|
#if defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT
|
|
uint8_t dmamux_idx, dmamux_channel;
|
|
|
|
dmamux_idx = DEV_DMAMUX_IDX(dev, channel);
|
|
dmamux_channel = DEV_DMAMUX_CHANNEL(dev, channel);
|
|
#endif
|
|
data->transfer_settings.valid = false;
|
|
|
|
switch (config->channel_direction) {
|
|
case MEMORY_TO_MEMORY:
|
|
transfer_type = kEDMA_MemoryToMemory;
|
|
break;
|
|
case MEMORY_TO_PERIPHERAL:
|
|
transfer_type = kEDMA_MemoryToPeripheral;
|
|
break;
|
|
case PERIPHERAL_TO_MEMORY:
|
|
transfer_type = kEDMA_PeripheralToMemory;
|
|
break;
|
|
case PERIPHERAL_TO_PERIPHERAL:
|
|
transfer_type = kEDMA_PeripheralToPeripheral;
|
|
break;
|
|
default:
|
|
LOG_ERR("not support transfer direction");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!data_size_valid(config->source_data_size)) {
|
|
LOG_ERR("Source unit size error, %d", config->source_data_size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!data_size_valid(config->dest_data_size)) {
|
|
LOG_ERR("Dest unit size error, %d", config->dest_data_size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (block_config->source_gather_en || block_config->dest_scatter_en) {
|
|
if (config->block_count > CONFIG_DMA_TCD_QUEUE_SIZE) {
|
|
LOG_ERR("please config DMA_TCD_QUEUE_SIZE as %d", config->block_count);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
data->transfer_settings.source_data_size = config->source_data_size;
|
|
data->transfer_settings.dest_data_size = config->dest_data_size;
|
|
data->transfer_settings.source_burst_length = config->source_burst_length;
|
|
data->transfer_settings.dest_burst_length = config->dest_burst_length;
|
|
data->transfer_settings.direction = config->channel_direction;
|
|
data->transfer_settings.transfer_type = transfer_type;
|
|
data->transfer_settings.valid = true;
|
|
data->transfer_settings.cyclic = config->cyclic;
|
|
|
|
/* Lock and page in the channel configuration */
|
|
key = irq_lock();
|
|
|
|
#if defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT
|
|
|
|
#if DT_INST_PROP(0, nxp_a_on)
|
|
if (config->source_handshake || config->dest_handshake ||
|
|
transfer_type == kEDMA_MemoryToMemory) {
|
|
/*software trigger make the channel always on*/
|
|
LOG_DBG("ALWAYS ON");
|
|
DMAMUX_EnableAlwaysOn(DEV_DMAMUX_BASE(dev, dmamux_idx), dmamux_channel, true);
|
|
} else {
|
|
DMAMUX_SetSource(DEV_DMAMUX_BASE(dev, dmamux_idx), dmamux_channel, slot);
|
|
}
|
|
#else
|
|
DMAMUX_SetSource(DEV_DMAMUX_BASE(dev, dmamux_idx), dmamux_channel, slot);
|
|
#endif
|
|
|
|
/* dam_imx_rt_set_channel_priority(dev, channel, config); */
|
|
DMAMUX_EnableChannel(DEV_DMAMUX_BASE(dev, dmamux_idx), dmamux_channel);
|
|
|
|
#endif
|
|
|
|
if (data->busy) {
|
|
EDMA_AbortTransfer(p_handle);
|
|
}
|
|
EDMA_ResetChannel(DEV_BASE(dev), hw_channel);
|
|
EDMA_CreateHandle(p_handle, DEV_BASE(dev), hw_channel);
|
|
EDMA_SetCallback(p_handle, nxp_edma_callback, (void *)data);
|
|
|
|
#if defined(FSL_FEATURE_EDMA_HAS_CHANNEL_MUX) && FSL_FEATURE_EDMA_HAS_CHANNEL_MUX
|
|
/* First release any peripheral previously associated with this channel */
|
|
EDMA_SetChannelMux(DEV_BASE(dev), hw_channel, 0);
|
|
EDMA_SetChannelMux(DEV_BASE(dev), hw_channel, slot);
|
|
#endif
|
|
|
|
LOG_DBG("channel is %d", channel);
|
|
EDMA_EnableChannelInterrupts(DEV_BASE(dev), hw_channel, kEDMA_ErrorInterruptEnable);
|
|
|
|
/* Initialize all TCD pool as 0*/
|
|
for (int i = 0; i < CONFIG_DMA_TCD_QUEUE_SIZE; i++) {
|
|
memset(&DEV_CFG(dev)->tcdpool[channel][i], 0,
|
|
sizeof(DEV_CFG(dev)->tcdpool[channel][i]));
|
|
}
|
|
|
|
if (block_config->source_gather_en || block_config->dest_scatter_en) {
|
|
if (config->cyclic) {
|
|
/* Loop SG mode */
|
|
data->transfer_settings.write_idx = 0;
|
|
data->transfer_settings.empty_tcds = CONFIG_DMA_TCD_QUEUE_SIZE;
|
|
|
|
EDMA_PrepareTransfer(
|
|
&data->transferConfig, (void *)block_config->source_address,
|
|
config->source_data_size, (void *)block_config->dest_address,
|
|
config->dest_data_size, config->source_burst_length,
|
|
block_config->block_size, transfer_type);
|
|
|
|
/* Init all TCDs with the para in transfer config and link them. */
|
|
for (int i = 0; i < CONFIG_DMA_TCD_QUEUE_SIZE; i++) {
|
|
EDMA_TcdSetTransferConfig(
|
|
&DEV_CFG(dev)->tcdpool[channel][i], &data->transferConfig,
|
|
&DEV_CFG(dev)->tcdpool[channel][(i + 1) %
|
|
CONFIG_DMA_TCD_QUEUE_SIZE]);
|
|
|
|
/* Enable Major loop interrupt.*/
|
|
EDMA_TcdEnableInterrupts(&DEV_CFG(dev)->tcdpool[channel][i],
|
|
kEDMA_MajorInterruptEnable);
|
|
}
|
|
|
|
/* Load valid transfer parameters */
|
|
while (block_config != NULL && data->transfer_settings.empty_tcds > 0) {
|
|
tcd = &(DEV_CFG(dev)->tcdpool[channel]
|
|
[data->transfer_settings.write_idx]);
|
|
|
|
EDMA_TCD_SADDR(tcd, kEDMA_EDMA4Flag) = block_config->source_address;
|
|
EDMA_TCD_DADDR(tcd, kEDMA_EDMA4Flag) = block_config->dest_address;
|
|
EDMA_TCD_BITER(tcd, kEDMA_EDMA4Flag) =
|
|
block_config->block_size / config->source_data_size;
|
|
EDMA_TCD_CITER(tcd, kEDMA_EDMA4Flag) =
|
|
block_config->block_size / config->source_data_size;
|
|
/*Enable auto stop for last transfer.*/
|
|
if (block_config->next_block == NULL) {
|
|
EDMA_TCD_CSR(tcd, kEDMA_EDMA4Flag) |= DMA_CSR_DREQ(1U);
|
|
} else {
|
|
EDMA_TCD_CSR(tcd, kEDMA_EDMA4Flag) &= ~DMA_CSR_DREQ(1U);
|
|
}
|
|
|
|
data->transfer_settings.write_idx =
|
|
(data->transfer_settings.write_idx + 1) %
|
|
CONFIG_DMA_TCD_QUEUE_SIZE;
|
|
data->transfer_settings.empty_tcds--;
|
|
block_config = block_config->next_block;
|
|
}
|
|
|
|
if (block_config != NULL && data->transfer_settings.empty_tcds == 0) {
|
|
/* User input more blocks than TCD number, return error */
|
|
LOG_ERR("Too much request blocks,increase TCD buffer size!");
|
|
ret = -ENOBUFS;
|
|
}
|
|
/* Push the 1st TCD into HW */
|
|
EDMA_InstallTCD(p_handle->base, hw_channel,
|
|
&DEV_CFG(dev)->tcdpool[channel][0]);
|
|
|
|
} else {
|
|
/* Dynamic Scatter Gather mode */
|
|
EDMA_InstallTCDMemory(p_handle, DEV_CFG(dev)->tcdpool[channel],
|
|
CONFIG_DMA_TCD_QUEUE_SIZE);
|
|
|
|
while (block_config != NULL) {
|
|
EDMA_PrepareTransfer(&(data->transferConfig),
|
|
(void *)block_config->source_address,
|
|
config->source_data_size,
|
|
(void *)block_config->dest_address,
|
|
config->dest_data_size,
|
|
config->source_burst_length,
|
|
block_config->block_size, transfer_type);
|
|
|
|
const status_t submit_status =
|
|
EDMA_SubmitTransfer(p_handle, &(data->transferConfig));
|
|
if (submit_status != kStatus_Success) {
|
|
LOG_ERR("Error submitting EDMA Transfer: 0x%x",
|
|
submit_status);
|
|
ret = -EFAULT;
|
|
}
|
|
block_config = block_config->next_block;
|
|
}
|
|
}
|
|
} else {
|
|
/* block_count shall be 1 */
|
|
LOG_DBG("block size is: %d", block_config->block_size);
|
|
EDMA_PrepareTransfer(&(data->transferConfig),
|
|
(void *)block_config->source_address,
|
|
config->source_data_size,
|
|
(void *)block_config->dest_address,
|
|
config->dest_data_size,
|
|
config->source_burst_length,
|
|
block_config->block_size, transfer_type);
|
|
|
|
const status_t submit_status =
|
|
EDMA_SubmitTransfer(p_handle, &(data->transferConfig));
|
|
if (submit_status != kStatus_Success) {
|
|
LOG_ERR("Error submitting EDMA Transfer: 0x%x", submit_status);
|
|
ret = -EFAULT;
|
|
}
|
|
|
|
LOG_DBG("DMA TCD CSR 0x%x", EDMA_HW_TCD_CSR(dev, hw_channel));
|
|
}
|
|
|
|
if (config->dest_chaining_en) {
|
|
LOG_DBG("link major channel %d", config->linked_channel);
|
|
EDMA_SetChannelLink(DEV_BASE(dev), channel, kEDMA_MajorLink,
|
|
config->linked_channel);
|
|
}
|
|
if (config->source_chaining_en) {
|
|
LOG_DBG("link minor channel %d", config->linked_channel);
|
|
EDMA_SetChannelLink(DEV_BASE(dev), channel, kEDMA_MinorLink,
|
|
config->linked_channel);
|
|
}
|
|
|
|
data->busy = false;
|
|
if (config->dma_callback) {
|
|
LOG_DBG("INSTALL call back on channel %d", channel);
|
|
data->user_data = config->user_data;
|
|
data->dma_callback = config->dma_callback;
|
|
data->dev = dev;
|
|
}
|
|
|
|
irq_unlock(key);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int dma_mcux_edma_start(const struct device *dev, uint32_t channel)
|
|
{
|
|
struct call_back *data = DEV_CHANNEL_DATA(dev, channel);
|
|
|
|
LOG_DBG("START TRANSFER");
|
|
|
|
#if defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT
|
|
uint8_t dmamux_idx = DEV_DMAMUX_IDX(dev, channel);
|
|
uint8_t dmamux_channel = DEV_DMAMUX_CHANNEL(dev, channel);
|
|
|
|
LOG_DBG("DMAMUX CHCFG 0x%x", DEV_DMAMUX_BASE(dev, dmamux_idx)->CHCFG[dmamux_channel]);
|
|
#endif
|
|
|
|
#if !defined(CONFIG_DMA_MCUX_EDMA_V3) && !defined(CONFIG_DMA_MCUX_EDMA_V4)
|
|
LOG_DBG("DMA CR 0x%x", DEV_BASE(dev)->CR);
|
|
#endif
|
|
data->busy = true;
|
|
EDMA_StartTransfer(DEV_EDMA_HANDLE(dev, channel));
|
|
return 0;
|
|
}
|
|
|
|
static int dma_mcux_edma_stop(const struct device *dev, uint32_t channel)
|
|
{
|
|
struct dma_mcux_edma_data *data = DEV_DATA(dev);
|
|
uint32_t hw_channel;
|
|
|
|
hw_channel = dma_mcux_edma_add_channel_gap(dev, channel);
|
|
|
|
data->data_cb[channel].transfer_settings.valid = false;
|
|
|
|
if (!data->data_cb[channel].busy) {
|
|
return 0;
|
|
}
|
|
|
|
EDMA_AbortTransfer(DEV_EDMA_HANDLE(dev, channel));
|
|
EDMA_ClearChannelStatusFlags(DEV_BASE(dev), hw_channel,
|
|
kEDMA_DoneFlag | kEDMA_ErrorFlag |
|
|
kEDMA_InterruptFlag);
|
|
EDMA_ResetChannel(DEV_BASE(dev), hw_channel);
|
|
data->data_cb[channel].busy = false;
|
|
return 0;
|
|
}
|
|
|
|
static int dma_mcux_edma_suspend(const struct device *dev, uint32_t channel)
|
|
{
|
|
struct call_back *data = DEV_CHANNEL_DATA(dev, channel);
|
|
|
|
if (!data->busy) {
|
|
return -EINVAL;
|
|
}
|
|
EDMA_StopTransfer(DEV_EDMA_HANDLE(dev, channel));
|
|
return 0;
|
|
}
|
|
|
|
static int dma_mcux_edma_resume(const struct device *dev, uint32_t channel)
|
|
{
|
|
struct call_back *data = DEV_CHANNEL_DATA(dev, channel);
|
|
|
|
if (!data->busy) {
|
|
return -EINVAL;
|
|
}
|
|
EDMA_StartTransfer(DEV_EDMA_HANDLE(dev, channel));
|
|
return 0;
|
|
}
|
|
|
|
static void dma_mcux_edma_update_hw_tcd(const struct device *dev, uint32_t channel, uint32_t src,
|
|
uint32_t dst, size_t size)
|
|
{
|
|
EDMA_HW_TCD_SADDR(dev, channel) = src;
|
|
EDMA_HW_TCD_DADDR(dev, channel) = dst;
|
|
EDMA_HW_TCD_BITER(dev, channel) = size;
|
|
EDMA_HW_TCD_CITER(dev, channel) = size;
|
|
EDMA_HW_TCD_CSR(dev, channel) |= DMA_CSR_DREQ(1U);
|
|
}
|
|
|
|
static int dma_mcux_edma_reload(const struct device *dev, uint32_t channel,
|
|
uint32_t src, uint32_t dst, size_t size)
|
|
{
|
|
struct call_back *data = DEV_CHANNEL_DATA(dev, channel);
|
|
edma_tcd_t *tcd = NULL;
|
|
edma_tcd_t *pre_tcd = NULL;
|
|
uint32_t hw_id, sw_id;
|
|
uint8_t pre_idx;
|
|
|
|
/* Lock the channel configuration */
|
|
const unsigned int key = irq_lock();
|
|
int ret = 0;
|
|
|
|
if (!data->transfer_settings.valid) {
|
|
LOG_ERR("Invalid EDMA settings on initial config. Configure DMA before reload.");
|
|
ret = -EFAULT;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (data->transfer_settings.cyclic) {
|
|
if (data->transfer_settings.empty_tcds == 0) {
|
|
LOG_ERR("TCD list is full in loop mode.");
|
|
ret = -ENOBUFS;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Convert size into major loop count */
|
|
size = size / data->transfer_settings.dest_data_size;
|
|
|
|
/* Previous TCD index in circular list */
|
|
pre_idx = data->transfer_settings.write_idx - 1;
|
|
if (pre_idx >= CONFIG_DMA_TCD_QUEUE_SIZE)
|
|
pre_idx = CONFIG_DMA_TCD_QUEUE_SIZE - 1;
|
|
|
|
/* Configure a TCD for the transfer */
|
|
tcd = &(DEV_CFG(dev)->tcdpool[channel][data->transfer_settings.write_idx]);
|
|
pre_tcd = &(DEV_CFG(dev)->tcdpool[channel][pre_idx]);
|
|
|
|
EDMA_TCD_SADDR(tcd, kEDMA_EDMA4Flag) = src;
|
|
EDMA_TCD_DADDR(tcd, kEDMA_EDMA4Flag) = dst;
|
|
EDMA_TCD_BITER(tcd, kEDMA_EDMA4Flag) = size;
|
|
EDMA_TCD_CITER(tcd, kEDMA_EDMA4Flag) = size;
|
|
/* Enable automatically stop */
|
|
EDMA_TCD_CSR(tcd, kEDMA_EDMA4Flag) |= DMA_CSR_DREQ(1U);
|
|
sw_id = EDMA_TCD_DLAST_SGA(tcd, kEDMA_EDMA4Flag);
|
|
|
|
/* Block the peripheral's hardware request trigger to prevent
|
|
* starting the DMA before updating the TCDs. Make sure the
|
|
* code between EDMA_DisableChannelRequest() and
|
|
* EDMA_EnableChannelRequest() is minimum.
|
|
*/
|
|
EDMA_DisableChannelRequest(DEV_BASE(dev), channel);
|
|
|
|
/* Wait for the DMA to be inactive before updating the TCDs.
|
|
* The CSR[ACTIVE] bit will deassert quickly after the EDMA's
|
|
* minor loop burst completes.
|
|
*/
|
|
while (EDMA_HW_TCD_CSR(dev, channel) & EDMA_HW_TCD_CH_ACTIVE_MASK) {
|
|
;
|
|
}
|
|
|
|
/* Identify the current active TCD. Use DLAST_SGA as the HW ID */
|
|
hw_id = EDMA_GetNextTCDAddress(DEV_EDMA_HANDLE(dev, channel));
|
|
if (data->transfer_settings.empty_tcds >= CONFIG_DMA_TCD_QUEUE_SIZE ||
|
|
hw_id == sw_id) {
|
|
/* All transfers have been done.DMA is stopped automatically,
|
|
* invalid TCD has been loaded into the HW, update HW.
|
|
*/
|
|
dma_mcux_edma_update_hw_tcd(dev, channel, src, dst, size);
|
|
LOG_DBG("Transfer done,auto stop");
|
|
|
|
} else {
|
|
/* Previous TCD can automatically start this TCD.
|
|
* Enable the peripheral DMA request in the previous TCD
|
|
*/
|
|
EDMA_TCD_CSR(pre_tcd, kEDMA_EDMA4Flag) &= ~DMA_CSR_DREQ(1U);
|
|
|
|
if (data->transfer_settings.empty_tcds == CONFIG_DMA_TCD_QUEUE_SIZE - 1 ||
|
|
hw_id == (uint32_t)tcd) {
|
|
/* DMA is running on last transfer. HW has loaded the last one,
|
|
* we need ensure it's DREQ is cleared.
|
|
*/
|
|
EDMA_EnableAutoStopRequest(DEV_BASE(dev), channel, false);
|
|
LOG_DBG("Last transfer.");
|
|
}
|
|
LOG_DBG("Manu stop");
|
|
}
|
|
|
|
#ifdef CONFIG_DMA_MCUX_EDMA
|
|
/* It seems that there is HW issue which may cause ESG bit is cleared.
|
|
* This is a workaround. Clear the DONE bit before setting ESG bit.
|
|
*/
|
|
EDMA_ClearChannelStatusFlags(DEV_BASE(dev), channel, kEDMA_DoneFlag);
|
|
EDMA_HW_TCD_CSR(dev, channel) |= DMA_CSR_ESG_MASK;
|
|
#elif (CONFIG_DMA_MCUX_EDMA_V3 || CONFIG_DMA_MCUX_EDMA_V4)
|
|
/*We have not verified if this issue exist on V3/V4 HW, jut place a holder here. */
|
|
#endif
|
|
/* TCDs are configured. Resume DMA */
|
|
EDMA_EnableChannelRequest(DEV_BASE(dev), channel);
|
|
|
|
/* Update the write index and available TCD numbers. */
|
|
data->transfer_settings.write_idx =
|
|
(data->transfer_settings.write_idx + 1) % CONFIG_DMA_TCD_QUEUE_SIZE;
|
|
data->transfer_settings.empty_tcds--;
|
|
|
|
LOG_DBG("w_idx:%d no:%d(ch:%d)", data->transfer_settings.write_idx,
|
|
data->transfer_settings.empty_tcds, channel);
|
|
|
|
} else {
|
|
/* Dynamice Scatter/Gather mode:
|
|
* If the tcdPool is not in use (no s/g) then only a single TCD
|
|
* can be active at once.
|
|
*/
|
|
if (data->busy && data->edma_handle.tcdPool == NULL) {
|
|
LOG_ERR("EDMA busy. Wait until the transfer completes before reloading.");
|
|
ret = -EBUSY;
|
|
goto cleanup;
|
|
}
|
|
|
|
EDMA_PrepareTransfer(&(data->transferConfig), (void *)src,
|
|
data->transfer_settings.source_data_size, (void *)dst,
|
|
data->transfer_settings.dest_data_size,
|
|
data->transfer_settings.source_burst_length, size,
|
|
data->transfer_settings.transfer_type);
|
|
|
|
const status_t submit_status =
|
|
EDMA_SubmitTransfer(DEV_EDMA_HANDLE(dev, channel), &(data->transferConfig));
|
|
|
|
if (submit_status != kStatus_Success) {
|
|
LOG_ERR("Error submitting EDMA Transfer: 0x%x", submit_status);
|
|
ret = -EFAULT;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
irq_unlock(key);
|
|
return ret;
|
|
}
|
|
|
|
static int dma_mcux_edma_get_status(const struct device *dev, uint32_t channel,
|
|
struct dma_status *status)
|
|
{
|
|
uint32_t hw_channel = dma_mcux_edma_add_channel_gap(dev, channel);
|
|
|
|
if (DEV_CHANNEL_DATA(dev, channel)->busy) {
|
|
status->busy = true;
|
|
/* pending_length is in bytes. Multiply remaining major loop
|
|
* count by NBYTES for each minor loop
|
|
*/
|
|
status->pending_length =
|
|
EDMA_GetRemainingMajorLoopCount(DEV_BASE(dev), hw_channel) *
|
|
DEV_CHANNEL_DATA(dev, channel)->transfer_settings.source_data_size;
|
|
} else {
|
|
status->busy = false;
|
|
status->pending_length = 0;
|
|
}
|
|
status->dir = DEV_CHANNEL_DATA(dev, channel)->transfer_settings.direction;
|
|
|
|
#if defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT
|
|
uint8_t dmamux_idx = DEV_DMAMUX_IDX(dev, channel);
|
|
uint8_t dmamux_channel = DEV_DMAMUX_CHANNEL(dev, channel);
|
|
|
|
LOG_DBG("DMAMUX CHCFG 0x%x", DEV_DMAMUX_BASE(dev, dmamux_idx)->CHCFG[dmamux_channel]);
|
|
#endif
|
|
|
|
#if defined(CONFIG_DMA_MCUX_EDMA_V3) || defined(CONFIG_DMA_MCUX_EDMA_V4)
|
|
LOG_DBG("DMA MP_CSR 0x%x", DEV_BASE(dev)->MP_CSR);
|
|
LOG_DBG("DMA MP_ES 0x%x", DEV_BASE(dev)->MP_ES);
|
|
LOG_DBG("DMA CHx_ES 0x%x", DEV_BASE(dev)->CH[hw_channel].CH_ES);
|
|
LOG_DBG("DMA CHx_CSR 0x%x", DEV_BASE(dev)->CH[hw_channel].CH_CSR);
|
|
LOG_DBG("DMA CHx_ES 0x%x", DEV_BASE(dev)->CH[hw_channel].CH_ES);
|
|
LOG_DBG("DMA CHx_INT 0x%x", DEV_BASE(dev)->CH[hw_channel].CH_INT);
|
|
LOG_DBG("DMA TCD_CSR 0x%x", DEV_BASE(dev)->CH[hw_channel].TCD_CSR);
|
|
#else
|
|
LOG_DBG("DMA CR 0x%x", DEV_BASE(dev)->CR);
|
|
LOG_DBG("DMA INT 0x%x", DEV_BASE(dev)->INT);
|
|
LOG_DBG("DMA ERQ 0x%x", DEV_BASE(dev)->ERQ);
|
|
LOG_DBG("DMA ES 0x%x", DEV_BASE(dev)->ES);
|
|
LOG_DBG("DMA ERR 0x%x", DEV_BASE(dev)->ERR);
|
|
LOG_DBG("DMA HRS 0x%x", DEV_BASE(dev)->HRS);
|
|
LOG_DBG("data csr is 0x%x", DEV_BASE(dev)->TCD[hw_channel].CSR);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static bool dma_mcux_edma_channel_filter(const struct device *dev,
|
|
int channel_id, void *param)
|
|
{
|
|
enum dma_channel_filter *filter = (enum dma_channel_filter *)param;
|
|
|
|
if (filter && *filter == DMA_CHANNEL_PERIODIC) {
|
|
if (channel_id > 3) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static DEVICE_API(dma, dma_mcux_edma_api) = {
|
|
.reload = dma_mcux_edma_reload,
|
|
.config = dma_mcux_edma_configure,
|
|
.start = dma_mcux_edma_start,
|
|
.stop = dma_mcux_edma_stop,
|
|
.suspend = dma_mcux_edma_suspend,
|
|
.resume = dma_mcux_edma_resume,
|
|
.get_status = dma_mcux_edma_get_status,
|
|
.chan_filter = dma_mcux_edma_channel_filter,
|
|
};
|
|
|
|
static int dma_mcux_edma_init(const struct device *dev)
|
|
{
|
|
const struct dma_mcux_edma_config *config = dev->config;
|
|
struct dma_mcux_edma_data *data = dev->data;
|
|
|
|
edma_config_t userConfig = { 0 };
|
|
|
|
LOG_DBG("INIT NXP EDMA");
|
|
|
|
#if defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < config->dma_channels / config->channels_per_mux; i++) {
|
|
DMAMUX_Init(DEV_DMAMUX_BASE(dev, i));
|
|
}
|
|
#endif
|
|
|
|
EDMA_GetDefaultConfig(&userConfig);
|
|
EDMA_Init(DEV_BASE(dev), &userConfig);
|
|
#ifdef CONFIG_DMA_MCUX_EDMA_V3
|
|
/* Channel linking available and will be controlled by each channel's link settings */
|
|
EDMA_EnableAllChannelLink(DEV_BASE(dev), true);
|
|
#endif
|
|
config->irq_config_func(dev);
|
|
data->dma_ctx.magic = DMA_MAGIC;
|
|
data->dma_ctx.dma_channels = config->dma_channels;
|
|
data->dma_ctx.atomic = data->channels_atomic;
|
|
return 0;
|
|
}
|
|
|
|
/* The shared error interrupt (if have) must be declared as the last element in devicetree */
|
|
#if !DT_INST_PROP(0, no_error_irq)
|
|
#define NUM_IRQS_WITHOUT_ERROR_IRQ(n) UTIL_DEC(DT_NUM_IRQS(DT_DRV_INST(n)))
|
|
#else
|
|
#define NUM_IRQS_WITHOUT_ERROR_IRQ(n) DT_NUM_IRQS(DT_DRV_INST(n))
|
|
#endif
|
|
|
|
#define IRQ_CONFIG(n, idx, fn) \
|
|
{ \
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, idx, irq), \
|
|
DT_INST_IRQ_BY_IDX(n, idx, priority), \
|
|
fn, \
|
|
DEVICE_DT_INST_GET(n), 0); \
|
|
irq_enable(DT_INST_IRQ_BY_IDX(n, idx, irq)); \
|
|
}
|
|
|
|
#define EDMA_CHANNELS_MASK(n) static uint32_t edma_channel_mask_##n[] = \
|
|
DT_PROP(DT_DRV_INST(n), channels_shared_irq_mask);
|
|
|
|
#define GET_EDMA_CHANNEL_SHARED_IRQ_MASK_WIDTH(n) \
|
|
(DT_INST_PROP(n, dma_channels) / 32)
|
|
|
|
#define EDMA_CHANNELS_SHARED_REGISTER_IN_IRQ(dev, idx, n) \
|
|
dma_mcux_edma_multi_channels_irq_handler(dev, idx, edma_channel_mask_##n, \
|
|
GET_EDMA_CHANNEL_SHARED_IRQ_MASK_WIDTH(n));
|
|
|
|
#define DMA_MCUX_EDMA_IRQ_DEFINE(idx, n) \
|
|
static void dma_mcux_edma_##n##_irq_##idx(const struct device *dev) \
|
|
{ \
|
|
COND_CODE_1(DT_INST_NODE_HAS_PROP(n, channels_shared_irq_mask), \
|
|
(EDMA_CHANNELS_SHARED_REGISTER_IN_IRQ(dev, idx, n)), \
|
|
(dma_mcux_edma_irq_handler(dev, idx);)) \
|
|
\
|
|
IF_ENABLED(UTIL_BOOL(DT_INST_PROP(n, irq_shared_offset)), \
|
|
(dma_mcux_edma_irq_handler(dev, \
|
|
idx + DT_INST_PROP(n, irq_shared_offset));)) \
|
|
\
|
|
IF_ENABLED(CONFIG_CPU_CORTEX_M4, (barrier_dsync_fence_full();)) \
|
|
}
|
|
|
|
#define DMA_MCUX_EDMA_IRQ_CONFIG(idx, n) \
|
|
IRQ_CONFIG(n, idx, dma_mcux_edma_##n##_irq_##idx)
|
|
|
|
#define DMA_MCUX_EDMA_CONFIG_FUNC(n) \
|
|
IF_ENABLED(DT_INST_NODE_HAS_PROP(n, channels_shared_irq_mask), \
|
|
(EDMA_CHANNELS_MASK(n))) \
|
|
LISTIFY(NUM_IRQS_WITHOUT_ERROR_IRQ(n), DMA_MCUX_EDMA_IRQ_DEFINE, (), n) \
|
|
static void dma_imx_config_func_##n(const struct device *dev) \
|
|
{ \
|
|
ARG_UNUSED(dev); \
|
|
\
|
|
LISTIFY(NUM_IRQS_WITHOUT_ERROR_IRQ(n), \
|
|
DMA_MCUX_EDMA_IRQ_CONFIG, (;), n) \
|
|
\
|
|
COND_CODE_1(DT_INST_PROP(n, no_error_irq), (), \
|
|
(IRQ_CONFIG(n, NUM_IRQS_WITHOUT_ERROR_IRQ(n), \
|
|
dma_mcux_edma_error_irq_handler))) \
|
|
\
|
|
LOG_DBG("install irq done"); \
|
|
}
|
|
|
|
#if DMA_MCUX_HAS_CHANNEL_GAP
|
|
#define DMA_MCUX_EDMA_CHANNEL_GAP(n) \
|
|
.channel_gap = DT_INST_PROP_OR(n, channel_gap, \
|
|
{[0 ... 1] = DT_INST_PROP(n, dma_channels)}),
|
|
#else
|
|
#define DMA_MCUX_EDMA_CHANNEL_GAP(n)
|
|
#endif
|
|
|
|
#if defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT
|
|
#define DMA_MCUX_EDMA_MUX(idx, n) \
|
|
(DMAMUX_Type *)DT_INST_REG_ADDR_BY_IDX(n, UTIL_INC(idx))
|
|
|
|
#define DMAMUX_BASE_INIT_DEFINE(n) \
|
|
static DMAMUX_Type *dmamux_base_##n[] = { \
|
|
LISTIFY(UTIL_DEC(DT_NUM_REGS(DT_DRV_INST(n))), \
|
|
DMA_MCUX_EDMA_MUX, (,), n) \
|
|
};
|
|
|
|
#define DMAMUX_BASE_INIT(n) .dmamux_base = &dmamux_base_##n[0],
|
|
#define CHANNELS_PER_MUX(n) .channels_per_mux = DT_INST_PROP(n, dma_channels) / \
|
|
ARRAY_SIZE(dmamux_base_##n),
|
|
|
|
#else
|
|
#define DMAMUX_BASE_INIT_DEFINE(n)
|
|
#define DMAMUX_BASE_INIT(n)
|
|
#define CHANNELS_PER_MUX(n)
|
|
#endif
|
|
|
|
/*
|
|
* define the dma
|
|
*/
|
|
#define DMA_INIT(n) \
|
|
DMAMUX_BASE_INIT_DEFINE(n) \
|
|
static void dma_imx_config_func_##n(const struct device *dev); \
|
|
static __aligned(32) EDMA_TCDPOOL_CACHE_ATTR edma_tcd_t \
|
|
dma_tcdpool##n[DT_INST_PROP(n, dma_channels)][CONFIG_DMA_TCD_QUEUE_SIZE];\
|
|
static const struct dma_mcux_edma_config dma_config_##n = { \
|
|
.base = (DMA_Type *)DT_INST_REG_ADDR(n), \
|
|
DMAMUX_BASE_INIT(n) \
|
|
.dma_requests = DT_INST_PROP(n, dma_requests), \
|
|
.dma_channels = DT_INST_PROP(n, dma_channels), \
|
|
CHANNELS_PER_MUX(n) \
|
|
.irq_config_func = dma_imx_config_func_##n, \
|
|
.dmamux_reg_offset = DT_INST_PROP(n, dmamux_reg_offset), \
|
|
DMA_MCUX_EDMA_CHANNEL_GAP(n) \
|
|
.tcdpool = dma_tcdpool##n, \
|
|
}; \
|
|
\
|
|
static struct call_back \
|
|
dma_data_callback_##n[DT_INST_PROP(n, dma_channels)]; \
|
|
static ATOMIC_DEFINE( \
|
|
dma_channels_atomic_##n, DT_INST_PROP(n, dma_channels)); \
|
|
static struct dma_mcux_edma_data dma_data_##n = { \
|
|
.data_cb = dma_data_callback_##n, \
|
|
.channels_atomic = dma_channels_atomic_##n, \
|
|
}; \
|
|
\
|
|
DEVICE_DT_INST_DEFINE(n, \
|
|
&dma_mcux_edma_init, NULL, \
|
|
&dma_data_##n, &dma_config_##n, \
|
|
PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, \
|
|
&dma_mcux_edma_api); \
|
|
\
|
|
DMA_MCUX_EDMA_CONFIG_FUNC(n);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(DMA_INIT)
|