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" |
关键规则:
dmas中的每个条目由两部分组成:DMA控制器的phandle(引用)和通道/请求号(具体含义由DMA控制器驱动定义)。dma-names中的字符串顺序必须与dmas条目顺序一一对应。- 驱动通过
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图展示了两种中断关联模式的拓扑对比:
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:声明硬件通道数量,驱动据此初始化通道描述符数组。
移植检查清单:
- 确认
#dma-cells的值与DMA控制器驱动的解析代码一致。 - 检查
interrupts数量是否与dma-channels匹配(或按硬件手册的通道分组方式配置)。 - 验证
clocks和resets是否在系统时钟树和复位控制器中正确定义。 - 如果外设节点使用了
dma-coherent,确保DMA控制器节点也支持一致性映射,否则驱动需手动处理缓存同步。
以下SVG图展示了DMA引擎节点与外设节点之间的绑定关系:
最佳实践:在编写DMA引擎节点时,建议将
interrupt-names与dma-channels的通道编号对齐,例如"ch0"对应通道0。这样在驱动调试时,通过/proc/interrupts可以直观地看到每个DMA通道的中断活动,极大简化故障排查。