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。
DMA设备树节点解析流程 设备树根节点 dma-controller@01c02000 uart3@01c28c00 dmas = <&dma 6 6 0> dma-names = "rx", "tx" #dma-cells = 3

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;
}
DMA传输方向配置架构 系统内存 TX缓冲区 RX缓冲区 DMA控制器 TX通道 (Ch6) RX通道 (Ch7) UART3外设 发送FIFO (THR) 接收FIFO (RBR) TX RX 传输方向配置关键参数 • TX方向:dst_addr = UART_THR, direction = DMA_MEM_TO_DEV • RX方向:src_addr = UART_RBR, direction = DMA_DEV_TO_MEM • 数据宽度:DMA_SLAVE_BUSWIDTH_1_BYTE(UART为字节传输) • 突发长度:src/dst_maxburst = 4(匹配T113 UART FIFO深度) • 地址类型:外设地址使用物理地址,需通过ioremap或平台资源获取

3.4 初始化流程整合

将上述三个步骤整合到UART驱动的probe函数中,形成完整的DMA引擎初始化流程。以下为推荐的初始化顺序和关键检查点:

  1. 解析设备树:通过of_property_read_u32获取DMA相关属性,验证dmas和dma-names是否存在。
  2. 申请DMA通道:分别调用dma_request_slave_channel申请RX和TX通道,检查返回值。
  3. 配置传输方向:填充dma_slave_config结构体,调用dmaengine_slave_config生效。
  4. 分配DMA缓冲区:使用dma_alloc_coherent或dma_map_single分配一致/流式DMA缓冲区。
  5. 注册DMA回调:通过dmaengine_prep_slave_sg或dmaengine_prep_dma_cyclic准备传输描述符,并设置完成回调函数。
性能调试建议:在初始化完成后,可以通过/sys/kernel/debug/dmaengine/summary查看DMA通道的使用状态。全志T113的DMA引擎支持循环传输模式(cyclic),适用于UART的连续数据收发场景,可减少中断频率,提升吞吐量。