2. 设备树基础回顾:核心语法与关键属性

设备树(Device Tree,DT)是嵌入式 Linux 驱动开发中描述硬件拓扑的标准机制。它使用树形结构将 CPU、内存、外设、中断控制器等硬件资源以节点和属性的形式呈现,供内核在启动时解析并生成平台设备。理解设备树的语法与核心属性,是排查 reg 地址映射错误的前提。

2.1 设备树语法概览

设备树源文件(.dts)采用人类可读的文本格式,经过 dtc 编译为二进制 .dtb 后由 Bootloader 传递给内核。其基本结构如下:

/dts-v1/;

/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";

    chosen {
        stdout-path = &uart0;
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x10000000>;  /* 256 MB */
    };

    leds {
        compatible = "gpio-leds";
        led0 {
            label = "beaglebone:green:usr0";
            gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>;
        };
    };
};
关键点:设备树以根节点 / 开始,每个节点用 node-name@unit-address 标识。unit-address 通常与 reg 属性的首地址一致,用于区分同名节点。

2.2 节点与属性

节点是设备树的骨架,属性是节点的具体描述。下表列出最常用的属性及其用途:

属性名 类型 用途说明 示例
compatible 字符串列表 匹配驱动,格式为 "厂商,型号" "ti,am335x-uart"
reg u32 数组 描述地址空间:<起始地址 长度> <0x44E09000 0x2000>
interrupts u32 数组 描述中断号与触发类型 <0 56 4>
interrupt-parent phandle 指定中断控制器节点 &intc
clocks phandle 列表 引用时钟源 <&clk_uart1>
status 字符串 设备状态:"okay" / "disabled" "okay"

2.3 compatible 属性:驱动匹配的钥匙

compatible 是驱动与设备绑定的核心桥梁。内核在解析设备树时,会遍历节点的 compatible 列表,与驱动中 of_match_table 声明的字符串逐一比对。匹配顺序遵循“最具体优先”原则:

  • 格式规范:"厂商,设备型号",如 "fsl,imx6q-uart"。
  • 回退机制:可列出多个值,内核按顺序匹配。例如 "ti,am335x-uart", "ns16550" 表示优先使用 TI 专用驱动,否则回退到通用 16550 驱动。
  • 常见错误:拼写错误、厂商前缀未注册、大小写不一致,导致驱动无法加载。
调试技巧:在驱动入口打印 of_match_device 的返回值,确认匹配是否成功。若返回 NULL,则检查 compatible 字符串是否完全一致。

2.4 reg 属性:地址映射的基石

reg 属性描述设备在总线上的地址范围,格式为 <address1 length1 address2 length2 ...>。对于内存映射外设,内核通过 of_iomap() 或 platform_get_resource() 读取 reg 并建立虚拟地址映射。

/* 设备树片段:UART1 控制器 */
uart1: serial@44E09000 {
    compatible = "ti,am335x-uart", "ns16550";
    reg = <0x44E09000 0x2000>;   /* 起始物理地址 0x44E09000,长度 8KB */
    interrupts = <72>;
    clock-frequency = <48000000>;
    status = "okay";
};

reg 的解析依赖 #address-cells 和 #size-cells 属性:

属性 含义 默认值 示例
#address-cells 地址字段占用的 u32 个数 2 1 表示 32 位地址
#size-cells 长度字段占用的 u32 个数 1 1 表示 32 位长度
常见陷阱:若父节点 #address-cells = <2>,则 reg 中的地址必须写两个 u32,如 <0x0 0x44E09000 0x0 0x2000>。遗漏会导致地址解析错位,映射到错误的物理地址。

2.5 interrupts 属性:中断路由描述

interrupts 属性描述设备的中断请求线。其格式由中断控制器节点的 #interrupt-cells 决定。以 ARM GIC(通用中断控制器)为例:

/* GIC 中断控制器 */
intc: interrupt-controller@48200000 {
    compatible = "arm,cortex-a9-gic";
    #interrupt-cells = <3>;   /* 中断类型、中断号、触发标志 */
    interrupt-controller;
    reg = <0x48200000 0x1000>;
};

/* 使用示例:GPIO 按键中断 */
key {
    compatible = "gpio-keys";
    interrupt-parent = <&intc>;
    interrupts = <0 30 4>;   /* SPI 中断,中断号 30,高电平触发 */
};

中断号与触发类型的常见组合:

触发标志值 含义
0 未指定
1 上升沿触发
2 下降沿触发
4 高电平触发
8 低电平触发

2.6 设备树结构总览

下图展示了一个典型嵌入式系统的设备树层级关系,包含根节点、内存节点、中断控制器、外设节点及其属性:

设备树层级结构示例 根节点 / memory@80000000 chosen intc@48200000 uart1@44E09000 leds gpio1@4804C000 led0 节点属性示例 uart1: compatible = "ti,am335x-uart", "ns16550"; reg = <0x44E09000 0x2000>; interrupts = <72>; interrupt-parent = <&intc>;

2.7 核心概念总结

  • 节点:树形结构的基本单元,通过 node-name@unit-address 唯一标识。
  • 属性:描述节点特征,compatible 用于驱动匹配,reg 用于地址映射,interrupts 用于中断路由。
  • 地址单元:#address-cells 和 #size-cells 决定 reg 的解析方式,错误配置会导致地址错位。
  • 中断父节点:interrupt-parent 指定中断控制器,#interrupt-cells 决定中断描述格式。
学习建议:在调试前,先使用 dtc -I dtb -O dts -o dump.dts your.dtb 反编译设备树,检查 reg 和 interrupts 的数值是否与硬件手册一致。这是排查地址映射错误的第一步。