Skip to content

Instantly share code, notes, and snippets.

@Sutter099
Last active April 28, 2025 09:07
Show Gist options
  • Save Sutter099/4fa99bb2d89e5af975983124704b3861 to your computer and use it in GitHub Desktop.
Save Sutter099/4fa99bb2d89e5af975983124704b3861 to your computer and use it in GitHub Desktop.
duo dma test
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/slab.h>
#define TEST_BUF_SIZE 16
#define TEST_CPY_SIZE 16
struct dma_test_info {
struct dma_chan *chan;
struct device *dma_dev;
dma_cookie_t cookie;
u8 *src_buf;
u8 *dst_buf;
dma_addr_t src_phys;
dma_addr_t dst_phys;
};
static int verify_data(u8 *src, u8 *dst, size_t len)
{
size_t i;
for (i = 0; i < len; i++) {
printk("offset %zu: %02x vs %02x\n",
i, src[i], dst[i]);
if (src[i] != dst[i]) {
pr_err("Mismatch at offset %zu: %02x vs %02x\n",
i, src[i], dst[i]);
return -EINVAL;
}
}
return 0;
}
static void dma_callback(void *completion)
{
complete(completion);
}
static int perform_dma_transfer(struct dma_test_info *info)
{
struct dma_async_tx_descriptor *tx;
struct completion dma_complete;
int ret = 0;
memset(info->src_buf, 0xAF, TEST_BUF_SIZE);
memset(info->dst_buf, 0x00, TEST_BUF_SIZE);
tx = dmaengine_prep_dma_memcpy(info->chan,
info->dst_phys,
info->src_phys,
// TEST_BUF_SIZE,
TEST_CPY_SIZE,
DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
if (!tx) {
pr_err("Failed to prepare DMA transaction\n");
return -EIO;
}
init_completion(&dma_complete);
tx->callback = dma_callback;
tx->callback_param = &dma_complete;
info->cookie = dmaengine_submit(tx);
if (dma_submit_error(info->cookie)) {
pr_err("Failed to submit DMA transaction\n");
return -EIO;
}
// verify_data(info->src_buf, info->dst_buf, TEST_BUF_SIZE);
dma_async_issue_pending(info->chan);
wait_for_completion_timeout(&dma_complete, msecs_to_jiffies(1000));
ret = verify_data(info->src_buf, info->dst_buf, TEST_BUF_SIZE);
if (ret) {
pr_err("Data verification failed\n");
} else {
pr_info("DMA transfer succeeded\n");
}
return ret;
}
static int __init dma_mem2mem_init(void)
{
struct dma_test_info *info;
int ret;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
ret = -ENOMEM;
goto err_free;
}
dma_cap_mask_t cap;
dma_cap_zero(cap);
dma_cap_set(DMA_MEMCPY, cap);
info->chan = dma_request_channel(cap, NULL, NULL);
if (!info->chan) {
printk(KERN_ERR "Failed to request DMA channel.\n");
ret = -ENODEV;
goto err_release_chan;
}
info->dma_dev = info->chan->device->dev;
info->src_buf = dma_alloc_coherent(info->dma_dev, TEST_BUF_SIZE, &info->src_phys, GFP_ATOMIC);
info->dst_buf = dma_alloc_coherent(info->dma_dev, TEST_BUF_SIZE, &info->dst_phys, GFP_ATOMIC);
if (!info->src_buf || !info->dst_buf) {
ret = -ENOMEM;
goto err_free_buf;
}
ret = perform_dma_transfer(info);
if (ret)
goto err_free_buf;
dma_release_channel(info->chan);
dma_free_coherent(info->dma_dev, TEST_BUF_SIZE, info->src_buf, info->src_phys);
dma_free_coherent(info->dma_dev, TEST_BUF_SIZE, info->dst_buf, info->dst_phys);
kfree(info);
return 0;
err_free_buf:
if (info->src_buf)
dma_free_coherent(info->dma_dev, TEST_BUF_SIZE, info->src_buf, info->src_phys);
if (info->dst_buf)
dma_free_coherent(info->dma_dev, TEST_BUF_SIZE, info->dst_buf, info->dst_phys);
err_release_chan:
dma_release_channel(info->chan);
err_free:
kfree(info);
return ret;
}
static void __exit dma_mem2mem_exit(void)
{
}
module_init(dma_mem2mem_init);
module_exit(dma_mem2mem_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("duo DMA mem2mem test module");

1. Mem2Mem DMA Test

The DMA functionality on the Duo board has been tested successfully in the Mem2Mem mode. The following is the test code and the corresponding log output:

Test Code: duo-dma-test.c in attachment

Test Log:

[    1.735104] dma dma0chan0: dma0chan0: allocating
[    1.740268] dmaengine: __dma_request_channel: success (dma0chan0)
[    1.747388] dma dma0chan0: dma0chan0: memcpy: src: 0x0000000081da0000 dst: 0x0000000081da1000 length: 163
[    1.758468] dma dma0chan0: dma0chan0: len: 16, xfer_width: 2, num: 1
[    1.765285] dma dma0chan0: dma0chan0: block_ts: 4, max_block_ts: 1024, xfer_width: 2
[    1.773917] dma dma0chan0: running: len: 0, xfer_width: 2, xfer_len: 16, num: 0
[    1.781742] dw_axi_dmac_platform 4330000.dma-controller: vchan (____ptrval____): txd (____ptrval____)[2]d
[    1.792775] dma dma0chan0: dma0chan0: started 2
[    2.819995] offset 0: af vs af
[    2.823281] offset 1: af vs af
[    2.826668] offset 2: af vs af
[    2.830044] offset 3: af vs af
[    2.833411] offset 4: af vs af
[    2.836779] offset 5: af vs af
[    2.840145] offset 6: af vs af
[    2.843378] offset 7: af vs af
[    2.846713] offset 8: af vs af
[    2.850077] offset 9: af vs af
[    2.853551] offset 10: af vs af
[    2.857038] offset 11: af vs af
[    2.860498] offset 12: af vs af
[    2.863950] offset 13: af vs af
[    2.867274] offset 14: af vs af
[    2.870696] offset 15: af vs af
[    2.874150] DMA transfer succeeded
[    2.877956] dma dma0chan0: dma0chan0: 1 descs put, 0 still allocated
[    2.884864] dma dma0chan0: dma0chan0: free resources, descriptor still allocated: 0

Prior to correcting the snps,data-width value, the driver incorrectly assumed a data width of 128 bits, resulting in only 1/4 of the intended memory being transferred. The earlier (incorrect) test log would look like:

# alloc 16, memcpy 16

[    1.752567] dma dma0chan0: dma0chan0: allocating
[    1.757717] dmaengine: __dma_request_channel: success (dma0chan0)
[    1.764882] dma dma0chan0: dma0chan0: memcpy: src: 0x0000000081da0000 dst: 0x0000000081da1000 length: 3
[    1.775972] dma dma0chan0: dma0chan0: len: 16, xfer_width: 4, num: 1
[    1.783195] dw_axi_dmac_platform 4330000.dma-controller: vchan (____ptrval____): txd (____ptrval____)[d
[    1.794245] dma dma0chan0: dma0chan0: started 2
[    2.820446] offset 0: af vs af
[    2.823730] offset 1: af vs af
[    2.827127] offset 2: af vs af
[    2.830536] offset 3: af vs af
[    2.833917] offset 4: af vs 00
[    2.837278] offset 5: af vs 00
[    2.840645] offset 6: af vs 00
[    2.843878] offset 7: af vs 00
[    2.847219] offset 8: af vs 00
[    2.850588] offset 9: af vs 00
[    2.854065] offset 10: af vs 00
[    2.857552] offset 11: af vs 00
[    2.861015] offset 12: af vs 00
[    2.864472] offset 13: af vs 00
[    2.867793] offset 14: af vs 00
[    2.871225] offset 15: af vs 00
[    2.874684] DMA transfer succeeded
[    2.878487] dma dma0chan0: dma0chan0: 1 descs put, 0 still allocated
[    2.885385] dma dma0chan0: dma0chan0: free resources, descriptor still allocated: 0

2. Issues in DTS Configuration

The Duo-S board's DMA controller supports a 32-bit bus width. Although this is not explicitly mentioned in the manual, it has been verified through testing and the vendor's code. When the configuration is incorrect, the block transfer size set by the DMA controller can exceed the hardware's maximum supported size. This results in missing data during each transfer. Relevant Code:

// Extracted from vendor code

https://github.com/milkv-duo/duo-buildroot-sdk/blob/develop/linux_5.10/drivers/dma/cvitek/cvitek-dma.c#L885

	/*
	 * for mem2mem type we defaultly set the src/dst width bits
	 * to max value 32(axi bus width) consider of performence,
	 * you can change it by dts.
	 */

	trans_width = __ffs(data_width | src | dest | len);

snps,data-width stands for power of width, which should be 32 bits (1Byte * 2 ^ 2)

diff --git a/arch/riscv/boot/dts/sophgo/cv18xx.dtsi b/arch/riscv/boot/dts/sophgo/cv18xx.dtsi
index aa635c5d436a..ccb4dfb8a794 100644
--- a/arch/riscv/boot/dts/sophgo/cv18xx.dtsi
+++ b/arch/riscv/boot/dts/sophgo/cv18xx.dtsi
@@ -521,7 +521,7 @@ dmac: dma-controller@4330000 {
                                           1024 1024 1024 1024>;
                        snps,priority = <0 1 2 3 4 5 6 7>;
                        snps,dma-masters = <2>;
-                       snps,data-width = <4>;
+                       snps,data-width = <2>;
                        status = "disabled";
                };

Link to mainline dts: https://github.com/torvalds/linux/blob/master/arch/riscv/boot/dts/sophgo/cv18xx.dtsi#L332

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment