4. 设备树(Device Tree)基础:设备树语法与核心属性

设备树(Device Tree, DT)是嵌入式 Linux 系统中用于描述硬件拓扑结构的数据结构,它替代了传统平台设备(Platform Device)的硬编码方式,使得同一内核镜像可以支持多种硬件平台。对于蓝牙驱动开发者而言,设备树是配置蓝牙控制器(如 UART、SDIO、USB 接口的蓝牙芯片)与主机通信的关键入口。

4.1 设备树文件结构:dts 与 dtsi

设备树源文件分为两种扩展名:.dts(Device Tree Source)和 .dtsi(Device Tree Source Include)。.dtsi 文件通常包含 SoC 公共定义、外设 IP 核模板,而 .dts 文件则包含具体板级的差异化配置。

文件类型 扩展名 用途 包含关系
设备树源文件 .dts 板级硬件描述,定义具体外设、GPIO、时钟等 #include 引用 .dtsi
设备树包含文件 .dtsi SoC 公共定义,如 CPU 核、中断控制器、总线节点 被 .dts 或其他 .dtsi 包含
设备树二进制 .dtb 编译后的二进制文件,由 bootloader 加载 由 .dts 通过 dtc 编译生成
蓝牙开发提示: 在调试蓝牙驱动时,经常需要修改 .dts 文件中的 UART 波特率、蓝牙复位 GPIO 或电源控制引脚。修改后需重新编译 .dtb 并更新到 boot 分区。

4.2 节点与属性:设备树的基本单元

设备树由节点(Node)和属性(Property)组成。每个节点代表一个硬件设备或总线,属性描述该设备的配置参数。节点路径从根节点 / 开始,使用斜杠分隔。

/dts-v1/;

#include "imx8mp.dtsi"

/ {
    model = "i.MX8MP EVK Board";
    compatible = "fsl,imx8mp-evk", "fsl,imx8mp";

    chosen {
        stdout-path = &uart2;
    };

    /* 蓝牙 UART 节点 */
    &uart3 {
        pinctrl-names = "default";
        pinctrl-0 = &pinctrl_uart3;
        status = "okay";

        bluetooth {
            compatible = "brcm,bcm43438-bt";
            max-speed = <3000000>;
            shutdown-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
            device-wakeup-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>;
            host-wakeup-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
        };
    };
};
节点命名规则: 节点名称格式为 node-name@unit-address,其中 unit-address 通常为寄存器基地址或设备地址。例如 uart@30890000 表示基地址为 0x30890000 的 UART 控制器。

4.3 核心属性详解:reg、interrupts、compatible

这三个属性是设备树中最基础、最关键的属性,几乎每个硬件节点都会用到。理解它们对于蓝牙驱动开发至关重要。

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

compatible 属性是一个字符串列表,用于将设备节点与内核驱动进行匹配。内核通过 of_match_table 查找与 compatible 值匹配的驱动。

/* 设备树节点 */
bluetooth {
    compatible = "brcm,bcm43438-bt", "brcm,bcm4345-bt";
};

/* 内核驱动匹配表 */
static const struct of_device_id brcm_bluetooth_of_match[] = {
    { .compatible = "brcm,bcm43438-bt" },
    { .compatible = "brcm,bcm4345-bt" },
    { }
};
MODULE_DEVICE_TABLE(of, brcm_bluetooth_of_match);
属性 格式 说明 蓝牙应用示例
compatible 字符串列表 设备名称,用于驱动匹配,越具体越优先 "brcm,bcm43438-bt"
reg 32 位整数对列表 寄存器地址和长度,或总线地址 <0x30890000 0x1000>
interrupts 32 位整数列表 中断号、触发类型等 <0 42 IRQ_TYPE_EDGE_RISING>

4.3.2 reg 属性:地址空间描述

reg 属性描述设备在总线上的地址范围。对于内存映射设备,它包含基地址和长度;对于 I2C/SPI 设备,它包含从设备地址。

/* 内存映射设备:UART 控制器 */
uart3: serial@30890000 {
    compatible = "fsl,imx8mp-uart", "fsl,imx6q-uart";
    reg = <0x30890000 0x10000>;  /* 基地址 0x30890000,长度 64KB */
    interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
};

/* I2C 设备:蓝牙音频编解码器 */
codec@1a {
    compatible = "realtek,rt5677";
    reg = <0x1a>;  /* I2C 从设备地址 0x1a */
};

4.3.3 interrupts 属性:中断资源配置

interrupts 属性描述设备使用的中断线。其格式依赖于中断控制器(如 GIC、INTC)。对于 ARM GICv2,通常使用 <中断类型 中断号 触发类型> 三元组。

中断类型 值 说明
GIC_SPI 0 共享外设中断,用于外设
GIC_PPI 1 私有外设中断,用于 CPU 本地
/* 蓝牙控制器中断配置示例 */
bluetooth {
    compatible = "brcm,bcm43438-bt";
    interrupt-parent = <&gpio1>;       /* 中断控制器为 GPIO1 */
    interrupts = <15 IRQ_TYPE_EDGE_FALLING>;  /* GPIO1_15,下降沿触发 */
    host-wakeup-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
};
常见错误: 蓝牙驱动中 interrupt-parent 与 interrupts 必须匹配。如果蓝牙芯片的中断连接到 GPIO 控制器,则 interrupt-parent 应指向 GPIO 节点,而非 GIC。否则内核无法正确注册中断处理函数。

4.4 设备树结构可视化

下图展示了蓝牙控制器在设备树中的典型层级结构,以及核心属性如何关联到硬件资源。

设备树蓝牙节点层级结构与核心属性 / (根节点) soc@0 uart@30890000 (UART3) bluetooth (蓝牙子节点) compatible = "brcm,bcm43438-bt" max-speed = <3000000> shutdown-gpios / device-wakeup-gpios / host-wakeup-gpios compatible 属性 驱动匹配标识 格式: "厂商,型号" 优先级: 从左到右 匹配失败则 probe 不执行 reg 属性 地址空间描述 格式: <地址 长度> UART: 基址+64KB I2C: 从设备地址 interrupts 属性 中断资源配置 格式: <类型 号 触发> 蓝牙: GPIO 中断 需指定 interrupt-parent

4.5 蓝牙驱动中的设备树实践要点

  1. GPIO 属性命名规范: 蓝牙驱动中常用的 GPIO 属性包括 shutdown-gpios(复位)、device-wakeup-gpios(设备唤醒主机)、host-wakeup-gpios(主机唤醒设备)。属性名后缀必须为 -gpios(复数形式)。
  2. 时钟与频率配置: 使用 clocks 和 clock-names 属性指定蓝牙控制器所需的时钟源,max-speed 属性设置 UART 波特率。
  3. 电源管理: 通过 vbat-supply 和 vddio-supply 属性关联稳压器节点,实现蓝牙模块的电源控制。
  4. 中断触发类型: 蓝牙主机唤醒中断通常配置为 IRQ_TYPE_EDGE_FALLING 或 IRQ_TYPE_EDGE_RISING,需与蓝牙芯片规格书一致。
/* 完整的蓝牙设备树节点示例(基于 i.MX8MP + BCM43438) */
&uart3 {
    pinctrl-names = "default";
    pinctrl-0 = <pinctrl_uart3>;
    status = "okay";

    bluetooth {
        compatible = "brcm,bcm43438-bt";
        max-speed = <3000000>;
        shutdown-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
        device-wakeup-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>;
        host-wakeup-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
        interrupt-parent = <&gpio1>;
        interrupts = <15 IRQ_TYPE_EDGE_FALLING>;
        vbat-supply = <&reg_bt_vbat>;
        vddio-supply = <&reg_bt_vddio>;
        clocks = <&clk IMX8MP_CLK_UART3>;
        clock-names = "uart";
    };
};
调试技巧: 使用 dtc -I dtb -O dts -o output.dts input.dtb 反编译 DTB 文件,检查蓝牙节点属性是否正确加载。内核启动时可通过 of_device_is_available() 函数判断节点是否启用(status = "okay")。

4.6 设备树编译与加载流程

设备树的编译与加载涉及多个阶段,从源文件到内核驱动识别,每一步都可能影响蓝牙功能的正常启用。

设备树编译与加载流程 .dts / .dtsi 源文件 dtc 编译器 设备树编译器 .dtb 文件 二进制 blob Bootloader 加载 U-Boot / TF-A Linux 内核 of_* 子系统 解析 DTB → 创建 device_node → 匹配驱动 → probe

在蓝牙驱动开发中,设备树配置的正确性直接影响驱动 probe 是否成功。建议在开发初期使用 dtc -I dts -O dtb -o test.dtb test.dts 进行快速验证,确保语法无误后再集成到完整固件中。