3、DMA引擎初始化:DMA设备树节点解析、DMA通道申请、DMA传输方向配置
在全志T113平台上,UART的DMA模式驱动开发中,DMA引擎的初始化是连接UART外设与DMA控制器(DMAC)的关键桥梁。本节将深入解析DMA设备树节点的解析过程、DMA通道的申请机制以及传输方向的配置方法,帮助开发者建立从硬件描述到软件驱动的完整链路。
3.1 DMA设备树节点解析
全志T113的DMA控制器在设备树中通常以dma-controller节点描述,而UART节点则通过dmas和dma-names属性引用DMA通道。内核在启动时通过of_dma_controller_register或of_dma_simple_xlate机制解析这些节点。
关键设备树属性说明:
dmas:定义UART使用的DMA通道句柄,格式为<DMA控制器句柄 通道号 请求号>。dma-names:为每个DMA通道指定逻辑名称,如"rx"和"tx",便于驱动代码索引。#dma-cells:DMA控制器节点中定义,表示每个DMA描述符需要的参数个数,T113通常为3。
// 全志T113 DMA控制器设备树节点示例
dma: dma-controller@01c02000 {
compatible = "allwinner,sun8i-t113-dma";
reg = <0x01c02000 0x1000>;
interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_DMA>;
resets = <&ccu RST_BUS_DMA>;
#dma-cells = <3>; // 参数:通道号、请求号、优先级
dma-channels = <8>; // 支持8个DMA通道
};
// UART3节点引用DMA通道示例
uart3: serial@01c28c00 {
compatible = "allwinner,sun8i-t113-uart";
reg = <0x01c28c00 0x400>;
interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_UART3>;
dmas = <&dma 6 6 0>, <&dma 6 7 0>; // RX通道6, TX通道7
dma-names = "rx", "tx";
status = "okay";
};
解析流程提示:驱动中通过
of_parse_phandle或of_property_match_string获取dma-names对应的句柄,再调用of_dma_request_slave_channel完成通道申请。T113的DMA请求号与UART实例绑定,例如UART3的RX请求号为6,TX请求号为7。
3.2 DMA通道申请
在UART驱动初始化阶段,通过dma_request_slave_channel或dma_request_chan接口申请DMA通道。全志T113的DMA驱动基于dmaengine框架,申请成功后返回struct dma_chan指针,用于后续传输控制。
| API函数 | 功能描述 | 返回类型 |
|---|---|---|
dma_request_slave_channel(dev, name) |
根据设备指针和通道名称申请DMA通道 | struct dma_chan * |
dma_request_chan(dev, name) |
新版API,支持更灵活的通道匹配 | struct dma_chan * |
dma_release_channel(chan) |
释放已申请的DMA通道 | void |
// UART驱动中DMA通道申请示例代码
#include <linux/dmaengine.h>
struct uart_dma_data {
struct dma_chan *rxchan;
struct dma_chan *txchan;
struct dma_slave_config rx_config;
struct dma_slave_config tx_config;
};
static int uart_dma_init(struct platform_device *pdev, struct uart_dma_data *dma)
{
int ret;
// 申请RX DMA通道
dma->rxchan = dma_request_slave_channel(&pdev->dev, "rx");
if (!dma->rxchan) {
dev_err(&pdev->dev, "Failed to request RX DMA channel\n");
return -ENODEV;
}
// 申请TX DMA通道
dma->txchan = dma_request_slave_channel(&pdev->dev, "tx");
if (!dma->txchan) {
dev_err(&pdev->dev, "Failed to request TX DMA channel\n");
dma_release_channel(dma->rxchan);
return -ENODEV;
}
dev_info(&pdev->dev, "DMA channels allocated: RX=%s, TX=%s\n",
dma_chan_name(dma->rxchan), dma_chan_name(dma->txchan));
return 0;
}
通道申请避坑:
- 确保设备树中
dma-names与驱动中传入的名称完全一致(大小写敏感)。 - T113的DMA通道数量有限(8个),多UART实例同时使用DMA时需检查通道占用情况。
- 申请失败时务必释放已成功申请的通道,避免资源泄漏。
3.3 DMA传输方向配置
DMA传输方向通过dma_slave_config结构体配置,核心参数包括源/目标地址、数据宽度、突发长度等。全志T113的DMA控制器支持DMA_MEM_TO_DEV(内存到设备,TX方向)和DMA_DEV_TO_MEM(设备到内存,RX方向)两种传输方向。
| 配置参数 | TX方向(MEM_TO_DEV) | RX方向(DEV_TO_MEM) |
|---|---|---|
direction |
DMA_MEM_TO_DEV |
DMA_DEV_TO_MEM |
src_addr |
内存缓冲区地址(自动设置) | UART接收FIFO物理地址 |
dst_addr |
UART发送FIFO物理地址 | 内存缓冲区地址(自动设置) |
src_addr_width |
DMA_SLAVE_BUSWIDTH_1_BYTE |
DMA_SLAVE_BUSWIDTH_1_BYTE |
dst_addr_width |
DMA_SLAVE_BUSWIDTH_1_BYTE |
DMA_SLAVE_BUSWIDTH_1_BYTE |
src_maxburst |
4(建议值,与FIFO深度匹配) | 4(建议值,与FIFO深度匹配) |
dst_maxburst |
4(建议值,与FIFO深度匹配) | 4(建议值,与FIFO深度匹配) |
// DMA传输方向配置示例代码
static int uart_dma_configure(struct uart_dma_data *dma, phys_addr_t uart_base)
{
int ret;
// 配置TX方向:内存 -> UART发送FIFO
dma->tx_config.direction = DMA_MEM_TO_DEV;
dma->tx_config.dst_addr = uart_base + 0x00; // UART_THR寄存器偏移
dma->tx_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma->tx_config.dst_maxburst = 4; // 每次突发传输4字节
dma->tx_config.src_maxburst = 4;
ret = dmaengine_slave_config(dma->txchan, &dma->tx_config);
if (ret) {
pr_err("TX DMA slave config failed: %d\n", ret);
return ret;
}
// 配置RX方向:UART接收FIFO -> 内存
dma->rx_config.direction = DMA_DEV_TO_MEM;
dma->rx_config.src_addr = uart_base + 0x00; // UART_RBR寄存器偏移
dma->rx_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma->rx_config.src_maxburst = 4;
dma->rx_config.dst_maxburst = 4;
ret = dmaengine_slave_config(dma->rxchan, &dma->rx_config);
if (ret) {
pr_err("RX DMA slave config failed: %d\n", ret);
return ret;
}
return 0;
}
3.4 初始化流程整合
将上述三个步骤整合到UART驱动的probe函数中,形成完整的DMA引擎初始化流程。以下为推荐的初始化顺序和关键检查点:
- 解析设备树:通过
of_property_read_u32获取DMA相关属性,验证dmas和dma-names是否存在。 - 申请DMA通道:分别调用
dma_request_slave_channel申请RX和TX通道,检查返回值。 - 配置传输方向:填充
dma_slave_config结构体,调用dmaengine_slave_config生效。 - 分配DMA缓冲区:使用
dma_alloc_coherent或dma_map_single分配一致/流式DMA缓冲区。 - 注册DMA回调:通过
dmaengine_prep_slave_sg或dmaengine_prep_dma_cyclic准备传输描述符,并设置完成回调函数。
性能调试建议:在初始化完成后,可以通过
/sys/kernel/debug/dmaengine/summary查看DMA通道的使用状态。全志T113的DMA引擎支持循环传输模式(cyclic),适用于UART的连续数据收发场景,可减少中断频率,提升吞吐量。