Commit 6abc5921 authored by Laurentiu Mihalcea's avatar Laurentiu Mihalcea Committed by Anas Nashif
Browse files

drivers: dma: Introduce driver for NXP's eDMA IP

This commit introduces a driver for NXP's eDMA IP.

The main reasons for introducing a new driver are the following:

	1) The HAL EDMA wrappers don't support well different
	eDMA versions (e.g: i.MX93 and i.MX8QM). As such, a new
	revision had to be introduced, thus requiring a new Zephyr
	driver.

	2) The eDMA versions found on i.MX93, i.MX8QM, and i.MX8QXP
	don't use the DMAMUX IP (instead, channel MUX-ing is performed
	through an eDMA register in the case of i.MX93).

Signed-off-by: default avatarLaurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
parent c3a185f4
Loading
Loading
Loading
Loading
+1 −0
Changes for drivers/dma/CMakeLists.txt: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -39,3 +39,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_SEDI dma_sedi.c)
zephyr_library_sources_ifdef(CONFIG_DMA_SMARTBOND   dma_smartbond.c)
zephyr_library_sources_ifdef(CONFIG_DMA_NXP_SOF_HOST_DMA dma_nxp_sof_host_dma.c)
zephyr_library_sources_ifdef(CONFIG_DMA_EMUL	    dma_emul.c)
zephyr_library_sources_ifdef(CONFIG_DMA_NXP_EDMA dma_nxp_edma.c)
+2 −0
Changes for drivers/dma/Kconfig: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -74,4 +74,6 @@ source "drivers/dma/Kconfig.nxp_sof_host_dma"

source "drivers/dma/Kconfig.emul"

source "drivers/dma/Kconfig.nxp_edma"

endif # DMA
+34 −0
Changes for drivers/dma/Kconfig.nxp_edma: 34 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Copyright 2024 NXP
# SPDX-License-Identifier: Apache-2.0

config DMA_NXP_EDMA
	bool "NXP enhanced Direct Memory Access (eDMA) driver"
	default y
	depends on DT_HAS_NXP_EDMA_ENABLED
	help
		Enable driver for NXP's eDMA IP.

if DMA_NXP_EDMA

config DMA_NXP_EDMA_ALIGN
	int "Alignment (in bytes) required for the transfers"
	default 8
	help
		Use this to set the alignment (in bytes)
		used by entities employing this driver to
		adjust the addresses and sizes of the memory
		regions involved in the transfer process.
		This value needs to match one of the possible
		values for SSIZE and DSIZE, otherwise the
		driver will return an error upon configuration.

config DMA_NXP_EDMA_ENABLE_HALFMAJOR_IRQ
	bool "Set if CPU should be interrupted when CITER = BITER / 2"
	default n
	help
		Enable this configuration if the CPU should be
		interrupted when CITER = BITER / 2. Using this,
		the CPU will be interrupted when CITER = BITER and
		when CITER = BITER / 2.

endif # DMA_NXP_EDMA
+669 −0
Changes for drivers/dma/dma_nxp_edma.c: 669 added lines, 0 removed lines.
Original line number Diff line number Diff line
/*
 * Copyright 2024 NXP
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "dma_nxp_edma.h"

/* TODO list:
 * 1) Support for requesting a specific channel.
 * 2) Support for checking if DMA transfer is pending when attempting config. (?)
 * 3) Support for error interrupt.
 * 4) Support for error if buffer overflow/underrun.
 * 5) Ideally, HALFMAJOR should be set on a per-channel basis not through a
 * config. If not possible, this should be done through a DTS property. Also,
 * maybe do the same for INTMAJOR IRQ.
 */

static void edma_isr(const void *parameter)
{
	const struct edma_config *cfg;
	struct edma_data *data;
	struct edma_channel *chan;
	int ret;
	uint32_t update_size;

	chan = (struct edma_channel *)parameter;
	cfg = chan->dev->config;
	data = chan->dev->data;

	if (!EDMA_ChannelRegRead(data->hal_cfg, chan->id, EDMA_TCD_CH_INT)) {
		/* skip, interrupt was probably triggered by another channel */
		return;
	}

	/* clear interrupt */
	EDMA_ChannelRegUpdate(data->hal_cfg, chan->id,
			      EDMA_TCD_CH_INT, EDMA_TCD_CH_INT_MASK, 0);

	if (chan->cyclic_buffer) {
		update_size = chan->bsize;

		if (IS_ENABLED(CONFIG_DMA_NXP_EDMA_ENABLE_HALFMAJOR_IRQ)) {
			update_size = chan->bsize / 2;
		} else {
			update_size = chan->bsize;
		}

		/* TODO: add support for error handling here */
		ret = EDMA_CHAN_PRODUCE_CONSUME_A(chan, update_size);
		if (ret < 0) {
			LOG_ERR("chan %d buffer overflow/underrun", chan->id);
		}
	}

	/* TODO: are there any sanity checks we have to perform before invoking
	 * the registered callback?
	 */
	if (chan->cb) {
		chan->cb(chan->dev, chan->arg, chan->id, DMA_STATUS_COMPLETE);
	}
}

static struct edma_channel *lookup_channel(const struct device *dev,
					   uint32_t chan_id)
{
	struct edma_data *data;
	const struct edma_config *cfg;
	int i;

	data = dev->data;
	cfg = dev->config;


	/* optimization: if dma-channels property is present then
	 * the channel data associated with the passed channel ID
	 * can be found at index chan_id in the array of channels.
	 */
	if (cfg->contiguous_channels) {
		/* check for index out of bounds */
		if (chan_id >= data->ctx.dma_channels) {
			return NULL;
		}

		return &data->channels[chan_id];
	}

	/* channels are passed through the valid-channels property.
	 * As such, since some channels may be missing we need to
	 * look through the entire channels array for an ID match.
	 */
	for (i = 0; i < data->ctx.dma_channels; i++) {
		if (data->channels[i].id == chan_id) {
			return &data->channels[i];
		}
	}

	return NULL;
}

static int edma_config(const struct device *dev, uint32_t chan_id,
		       struct dma_config *dma_cfg)
{
	struct edma_data *data;
	const struct edma_config *cfg;
	struct edma_channel *chan;
	uint32_t transfer_type;
	int ret;

	data = dev->data;
	cfg = dev->config;

	if (!dma_cfg->head_block) {
		LOG_ERR("head block shouldn't be NULL");
		return -EINVAL;
	}

	/* validate source data size (SSIZE) */
	if (!EDMA_TransferWidthIsValid(data->hal_cfg, dma_cfg->source_data_size)) {
		LOG_ERR("invalid source data size: %d",
			dma_cfg->source_data_size);
		return -EINVAL;
	}

	/* validate destination data size (DSIZE) */
	if (!EDMA_TransferWidthIsValid(data->hal_cfg, dma_cfg->dest_data_size)) {
		LOG_ERR("invalid destination data size: %d",
			dma_cfg->dest_data_size);
		return -EINVAL;
	}

	/* validate configured alignment */
	if (!EDMA_TransferWidthIsValid(data->hal_cfg, CONFIG_DMA_NXP_EDMA_ALIGN)) {
		LOG_ERR("configured alignment %d is invalid",
			CONFIG_DMA_NXP_EDMA_ALIGN);
		return -EINVAL;
	}

	/* Scatter-Gather configurations currently not supported */
	if (dma_cfg->block_count != 1) {
		LOG_ERR("number of blocks %d not supported", dma_cfg->block_count);
		return -ENOTSUP;
	}

	/* source address shouldn't be NULL */
	if (!dma_cfg->head_block->source_address) {
		LOG_ERR("source address cannot be NULL");
		return -EINVAL;
	}

	/* destination address shouldn't be NULL */
	if (!dma_cfg->head_block->dest_address) {
		LOG_ERR("destination address cannot be NULL");
		return -EINVAL;
	}

	/* check source address's (SADDR) alignment with respect to the data size (SSIZE)
	 *
	 * Failing to meet this condition will lead to the assertion of the SAE
	 * bit (see CHn_ES register).
	 *
	 * TODO: this will also restrict scenarios such as the following:
	 *	SADDR is 8B aligned and SSIZE is 16B. I've tested this
	 *	scenario and seems to raise no hardware errors (I'm assuming
	 *	because this doesn't break the 8B boundary of the 64-bit system
	 *	I tested it on). Is there a need to allow such a scenario?
	 */
	if (dma_cfg->head_block->source_address % dma_cfg->source_data_size) {
		LOG_ERR("source address 0x%x alignment doesn't match data size %d",
			dma_cfg->head_block->source_address,
			dma_cfg->source_data_size);
		return -EINVAL;
	}

	/* check destination address's (DADDR) alignment with respect to the data size (DSIZE)
	 * Failing to meet this condition will lead to the assertion of the DAE
	 * bit (see CHn_ES register).
	 */
	if (dma_cfg->head_block->dest_address % dma_cfg->dest_data_size) {
		LOG_ERR("destination address 0x%x alignment doesn't match data size %d",
			dma_cfg->head_block->dest_address,
			dma_cfg->dest_data_size);
		return -EINVAL;
	}

	/* source burst length should match destination burst length.
	 * This is because the burst length is the equivalent of NBYTES which
	 * is used for both the destination and the source.
	 */
	if (dma_cfg->source_burst_length !=
	    dma_cfg->dest_burst_length) {
		LOG_ERR("source burst length %d doesn't match destination burst length %d",
			dma_cfg->source_burst_length,
			dma_cfg->dest_burst_length);
		return -EINVAL;
	}

	/* total number of bytes should be a multiple of NBYTES.
	 *
	 * This is needed because the EDMA engine performs transfers based
	 * on CITER (integer value) and NBYTES, thus it has no knowledge of
	 * the total transfer size. If the total transfer size is not a
	 * multiple of NBYTES then we'll end up with copying a wrong number
	 * of bytes (CITER = TOTAL_SIZE / BITER). This, of course, raises
	 * no error in the hardware but it's still wrong.
	 */
	if (dma_cfg->head_block->block_size % dma_cfg->source_burst_length) {
		LOG_ERR("block size %d should be a multiple of NBYTES %d",
			dma_cfg->head_block->block_size,
			dma_cfg->source_burst_length);
		return -EINVAL;
	}

	/* check if NBYTES is a multiple of MAX(SSIZE, DSIZE).
	 *
	 * This stems from the fact that NBYTES needs to be a multiple
	 * of SSIZE AND DSIZE. If NBYTES is a multiple of MAX(SSIZE, DSIZE)
	 * then it will for sure satisfy the aforementioned condition (since
	 * SSIZE and DSIZE are powers of 2).
	 *
	 * Failing to meet this condition will lead to the assertion of the
	 * NCE bit (see CHn_ES register).
	 */
	if (dma_cfg->source_burst_length %
	    MAX(dma_cfg->source_data_size, dma_cfg->dest_data_size)) {
		LOG_ERR("NBYTES %d should be a multiple of MAX(SSIZE(%d), DSIZE(%d))",
			dma_cfg->source_burst_length,
			dma_cfg->source_data_size,
			dma_cfg->dest_data_size);
		return -EINVAL;
	}

	/* fetch channel data */
	chan = lookup_channel(dev, chan_id);
	if (!chan) {
		LOG_ERR("channel ID %u is not valid", chan_id);
		return -EINVAL;
	}

	/* save the block size for later usage in edma_reload */
	chan->bsize = dma_cfg->head_block->block_size;

	if (dma_cfg->cyclic) {
		chan->cyclic_buffer = true;

		chan->stat.read_position = 0;
		chan->stat.write_position = 0;

		/* ASSUMPTION: for CONSUMER-type channels, the buffer from
		 * which the engine consumes should be full, while in the
		 * case of PRODUCER-type channels it should be empty.
		 */
		switch (dma_cfg->channel_direction) {
		case MEMORY_TO_PERIPHERAL:
			chan->type = CHAN_TYPE_CONSUMER;
			chan->stat.free = 0;
			chan->stat.pending_length = chan->bsize;
			break;
		case PERIPHERAL_TO_MEMORY:
			chan->type = CHAN_TYPE_PRODUCER;
			chan->stat.pending_length = 0;
			chan->stat.free = chan->bsize;
			break;
		default:
			LOG_ERR("unsupported transfer dir %d for cyclic mode",
				dma_cfg->channel_direction);
			return -ENOTSUP;
		}
	} else {
		chan->cyclic_buffer = false;
	}

	/* change channel's state to CONFIGURED */
	ret = channel_change_state(chan, CHAN_STATE_CONFIGURED);
	if (ret < 0) {
		LOG_ERR("failed to change channel %d state to CONFIGURED", chan_id);
		return ret;
	}

	ret = get_transfer_type(dma_cfg->channel_direction, &transfer_type);
	if (ret < 0) {
		return ret;
	}

	chan->cb = dma_cfg->dma_callback;
	chan->arg = dma_cfg->user_data;

	/* warning: this sets SOFF and DOFF to SSIZE and DSIZE which are POSITIVE. */
	ret = EDMA_ConfigureTransfer(data->hal_cfg, chan_id,
				     dma_cfg->head_block->source_address,
				     dma_cfg->head_block->dest_address,
				     dma_cfg->source_data_size,
				     dma_cfg->dest_data_size,
				     dma_cfg->source_burst_length,
				     dma_cfg->head_block->block_size,
				     transfer_type);
	if (ret < 0) {
		LOG_ERR("failed to configure transfer");
		return to_std_error(ret);
	}

	/* TODO: channel MUX should be forced to 0 based on the previous state */
	if (EDMA_HAS_MUX(data->hal_cfg)) {
		ret = EDMA_SetChannelMux(data->hal_cfg, chan_id, dma_cfg->dma_slot);
		if (ret < 0) {
			LOG_ERR("failed to set channel MUX");
			return to_std_error(ret);
		}
	}

	/* set SLAST and DLAST */
	ret = set_slast_dlast(dma_cfg, transfer_type, data, chan_id);
	if (ret < 0) {
		return ret;
	}

	/* allow interrupting the CPU when a major cycle is completed.
	 *
	 * interesting note: only 1 major loop is performed per slave peripheral
	 * DMA request. For instance, if block_size = 768 and burst_size = 192
	 * we're going to get 4 transfers of 192 bytes. Each of these transfers
	 * translates to a DMA request made by the slave peripheral.
	 */
	EDMA_ChannelRegUpdate(data->hal_cfg, chan_id,
			      EDMA_TCD_CSR, EDMA_TCD_CSR_INTMAJOR_MASK, 0);

	if (IS_ENABLED(CONFIG_DMA_NXP_EDMA_ENABLE_HALFMAJOR_IRQ)) {
		/* if enabled through the above configuration, also
		 * allow the CPU to be interrupted when CITER = BITER / 2.
		 */
		EDMA_ChannelRegUpdate(data->hal_cfg, chan_id, EDMA_TCD_CSR,
				      EDMA_TCD_CSR_INTHALF_MASK, 0);
	}

	/* enable channel interrupt */
	irq_enable(chan->irq);

	/* dump register status - for debugging purposes */
	edma_dump_channel_registers(data, chan_id);

	return 0;
}

static int edma_get_status(const struct device *dev, uint32_t chan_id,
			   struct dma_status *stat)
{
	struct edma_data *data;
	struct edma_channel *chan;
	uint32_t citer, biter, done;
	unsigned int key;

	data = dev->data;

	/* fetch channel data */
	chan = lookup_channel(dev, chan_id);
	if (!chan) {
		LOG_ERR("channel ID %u is not valid", chan_id);
		return -EINVAL;
	}

	if (chan->cyclic_buffer) {
		key = irq_lock();

		stat->free = chan->stat.free;
		stat->pending_length = chan->stat.pending_length;

		irq_unlock(key);
	} else {
		/* note: no locking required here. The DMA interrupts
		 * have no effect over CITER and BITER.
		 */
		citer = EDMA_ChannelRegRead(data->hal_cfg, chan_id, EDMA_TCD_CITER);
		biter = EDMA_ChannelRegRead(data->hal_cfg, chan_id, EDMA_TCD_BITER);
		done = EDMA_ChannelRegRead(data->hal_cfg, chan_id, EDMA_TCD_CH_CSR) &
			EDMA_TCD_CH_CSR_DONE_MASK;
		if (done) {
			stat->free = chan->bsize;
			stat->pending_length = 0;
		} else {
			stat->free = (biter - citer) * (chan->bsize / biter);
			stat->pending_length = chan->bsize - stat->free;
		}
	}

	LOG_DBG("free: %d, pending: %d", stat->free, stat->pending_length);

	return 0;
}

static int edma_suspend(const struct device *dev, uint32_t chan_id)
{
	struct edma_data *data;
	const struct edma_config *cfg;
	struct edma_channel *chan;
	int ret;

	data = dev->data;
	cfg = dev->config;

	/* fetch channel data */
	chan = lookup_channel(dev, chan_id);
	if (!chan) {
		LOG_ERR("channel ID %u is not valid", chan_id);
		return -EINVAL;
	}

	edma_dump_channel_registers(data, chan_id);

	/* change channel's state to SUSPENDED */
	ret = channel_change_state(chan, CHAN_STATE_SUSPENDED);
	if (ret < 0) {
		LOG_ERR("failed to change channel %d state to SUSPENDED", chan_id);
		return ret;
	}

	LOG_DBG("suspending channel %u", chan_id);

	/* disable HW requests */
	EDMA_ChannelRegUpdate(data->hal_cfg, chan_id,
			      EDMA_TCD_CH_CSR, 0, EDMA_TCD_CH_CSR_ERQ_MASK);

	return 0;
}

static int edma_stop(const struct device *dev, uint32_t chan_id)
{
	struct edma_data *data;
	const struct edma_config *cfg;
	struct edma_channel *chan;
	enum channel_state prev_state;
	int ret;

	data = dev->data;
	cfg = dev->config;

	/* fetch channel data */
	chan = lookup_channel(dev, chan_id);
	if (!chan) {
		LOG_ERR("channel ID %u is not valid", chan_id);
		return -EINVAL;
	}

	prev_state = chan->state;

	/* change channel's state to STOPPED */
	ret = channel_change_state(chan, CHAN_STATE_STOPPED);
	if (ret < 0) {
		LOG_ERR("failed to change channel %d state to STOPPED", chan_id);
		return ret;
	}

	LOG_DBG("stopping channel %u", chan_id);

	if (prev_state == CHAN_STATE_SUSPENDED) {
		/* if the channel has been suspended then there's
		 * no point in disabling the HW requests again. Just
		 * jump to the channel release operation.
		 */
		goto out_release_channel;
	}

	/* disable HW requests */
	EDMA_ChannelRegUpdate(data->hal_cfg, chan_id, EDMA_TCD_CH_CSR, 0,
			      EDMA_TCD_CH_CSR_ERQ_MASK);
out_release_channel:

	/* clear the channel MUX so that it can used by a different peripheral.
	 *
	 * note: because the channel is released during dma_stop() that means
	 * dma_start() can no longer be immediately called. This is because
	 * one needs to re-configure the channel MUX which can only be done
	 * through dma_config(). As such, if one intends to reuse the current
	 * configuration then please call dma_suspend() instead of dma_stop().
	 */
	if (EDMA_HAS_MUX(data->hal_cfg)) {
		ret = EDMA_SetChannelMux(data->hal_cfg, chan_id, 0);
		if (ret < 0) {
			LOG_ERR("failed to set channel MUX");
			return to_std_error(ret);
		}
	}

	edma_dump_channel_registers(data, chan_id);

	return 0;
}

static int edma_start(const struct device *dev, uint32_t chan_id)
{
	struct edma_data *data;
	const struct edma_config *cfg;
	struct edma_channel *chan;
	int ret;

	data = dev->data;
	cfg = dev->config;

	/* fetch channel data */
	chan = lookup_channel(dev, chan_id);
	if (!chan) {
		LOG_ERR("channel ID %u is not valid", chan_id);
		return -EINVAL;
	}

	/* change channel's state to STARTED */
	ret = channel_change_state(chan, CHAN_STATE_STARTED);
	if (ret < 0) {
		LOG_ERR("failed to change channel %d state to STARTED", chan_id);
		return ret;
	}

	LOG_DBG("starting channel %u", chan_id);

	/* enable HW requests */
	EDMA_ChannelRegUpdate(data->hal_cfg, chan_id,
			      EDMA_TCD_CH_CSR, EDMA_TCD_CH_CSR_ERQ_MASK, 0);

	return 0;
}

static int edma_reload(const struct device *dev, uint32_t chan_id, uint32_t src,
		       uint32_t dst, size_t size)
{
	struct edma_data *data;
	struct edma_channel *chan;
	int ret;
	unsigned int key;

	data = dev->data;

	/* fetch channel data */
	chan = lookup_channel(dev, chan_id);
	if (!chan) {
		LOG_ERR("channel ID %u is not valid", chan_id);
		return -EINVAL;
	}

	/* channel needs to be started to allow reloading */
	if (chan->state != CHAN_STATE_STARTED) {
		LOG_ERR("reload is only supported on started channels");
		return -EINVAL;
	}

	if (chan->cyclic_buffer) {
		key = irq_lock();
		ret = EDMA_CHAN_PRODUCE_CONSUME_B(chan, size);
		irq_unlock(key);
		if (ret < 0) {
			LOG_ERR("chan %d buffer overflow/underrun", chan_id);
			return ret;
		}
	}

	return 0;
}

static int edma_get_attribute(const struct device *dev, uint32_t type, uint32_t *val)
{
	switch (type) {
	case DMA_ATTR_BUFFER_SIZE_ALIGNMENT:
	case DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT:
		*val = CONFIG_DMA_NXP_EDMA_ALIGN;
		break;
	case DMA_ATTR_MAX_BLOCK_COUNT:
		/* this is restricted to 1 because SG configurations are not supported */
		*val = 1;
		break;
	default:
		LOG_ERR("invalid attribute type: %d", type);
		return -EINVAL;
	}

	return 0;
}

static const struct dma_driver_api edma_api = {
	.reload = edma_reload,
	.config = edma_config,
	.start = edma_start,
	.stop = edma_stop,
	.suspend = edma_suspend,
	.resume = edma_start,
	.get_status = edma_get_status,
	.get_attribute = edma_get_attribute,
};

static int edma_init(const struct device *dev)
{
	const struct edma_config *cfg;
	struct edma_data *data;
	mm_reg_t regmap;

	data = dev->data;
	cfg = dev->config;

	/* map instance MMIO */
	device_map(&regmap, cfg->regmap_phys, cfg->regmap_size, K_MEM_CACHE_NONE);

	/* overwrite physical address set in the HAL configuration.
	 * We can down-cast the virtual address to a 32-bit address because
	 * we know we're working with 32-bit addresses only.
	 */
	data->hal_cfg->regmap = (uint32_t)POINTER_TO_UINT(regmap);

	cfg->irq_config();

	/* dma_request_channel() uses this variable to keep track of the
	 * available channels. As such, it needs to be initialized with NULL
	 * which signifies that all channels are initially available.
	 */
	data->channel_flags = ATOMIC_INIT(0);
	data->ctx.atomic = &data->channel_flags;

	return 0;
}

/* a few comments about the BUILD_ASSERT statements:
 *	1) dma-channels and valid-channels should be mutually exclusive.
 *	This means that you specify the one or the other. There's no real
 *	need to have both of them.
 *	2) Number of channels should match the number of interrupts for
 *	said channels (TODO: what about error interrupts?)
 *	3) The channel-mux property shouldn't be specified unless
 *	the eDMA is MUX-capable (signaled via the EDMA_HAS_CHAN_MUX
 *	configuration).
 */
#define EDMA_INIT(inst)								\
										\
BUILD_ASSERT(!DT_NODE_HAS_PROP(DT_INST(inst, DT_DRV_COMPAT), dma_channels) ||	\
	     !DT_NODE_HAS_PROP(DT_INST(inst, DT_DRV_COMPAT), valid_channels),	\
	     "dma_channels and valid_channels are mutually exclusive");		\
										\
BUILD_ASSERT(DT_INST_PROP_OR(inst, dma_channels, 0) ==				\
	     DT_NUM_IRQS(DT_INST(inst, DT_DRV_COMPAT)) ||			\
	     DT_INST_PROP_LEN_OR(inst, valid_channels, 0) ==			\
	     DT_NUM_IRQS(DT_INST(inst, DT_DRV_COMPAT)),				\
	     "number of interrupts needs to match number of channels");		\
										\
BUILD_ASSERT(DT_PROP_OR(DT_INST(inst, DT_DRV_COMPAT), hal_cfg_index, 0) <	\
	     ARRAY_SIZE(s_edmaConfigs),						\
	     "HAL configuration index out of bounds");				\
										\
static struct edma_channel channels_##inst[] = EDMA_CHANNEL_ARRAY_GET(inst);	\
										\
static void interrupt_config_function_##inst(void)				\
{										\
	EDMA_CONNECT_INTERRUPTS(inst);						\
}										\
										\
static struct edma_config edma_config_##inst = {				\
	.regmap_phys = DT_INST_REG_ADDR(inst),					\
	.regmap_size = DT_INST_REG_SIZE(inst),					\
	.irq_config = interrupt_config_function_##inst,				\
	.contiguous_channels = EDMA_CHANS_ARE_CONTIGUOUS(inst),			\
};										\
										\
static struct edma_data edma_data_##inst = {					\
	.channels = channels_##inst,						\
	.ctx.dma_channels = ARRAY_SIZE(channels_##inst),			\
	.ctx.magic = DMA_MAGIC,							\
	.hal_cfg = &EDMA_HAL_CFG_GET(inst),					\
};										\
										\
DEVICE_DT_INST_DEFINE(inst, &edma_init, NULL,					\
		      &edma_data_##inst, &edma_config_##inst,			\
		      PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY,			\
		      &edma_api);						\

DT_INST_FOREACH_STATUS_OKAY(EDMA_INIT);
+530 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading