【问题标题】:Linux DMA API: specifying address increment behavior?Linux DMA API:指定地址增量行为?
【发布时间】:2015-04-09 13:13:37
【问题描述】:

我正在为Altera Soc Developement Kit 编写驱动程序,并且需要支持两种模式的数据与 FPGA 之间的传输:

  1. FIFO 传输:写入(或读取)FPGA FIFO 时,目标(或源)地址不得由 DMA 控制器递增。
  2. 非 FIFO 传输:这些是正常(类似于 RAM)的传输,其中源地址和目标地址都需要为每个传输的字增加一个增量。

我使用的特定 DMA 控制器是 CoreLink DMA-330 DMA 控制器,它的 Linux 驱动程序是 pl330.c (drivers/dma/pl330.c)。这个 DMA 控制器确实提供了一种在“固定地址突发”和“递增地址突发”之间切换的机制(这些是我的“FIFO 传输”和“非 FIFO 传输”的同义词)。 pl330 驱动程序通过在 CCRn 寄存器中设置适当的位来指定它想要的行为

#define CC_SRCINC       (1 << 0)
#define CC_DSTINC       (1 << 14)

我的问题:我完全不清楚 pl330 的客户端(例如我的驱动程序)应该如何指定地址递增行为。

DMA engine client API 没有说明如何指定这一点,而 DMA engine provider API 只是声明:

指向 RAM 的地址通常递增(或递减) 每次转移后。如果是环形缓冲区,它们可能会循环 (DMA_CYCLIC)。指向设备寄存器的地址(例如 FIFO) 通常是固定的。

没有详细说明地址类型如何与提供者通信(在我的例子中是 pl300 驱动程序)。

它在 pl330_prep_slave_sg 方法中的作用:

 if (direction == DMA_MEM_TO_DEV) {                  
     desc->rqcfg.src_inc = 1;                        
     desc->rqcfg.dst_inc = 0;                        
     desc->req.rqtype = MEMTODEV;                    
     fill_px(&desc->px,                              
         addr, sg_dma_address(sg), sg_dma_len(sg));  
 } else {                                            
     desc->rqcfg.src_inc = 0;                        
     desc->rqcfg.dst_inc = 1;                        
     desc->req.rqtype = DEVTOMEM;                    
     fill_px(&desc->px,                              
         sg_dma_address(sg), addr, sg_dma_len(sg));  
 }    

之后,驱动程序使用 desc->rqcfg.src_inc 和 desc->rqcfg.dst_inc 来指定地址增量行为。

这意味着以下内容:

  • 指定方向 = DMA_MEM_TO_DEV 表示客户端希望将数据从 FIFO 拉入 RAM。并且推测 DMA_DEV_TO_MEM 意味着客户端希望将数据从 RAM 推送到 FIFO。
  • Scatter-gather DMA 操作(至少对于 pl300)仅限于源端点或目标端点是 FIFO 的情况。如果我想从系统 RAM 到 FPGA(非 FIFO)内存进行分散收集操作怎么办?

我是否误解和/或忽略了什么? DMA 引擎是否已经提供(未记录的)机制来指定地址增量行为?

【问题讨论】:

    标签: linux linux-kernel dma


    【解决方案1】:

    看看这个

    pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
    pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
    pd->device_prep_slave_sg = pl330_prep_slave_sg;
    

    这意味着您有不同的方法,就像您在文档中阅读的那样。我怀疑可以通过device_prep_dma_memcpy() 进行类似 RAM 的传输。

    【讨论】:

    • 是的,我从阅读 pl330 驱动程序中收集到这一点。看起来我可以为这个特定的 DMA 控制器驱动程序指定地址增量行为(通过 prep_dma_memcpy() 或 prep_slave_sg() 方法)。但这是所有 DMA 控制器驱动程序使用的约定吗?如果使用新的 DMA 控制器驱动程序,我不希望我的 DMA 客户端驱动程序中断。
    • 您必须修复所有您想使用的驱动程序。通用实现允许您使用有限的功能集。不同的 DMA 控制器行为不同,因此 DMA 通道的用户应该应对所有可能性。
    【解决方案2】:

    在我看来(在查看内核中的各种驱动程序之后)唯一允许您(间接)控制自动增量行为的 DMA 传输样式是在其对应的enum dma_transfer_direction 函数中具有enum dma_transfer_direction 的那些。

    并且这个参数声明只针对device_prep_slave_sgdevice_prep_dma_cyclic,根据include/linux/dmaengine.h

    另一个选项应该是使用和struct dma_interleaved_template,它允许您直接指定增量行为。但是对这种方法的支持是有限的(例如,只有 i.MX DMA 驱动程序在 3.8 内核中支持它。甚至这种支持似乎也是有限的)

    所以我认为,我们在一段时间内被 device_prep_slave_sg 的所有与 sg 相关的复杂性所困扰。

    这就是我目前正在做的事情(虽然它是为了访问 Atmel SAM9 SOC 上的一些 EBI 连接设备)

    要考虑的另一件事是设备的总线宽度。 memcopy-variant 可以执行不同的总线宽度传输,具体取决于源和目标地址和大小。这可能与 FIFO 元素的大小不匹配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-24
      • 1970-01-01
      • 2016-06-20
      • 2015-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多