This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/AM3352: GPMC DMA transfer triggered by GPIO

Part Number:

原来在e2e上发的问题,tony让我在这边发一下,麻烦帮我解答一下,谢谢。

Tool/software: Linux

CPU:AM335x

Linux Kernel:4.4.10

I'm working on am335x's dma, my goal is to use a gpio interrupt to trigger a EDMA's mem to mem transfer, that copying 8 bytes from GPMC SRAM to ram.

The trigger gpio is GP2[29], which is a indirect mapped dma event.

I add the dma binding code in dts file:

1
2
3
4
5
6
dma_test {
    compatible = "cet,am335x-dma-test";
    status = "okay";
    dmas = <&edma_xbar 32 0 32>;
    dma-names = "dma_test";
};

My test code is listed below, I have tried two methods to request a dma channel, but have different result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
dma_cap_zero(mask);
    dma_cap_set(DMA_MEMCPY, mask);
/* !!!!!!!!!!!!!!!!!!!!!! */
/* Method 1: dma_request_channel without any param */
    dma_ch = dma_request_channel(mask, 0, NULL);
/* Methed 2: dma_request_chan, which can obtain dma channel info. specified in dts file */
    dma_ch = dma_request_chan(&pdev->dev, "dma_test");/* no place to pass mask */
    if (dma_ch == NULL)
    {
        printk("request_channel failed.\n");
        goto err;
    }
    printk("got dma_ch = 0x%x\n", dma_ch);
    rxconf.src_addr = ADC_ADDR;
    rxconf.dst_addr = dma_buf_phys_addr;
    rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
    rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
    rxconf.src_maxburst = 1;
    rxconf.dst_maxburst = 1;
    rxconf.device_fc = 0;
    rxconf.slave_id = 0;
    ret = dmaengine_slave_config(dma_ch, &rxconf);
    if (ret)
        goto err;
    printk("dmaengine_slave_config pass.\n");
    /* debug: print device struct info */
    dma_ch_debug(dma_ch);
    if (dma_ch->device->device_prep_dma_memcpy == NULL)
    {
        printk("device_prep_dma_memcpy = NULL\n");
        goto err;
    }
    tx_desc = dma_ch->device->device_prep_dma_memcpy(dma_ch, dma_buf_phys_addr, ADC_ADDR,
                            8, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
    if (!tx_desc)
        goto err;
    printk("device_prep_dma_memcpy pass.\n");
    tx_desc->callback = dma_callback_func;
    tx_desc->callback_param = NULL;
    cookie = tx_desc->tx_submit(tx_desc); //submit the desc
    if (dma_submit_error(cookie)){
        printk(KERN_INFO "Failed to do DMA tx_submit");
        goto err;
    }
    printk("tx_submit pass.\n");
    dma_async_issue_pending(dma_ch);
1
 

When I use dma_request_channel() API, I get a memcpy only dma channel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[ 1349.049411] Key get value 1
[ 1349.060336] read 8 bytes:
[ 1349.063125] 00 05 c1 13 0b 60 f0 5b
[ 1349.074218] got dma_buf = 0xf0ea8000 dma_buf_phys_addr = 0xbd042000
[ 1349.085953] got dma_ch = 0xee9715f0
[ 1349.092285] dmaengine_slave_config pass.
[ 1349.096434] device = 0xee93dcd0
[ 1349.104284] device->chancnt = 2
[ 1349.110178] device->dev_id = 1
[ 1349.113406] device->dev = 0xee90fa10
[ 1349.121617] device->filter = 0x00000000
[ 1349.125670] device->cap_mask = 0x601    //  !!
[ 1349.133862] device->device_config = 0xc02598c4
[ 1349.141089] device->device_prep_dma_memcpy=0xc0259938   //  !!
[ 1349.146424] device->device_prep_slave_sg = 0x00000000          //  !!
[ 1349.156189] device->device_prep_slave_cyclic = 0x00000000    //  !!
[ 1349.164455] tx_submit pass.
[ 1349.170017] 00 05 c1 13 0b 60 f0 5b

It can start a mem_to_mem transfer but without GPIO trigger.
The cap_mask is only DMA_MEMCPY, DMA_ASYNC_TX..

When I use dma_request_chanl() API, I get a hw triggered dma channel but it does not have device_prep_dma_memcpy() function registed.
The cap_mask is 0x1E00, which DMA_MEMCPY bit is not set.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[ 1373.798536] Key get value 1
[ 1373.810160] read 8 bytes:
[ 1373.812952] 00 05 c1 13 0b 60 f0 5b
[ 1373.824948] got dma_buf = 0xf0eca000 dma_buf_phys_addr = 0xbd042000
[ 1373.834724] got dma_ch = 0xee972310
[ 1373.841227] dmaengine_slave_config pass.
[ 1373.845376] device = 0xee94f044
[ 1373.853261] device->chancnt = 62
[ 1373.859267] device->dev_id = 0
[ 1373.862495] device->dev = 0xee90fa10
[ 1373.866261] device->filter = 0xc025a2bc
[ 1373.876557] device->cap_mask = 0x1e00     //   !!
[ 1373.883061] device->device_config = 0xc02598c4
[ 1373.890323] device->device_prep_dma_memcpy=0x00000000  //  !!
[ 1373.895657] device->device_prep_slave_sg = 0xc0259f8c           //  !!
[ 1373.905423] device->device_prep_slave_cyclic = 0xc0259b90    // !!
[ 1373.913609] device_prep_dma_memcpy = NULL

  

Question:

能否用dmaengine api实现gpio event触发的mem_to_mem传输?

  • 可以发整体的文件给我吗?我这边测试一下

  • 附件是测试代码

    有点乱,没怎么整理

    有个init_dma函数有两份,一个是从dts申请通道的的,一个是直接request的

    dts配置在上面帖子上

  • 问题自己已经解决

    通过自己修改driver/edma.c,为edma的非memcpy通道也配置上mem_to_mem函数指针即可

    static void edma_dma_init(struct edma_cc *ecc, bool legacy_mode)
    {
    	struct dma_device *s_ddev = &ecc->dma_slave;
    	struct dma_device *m_ddev = NULL;
    	s32 *memcpy_channels = ecc->info->memcpy_channels;
    	int i, j;
    
    	dma_cap_zero(s_ddev->cap_mask);
    	dma_cap_set(DMA_SLAVE, s_ddev->cap_mask);
    	dma_cap_set(DMA_CYCLIC, s_ddev->cap_mask);
    	if (ecc->legacy_mode && !memcpy_channels) {
    		dev_warn(ecc->dev,
    			 "Legacy memcpy is enabled, things might not work\n");
    
    		dma_cap_set(DMA_MEMCPY, s_ddev->cap_mask);
    		s_ddev->device_prep_dma_memcpy = edma_prep_dma_memcpy;
    		s_ddev->directions = BIT(DMA_MEM_TO_MEM);
    	}
    
    	s_ddev->device_prep_slave_sg = edma_prep_slave_sg;
    	s_ddev->device_prep_dma_cyclic = edma_prep_dma_cyclic;
    	s_ddev->device_alloc_chan_resources = edma_alloc_chan_resources;
    	s_ddev->device_free_chan_resources = edma_free_chan_resources;
    	s_ddev->device_issue_pending = edma_issue_pending;
    	s_ddev->device_tx_status = edma_tx_status;
    	s_ddev->device_config = edma_slave_config;
    	s_ddev->device_pause = edma_dma_pause;
    	s_ddev->device_resume = edma_dma_resume;
    	s_ddev->device_terminate_all = edma_terminate_all;
    	s_ddev->device_synchronize = edma_synchronize;
    
    	s_ddev->src_addr_widths = EDMA_DMA_BUSWIDTHS;
    	s_ddev->dst_addr_widths = EDMA_DMA_BUSWIDTHS;
    	s_ddev->directions |= (BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV));
    	s_ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
    
    //////////////////////////////////////////////////////
    	/* cet,liudachuan: try to support mem_to_mem transfer */
    	s_ddev->device_prep_dma_memcpy = edma_prep_dma_memcpy;
    	s_ddev->directions |= BIT(DMA_MEM_TO_MEM);
    	/* end of mod */
    //////////////////////////////////////////////////////
    
    	s_ddev->dev = ecc->dev;
    	INIT_LIST_HEAD(&s_ddev->channels);
    
    	if (memcpy_channels) {
    		m_ddev = devm_kzalloc(ecc->dev, sizeof(*m_ddev), GFP_KERNEL);
    		ecc->dma_memcpy = m_ddev;
    
    		dma_cap_zero(m_ddev->cap_mask);
    		dma_cap_set(DMA_MEMCPY, m_ddev->cap_mask);
    
    		m_ddev->device_prep_dma_memcpy = edma_prep_dma_memcpy;
    		m_ddev->device_alloc_chan_resources = edma_alloc_chan_resources;
    		m_ddev->device_free_chan_resources = edma_free_chan_resources;
    		m_ddev->device_issue_pending = edma_issue_pending;
    		m_ddev->device_tx_status = edma_tx_status;
    		m_ddev->device_config = edma_slave_config;
    		m_ddev->device_pause = edma_dma_pause;
    		m_ddev->device_resume = edma_dma_resume;
    		m_ddev->device_terminate_all = edma_terminate_all;
    		m_ddev->device_synchronize = edma_synchronize;
    
    		m_ddev->src_addr_widths = EDMA_DMA_BUSWIDTHS;
    		m_ddev->dst_addr_widths = EDMA_DMA_BUSWIDTHS;
    		m_ddev->directions = BIT(DMA_MEM_TO_MEM);
    		m_ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
    
    		m_ddev->dev = ecc->dev;
    		INIT_LIST_HEAD(&m_ddev->channels);
    	} else if (!ecc->legacy_mode) {
    		dev_info(ecc->dev, "memcpy is disabled\n");
    	}
    
    	for (i = 0; i < ecc->num_channels; i++) {
    		struct edma_chan *echan = &ecc->slave_chans[i];
    		echan->ch_num = EDMA_CTLR_CHAN(ecc->id, i);
    		echan->ecc = ecc;
    		echan->vchan.desc_free = edma_desc_free;
    
    		if (m_ddev && edma_is_memcpy_channel(i, memcpy_channels))
    			vchan_init(&echan->vchan, m_ddev);
    		else
    			vchan_init(&echan->vchan, s_ddev);
    
    		INIT_LIST_HEAD(&echan->node);
    		for (j = 0; j < EDMA_MAX_SLOTS; j++)
    			echan->slot[j] = -1;
    	}
    }

x 出现错误。请重试或与管理员联系。
x 出现错误。请重试或与管理员联系。