FDT DMA controller interface extension

Yuri Honegger <[email protected]> Sun, 25 Jan 2026 00:12:02 +0100
Newsgroups gmane.os.netbsd.ports.arm
Message-ID <[email protected]>
Hi everyone,

Recently, I've been writing a DMA controller for the i.MX23. The i.MX23 DMA
controller works by passing the hardware a pointer to a DMA command chain stored
in memory. A DMA command looks roughly like this:

struct dma_command {
	void *next; /* you can chain commands */
	uint32_t control;
	void *buffer; /* memory buffer to transfer to/from */
	uint32_t pio_words[3];
};

The i.MX23 DMA controller supports so-called "Programmable IO Words", short PIO.
These PIO words get written to the hardware configuration register of the
peripheral we are interacting over DMA before transferring data.

Due to some peculiarities of the i.MX23 hardware, you basically need to use PIO
for the SD card driver if you don't want to write a polling driver.

The issue is that the FDT DMA controller interface (sys/dev/fdt/fdt_dma.h)
doesn't provide a way to pass PIO data from a driver to the DMA controller.
Therefore, I suggest to extend the fdtbus_dma_req struct with some optional
parameters to specify PIO operations.

Here is my suggested diff. It solves the problem in the most straightforward way
that works for the i.MX23.

Is it fine like this? I’m a bit afraid of ballooning the fdtbus_dma_req struct even more.
Already right now most options are only supported by 1-2 controllers due to the wide
variety in DMA controllers. 

Thanks,
Yuri
fdt_dma.diff (application/octet-stream, 964 B)
diff --git a/sys/dev/fdt/fdt_dma.h b/sys/dev/fdt/fdt_dma.h
index a876bc62d67f..d8b382bbd66b 100644
--- a/sys/dev/fdt/fdt_dma.h
+++ b/sys/dev/fdt/fdt_dma.h
@@ -41,7 +41,8 @@ struct fdtbus_dma {
 
 enum fdtbus_dma_dir {
 	FDT_DMA_READ,		/* device -> memory */
-	FDT_DMA_WRITE		/* memory -> device */
+	FDT_DMA_WRITE,		/* memory -> device */
+	FDT_DMA_NO_XFER		/* doesn't transfer any data, but may do PIO */
 };
 
 struct fdtbus_dma_opt {
@@ -65,6 +66,11 @@ struct fdtbus_dma_req {
 	int dreq_block_multi;		/* Enable multiple block transfers */
 	int dreq_flow;			/* Enable flow control */
 
+	/* Some DMA controllers allow setting control registers as part of a
+	 * dma transfer. These fields are highly controller-specific. */
+	uint32_t dreg_pio_words[3]; 	/* space for PIO words */
+	int dreg_npio_words;		/* Number of used PIO words */
+
 	struct fdtbus_dma_opt dreq_mem_opt;	/* Memory options */
 	struct fdtbus_dma_opt dreq_dev_opt;	/* Device options */
 };