3. 设备树基础:DTS 语法入门、节点与属性详解

设备树(Device Tree,DT)是嵌入式 Linux 中用于描述硬件拓扑结构的数据结构。在全志 T113 平台中,SPI 接口的引脚分配、时钟频率、DMA 通道等硬件信息均通过设备树传递给内核驱动。本章将系统讲解 DTS 语法、节点与属性的定义,以及 reg、status、compatible 等关键属性的使用规范。

3.1 设备树文件体系与基本语法

设备树源文件(.dts)通过 dtc 编译器生成二进制 .dtb 文件,由 bootloader 加载并传递给内核。全志 T113 平台通常包含以下文件层次:

文件类型 扩展名 作用
设备树源文件 .dts 描述单板级硬件,包含所有外设节点
设备树头文件 .dtsi 描述 SoC 级通用硬件,被 .dts 包含
设备树二进制 .dtb 编译后的二进制文件,供内核解析
设备树覆盖 .dtbo 运行时动态加载的硬件描述片段

DTS 语法采用树形结构,以分号结束语句,使用大括号表示层级。以下是一个典型的节点定义:

/dts-v1/;

#include "sun8iw20p1.dtsi"

/ {
    model = "Allwinner T113 Evaluation Board";
    compatible = "allwinner,t113-evb", "allwinner,sun8iw20p1";

    soc {
        spi0: spi@04026000 {
            compatible = "allwinner,sun8i-spi";
            reg = <0x04026000 0x1000>;
            interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&ccu CLK_SPI0>, <&ccu CLK_BUS_SPI0>;
            clock-names = "spi", "ahb";
            resets = <&ccu RST_BUS_SPI0>;
            status = "disabled";
            #address-cells = <1>;
            #size-cells = <0>;
        };
    };
};
提示:全志 T113 的 .dtsi 文件通常位于 arch/arm/boot/dts/ 目录下,文件名以 sun8iw20p1 开头。SPI 控制器节点已在 .dtsi 中预定义,板级 .dts 只需修改 status 和添加子节点。
设备树文件编译与加载流程 .dts 源文件 板级硬件描述 .dtsi 头文件 SoC 级通用定义 dtc 编译器 设备树编译器 .dtb 二进制 设备树二进制 Bootloader U-Boot 加载 Linux 内核解析 关键路径说明 1. .dts 通过 #include 引用 .dtsi 2. dtc 输出 .dtb 到 boot 分区 3. U-Boot 将 .dtb 传递给内核

3.2 节点与属性基础

设备树由节点(node)和属性(property)组成。每个节点代表一个硬件设备或总线,属性描述该设备的配置参数。

3.2.1 节点命名规则

  • 节点名称:格式为 node-name@unit-address,例如 spi0@04026000。
  • unit-address:与 reg 属性的第一个地址段对应,用于区分同一层级下的相同名称节点。
  • 标签(label):在节点前添加标签,如 spi0:,便于在 .dts 中通过 &spi0 引用。

3.2.2 属性类型与写法

属性类型 语法示例 说明
字符串 compatible = "allwinner,sun8i-spi"; 用于驱动匹配,可包含多个字符串
32 位整数 reg = <0x04026000 0x1000>; 尖括号内为 32 位整数,多个值用空格分隔
64 位整数 reg = <0x00000000 0x04026000 0x00000000 0x00001000>; 使用两个 32 位整数表示一个 64 位地址
布尔值 status = "disabled"; 通过字符串表示,常用 okay 或 disabled
字节数组 local-mac-address = [00 11 22 33 44 55]; 方括号内为十六进制字节
字符串列表 clock-names = "spi", "ahb"; 逗号分隔的多个字符串
phandle clocks = <&ccu CLK_SPI0>; & 引用其他节点的标签
实战要点:在全志 T113 的 SPI 节点中,clocks 属性通常引用两个时钟源:SPI 模块时钟(CLK_SPI0)和 AHB 总线时钟(CLK_BUS_SPI0)。resets 属性用于复位控制,必须与时钟配对使用。

3.3 关键属性详解

3.3.1 compatible 属性——驱动匹配的钥匙

compatible 是设备树中最关键的属性,内核通过该属性查找对应的驱动程序。其值是一个字符串列表,优先级从前往后递减。

// SPI 控制器节点中的 compatible
compatible = "allwinner,sun8i-spi", "allwinner,sun6i-spi";

// 内核驱动匹配表(of_match_table)
static const struct of_device_id sun8i_spi_of_match[] = {
    { .compatible = "allwinner,sun8i-spi", .data = &sun8i_spi_variant },
    { .compatible = "allwinner,sun6i-spi", .data = &sun6i_spi_variant },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun8i_spi_of_match);
compatible 值 匹配优先级 适用平台
allwinner,sun8i-spi 最高(精确匹配) T113、H616 等 sun8i 系列
allwinner,sun6i-spi 次高(兼容匹配) A31、H3 等 sun6i 系列
allwinner,sun4i-spi 最低(通用匹配) A10、A20 等 sun4i 系列
注意:驱动匹配时,内核会遍历 compatible 列表,找到第一个匹配的驱动即停止。因此,应将最精确的兼容字符串放在最前面。在全志 T113 中,sun8i-spi 驱动已支持 SPI 时钟极性、DMA 传输等特性,无需回退到旧版驱动。

3.3.2 reg 属性——地址空间描述

reg 属性描述设备在总线上的地址范围,由 #address-cells 和 #size-cells 决定地址和长度的编码方式。

// 父节点定义地址编码
spi0: spi@04026000 {
    #address-cells = <1>;   // 子节点地址使用 1 个 32 位整数
    #size-cells = <0>;      // 子节点无长度字段

    // 控制器本身的 reg
    reg = <0x04026000 0x1000>;  // 基地址 0x04026000,长度 0x1000

    // SPI 从设备子节点
    spidev@0 {
        reg = <0>;  // 片选号 0,无长度字段
    };
};
字段 含义 全志 T113 典型值
#address-cells 地址字段占用的 32 位整数个数 1(32 位地址空间)
#size-cells 长度字段占用的 32 位整数个数 1(长度用 32 位表示)
reg 地址段 基地址 0x04026000(SPI0)
reg 长度段 地址空间大小 0x1000(4KB)

3.3.3 status 属性——设备使能开关

status 属性控制设备节点的启用状态。在 .dtsi 中默认设置为 disabled,板级 .dts 中根据需要修改为 okay。

// sun8iw20p1.dtsi 中默认关闭
spi0: spi@04026000 {
    status = "disabled";
};

// 板级 t113-evb.dts 中启用
&spi0 {
    status = "okay";
    pinctrl-0 = <&spi0_pins>;
    pinctrl-names = "default";
};
status 值 含义 内核行为
okay 设备启用 内核会为节点创建 platform_device 并尝试绑定驱动
disabled 设备禁用 内核跳过该节点,不创建设备
fail 设备检测失败 内核创建设备但标记为失败状态
reserved 保留 通常用于固件保留的设备
最佳实践:在全志 T113 的 SPI 配置中,始终在 .dtsi 中保持 status = "disabled",在板级文件中通过 &spi0 引用并修改为 okay。这样可以避免未使用的 SPI 控制器占用资源。

3.4 中断与时钟属性

3.4.1 interrupts 属性

中断属性描述设备使用的中断号、触发类型等信息。全志 T113 使用 GIC(通用中断控制器),中断描述格式如下:

interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
// GIC_SPI: 共享外设中断类型
// 11: 中断号(SPI 0 的中断号为 11)
// IRQ_TYPE_LEVEL_HIGH: 高电平触发
中断类型宏 值 说明
GIC_SPI 0 共享外设中断,用于所有外设
GIC_PPI 1 私有外设中断,用于 CPU 本地
IRQ_TYPE_LEVEL_HIGH 4 高电平触发
IRQ_TYPE_EDGE_RISING 1 上升沿触发

3.4.2 clocks 与 clock-names 属性

时钟属性为设备提供时钟源,clock-names 用于区分不同的时钟角色。

clocks = <&ccu CLK_SPI0>, <&ccu CLK_BUS_SPI0>;
clock-names = "spi", "ahb";

// 驱动中通过 devm_clk_get 获取时钟
struct spi_master *master = spi_alloc_master(dev, sizeof(struct sun8i_spi));
struct sun8i_spi *sspi = spi_master_get_devdata(master);

sspi->clk = devm_clk_get(dev, "spi");   // 获取 SPI 模块时钟
sspi->bus_clk = devm_clk_get(dev, "ahb"); // 获取 AHB 总线时钟
SPI 设备树节点关键属性结构 spi0@04026000 compatible 驱动匹配字符串 reg 基地址 + 长度 interrupts 中断号 + 触发类型 clocks 时钟源 phandle status okay / disabled pinctrl-0 引脚复用配置 每个属性都对应内核驱动中的特定处理逻辑 缺少任一关键属性可能导致设备无法正常工作

3.5 引脚复用配置(pinctrl)

全志 T113 的 SPI 引脚通过 pinctrl 子系统配置复用功能。引脚配置在 .dtsi 中预定义,板级文件通过 pinctrl-0 引用。

// sun8iw20p1.dtsi 中的引脚定义
spi0_pins: spi0-pins {
    pins = "PC0", "PC1", "PC2", "PC3";
    function = "spi0";
    drive-strength = <10>;
    bias-pull-up;
};

spi0_cs_pins: spi0-cs-pins {
    pins = "PC4";
    function = "spi0";
    drive-strength = <10>;
    bias-pull-up;
};

// 板级文件引用
&spi0 {
    pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
    pinctrl-names = "default";
    status = "okay";
};
pinctrl 属性 说明 全志 T113 典型值
pins 引脚列表 "PC0", "PC1", "PC2", "PC3"
function 复用功能 "spi0"
drive-strength 驱动能力(mA) 10
bias-pull-up 内部上拉 无值(布尔属性)
bias-pull-down 内部下拉 无值(布尔属性)
避坑指南:全志 T113 的 SPI 引脚必须配置正确的复用功能。如果引脚被其他外设占用(如 UART、I2C),SPI 通信将失败。建议在配置前查阅芯片手册的引脚复用表,确认引脚未被冲突使用。

3.6 SPI 从设备子节点配置

在 SPI 控制器节点下,需要添加从设备子节点来描述挂载在 SPI 总线上的外设芯片。

&spi0 {
    status = "okay";
    pinctrl-0 = <&spi0_pins>;
    pinctrl-names = "default";

    spidev@0 {
        compatible = "spidev";
        reg = <0>;               // 片选 0
        spi-max-frequency = <50000000>;  // 50MHz
        spi-cpha;                // 时钟相位模式
        spi-cpol;                // 时钟极性模式
    };

    spidev@1 {
        compatible = "spidev";
        reg = <1>;               // 片选 1
        spi-max-frequency = <10000000>;  // 10MHz
    };
};
子节点属性 说明 可选值
reg 片选号(从 0 开始) 0、1、2...
spi-max-frequency 最大 SPI 时钟频率(Hz) 1000000 ~ 100000000
spi-cpha 时钟相位(CPHA=1) 无值(布尔属性)
spi-cpol 时钟极性(CPOL=1) 无值(布尔属性)
spi-cs-high 片选高电平有效 无值(布尔属性)
spi-3wire 三线制 SPI 模式 无值(布尔属性)
调试建议:在全志 T113 上调试 SPI 时,可以先使用 spidev 驱动(compatible = "spidev")进行用户空间测试。确认通信正常后,再替换为实际的设备驱动。注意内核配置需启用 CONFIG_SPI_SPIDEV。

3.7 设备树编译与反编译

掌握 dtc 工具的使用是调试设备树的必备技能。

# 编译设备树:.dts -> .dtb
dtc -I dts -O dtb -o sun8iw20p1-t113-evb.dtb sun8iw20p1-t113-evb.dts
# 反编译设备树:.dtb -> .dts
dtc -I dtb -O dts -o sun8iw20p1-t113-evb.dts sun8iw20p1-t113-evb.dtb
# 检查设备树语法错误
dtc -I dts -O dtb -o /dev/null sun8iw20p1-t113-evb.dts
# 查看内核中当前使用的设备树
# 在目标板上执行:
cat /proc/device-tree/model
ls /proc/device-tree/soc/sp