4、设备树DMA绑定:dmas与dma-names属性解析、DMA通道与中断关联、DMA引擎节点配置

设备树(Device Tree)是嵌入式Linux系统中描述硬件拓扑的核心手段。对于DMA控制器及其外设客户端,设备树通过标准化的绑定属性将DMA通道、中断信号和传输配置暴露给内核驱动。本章深入解析dmas、dma-names属性,梳理DMA通道与中断的关联逻辑,并给出DMA引擎节点的完整配置范例。

4.1 dmas与dma-names属性解析

在设备树中,任何需要DMA服务的外设节点(如UART、SPI、I2S)都必须通过dmas属性声明其使用的DMA通道,并通过dma-names属性为每个通道赋予语义名称。这两个属性共同构成了外设与DMA控制器之间的“软连接”。

属性 类型 描述 示例值
dmas <phandle> <u32> ... 指向DMA控制器的phandle + 通道/请求号列表 <&dma0 2>, <&dma0 3>
dma-names stringlist 为每个dmas条目指定名称,驱动通过名称查找通道 "rx", "tx"
关键规则:
  1. dmas中的每个条目由两部分组成:DMA控制器的phandle(引用)和通道/请求号(具体含义由DMA控制器驱动定义)。
  2. dma-names中的字符串顺序必须与dmas条目顺序一一对应。
  3. 驱动通过dma_request_slave_channel(dev, name)或dma_request_chan(dev, name)函数,传入dma-names中的名称来获取对应的DMA通道。

以下是一个典型的UART设备节点DMA绑定示例:

uart0: serial@10000000 {
    compatible = "vendor,uart-ip";
    reg = <0x10000000 0x1000>;
    interrupts = <0 33 4>;
    dmas = <&dma0 2>, <&dma0 3>;
    dma-names = "rx", "tx";
    dma-coherent;
    status = "okay";
};

上述代码中:

  • &dma0 2:表示使用dma0控制器的通道2,用于接收(rx)。
  • &dma0 3:表示使用dma0控制器的通道3,用于发送(tx)。
  • dma-coherent:可选属性,声明外设与CPU之间DMA操作是缓存一致的,避免驱动额外执行缓存刷新操作。
调试提示:在设备树调试阶段,如果驱动无法获取DMA通道,首先检查dmas中的phandle是否正确指向了已使能的DMA控制器节点,其次确认dma-names字符串与驱动中调用的名称完全匹配(包括大小写)。

4.2 DMA通道与中断关联

DMA传输的完成、错误或半传输事件通常通过中断通知CPU。在设备树中,DMA通道与中断的关联有两种常见模式:

模式 描述 适用场景
外设中断模式 中断号定义在外设节点中,DMA传输完成后触发外设中断 传统外设(UART、SPI),DMA只是加速手段
DMA控制器中断模式 中断号定义在DMA控制器节点中,每个DMA通道有独立中断 专用DMA引擎、内存到内存传输、多通道并发

在外设中断模式下,DMA传输完成后,DMA控制器通过硬件握手信号通知外设,外设再向中断控制器提交中断。此时设备树中只有外设节点包含interrupts属性,DMA控制器节点通常不暴露通道中断。

在DMA控制器中断模式下,每个DMA通道在DMA控制器内部有独立的中断线。设备树中DMA控制器节点会定义interrupts属性,每个中断对应一个通道或一组通道。外设节点通过dmas引用通道时,中断关联由DMA引擎驱动在内部建立。

移植陷阱:当从外设中断模式迁移到DMA控制器中断模式时,必须修改外设驱动的中断处理逻辑。外设驱动不再直接处理DMA完成中断,而是由DMA引擎驱动回调外设驱动的完成回调函数。忽略此差异会导致传输完成无法被感知,造成数据丢失。

以下SVG图展示了两种中断关联模式的拓扑对比:

DMA通道与中断关联模式对比 外设中断模式 DMA控制器 外设 (UART) 中断控制器 握手 中断请求 DMA控制器中断模式 DMA控制器 (通道0 IRQ) 外设 (SPI) 中断控制器 通道中断 数据流 外设节点包含 interrupts 属性 DMA完成中断由外设处理 DMA控制器节点包含 interrupts 属性 驱动通过回调通知外设

4.3 DMA引擎节点配置

DMA引擎节点(DMA控制器本身)的设备树配置决定了通道数量、中断映射、时钟和复位控制等底层参数。一个完整的DMA控制器节点通常包含以下关键属性:

属性 类型 描述 示例值
compatible string DMA控制器兼容字符串,用于匹配驱动 "vendor,dma-engine"
reg <u64> <u64> 寄存器基地址和范围 <0x20000000 0x1000>
interrupts <u32> ... 每个通道或通道组的中断号 <0 0 4>, <0 1 4>
#dma-cells <u32> 定义dmas属性中phandle之后的参数个数 <1>
dma-channels <u32> 硬件支持的DMA通道总数 <8>
clocks <phandle> DMA控制器的时钟源 <&clk_dma>
resets <phandle> DMA控制器的复位信号 <&rst_dma>

以下是一个完整的DMA引擎节点配置示例:

dma0: dma-controller@20000000 {
    compatible = "vendor,dma-engine";
    reg = <0x20000000 0x1000>;
    interrupts = <0 0 4>, <0 1 4>, <0 2 4>, <0 3 4>;
    interrupt-names = "ch0", "ch1", "ch2", "ch3";
    #dma-cells = <1>;
    dma-channels = <4>;
    clocks = <&clk_dma>;
    resets = <&rst_dma>;
    dma-coherent;
    status = "okay";
};

配置要点说明:

  • #dma-cells = <1>:表示外设在dmas属性中引用此控制器时,phandle后只需1个参数(通道号)。如果DMA控制器需要更多参数(如请求线、优先级),则#dma-cells值相应增加。
  • interrupt-names:为每个中断指定名称,便于驱动通过platform_get_irq_byname()获取特定通道的中断号。
  • dma-channels:声明硬件通道数量,驱动据此初始化通道描述符数组。
移植检查清单:
  1. 确认#dma-cells的值与DMA控制器驱动的解析代码一致。
  2. 检查interrupts数量是否与dma-channels匹配(或按硬件手册的通道分组方式配置)。
  3. 验证clocks和resets是否在系统时钟树和复位控制器中正确定义。
  4. 如果外设节点使用了dma-coherent,确保DMA控制器节点也支持一致性映射,否则驱动需手动处理缓存同步。

以下SVG图展示了DMA引擎节点与外设节点之间的绑定关系:

设备树DMA绑定关系拓扑 dma0: dma-controller #dma-cells = <1> dma-channels = <4> interrupts = <ch0..ch3> uart0: serial dmas = <&dma0 2> dma-names = "rx" spi0: spi dmas = <&dma0 0>, <&dma0 1> dma-names = "rx", "tx" i2s0: i2s dmas = <&dma0 3> dma-names = "tx" 通道2 通道0,1 通道3 中断控制器 (GIC) 通道中断
最佳实践:在编写DMA引擎节点时,建议将interrupt-names与dma-channels的通道编号对齐,例如"ch0"对应通道0。这样在驱动调试时,通过/proc/interrupts可以直观地看到每个DMA通道的中断活动,极大简化故障排查。