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.

[参考译文] AM3358:将 GPMC EDMA 解决方案从 3.2.X 内核移植到 6.12.X

Guru**** 2416110 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1529261/am3358-porting-gpmc-edma-solution-from-3-2-x-kernel-to-6-12-x

部件号:AM3358

工具/软件:

尊敬的同事:

在这一刻,我正在努力将源代码从 3.2 Linux 内核移植到 6.12(是的,这是相当大的跳跃:))。

我使用的内核来自: git.ti.com/.../

在我的器件中、我使用 GPMC 总线与 FPGA 进行通信。 为了加快传输过程、我使用 DMA。

下面展示了 3.2 内核的代码片段:

dma_ch = edma_alloc_channel(EDMA_CHANNEL_ANY, dma_complete_callback, NULL, EVENTQ_DEFAULT);

edma_set_src (dma_ch, (dma_addr_t)(FPGA_BASE_DMA), INCR, W16BIT);
edma_set_dest(dma_ch, dmaphysdest + (bufnum * frame_size) +
                      (portion * dma_transfer_size), INCR, W16BIT);
edma_set_src_index(dma_ch, dma_transfer_size, dma_transfer_size);
edma_set_dest_index(dma_ch, dma_transfer_size, dma_transfer_size);
// Use A-Sync Transfer Mode
edma_set_transfer_params(dma_ch, dma_transfer_size,
                            dma_bcount,
                            dma_ccount,
                            dma_bcount_reload, ASYNC);


 edma_read_slot(dma_ch, &param_set);
// disable the Intermediate Int's
param_set.opt &= ~(ITCINTEN);
if ((dma_bcount > 1) || (dma_ccount > 1)){
    // enable the Intermediate Int's
     //param_set.opt |= (ITCINTEN);
     // enable chaining
     param_set.opt |= (ITCCHEN);
}else{
    // disable the Intermediate Int's
    param_set.opt &= ~(ITCINTEN);
    // disble chaining
    param_set.opt &= ~(ITCCHEN);
}
   // enable the final int tc
param_set.opt |= (TCINTEN);
param_set.opt |= EDMA_TCC((dma_ch & 0x3f));
param_set.opt |= EDMA_TCC(EDMA_CHAN_SLOT(dma_ch));

edma_write_slot(dma_ch, &param_set);

edma_start(dma_ch);

3.2 上的代码运行良好。

在 6.12 上、我必须使用 dmaengine。

我的器件树相关 GPMC 如下所示:

&gpmc {
	pinctrl-names = "default";
	pinctrl-0 = <&gpmc_pins>;
    compatible = "test,fpga";
    interrupts = <100>;
    gpmc,num-cs = <2>;
	gpmc,num-waitpins = <2>;

	status = "okay";
    dmas = <&edma 52 0>;
    dma-names = "rx";

    interrupt-controller;
	#interrupt-cells = <2>;

	#address-cells = <2>;
	#size-cells = <1>;
	ranges = <0 0 0x08000000 0x01000000   /* CS0 @addr 0x08000000, size 0x1000000 */
	          1 0 0x09000000 0x01000000>; /* CS1 @addr 0x09000000, size 0x1000000 */

	    fpga_control@0,0 {
		
			status = "okay";
			#address-cells = <1>;
			#size-cells = <1>;
			reg = <0 0 0x01000000>;
			rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */
	    };
 
		fpga_stream@1,0 {
		
			status = "okay";
			#address-cells = <1>;
			#size-cells = <1>;
			reg = <1 0 0x01000000>;
			rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */

	};
};

我从源代码中配置 GPMC 寄存器、就像之前在 3.2 上配置的一样、如下所示:

CS[0] GPMC_CONFIG1 0xA9401001
CS[0] GPMC_CONFIG2 0x00060800
CS[0] GPMC_CONFIG3 0x00020200
CS[0] GPMC_CONFIG4 0x04020600
CS[0] GPMC_CONFIG5 0x0204080A
CS[0] GPMC_CONFIG6 0x040702C0
CS[0] GPMC_CONFIG7 0x00000F48

CS[1] GPMC_CONFIG1 0xE9401001
CS[1] GPMC_CONFIG2 0x00060800
CS[1] GPMC_CONFIG3 0x00020200
CS[1] GPMC_CONFIG4 0x04020600
CS[1] GPMC_CONFIG5 0x0204080A
CS[1] GPMC_CONFIG6 0x040702C0
CS[1] GPMC_CONFIG7 0x00000F49

负责启动 DMA 的源代码:

void __iomem* dma_virt_addr = dma_alloc_coherent(dev->parent, 1024*1024, &dma_addr, GFP_KERNEL);
  

memset(&dma_cfg, 0, sizeof(dma_cfg));
dma_cfg.direction = DMA_DEV_TO_MEM;

dma_cfg.src_addr = fpga_stream.gpmc_base.start; //0x09000000
dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dma_cfg.src_maxburst = 16;

ret = dmaengine_slave_config(fpga_stream.dma_ch, &dma_cfg);
if (ret) {
	DRV_MSG("DMA engine slave config failed: %d\n", ret);
	return -1;
}  
 
 
dma_desc = dmaengine_prep_slave_single(fpga_stream.dma_ch, dma_addr, 1024, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);

if (!dma_desc)
{
	DRV_MSG("DMA busy\n");
	return -1;
}

dma_desc->callback = dma_complete_callback;
dma_desc->callback_param = NULL;

dma_cookie = dmaengine_submit(dma_desc);
if (dma_submit_error(dma_cookie)) {
	DRV_MSG("DMA transaction submission FAILED\n");
	return -1;
}

DRV_MSG("DMA transaction submission SUCCESS\n");
    

dma_async_issue_pending(fpga_stream.dma_ch);
DRV_MSG("DMA start\n");

在 dmesg 上、我看到 DMA 通道已配置:

[   67.974832] edma 49000000.dma: Got eDMA channel 52 for virt channel 50 (HW trigger)
[   68.056257] edma 49000000.dma: vchan a74ee4d7: txd fd8f725f[2]: submitted
[   68.062970] edma 49000000.dma: 
[   68.062970]  pset[0]:
[   68.062970]   chnum  52
[   68.062970]   slot   49
[   68.062970]   opt    00134004
[   68.062970]   src    09000000
[   68.062970]   dst    95100000
[   68.062970]   abcnt  00100004
[   68.062970]   ccnt   00000010
[   68.062970]   bidx   00040000
[   68.062970]   cidx   00400000
[   68.062970]   lkrld  ffffffff
[   68.062995] edma 49000000.dma: first transfer starting on channel 52
[   68.063008] edma 49000000.dma: ER1 00000000
[   68.063021] edma 49000000.dma: EER1 00100000

我的问题是我看不到 DMA 完全回调。 在 3.2 内核回调中正确执行、没有任何问题。

我将感谢任何关于这种行为的原因的建议。

提前感谢!

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Viktor:

    II 无法仅通过查看您提供的代码段来指出错误配置的确切内容。 您可能需要比较 EDMA 寄存器以查看不同的配置。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好:

    配置与 EDMA 相关的问题是相同的。

    也许在 6.12 中、我必须显式配置一些 在 3.2 中隐式配置的中断?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    经过更深入的调查、我发现如果是 3.2 内核、param_set opt 寄存器如下所示:

    EDMA_OPT 0x80102000

    位 31 (PRIV) 的值为 1。 对于 6.12 内核、该位为 0。

     为什么会这样呢?

    谢谢!

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    最后、我通过 EDMA 从 GPMC 向存储器启动了 DMA 事务。

    问题是我已将 DMA 配置为 dma_dev_to_MEM 模式。 它在大多数文档页面中的建议方式。 dma_dev_to_MEM 模式需要硬件触发器。 老实说、调试消息中提到了这个问题:

    edma 49000000.dma: Got eDMA channel 52 for virt channel 50 (HW trigger)

    但在本例中我没有硬件触发器。 我在 3.2 内核中使用的源代码描述了 DMA_MEM_TO_MEM 概念。

    下面展示了我在我这边工作的代码片段:

    dma_cap_zero(mask);
    dma_cap_set(DMA_MEMCPY,  mask);
    dma_ch = dma_request_channel(mask, NULL, NULL);
        
    dma_virt_addr = dma_alloc_coherent(dev->parent, 1024*1024, &dma_addr, GFP_KERNEL);
    dma_desc = dmaengine_prep_dma_memcpy(dma_ch, dma_addr, gpmc_base.start,	2*0x2480, DMA_PREP_INTERRUPT);
     
    if (!dma_desc)
    {
    	DRV_MSG("DMA busy\n");
        return -1;
    }
    
    dma_desc->callback = dma_complete_callback
    dma_desc->callback_param = dma_virt_addr;
    
    dma_cookie = dmaengine_submit(dma_desc);
    if (dma_submit_error(dma_cookie)) {
    	DRV_MSG("DMA transaction submission FAILED\n");
    	return -1;
    }
    
    DRV_MSG("DMA transaction submission SUCCESS\n");
        
    dma_async_issue_pending(fpga_stream.dma_ch);
    DRV_MSG("DMA start\n");	

    启动我的代码后、我在 dmesg 中看到以下消息:

    edma 49000000.dma: Got eDMA channel 20 for virt channel 0 (SW trigger)

    在本例中、我有一个问题。 使用 DMA_MEM_TO_MEM 而不是  与 DMA_DEV_TO_MEM 相关的 GPMC 总线的正确方法是否?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Viktor:

    很抱歉、我今天正在调试另一个问题、没有时间查看您的消息。 我会试着在明天回顾一下、然后回到您的身边。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Viktor:

    我的配置方式与您在内核 6.12 上使用 DMA 的方式相同。

    在您的原始帖子中:

    dma_cfg.src_addr = FPGA_STREAM.GPMC_BASE.START;//0x09000000
    dma_cfg.src_addr_width = dma_slave_BUSWIDTH_4_bytes;
    dma_cfg.src_maxburst = 16;

    RET = dmaengine_slave_config (fgo_stream.dma_ch、&dma_cfg);
    if (ret){
       DRV_MSG(“DMA 引擎从配置失败:%d\n“、ret);
       返回–1;
    }  
     
    dma_desc = dmaenge_prep_slave_single (fgo_stream.dma_slave_ch、dma_addr、1024、dma_dev_to_MEM、dma_prep_interrupt | dma_CTRL_ACK);

    使用 DEV_TO_MEM 传输 1024 个字节、总线宽度为 4 个字节。 但在您于 6 月 19 日发布的帖子中:

    DMA_CAP_SET (DMA_memcpy、 MASK);
    DMA_ch = DMA_REQUEST_CHANNEL (MASK、NULL、NULL);
       
    dma_virt_addr = dma_alloc_ocherent (dev->parent、1024*1024、&dma_addr、gfp_kernel);
    dma_desc = dmaENGINE_PREP_DMA_memcpy (dma_ch、dma_addr、GPMC_BASE.start   、2* 0x2480、dma_prep_interrupt);

    使用 MEM_TO_MEM 传输 2* 0x2480 字节。

    哪个版本是最终版本? 您的 FPGA 数据如何呈现到 GPMC 接口? 它是处于具有 4 字节 FIFO 宽度的 FIFO 模式、还是整个数据帧都映射到 GPMC 地址空间? 对于前一种情况、需要 DEV_TO_MEM、而对于后一种情况、需要 MEM_TO_MEM。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Bin、

    1024 2*0x2480 两种大小均有效。 这只是 FPGA 可以通过 GPMC 发送到 CPU 的不同帧。

    对于 DMA_MEM_TO_MEM、两个大小都能正常工作。 如果是 DMA_DEV_TO_MEM、两者也不起作用。

    我们使用到 GPMC 存储器空间的数据映射(在 leas ACNT=FPGA_FRAME_SIZE)

    DMA_MEM_TO_MEM 是否正确。 但我有点担心未来。  

    在物理上、我们讨论的是 DEV ->通过 GPMC 总线进行存储器数据传输。

    在我们的项目中,我们必须执行以下要点:

    -安全方面

    -在 Linux 内核的下一个版本上迁移的可能性

    我担心、如果我们讨论从器件传输数据、未来的方法 MEM -> MEM 可能会被弃用和禁止。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Viktor:

    DMA_MEM_TO_MEM 并不意味着它仅适用于存储器、不能在器件上使用。 MEM_TO_MEM 用于在源地址和目标地址中具有相同大小的 DMA 传输。 例如、从 GPMC NAND 读取时、会使用 DMA_DEV_TO_MEM、而从 GPMC NOR 读取时、会使用 DMA_MEM_TO_MEM。

    我担心未来的方法 MEM -> MEM 可以被弃用

    您在哪里获得了 MEM_TO_MEM 将被弃用的信息?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Bin、

    您在哪里获得了 MEM_TO_MEM 将被弃用的信息?

    这只是我的私人疑问。

    关于 NAND 接口我有一个很好的使用 DMA 接口的示例:

    https://git.ti.com/cgit/ti-linux-kernel/ti-linux-kernel/tree/drivers/mtd/nand/raw/omap2.c?h=ti-linux-6.12.y

    您能否提供有关使用 DMA 的 GPMC 或 NOR 实现的链接。

    谢谢!

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Viktor:

    我不知道有任何 GPMC 或非驱动程序、但以下是连接了串行或非器件的内核 OSPI 驱动程序。

    drivers/spi/spi-cadence-quadspi.c

    它的函数 cqspi_direct_read_dma () 使用 dmaenger_prep_dma_mac_memcpy ()