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 设备树结构可视化
下图展示了蓝牙控制器在设备树中的典型层级结构,以及核心属性如何关联到硬件资源。
4.5 蓝牙驱动中的设备树实践要点
- GPIO 属性命名规范: 蓝牙驱动中常用的 GPIO 属性包括
shutdown-gpios(复位)、device-wakeup-gpios(设备唤醒主机)、host-wakeup-gpios(主机唤醒设备)。属性名后缀必须为-gpios(复数形式)。 - 时钟与频率配置: 使用
clocks和clock-names属性指定蓝牙控制器所需的时钟源,max-speed属性设置 UART 波特率。 - 电源管理: 通过
vbat-supply和vddio-supply属性关联稳压器节点,实现蓝牙模块的电源控制。 - 中断触发类型: 蓝牙主机唤醒中断通常配置为
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 = <®_bt_vbat>;
vddio-supply = <®_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 设备树编译与加载流程
设备树的编译与加载涉及多个阶段,从源文件到内核驱动识别,每一步都可能影响蓝牙功能的正常启用。
在蓝牙驱动开发中,设备树配置的正确性直接影响驱动 probe 是否成功。建议在开发初期使用 dtc -I dts -O dtb -o test.dtb test.dts 进行快速验证,确保语法无误后再集成到完整固件中。