Repository: zephyrproject-rtos/zephyr
Pull request #84519: Add DMA support to TI cc23x0 SoC
Author: login: jpanisbl
Labels: name: area: DMA
description: Direct Memory Access
name: platform: TI SimpleLink
description: Texas Instruments SimpleLink MCU
Created: 2025-01-24T15:40:28Z | Merged: 2025-03-31T06:05:53Z

This series adds DMA support to TI cc23x0 SoC.

Datasheet: https://www.ti.com/lit/ds/symlink/cc2340r5.pdf



Diff:
diff --git a/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5.dts b/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5.dts
index 0b138441ddb88..698820d81c373 100644
--- a/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5.dts
+++ b/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5.dts
@@ -77,3 +77,7 @@
 &aes {
 	status = "okay";
 };
+
+&dma {
+	status = "okay";
+};
diff --git a/drivers/dma/CMakeLists.txt b/drivers/dma/CMakeLists.txt
index 43f7a87a247af..4a41780d14433 100644
--- a/drivers/dma/CMakeLists.txt
+++ b/drivers/dma/CMakeLists.txt
@@ -50,3 +50,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_DW_AXI		dma_dw_axi.c)
 zephyr_library_sources_ifdef(CONFIG_DMA_XILINX_AXI_DMA		dma_xilinx_axi_dma.c)
 zephyr_library_sources_ifdef(CONFIG_DMA_NXP_SDMA dma_nxp_sdma.c)
 zephyr_library_sources_ifdef(CONFIG_DMA_WCH             dma_wch.c)
+zephyr_library_sources_ifdef(CONFIG_DMA_TI_CC23X0	dma_ti_cc23x0.c)
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index f89364d459f17..782722cfd2ea4 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -93,4 +93,6 @@ source "drivers/dma/Kconfig.nxp_sdma"
 
 source "drivers/dma/Kconfig.wch"
 
+source "drivers/dma/Kconfig.ti_cc23x0"
+
 endif # DMA
diff --git a/drivers/dma/Kconfig.ti_cc23x0 b/drivers/dma/Kconfig.ti_cc23x0
new file mode 100644
index 0000000000000..eeac23f0b2718
--- /dev/null
+++ b/drivers/dma/Kconfig.ti_cc23x0
@@ -0,0 +1,11 @@
+# TI CC23X0 DMA configuration options
+
+# Copyright (c) 2024 BayLibre, SAS
+# SPDX-License-Identifier: Apache-2.0
+
+config DMA_TI_CC23X0
+	bool "TI CC23X0 DMA driver"
+	default y
+	depends on DT_HAS_TI_CC23X0_DMA_ENABLED
+	help
+	  DMA driver for TI CC23X0 MCUs.
diff --git a/drivers/dma/dma_ti_cc23x0.c b/drivers/dma/dma_ti_cc23x0.c
new file mode 100644
index 0000000000000..5b1a6c7228332
--- /dev/null
+++ b/drivers/dma/dma_ti_cc23x0.c
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2024 BayLibre, SAS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ti_cc23x0_dma
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(dma_cc23x0, CONFIG_DMA_LOG_LEVEL);
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/dma.h>
+#include <zephyr/irq.h>
+#include <zephyr/sys/util.h>
+
+#include <driverlib/clkctl.h>
+#include <driverlib/udma.h>
+
+#include <inc/hw_evtsvt.h>
+#include <inc/hw_memmap.h>
+#include <inc/hw_types.h>
+
+/*
+ * Channels 0 to 5 are assigned to peripherals.
+ * Channels 6 and 7 are assigned to SW-initiated transfers.
+ */
+#define DMA_CC23_PERIPH_CH_MAX	5
+#define DMA_CC23_SW_CH_MIN	6
+#define DMA_CC23_SW_CH_MAX	7
+
+#define DMA_CC23_IS_SW_CH(ch)	((ch) >= DMA_CC23_SW_CH_MIN)
+
+/*
+ * In basic mode, the DMA controller performs transfers as long as there are more items
+ * to transfer, and a transfer request is present. This mode is used with peripherals that
+ * assert a DMA request signal whenever the peripheral is ready for a data transfer.
+ * Auto mode is similar to basic mode, except that when a transfer request is received,
+ * the transfer completes, even if the DMA request is removed. This mode is suitable for
+ * software-triggered transfers.
+ */
+#define DMA_CC23_MODE(ch)	(DMA_CC23_IS_SW_CH(ch) ? UDMA_MODE_AUTO : UDMA_MODE_BASIC)
+
+/* Each DMA channel is multiplexed between two peripherals which ID is in range 0 to 7 */
+#define DMA_CC23_IPID_MASK	GENMASK(2, 0)
+#define DMA_CC23_CHXSEL_REG(ch)	HWREG(EVTSVT_BASE + EVTSVT_O_DMACH0SEL + sizeof(uint32_t) * (ch))
+
+struct dma_cc23x0_channel {
+	uint8_t data_size;
+	dma_callback_t cb;
+	void *user_data;
+};
+
+struct dma_cc23x0_data {
+	__aligned(1024) uDMAControlTableEntry desc[UDMA_NUM_CHANNELS];
+	struct dma_cc23x0_channel channels[UDMA_NUM_CHANNELS];
+};
+
+/*
+ * If the channel is a software channel, then the completion will be signaled
+ * on this DMA dedicated interrupt.
+ * If a peripheral channel is used, then the completion will be signaled on the
+ * peripheral's interrupt.
+ */
+static void dma_cc23x0_isr(const struct device *dev)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	uint32_t done_flags;
+	int i;
+
+	done_flags = uDMAIntStatus();
+
+	for (i = DMA_CC23_SW_CH_MIN; i <= DMA_CC23_SW_CH_MAX; i++) {
+		if (!(done_flags & BIT(i))) {
+			continue;
+		}
+
+		LOG_DBG("DMA transfer completed on channel %u", i);
+
+		ch_data = &data->channels[i];
+		if (ch_data->cb) {
+			ch_data->cb(dev, ch_data->user_data, i, DMA_STATUS_COMPLETE);
+		}
+
+		uDMAClearInt(done_flags & BIT(i));
+	}
+}
+
+static uint32_t dma_cc23x0_set_addr_adj(uint32_t *control, uint16_t addr_adj,
+					uint32_t inc_flags, uint32_t no_inc_flags)
+{
+	switch (addr_adj) {
+	case DMA_ADDR_ADJ_INCREMENT:
+		*control |= inc_flags;
+		break;
+	case DMA_ADDR_ADJ_NO_CHANGE:
+		*control |= no_inc_flags;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int dma_cc23x0_config(const struct device *dev, uint32_t channel,
+			     struct dma_config *config)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	struct dma_block_config *block = config->head_block;
+	uint32_t control;
+	uint32_t data_size;
+	uint32_t src_inc_flags;
+	uint32_t dst_inc_flags;
+	uint32_t xfer_size;
+	uint32_t burst_len;
+	int ret;
+
+	if (channel >= UDMA_NUM_CHANNELS) {
+		LOG_ERR("Invalid channel (%u)", channel);
+		return -EINVAL;
+	}
+
+	if (config->dma_slot > DMA_CC23_IPID_MASK) {
+		LOG_ERR("Invalid trigger (%u)", config->dma_slot);
+		return -EINVAL;
+	}
+
+	if (config->block_count > 1) {
+		LOG_ERR("Chained transfers not supported");
+		return -ENOTSUP;
+	}
+
+	switch (config->source_data_size) {
+	case 1:
+		src_inc_flags = UDMA_SRC_INC_8;
+		break;
+	case 2:
+		src_inc_flags = UDMA_SRC_INC_16;
+		break;
+	case 4:
+		src_inc_flags = UDMA_SRC_INC_32;
+		break;
+	default:
+		LOG_ERR("Invalid source data size (%u)", config->source_data_size);
+		return -EINVAL;
+	}
+
+	switch (config->dest_data_size) {
+	case 1:
+		dst_inc_flags = UDMA_DST_INC_8;
+		break;
+	case 2:
+		dst_inc_flags = UDMA_DST_INC_16;
+		break;
+	case 4:
+		dst_inc_flags = UDMA_DST_INC_32;
+		break;
+	default:
+		LOG_ERR("Invalid destination data size (%u)", config->dest_data_size);
+		return -EINVAL;
+	}
+
+	data_size = MIN(config->source_data_size, config->dest_data_size);
+
+	switch (data_size) {
+	case 1:
+		control = UDMA_SIZE_8;
+		break;
+	case 2:
+		control = UDMA_SIZE_16;
+		break;
+	case 4:
+		control = UDMA_SIZE_32;
+		break;
+	default:
+		LOG_ERR("Invalid data size (%u)", data_size);
+		return -EINVAL;
+	}
+
+	ret = dma_cc23x0_set_addr_adj(&control, block->source_addr_adj,
+				      src_inc_flags, UDMA_SRC_INC_NONE);
+	if (ret) {
+		LOG_ERR("Invalid source address adjustment type (%u)", block->source_addr_adj);
+		return ret;
+	}
+
+	ret = dma_cc23x0_set_addr_adj(&control, block->dest_addr_adj,
+				      dst_inc_flags, UDMA_DST_INC_NONE);
+	if (ret) {
+		LOG_ERR("Invalid dest address adjustment type (%u)", block->dest_addr_adj);
+		return ret;
+	}
+
+	xfer_size = block->block_size / data_size;
+	if (!xfer_size || xfer_size > UDMA_XFER_SIZE_MAX) {
+		LOG_ERR("Invalid block size (%u - must be in range %u to %u)",
+			block->block_size, data_size, data_size * UDMA_XFER_SIZE_MAX);
+		return -EINVAL;
+	}
+
+	burst_len = config->source_burst_length / data_size;
+	if (burst_len <= UDMA_XFER_SIZE_MAX && IS_POWER_OF_TWO(burst_len)) {
+		control |= LOG2(burst_len) << UDMA_ARB_S;
+	} else {
+		LOG_ERR("Invalid burst length (%u - must be a power of 2 between %u and %u)",
+			config->source_burst_length, data_size, data_size * UDMA_XFER_SIZE_MAX);
+		return -EINVAL;
+	}
+
+	ch_data = &data->channels[channel];
+	ch_data->data_size = data_size;
+	ch_data->cb = config->dma_callback;
+	ch_data->user_data = config->user_data;
+
+	if (uDMAIsChannelEnabled(BIT(channel))) {
+		return -EBUSY;
+	}
+
+	if (DMA_CC23_IS_SW_CH(channel)) {
+		uDMAEnableSwEventInt(BIT(channel));
+	} else {
+		/* Select peripheral */
+		DMA_CC23_CHXSEL_REG(channel) = config->dma_slot;
+	}
+
+	uDMASetChannelControl(&data->desc[channel], control);
+
+	uDMASetChannelTransfer(&data->desc[channel], DMA_CC23_MODE(channel),
+			       (void *)block->source_address,
+			       (void *)block->dest_address,
+			       xfer_size);
+
+	LOG_DBG("Configured channel %u for %08x to %08x (%u bytes)",
+		channel,
+		block->source_address,
+		block->dest_address,
+		block->block_size);
+
+	return 0;
+}
+
+static int dma_cc23x0_start(const struct device *dev, uint32_t channel)
+{
+	if (uDMAIsChannelEnabled(BIT(channel))) {
+		return 0;
+	}
+
+	uDMAEnableChannel(BIT(channel));
+
+	if (DMA_CC23_IS_SW_CH(channel)) {
+		/* Request DMA channel to start a memory to memory transfer */
+		uDMARequestChannel(BIT(channel));
+	}
+
+	return 0;
+}
+
+static int dma_cc23x0_stop(const struct device *dev, uint32_t channel)
+{
+	uDMADisableChannel(BIT(channel));
+
+	return 0;
+}
+
+static int dma_cc23x0_reload(const struct device *dev, uint32_t channel,
+			     uint32_t src, uint32_t dst, size_t size)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data = &data->channels[channel];
+	uint32_t xfer_size = size / ch_data->data_size;
+
+	if (uDMAIsChannelEnabled(BIT(channel))) {
+		return -EBUSY;
+	}
+
+	uDMASetChannelTransfer(&data->desc[channel], DMA_CC23_MODE(channel),
+			       (void *)src, (void *)dst, xfer_size);
+
+	LOG_DBG("Reloaded channel %u for %08x to %08x (%u bytes)",
+		channel, src, dst, size);
+
+	return 0;
+}
+
+static int dma_cc23x0_get_status(const struct device *dev, uint32_t channel,
+				 struct dma_status *stat)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	uint8_t ch_sel;
+
+	if (channel >= UDMA_NUM_CHANNELS || !stat) {
+		return -EINVAL;
+	}
+
+	ch_sel = DMA_CC23_CHXSEL_REG(channel) & DMA_CC23_IPID_MASK;
+	switch (channel) {
+	case 0:
+		if (ch_sel == 0) {
+			/* spi0txtrg */
+			stat->dir = MEMORY_TO_PERIPHERAL;
+		} else {
+			/* uart0rxtrg */
+			stat->dir = PERIPHERAL_TO_MEMORY;
+		}
+		break;
+	case 1:
+		if (ch_sel == 1) {
+			/* spi0rxtrg */
+			stat->dir = PERIPHERAL_TO_MEMORY;
+		} else {
+			/* uart0txtrg */
+			stat->dir = MEMORY_TO_PERIPHERAL;
+		}
+		break;
+	case 2:
+	case 4:
+		/* ch2: uart0txtrg | ch4: laestrga */
+		stat->dir = MEMORY_TO_PERIPHERAL;
+		break;
+	case 3:
+	case 5:
+		/* ch3: adc0trg or uart0rxtrg | ch5: laestrgb or adc0trg */
+		stat->dir = PERIPHERAL_TO_MEMORY;
+		break;
+	default:
+		/* ch6, ch7: SW trg */
+		stat->dir = MEMORY_TO_MEMORY;
+		break;
+	}
+
+	stat->busy = uDMAIsChannelEnabled(BIT(channel));
+
+	stat->pending_length = uDMAGetChannelSize(&data->desc[channel]);
+	if (data->desc[channel].control & UDMA_SIZE_16) {
+		stat->pending_length <<= 1;
+	} else if (data->desc[channel].control & UDMA_SIZE_32) {
+		stat->pending_length <<= 2;
+	}
+
+	return 0;
+}
+
+static int dma_cc23x0_init(const struct device *dev)
+{
+	struct dma_cc23x0_data *data = dev->data;
+
+	IRQ_CONNECT(DT_INST_IRQN(0),
+		    DT_INST_IRQ(0, priority),
+		    dma_cc23x0_isr,
+		    DEVICE_DT_INST_GET(0),
+		    0);
+	irq_enable(DT_INST_IRQN(0));
+
+	/* Enable clock */
+	CLKCTLEnable(CLKCTL_BASE, CLKCTL_DMA);
+
+	/* Enable DMA */
+	uDMAEnable();
+
+	/* Set base address for channel control table (descriptors) */
+	uDMASetControlBase(data->desc);
+
+	return 0;
+}
+
+static struct dma_cc23x0_data cc23x0_data;
+
+static const struct dma_driver_api dma_cc23x0_api = {
+	.config = dma_cc23x0_config,
+	.start = dma_cc23x0_start,
+	.stop = dma_cc23x0_stop,
+	.reload = dma_cc23x0_reload,
+	.get_status = dma_cc23x0_get_status,
+};
+
+DEVICE_DT_INST_DEFINE(0, &dma_cc23x0_init, NULL,
+		      &cc23x0_data, NULL,
+		      PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY,
+		      &dma_cc23x0_api);
diff --git a/dts/arm/ti/cc23x0.dtsi b/dts/arm/ti/cc23x0.dtsi
index 71e418ea4face..47f105e815b57 100644
--- a/dts/arm/ti/cc23x0.dtsi
+++ b/dts/arm/ti/cc23x0.dtsi
@@ -86,6 +86,14 @@
 			interrupts = <9 0>;
 			status = "disabled";
 		};
+
+		dma: dma@40026000 {
+			compatible = "ti,cc23x0-dma";
+			reg = <0x40026000 0x524>;
+			interrupts = <8 0>;
+			#dma-cells = <2>;
+			status = "disabled";
+		};
 	};
 };
 
diff --git a/dts/bindings/dma/ti,cc23x0-dma.yaml b/dts/bindings/dma/ti,cc23x0-dma.yaml
new file mode 100644
index 0000000000000..5c7ddb206b7f8
--- /dev/null
+++ b/dts/bindings/dma/ti,cc23x0-dma.yaml
@@ -0,0 +1,38 @@
+# Copyright (c) 2024 BayLibre, SAS
+# SPDX-License-Identifier: Apache-2.0
+
+description: TI CC23X0 DMA controller
+
+compatible: "ti,cc23x0-dma"
+
+include: dma-controller.yaml
+
+properties:
+  reg:
+    required: true
+
+  interrupts:
+    required: true
+
+  "#dma-cells":
+    const: 2
+    description: |
+      The 1st cell specifies the DMA channel to be used for the data transfer.
+      This channel should be unique between all peripherals that are using the
+      DMA instance.
+      The 2nd cell defines the peripheral trigger which is the source of the transfer.
+      See the SoC's reference manual for all the supported request sources.
+
+      Example of devicetree DMA channel configuration for the client part:
+
+      &uart0 {
+          dmas = <&dma 0 7>, <&dma 1 6>;
+          dma-names = "rx", "tx";
+      };
+
+      Above, fragment 0 and 1 represent the different channels used to transfer
+      data between peripheral and RAM. The numbers 7/6 are uart0 rx/tx triggers.
+
+dma-cells:
+  - channel
+  - trigsrc


Reviews:
user: login: teburd
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T17:21:35Z
user: login: jpanisbl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T08:23:42Z
user: login: teburd
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T14:53:07Z
user: login: teburd
body: Ok the coffee kicked in, I see the start issue was fixed and I misread the code. Thank you!
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T15:23:36Z
user: login: fabiobaltieri
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-27T18:09:31Z
user: login: jpanisbl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T10:07:10Z
user: login: fabiobaltieri
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T10:09:05Z
user: login: jpanisbl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T10:23:21Z
user: login: fabiobaltieri
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-29T21:16:48Z
user: login: teburd
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-29T21:18:22Z

Review comments:
diff_hunk: @@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2024 BayLibre, SAS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ti_cc23x0_dma
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(dma_cc23x0, CONFIG_DMA_LOG_LEVEL);
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/dma.h>
+#include <zephyr/irq.h>
+#include <zephyr/sys/util.h>
+
+#include <driverlib/clkctl.h>
+#include <driverlib/udma.h>
+
+#include <inc/hw_evtsvt.h>
+#include <inc/hw_memmap.h>
+#include <inc/hw_types.h>
+
+/*
+ * Channels 0 to 5 are assigned to peripherals.
+ * Channels 6 and 7 are assigned to SW-initiated transfers.
+ */
+#define DMA_CC23_PERIPH_CH_MAX	5
+#define DMA_CC23_SW_CH_MIN	6
+#define DMA_CC23_SW_CH_MAX	7
+
+#define DMA_CC23_IS_SW_CH(ch)	((ch) >= DMA_CC23_SW_CH_MIN)
+
+/*
+ * In basic mode, the DMA controller performs transfers as long as there are more items
+ * to transfer, and a transfer request is present. This mode is used with peripherals that
+ * assert a DMA request signal whenever the peripheral is ready for a data transfer.
+ * Auto mode is similar to basic mode, except that when a transfer request is received,
+ * the transfer completes, even if the DMA request is removed. This mode is suitable for
+ * software-triggered transfers.
+ */
+#define DMA_CC23_MODE(ch)	(DMA_CC23_IS_SW_CH(ch) ? UDMA_MODE_AUTO : UDMA_MODE_BASIC)
+
+/* Each DMA channel is multiplexed between two peripherals which ID is in range 0 to 7 */
+#define DMA_CC23_IPID_MASK	GENMASK(2, 0)
+#define DMA_CC23_CHXSEL_REG(ch)	HWREG(EVTSVT_BASE + EVTSVT_O_DMACH0SEL + sizeof(uint32_t) * (ch))
+
+struct dma_cc23x0_channel {
+	uint8_t data_size;
+	dma_callback_t cb;
+	void *user_data;
+};
+
+struct dma_cc23x0_data {
+	__aligned(1024) uDMAControlTableEntry desc[UDMA_NUM_CHANNELS];
+	struct dma_cc23x0_channel channels[UDMA_NUM_CHANNELS];
+};
+
+/*
+ * If the channel is a software channel, then the completion will be signaled
+ * on this DMA dedicated interrupt.
+ * If a peripheral channel is used, then the completion will be signaled on the
+ * peripheral's interrupt.
+ */
+static void dma_cc23x0_isr(const struct device *dev)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	uint32_t done_flags;
+	int i;
+
+	done_flags = uDMAIntStatus();
+
+	for (i = DMA_CC23_SW_CH_MIN ; i < DMA_CC23_SW_CH_MAX ; i++) {
+		if (done_flags & BIT(i)) {
+			LOG_DBG("DMA transfer completed on channel %d", i);
+
+			ch_data = &data->channels[i];
+			if (ch_data->cb) {
+				ch_data->cb(dev, ch_data->user_data, i, DMA_STATUS_COMPLETE);
+			}
+
+			uDMAClearInt(done_flags & BIT(i));
+		}
+	}
+}
+
+static uint32_t dma_cc23x0_set_addr_adj(uint32_t *control, uint16_t addr_adj,
+					uint32_t inc_flags, uint32_t no_inc_flags)
+{
+	switch (addr_adj) {
+	case DMA_ADDR_ADJ_INCREMENT:
+		*control |= inc_flags;
+		break;
+	case DMA_ADDR_ADJ_NO_CHANGE:
+		*control |= no_inc_flags;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int dma_cc23x0_config(const struct device *dev, uint32_t channel,
+			     struct dma_config *config)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	struct dma_block_config *block = config->head_block;
+	uint32_t control;
+	uint32_t data_size;
+	uint32_t src_inc_flags;
+	uint32_t dst_inc_flags;
+	uint32_t xfer_size;
+	uint32_t burst_len;
+	int ret;
+
+	if (channel >= UDMA_NUM_CHANNELS) {
+		LOG_ERR("Invalid channel");
+		return -EINVAL;
+	}
+
+	if (config->dma_slot > DMA_CC23_IPID_MASK) {
+		LOG_ERR("Invalid trigger");
+		return -EINVAL;
+	}
+
+	if (config->block_count > 1) {
+		LOG_ERR("Chained transfers not supported");
+		return -ENOTSUP;
+	}
+
+	switch (config->source_data_size) {
+	case 1:
+		src_inc_flags = UDMA_SRC_INC_8;
+		break;
+	case 2:
+		src_inc_flags = UDMA_SRC_INC_16;
+		break;
+	case 4:
+		src_inc_flags = UDMA_SRC_INC_32;
+		break;
+	default:
+		LOG_ERR("Invalid source data size");
+		return -EINVAL;
+	}
+
+	switch (config->dest_data_size) {
+	case 1:
+		dst_inc_flags = UDMA_DST_INC_8;
+		break;
+	case 2:
+		dst_inc_flags = UDMA_DST_INC_16;
+		break;
+	case 4:
+		dst_inc_flags = UDMA_DST_INC_32;
+		break;
+	default:
+		LOG_ERR("Invalid destination data size");
+		return -EINVAL;
+	}
+
+	data_size = MIN(config->source_data_size, config->dest_data_size);
+
+	switch (data_size) {
+	case 1:
+		control = UDMA_SIZE_8;
+		break;
+	case 2:
+		control = UDMA_SIZE_16;
+		break;
+	case 4:
+		control = UDMA_SIZE_32;
+		break;
+	default:
+		LOG_ERR("Invalid data size");
+		return -EINVAL;
+	}
+
+	ret = dma_cc23x0_set_addr_adj(&control, block->source_addr_adj,
+				      src_inc_flags, UDMA_SRC_INC_NONE);
+	if (ret) {
+		LOG_ERR("Invalid source address adjustment type");
+		return ret;
+	}
+
+	ret = dma_cc23x0_set_addr_adj(&control, block->dest_addr_adj,
+				      dst_inc_flags, UDMA_DST_INC_NONE);
+	if (ret) {
+		LOG_ERR("Invalid dest address adjustment type");
+		return ret;
+	}
+
+	xfer_size = block->block_size / data_size;
+	if (!xfer_size || xfer_size > UDMA_XFER_SIZE_MAX) {
+		LOG_ERR("Invalid block size (must be in range %d to %d)",
+			data_size, data_size * UDMA_XFER_SIZE_MAX);
+		return -EINVAL;
+	}
+
+	burst_len = config->source_burst_length / data_size;
+	if (burst_len <= UDMA_XFER_SIZE_MAX && IS_POWER_OF_TWO(burst_len)) {
+		control |= LOG2(burst_len) << UDMA_ARB_S;
+	} else {
+		LOG_ERR("Invalid burst length (must be a power of 2 between %d and %d)",
+			data_size, data_size * UDMA_XFER_SIZE_MAX);
+		return -EINVAL;
+	}
+
+	ch_data = &data->channels[channel];
+	ch_data->data_size = data_size;
+	ch_data->cb = config->dma_callback;
+	ch_data->user_data = config->user_data;
+
+	if (uDMAIsChannelEnabled(BIT(channel))) {
+		return -EBUSY;
+	}
+
+	if (DMA_CC23_IS_SW_CH(channel)) {
+		uDMAEnableSwEventInt(BIT(channel));
+	} else {
+		/* Select peripheral */
+		DMA_CC23_CHXSEL_REG(channel) = config->dma_slot;
+	}
+
+	uDMASetChannelControl(&data->desc[channel], control);
+
+	uDMASetChannelTransfer(&data->desc[channel], DMA_CC23_MODE(channel),
+			       (void *)block->source_address,
+			       (void *)block->dest_address,
+			       xfer_size);
+
+	LOG_DBG("Configured channel %d for %08x to %08x (%u bytes)",
+		channel,
+		block->source_address,
+		block->dest_address,
+		block->block_size);
+
+	return 0;
+}
+
+static int dma_cc23x0_start(const struct device *dev, uint32_t channel)
+{
+	if (uDMAIsChannelEnabled(BIT(channel))) {
path: drivers/dma/dma_ti_cc23x0.c
user: login: teburd
body: Repeated start shouldn't result in a failure, if the channel is already going then start should be viewed as a NOP.

See the state diagram https://docs.zephyrproject.org/latest/hardware/peripherals/dma.html#channel-state-machine-expectations
created_at: 2025-03-10T17:20:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2024 BayLibre, SAS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ti_cc23x0_dma
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(dma_cc23x0, CONFIG_DMA_LOG_LEVEL);
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/dma.h>
+#include <zephyr/irq.h>
+#include <zephyr/sys/util.h>
+
+#include <driverlib/clkctl.h>
+#include <driverlib/udma.h>
+
+#include <inc/hw_evtsvt.h>
+#include <inc/hw_memmap.h>
+#include <inc/hw_types.h>
+
+/*
+ * Channels 0 to 5 are assigned to peripherals.
+ * Channels 6 and 7 are assigned to SW-initiated transfers.
+ */
+#define DMA_CC23_PERIPH_CH_MAX	5
+#define DMA_CC23_SW_CH_MIN	6
+#define DMA_CC23_SW_CH_MAX	7
+
+#define DMA_CC23_IS_SW_CH(ch)	((ch) >= DMA_CC23_SW_CH_MIN)
+
+/*
+ * In basic mode, the DMA controller performs transfers as long as there are more items
+ * to transfer, and a transfer request is present. This mode is used with peripherals that
+ * assert a DMA request signal whenever the peripheral is ready for a data transfer.
+ * Auto mode is similar to basic mode, except that when a transfer request is received,
+ * the transfer completes, even if the DMA request is removed. This mode is suitable for
+ * software-triggered transfers.
+ */
+#define DMA_CC23_MODE(ch)	(DMA_CC23_IS_SW_CH(ch) ? UDMA_MODE_AUTO : UDMA_MODE_BASIC)
+
+/* Each DMA channel is multiplexed between two peripherals which ID is in range 0 to 7 */
+#define DMA_CC23_IPID_MASK	GENMASK(2, 0)
+#define DMA_CC23_CHXSEL_REG(ch)	HWREG(EVTSVT_BASE + EVTSVT_O_DMACH0SEL + sizeof(uint32_t) * (ch))
+
+struct dma_cc23x0_channel {
+	uint8_t data_size;
+	dma_callback_t cb;
+	void *user_data;
+};
+
+struct dma_cc23x0_data {
+	__aligned(1024) uDMAControlTableEntry desc[UDMA_NUM_CHANNELS];
+	struct dma_cc23x0_channel channels[UDMA_NUM_CHANNELS];
+};
+
+/*
+ * If the channel is a software channel, then the completion will be signaled
+ * on this DMA dedicated interrupt.
+ * If a peripheral channel is used, then the completion will be signaled on the
+ * peripheral's interrupt.
+ */
+static void dma_cc23x0_isr(const struct device *dev)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	uint32_t done_flags;
+	int i;
+
+	done_flags = uDMAIntStatus();
+
+	for (i = DMA_CC23_SW_CH_MIN ; i < DMA_CC23_SW_CH_MAX ; i++) {
+		if (done_flags & BIT(i)) {
+			LOG_DBG("DMA transfer completed on channel %d", i);
+
+			ch_data = &data->channels[i];
+			if (ch_data->cb) {
+				ch_data->cb(dev, ch_data->user_data, i, DMA_STATUS_COMPLETE);
+			}
+
+			uDMAClearInt(done_flags & BIT(i));
+		}
+	}
+}
+
+static uint32_t dma_cc23x0_set_addr_adj(uint32_t *control, uint16_t addr_adj,
+					uint32_t inc_flags, uint32_t no_inc_flags)
+{
+	switch (addr_adj) {
+	case DMA_ADDR_ADJ_INCREMENT:
+		*control |= inc_flags;
+		break;
+	case DMA_ADDR_ADJ_NO_CHANGE:
+		*control |= no_inc_flags;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int dma_cc23x0_config(const struct device *dev, uint32_t channel,
+			     struct dma_config *config)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	struct dma_block_config *block = config->head_block;
+	uint32_t control;
+	uint32_t data_size;
+	uint32_t src_inc_flags;
+	uint32_t dst_inc_flags;
+	uint32_t xfer_size;
+	uint32_t burst_len;
+	int ret;
+
+	if (channel >= UDMA_NUM_CHANNELS) {
+		LOG_ERR("Invalid channel");
+		return -EINVAL;
+	}
+
+	if (config->dma_slot > DMA_CC23_IPID_MASK) {
+		LOG_ERR("Invalid trigger");
+		return -EINVAL;
+	}
+
+	if (config->block_count > 1) {
+		LOG_ERR("Chained transfers not supported");
+		return -ENOTSUP;
+	}
+
+	switch (config->source_data_size) {
+	case 1:
+		src_inc_flags = UDMA_SRC_INC_8;
+		break;
+	case 2:
+		src_inc_flags = UDMA_SRC_INC_16;
+		break;
+	case 4:
+		src_inc_flags = UDMA_SRC_INC_32;
+		break;
+	default:
+		LOG_ERR("Invalid source data size");
+		return -EINVAL;
+	}
+
+	switch (config->dest_data_size) {
+	case 1:
+		dst_inc_flags = UDMA_DST_INC_8;
+		break;
+	case 2:
+		dst_inc_flags = UDMA_DST_INC_16;
+		break;
+	case 4:
+		dst_inc_flags = UDMA_DST_INC_32;
+		break;
+	default:
+		LOG_ERR("Invalid destination data size");
+		return -EINVAL;
+	}
+
+	data_size = MIN(config->source_data_size, config->dest_data_size);
+
+	switch (data_size) {
+	case 1:
+		control = UDMA_SIZE_8;
+		break;
+	case 2:
+		control = UDMA_SIZE_16;
+		break;
+	case 4:
+		control = UDMA_SIZE_32;
+		break;
+	default:
+		LOG_ERR("Invalid data size");
+		return -EINVAL;
+	}
+
+	ret = dma_cc23x0_set_addr_adj(&control, block->source_addr_adj,
+				      src_inc_flags, UDMA_SRC_INC_NONE);
+	if (ret) {
+		LOG_ERR("Invalid source address adjustment type");
+		return ret;
+	}
+
+	ret = dma_cc23x0_set_addr_adj(&control, block->dest_addr_adj,
+				      dst_inc_flags, UDMA_DST_INC_NONE);
+	if (ret) {
+		LOG_ERR("Invalid dest address adjustment type");
+		return ret;
+	}
+
+	xfer_size = block->block_size / data_size;
+	if (!xfer_size || xfer_size > UDMA_XFER_SIZE_MAX) {
+		LOG_ERR("Invalid block size (must be in range %d to %d)",
+			data_size, data_size * UDMA_XFER_SIZE_MAX);
+		return -EINVAL;
+	}
+
+	burst_len = config->source_burst_length / data_size;
+	if (burst_len <= UDMA_XFER_SIZE_MAX && IS_POWER_OF_TWO(burst_len)) {
+		control |= LOG2(burst_len) << UDMA_ARB_S;
+	} else {
+		LOG_ERR("Invalid burst length (must be a power of 2 between %d and %d)",
+			data_size, data_size * UDMA_XFER_SIZE_MAX);
+		return -EINVAL;
+	}
+
+	ch_data = &data->channels[channel];
+	ch_data->data_size = data_size;
+	ch_data->cb = config->dma_callback;
+	ch_data->user_data = config->user_data;
+
+	if (uDMAIsChannelEnabled(BIT(channel))) {
+		return -EBUSY;
+	}
+
+	if (DMA_CC23_IS_SW_CH(channel)) {
+		uDMAEnableSwEventInt(BIT(channel));
+	} else {
+		/* Select peripheral */
+		DMA_CC23_CHXSEL_REG(channel) = config->dma_slot;
+	}
+
+	uDMASetChannelControl(&data->desc[channel], control);
+
+	uDMASetChannelTransfer(&data->desc[channel], DMA_CC23_MODE(channel),
+			       (void *)block->source_address,
+			       (void *)block->dest_address,
+			       xfer_size);
+
+	LOG_DBG("Configured channel %d for %08x to %08x (%u bytes)",
+		channel,
+		block->source_address,
+		block->dest_address,
+		block->block_size);
+
+	return 0;
+}
+
+static int dma_cc23x0_start(const struct device *dev, uint32_t channel)
+{
+	if (uDMAIsChannelEnabled(BIT(channel))) {
path: drivers/dma/dma_ti_cc23x0.c
user: login: jpanisbl
body: Thank you for the review (and for the link). I'm going to fix that.
created_at: 2025-03-11T08:23:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2024 BayLibre, SAS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ti_cc23x0_dma
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(dma_cc23x0, CONFIG_DMA_LOG_LEVEL);
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/dma.h>
+#include <zephyr/irq.h>
+#include <zephyr/sys/util.h>
+
+#include <driverlib/clkctl.h>
+#include <driverlib/udma.h>
+
+#include <inc/hw_evtsvt.h>
+#include <inc/hw_memmap.h>
+#include <inc/hw_types.h>
+
+/*
+ * Channels 0 to 5 are assigned to peripherals.
+ * Channels 6 and 7 are assigned to SW-initiated transfers.
+ */
+#define DMA_CC23_PERIPH_CH_MAX	5
+#define DMA_CC23_SW_CH_MIN	6
+#define DMA_CC23_SW_CH_MAX	7
+
+#define DMA_CC23_IS_SW_CH(ch)	((ch) >= DMA_CC23_SW_CH_MIN)
+
+/*
+ * In basic mode, the DMA controller performs transfers as long as there are more items
+ * to transfer, and a transfer request is present. This mode is used with peripherals that
+ * assert a DMA request signal whenever the peripheral is ready for a data transfer.
+ * Auto mode is similar to basic mode, except that when a transfer request is received,
+ * the transfer completes, even if the DMA request is removed. This mode is suitable for
+ * software-triggered transfers.
+ */
+#define DMA_CC23_MODE(ch)	(DMA_CC23_IS_SW_CH(ch) ? UDMA_MODE_AUTO : UDMA_MODE_BASIC)
+
+/* Each DMA channel is multiplexed between two peripherals which ID is in range 0 to 7 */
+#define DMA_CC23_IPID_MASK	GENMASK(2, 0)
+#define DMA_CC23_CHXSEL_REG(ch)	HWREG(EVTSVT_BASE + EVTSVT_O_DMACH0SEL + sizeof(uint32_t) * (ch))
+
+struct dma_cc23x0_channel {
+	uint8_t data_size;
+	dma_callback_t cb;
+	void *user_data;
+};
+
+struct dma_cc23x0_data {
+	__aligned(1024) uDMAControlTableEntry desc[UDMA_NUM_CHANNELS];
+	struct dma_cc23x0_channel channels[UDMA_NUM_CHANNELS];
+};
+
+/*
+ * If the channel is a software channel, then the completion will be signaled
+ * on this DMA dedicated interrupt.
+ * If a peripheral channel is used, then the completion will be signaled on the
+ * peripheral's interrupt.
+ */
+static void dma_cc23x0_isr(const struct device *dev)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	uint32_t done_flags;
+	int i;
+
+	done_flags = uDMAIntStatus();
+
+	for (i = DMA_CC23_SW_CH_MIN ; i < DMA_CC23_SW_CH_MAX ; i++) {
path: drivers/dma/dma_ti_cc23x0.c
user: login: fabiobaltieri
body: there's normally no space before the ; on for

`for (i = DMA_CC23_SW_CH_MIN; i < DMA_CC23_SW_CH_MAX; i++) {`
created_at: 2025-03-27T18:03:03Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2024 BayLibre, SAS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ti_cc23x0_dma
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(dma_cc23x0, CONFIG_DMA_LOG_LEVEL);
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/dma.h>
+#include <zephyr/irq.h>
+#include <zephyr/sys/util.h>
+
+#include <driverlib/clkctl.h>
+#include <driverlib/udma.h>
+
+#include <inc/hw_evtsvt.h>
+#include <inc/hw_memmap.h>
+#include <inc/hw_types.h>
+
+/*
+ * Channels 0 to 5 are assigned to peripherals.
+ * Channels 6 and 7 are assigned to SW-initiated transfers.
+ */
+#define DMA_CC23_PERIPH_CH_MAX	5
+#define DMA_CC23_SW_CH_MIN	6
+#define DMA_CC23_SW_CH_MAX	7
+
+#define DMA_CC23_IS_SW_CH(ch)	((ch) >= DMA_CC23_SW_CH_MIN)
+
+/*
+ * In basic mode, the DMA controller performs transfers as long as there are more items
+ * to transfer, and a transfer request is present. This mode is used with peripherals that
+ * assert a DMA request signal whenever the peripheral is ready for a data transfer.
+ * Auto mode is similar to basic mode, except that when a transfer request is received,
+ * the transfer completes, even if the DMA request is removed. This mode is suitable for
+ * software-triggered transfers.
+ */
+#define DMA_CC23_MODE(ch)	(DMA_CC23_IS_SW_CH(ch) ? UDMA_MODE_AUTO : UDMA_MODE_BASIC)
+
+/* Each DMA channel is multiplexed between two peripherals which ID is in range 0 to 7 */
+#define DMA_CC23_IPID_MASK	GENMASK(2, 0)
+#define DMA_CC23_CHXSEL_REG(ch)	HWREG(EVTSVT_BASE + EVTSVT_O_DMACH0SEL + sizeof(uint32_t) * (ch))
+
+struct dma_cc23x0_channel {
+	uint8_t data_size;
+	dma_callback_t cb;
+	void *user_data;
+};
+
+struct dma_cc23x0_data {
+	__aligned(1024) uDMAControlTableEntry desc[UDMA_NUM_CHANNELS];
+	struct dma_cc23x0_channel channels[UDMA_NUM_CHANNELS];
+};
+
+/*
+ * If the channel is a software channel, then the completion will be signaled
+ * on this DMA dedicated interrupt.
+ * If a peripheral channel is used, then the completion will be signaled on the
+ * peripheral's interrupt.
+ */
+static void dma_cc23x0_isr(const struct device *dev)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	uint32_t done_flags;
+	int i;
+
+	done_flags = uDMAIntStatus();
+
+	for (i = DMA_CC23_SW_CH_MIN ; i < DMA_CC23_SW_CH_MAX ; i++) {
+		if (done_flags & BIT(i)) {
path: drivers/dma/dma_ti_cc23x0.c
user: login: fabiobaltieri
body: consider inverting the condition, you could do something like

```
if (done_flags & BIT(i)) {
    continue;
}
```

and then the code below would have one less indentation level. Normally that's for error path but I think it could be a good fit here, but consider this just a suggestion, in this case it's fine if you don't feel like reworking it.
created_at: 2025-03-27T18:04:41Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2024 BayLibre, SAS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ti_cc23x0_dma
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(dma_cc23x0, CONFIG_DMA_LOG_LEVEL);
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/dma.h>
+#include <zephyr/irq.h>
+#include <zephyr/sys/util.h>
+
+#include <driverlib/clkctl.h>
+#include <driverlib/udma.h>
+
+#include <inc/hw_evtsvt.h>
+#include <inc/hw_memmap.h>
+#include <inc/hw_types.h>
+
+/*
+ * Channels 0 to 5 are assigned to peripherals.
+ * Channels 6 and 7 are assigned to SW-initiated transfers.
+ */
+#define DMA_CC23_PERIPH_CH_MAX	5
+#define DMA_CC23_SW_CH_MIN	6
+#define DMA_CC23_SW_CH_MAX	7
+
+#define DMA_CC23_IS_SW_CH(ch)	((ch) >= DMA_CC23_SW_CH_MIN)
+
+/*
+ * In basic mode, the DMA controller performs transfers as long as there are more items
+ * to transfer, and a transfer request is present. This mode is used with peripherals that
+ * assert a DMA request signal whenever the peripheral is ready for a data transfer.
+ * Auto mode is similar to basic mode, except that when a transfer request is received,
+ * the transfer completes, even if the DMA request is removed. This mode is suitable for
+ * software-triggered transfers.
+ */
+#define DMA_CC23_MODE(ch)	(DMA_CC23_IS_SW_CH(ch) ? UDMA_MODE_AUTO : UDMA_MODE_BASIC)
+
+/* Each DMA channel is multiplexed between two peripherals which ID is in range 0 to 7 */
+#define DMA_CC23_IPID_MASK	GENMASK(2, 0)
+#define DMA_CC23_CHXSEL_REG(ch)	HWREG(EVTSVT_BASE + EVTSVT_O_DMACH0SEL + sizeof(uint32_t) * (ch))
+
+struct dma_cc23x0_channel {
+	uint8_t data_size;
+	dma_callback_t cb;
+	void *user_data;
+};
+
+struct dma_cc23x0_data {
+	__aligned(1024) uDMAControlTableEntry desc[UDMA_NUM_CHANNELS];
+	struct dma_cc23x0_channel channels[UDMA_NUM_CHANNELS];
+};
+
+/*
+ * If the channel is a software channel, then the completion will be signaled
+ * on this DMA dedicated interrupt.
+ * If a peripheral channel is used, then the completion will be signaled on the
+ * peripheral's interrupt.
+ */
+static void dma_cc23x0_isr(const struct device *dev)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	uint32_t done_flags;
+	int i;
+
+	done_flags = uDMAIntStatus();
+
+	for (i = DMA_CC23_SW_CH_MIN ; i < DMA_CC23_SW_CH_MAX ; i++) {
+		if (done_flags & BIT(i)) {
+			LOG_DBG("DMA transfer completed on channel %d", i);
+
+			ch_data = &data->channels[i];
+			if (ch_data->cb) {
+				ch_data->cb(dev, ch_data->user_data, i, DMA_STATUS_COMPLETE);
+			}
+
+			uDMAClearInt(done_flags & BIT(i));
+		}
+	}
+}
+
+static uint32_t dma_cc23x0_set_addr_adj(uint32_t *control, uint16_t addr_adj,
+					uint32_t inc_flags, uint32_t no_inc_flags)
+{
+	switch (addr_adj) {
+	case DMA_ADDR_ADJ_INCREMENT:
+		*control |= inc_flags;
+		break;
+	case DMA_ADDR_ADJ_NO_CHANGE:
+		*control |= no_inc_flags;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int dma_cc23x0_config(const struct device *dev, uint32_t channel,
+			     struct dma_config *config)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	struct dma_block_config *block = config->head_block;
+	uint32_t control;
+	uint32_t data_size;
+	uint32_t src_inc_flags;
+	uint32_t dst_inc_flags;
+	uint32_t xfer_size;
+	uint32_t burst_len;
+	int ret;
+
+	if (channel >= UDMA_NUM_CHANNELS) {
+		LOG_ERR("Invalid channel");
+		return -EINVAL;
+	}
+
+	if (config->dma_slot > DMA_CC23_IPID_MASK) {
+		LOG_ERR("Invalid trigger");
+		return -EINVAL;
+	}
+
+	if (config->block_count > 1) {
+		LOG_ERR("Chained transfers not supported");
+		return -ENOTSUP;
+	}
+
+	switch (config->source_data_size) {
+	case 1:
+		src_inc_flags = UDMA_SRC_INC_8;
+		break;
+	case 2:
+		src_inc_flags = UDMA_SRC_INC_16;
+		break;
+	case 4:
+		src_inc_flags = UDMA_SRC_INC_32;
+		break;
+	default:
+		LOG_ERR("Invalid source data size");
path: drivers/dma/dma_ti_cc23x0.c
user: login: fabiobaltieri
body: Consider logging the actual wrong value in the error messages, just adds a couple bytes to the string (if errors are enabled even) and  makes debugging easier, same for errors in `ret < 0` paths
created_at: 2025-03-27T18:05:32Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,39 @@
+# Copyright (c) 2024 BayLibre, SAS
+# SPDX-License-Identifier: Apache-2.0
+
+description: TI CC23X0 DMA controller
+
+compatible: "ti,cc23x0-dma"
+
+include: dma-controller.yaml
+
+properties:
+  reg:
+    required: true
+
+  interrupts:
+    required: true
+
+  "#dma-cells":
+    const: 2
+
+# #dma-cells: Must be <2>.
+# The 1st cell specifies the DMA channel to be used for the data transfer.
+#   This channel should be unique between all peripherals that are using the
+#   DMA instance.
+# The 2nd cell defines the peripheral trigger which is the source of the transfer.
+#   See the SoC's reference manual for all the supported request sources.
+#
+# Example of devicetree DMA channel configuration for the client part:
+#
+# &uart0 {
+#     dmas = <&dma 0 7>, <&dma 1 6>;
+#     dma-names = "rx", "tx";
+# };
+#
+# Above, fragment 0 and 1 represent the different channels used to transfer
+# data between peripheral and RAM. The numbers 7/6 are uart0 rx/tx triggers.
path: dts/bindings/dma/ti,cc23x0-dma.yaml
user: login: fabiobaltieri
body: If you rework this comment into the `description:` field then it would be parsed by the documentation generation stuff and shows up on the binding description, I think it's a better approach than letting this information lost in a yaml comment
created_at: 2025-03-27T18:09:18Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2024 BayLibre, SAS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ti_cc23x0_dma
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(dma_cc23x0, CONFIG_DMA_LOG_LEVEL);
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/dma.h>
+#include <zephyr/irq.h>
+#include <zephyr/sys/util.h>
+
+#include <driverlib/clkctl.h>
+#include <driverlib/udma.h>
+
+#include <inc/hw_evtsvt.h>
+#include <inc/hw_memmap.h>
+#include <inc/hw_types.h>
+
+/*
+ * Channels 0 to 5 are assigned to peripherals.
+ * Channels 6 and 7 are assigned to SW-initiated transfers.
+ */
+#define DMA_CC23_PERIPH_CH_MAX	5
+#define DMA_CC23_SW_CH_MIN	6
+#define DMA_CC23_SW_CH_MAX	7
+
+#define DMA_CC23_IS_SW_CH(ch)	((ch) >= DMA_CC23_SW_CH_MIN)
+
+/*
+ * In basic mode, the DMA controller performs transfers as long as there are more items
+ * to transfer, and a transfer request is present. This mode is used with peripherals that
+ * assert a DMA request signal whenever the peripheral is ready for a data transfer.
+ * Auto mode is similar to basic mode, except that when a transfer request is received,
+ * the transfer completes, even if the DMA request is removed. This mode is suitable for
+ * software-triggered transfers.
+ */
+#define DMA_CC23_MODE(ch)	(DMA_CC23_IS_SW_CH(ch) ? UDMA_MODE_AUTO : UDMA_MODE_BASIC)
+
+/* Each DMA channel is multiplexed between two peripherals which ID is in range 0 to 7 */
+#define DMA_CC23_IPID_MASK	GENMASK(2, 0)
+#define DMA_CC23_CHXSEL_REG(ch)	HWREG(EVTSVT_BASE + EVTSVT_O_DMACH0SEL + sizeof(uint32_t) * (ch))
+
+struct dma_cc23x0_channel {
+	uint8_t data_size;
+	dma_callback_t cb;
+	void *user_data;
+};
+
+struct dma_cc23x0_data {
+	__aligned(1024) uDMAControlTableEntry desc[UDMA_NUM_CHANNELS];
+	struct dma_cc23x0_channel channels[UDMA_NUM_CHANNELS];
+};
+
+/*
+ * If the channel is a software channel, then the completion will be signaled
+ * on this DMA dedicated interrupt.
+ * If a peripheral channel is used, then the completion will be signaled on the
+ * peripheral's interrupt.
+ */
+static void dma_cc23x0_isr(const struct device *dev)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	uint32_t done_flags;
+	int i;
+
+	done_flags = uDMAIntStatus();
+
+	for (i = DMA_CC23_SW_CH_MIN ; i < DMA_CC23_SW_CH_MAX ; i++) {
+		if (done_flags & BIT(i)) {
path: drivers/dma/dma_ti_cc23x0.c
user: login: jpanisbl
body: You probably mean:
```
if (!(done_flags & BIT(i))) {
    continue;
}
```

Thanks for the suggestion (and for the review), I'm going to change that.
created_at: 2025-03-28T10:07:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2024 BayLibre, SAS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ti_cc23x0_dma
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(dma_cc23x0, CONFIG_DMA_LOG_LEVEL);
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/dma.h>
+#include <zephyr/irq.h>
+#include <zephyr/sys/util.h>
+
+#include <driverlib/clkctl.h>
+#include <driverlib/udma.h>
+
+#include <inc/hw_evtsvt.h>
+#include <inc/hw_memmap.h>
+#include <inc/hw_types.h>
+
+/*
+ * Channels 0 to 5 are assigned to peripherals.
+ * Channels 6 and 7 are assigned to SW-initiated transfers.
+ */
+#define DMA_CC23_PERIPH_CH_MAX	5
+#define DMA_CC23_SW_CH_MIN	6
+#define DMA_CC23_SW_CH_MAX	7
+
+#define DMA_CC23_IS_SW_CH(ch)	((ch) >= DMA_CC23_SW_CH_MIN)
+
+/*
+ * In basic mode, the DMA controller performs transfers as long as there are more items
+ * to transfer, and a transfer request is present. This mode is used with peripherals that
+ * assert a DMA request signal whenever the peripheral is ready for a data transfer.
+ * Auto mode is similar to basic mode, except that when a transfer request is received,
+ * the transfer completes, even if the DMA request is removed. This mode is suitable for
+ * software-triggered transfers.
+ */
+#define DMA_CC23_MODE(ch)	(DMA_CC23_IS_SW_CH(ch) ? UDMA_MODE_AUTO : UDMA_MODE_BASIC)
+
+/* Each DMA channel is multiplexed between two peripherals which ID is in range 0 to 7 */
+#define DMA_CC23_IPID_MASK	GENMASK(2, 0)
+#define DMA_CC23_CHXSEL_REG(ch)	HWREG(EVTSVT_BASE + EVTSVT_O_DMACH0SEL + sizeof(uint32_t) * (ch))
+
+struct dma_cc23x0_channel {
+	uint8_t data_size;
+	dma_callback_t cb;
+	void *user_data;
+};
+
+struct dma_cc23x0_data {
+	__aligned(1024) uDMAControlTableEntry desc[UDMA_NUM_CHANNELS];
+	struct dma_cc23x0_channel channels[UDMA_NUM_CHANNELS];
+};
+
+/*
+ * If the channel is a software channel, then the completion will be signaled
+ * on this DMA dedicated interrupt.
+ * If a peripheral channel is used, then the completion will be signaled on the
+ * peripheral's interrupt.
+ */
+static void dma_cc23x0_isr(const struct device *dev)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	uint32_t done_flags;
+	int i;
+
+	done_flags = uDMAIntStatus();
+
+	for (i = DMA_CC23_SW_CH_MIN ; i < DMA_CC23_SW_CH_MAX ; i++) {
+		if (done_flags & BIT(i)) {
path: drivers/dma/dma_ti_cc23x0.c
user: login: fabiobaltieri
body: sorry -- yes
created_at: 2025-03-28T10:09:04Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2024 BayLibre, SAS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ti_cc23x0_dma
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(dma_cc23x0, CONFIG_DMA_LOG_LEVEL);
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/dma.h>
+#include <zephyr/irq.h>
+#include <zephyr/sys/util.h>
+
+#include <driverlib/clkctl.h>
+#include <driverlib/udma.h>
+
+#include <inc/hw_evtsvt.h>
+#include <inc/hw_memmap.h>
+#include <inc/hw_types.h>
+
+/*
+ * Channels 0 to 5 are assigned to peripherals.
+ * Channels 6 and 7 are assigned to SW-initiated transfers.
+ */
+#define DMA_CC23_PERIPH_CH_MAX	5
+#define DMA_CC23_SW_CH_MIN	6
+#define DMA_CC23_SW_CH_MAX	7
+
+#define DMA_CC23_IS_SW_CH(ch)	((ch) >= DMA_CC23_SW_CH_MIN)
+
+/*
+ * In basic mode, the DMA controller performs transfers as long as there are more items
+ * to transfer, and a transfer request is present. This mode is used with peripherals that
+ * assert a DMA request signal whenever the peripheral is ready for a data transfer.
+ * Auto mode is similar to basic mode, except that when a transfer request is received,
+ * the transfer completes, even if the DMA request is removed. This mode is suitable for
+ * software-triggered transfers.
+ */
+#define DMA_CC23_MODE(ch)	(DMA_CC23_IS_SW_CH(ch) ? UDMA_MODE_AUTO : UDMA_MODE_BASIC)
+
+/* Each DMA channel is multiplexed between two peripherals which ID is in range 0 to 7 */
+#define DMA_CC23_IPID_MASK	GENMASK(2, 0)
+#define DMA_CC23_CHXSEL_REG(ch)	HWREG(EVTSVT_BASE + EVTSVT_O_DMACH0SEL + sizeof(uint32_t) * (ch))
+
+struct dma_cc23x0_channel {
+	uint8_t data_size;
+	dma_callback_t cb;
+	void *user_data;
+};
+
+struct dma_cc23x0_data {
+	__aligned(1024) uDMAControlTableEntry desc[UDMA_NUM_CHANNELS];
+	struct dma_cc23x0_channel channels[UDMA_NUM_CHANNELS];
+};
+
+/*
+ * If the channel is a software channel, then the completion will be signaled
+ * on this DMA dedicated interrupt.
+ * If a peripheral channel is used, then the completion will be signaled on the
+ * peripheral's interrupt.
+ */
+static void dma_cc23x0_isr(const struct device *dev)
+{
+	struct dma_cc23x0_data *data = dev->data;
+	struct dma_cc23x0_channel *ch_data;
+	uint32_t done_flags;
+	int i;
+
+	done_flags = uDMAIntStatus();
+
+	for (i = DMA_CC23_SW_CH_MIN ; i < DMA_CC23_SW_CH_MAX ; i++) {
path: drivers/dma/dma_ti_cc23x0.c
user: login: jpanisbl
body: I also found a bug in the condition. Actually it must be:
`for (i = DMA_CC23_SW_CH_MIN; i <= DMA_CC23_SW_CH_MAX; i++) {`

**<=** so DMA_CC23_SW_CH_MAX can be tested too (it's a valid channel).
created_at: 2025-03-28T10:23:21Z
author_association: CONTRIBUTOR

PR comments:
user: login: zephyrbot
created_at: 2025-01-24T15:41:26Z
body: The following west manifest projects have changed revision in this Pull Request:

| Name | Old Revision | New Revision | Diff |
| ---- | ------------ | ------------ |------|

✅ **All manifest checks OK**

*Note: This message is automatically posted and updated by the Manifest GitHub Action.* 
author_association: NONE
user: login: jpanisbl
created_at: 2025-03-19T12:21:11Z
body: Hi @kartben 
Would it be possible to add a couple of reviewers for this PR please ?
@teburd took care of this PR (which is great), but AFAICS @vanti has no more activity related to Zephyr since Feb. 2021 and I don't think @vaishnavachath reviews DMA drivers. So it's very unlikely that this PR has any chance to ever get 2 approvals. :-)
author_association: CONTRIBUTOR
user: login: jpanisbl
created_at: 2025-03-19T14:59:44Z
body: @teburd I do not understand your comment. I there anything else I should fix ?
author_association: CONTRIBUTOR