imx6sx sdma m2m test issue

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

imx6sx sdma m2m test issue

3,410 Views
袁增兴
Contributor I

Frankly speaking, we failed to move data between memory using sdma, our kernel branch is 'imx_4.1.15_2.0.0_ga', the steps of our dma test is below, 

1) dma_request_channel() to get the available sdma channel

2) dma alloc the source buffer and dest buffer

3) dmaengine_slave_config() to config the dma direction to be DMA_MEM_TO_MEM

4) dma_map_sg() to map the source and desc buffer into scatterlist

5) device_prep_dma_sg() to process the scatterlist

6) dmaengine_submit() and wait_for_completion()

We found that the sdma will not work once we pend the request to such dma channel, it is blocked in waiting for completion, such a weird thing! By the way, imx's uart can employ the sdma in DMA_MEM_TO_DEV way that the sdma will be gererated.

Is there anybody who tested DMA_MEM_TO_MEM over imx6sx board of the kernel 'imx_4.1.15_2.0.0_ga'?

Thank you

Labels (1)
0 Kudos
10 Replies

2,333 Views
袁增兴
Contributor I

Thank you, I have submitted A professional service request to the official.

0 Kudos

2,333 Views
BiyongSUN
NXP Employee
NXP Employee

We have    module_test\mxc_sdma_memcopy_test.c in the uni_test

test\mxc_sdma_test\mxc_sdma_test.c  in the uni_test is the user space application to do the test.

If you read the code carefully, the driver is written by Linux DMA API and the BSP just mapping the Linux DMA API to sdma of the i.MX6.

If you check the Linux header file, the DMA_BIDIRECTIONAL, DMA_TO_DEVICE, DMA_FROM_DEVICE, DMA_NONE  is listed.

include\linux\dma-direction.h

#define _LINUX_DMA_DIRECTION_H
/*
 * These definitions mirror those in pci.h, so they can be used
 * interchangeably with their PCI_ counterparts.
 */
enum dma_data_direction {
 DMA_BIDIRECTIONAL = 0,
 DMA_TO_DEVICE = 1,
 DMA_FROM_DEVICE = 2,
 DMA_NONE = 3,
};
#endif

please note the following line in the driver.

dma_m2m_desc = dma_m2m_chan->device->device_prep_dma_memcpy(dma_m2m_chan, dma_dst, dma_src, SDMA_BUF_SIZE,0);

ssize_t sdma_write(struct file * filp, const char __user * buf, size_t count,
        loff_t * offset)
{
 u32 *index1, *index2, *index3, i, ret;
 struct dma_slave_config dma_m2m_config = {0};
 struct dma_async_tx_descriptor *dma_m2m_desc;
 u32 *index4 = wbuf4;
 dma_addr_t dma_src, dma_dst;

 index1 = wbuf;
 index2 = wbuf2;
 index3 = wbuf3;

 for (i=0; i<SDMA_BUF_SIZE/4; i++) {
  *(index1 + i) = 0x12121212;
 }

 for (i=0; i<SDMA_BUF_SIZE/2/4; i++) {
  *(index2 + i) = 0x34343434;
 }

 for (i=0; i<SDMA_BUF_SIZE/4; i++) {
  *(index3 + i) = 0x56565656;
 }

 for (i=0; i<SDMA_BUF_SIZE/4; i++) {
  *(index4 + i) = 0x56565656;
 }

#if 0
 for (i=0; i<SDMA_BUF_SIZE/4; i++) {
 printk("input data_%d : %x\n", i, *(wbuf+i));
 }

 for (i=0; i<SDMA_BUF_SIZE/2/4; i++) {
 printk("input data2_%d : %x\n", i, *(wbuf2+i));
 }

 for (i=0; i<SDMA_BUF_SIZE/4; i++) {
 printk("input data3_%d : %x\n", i, *(wbuf3+i));
 }
#endif
 dma_m2m_config.direction = DMA_MEM_TO_MEM;
 dma_m2m_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 dmaengine_slave_config(dma_m2m_chan, &dma_m2m_config);

 sg_init_table(sg, 3);
 sg_set_buf(&sg[0], wbuf, SDMA_BUF_SIZE);
 sg_set_buf(&sg[1], wbuf2, SDMA_BUF_SIZE/2);
 sg_set_buf(&sg[2], wbuf3, SDMA_BUF_SIZE);
 ret = dma_map_sg(NULL, sg, 3, dma_m2m_config.direction);

#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3,0,35))
 dma_m2m_desc = dma_m2m_chan->device->device_prep_slave_sg(dma_m2m_chan,sg, 3, dma_m2m_config.direction, 1);
#endif

 sg_init_table(sg2, 3);
 sg_set_buf(&sg2[0], rbuf, SDMA_BUF_SIZE);
 sg_set_buf(&sg2[1], rbuf2, SDMA_BUF_SIZE/2);
 sg_set_buf(&sg2[2], rbuf3, SDMA_BUF_SIZE);
 ret = dma_map_sg(NULL, sg2, 3, dma_m2m_config.direction);

#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3,0,35))
 dma_m2m_desc = dma_m2m_chan->device->device_prep_slave_sg(dma_m2m_chan,sg2, 3, dma_m2m_config.direction, 0);
#else
 dma_m2m_desc = dma_m2m_chan->device->device_prep_dma_sg(dma_m2m_chan,sg2, 3, sg, 3, 0);
#endif

 dma_m2m_desc->callback = dma_m2m_callback;
 dmaengine_submit(dma_m2m_desc);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,35))
 dma_async_issue_pending(dma_m2m_chan);
#endif

 wait_for_completion(&dma_m2m_ok);
 dma_unmap_sg(NULL, sg, 3, dma_m2m_config.direction);
 dma_unmap_sg(NULL, sg2, 3, dma_m2m_config.direction);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,35))
 dma_src = dma_map_single(NULL, wbuf4, SDMA_BUF_SIZE, DMA_TO_DEVICE);
 dma_dst = dma_map_single(NULL, rbuf4, SDMA_BUF_SIZE, DMA_FROM_DEVICE);
 dma_m2m_desc = dma_m2m_chan->device->device_prep_dma_memcpy(dma_m2m_chan, dma_dst, dma_src, SDMA_BUF_SIZE,0);
 if (!dma_m2m_desc)
  printk("prep error!!\n");
 dma_m2m_desc->callback = dma_m2m_callback;
 dmaengine_submit(dma_m2m_desc);
 dma_async_issue_pending(dma_m2m_chan);
 wait_for_completion(&dma_m2m_ok);
 dma_unmap_single(NULL, dma_src, SDMA_BUF_SIZE, DMA_TO_DEVICE);
 dma_unmap_single(NULL, dma_dst, SDMA_BUF_SIZE, DMA_FROM_DEVICE);
#endif

 return 0;
}

struct file_operations dma_fops = {
 open:  sdma_open,
 release: sdma_release,
 read:  sdma_read,
 write:  sdma_write,
};

0 Kudos

2,124 Views
antho
Contributor III

I was facing a memory mapping issue running this test module for imx8mm. Mapping functions were always returning NULL addresses.

It finally worked for me when replacing dma_map_ fuctions NULL device parameter by dma_m2m_chan->device->dev !

ex:

ret = dma_map_sg(dma_m2m_chan->device->dev, sg, 3, dma_m2m_config.direction);

instead of dma_map_sg(NULL, sg, 3, dma_m2m_config.direction);

Thanks for your help !

0 Kudos

2,333 Views
袁增兴
Contributor I

hi, 

I have tested your code, found that dma engine will be blocked using sg list mode, but succeed in device_prep_dma_memcpy.

best regard

0 Kudos

2,333 Views
igorpadykov
NXP Employee
NXP Employee

Hi

complete unit tests codes can be found on

www.nxp.com/lgfiles/NMG/MAD/YOCTO/imx-test-5.7.tar.gz

Best regards
igor

0 Kudos

2,333 Views
袁增兴
Contributor I

hi,

igor, thanks, this helps me a lot.

best

0 Kudos

2,333 Views
BiyongSUN
NXP Employee
NXP Employee

The code is not complete and please check the code in uni_test.

And another simple code is mxc_sdma_mem_test.c

0 Kudos

2,333 Views
BiyongSUN
NXP Employee
NXP Employee

The result using the dmatest inside the Linux source tree.

Untitled.png

0 Kudos

2,333 Views
袁增兴
Contributor I

hi,

Thanks for the support, I am trying to code the multi-thread in user space instead of the dmatest to use the multi-dma-channel concurently, which I found that the dma performance is bad, it is about  42MB/s~44MB/s, which is lower than the cpu memcpy. Can u provide the limit details of the sdma controller?

Best

0 Kudos

2,333 Views
袁增兴
Contributor I

hi biyong sun,

I use your sdma_write code in the kernel instead of mine, it is still blocked. My sdma_open() function is below,

#define SDMA_BUF_SIZE 1024*60

static bool dma_m2m_filter(struct dma_chan *chan, void *param)
{
if (!imx_dma_is_general_purpose(chan))
return false;
chan->private = param;
return true;
}

int sdma_open(struct inode *inode, struct file *filp)
{
dma_cap_mask_t dma_m2m_mask;
struct imx_dma_data m2m_dma_data;

init_completion(&dma_m2m_ok);

dma_cap_zero(dma_m2m_mask);
dma_cap_set(DMA_SLAVE, dma_m2m_mask);
m2m_dma_data.peripheral_type = IMX_DMATYPE_MEMORY;
m2m_dma_data.priority = DMA_PRIO_HIGH;
dma_m2m_chan = dma_request_channel(dma_m2m_mask, dma_m2m_filter,
&m2m_dma_data);
if (!dma_m2m_chan) {
printk("Error opening the SDMA memory to memory channel\n");
return -EINVAL;
}

wbuf = kzalloc(SDMA_BUF_SIZE, GFP_DMA);
if(!wbuf) {
printk("error wbuf !!!!!!!!!!!\n");
return -1;
}

wbuf2 = kzalloc(SDMA_BUF_SIZE, GFP_DMA);
if(!wbuf2) {
printk("error wbuf2 !!!!!!!!!!!\n");
return -1;
}

wbuf3 = kzalloc(SDMA_BUF_SIZE, GFP_DMA);
if(!wbuf3) {
printk("error wbuf3 !!!!!!!!!!!\n");
return -1;
}

wbuf4 = kzalloc(SDMA_BUF_SIZE, GFP_DMA);
if(!wbuf4) {
printk("error wbuf4 !!!!!!!!!!!\n");
return -1;
}

rbuf = kzalloc(SDMA_BUF_SIZE, GFP_DMA);
if(!rbuf) {
printk("error rbuf !!!!!!!!!!!\n");
return -1;
}

rbuf2 = kzalloc(SDMA_BUF_SIZE, GFP_DMA);
if(!rbuf2) {
printk("error rbuf2 !!!!!!!!!!!\n");
return -1;
}

rbuf3 = kzalloc(SDMA_BUF_SIZE, GFP_DMA);
if(!rbuf3) {
printk("error rbuf3 !!!!!!!!!!!\n");
return -1;
}

rbuf4 = kzalloc(SDMA_BUF_SIZE, GFP_DMA);
if(!rbuf4) {
printk("error rbuf4 !!!!!!!!!!!\n");
return -1;
}

return 0;
}

0 Kudos