1. 设备树节点修改实战
在嵌入式Linux开发中,设备树是描述硬件配置的重要机制。当我们需要修改或扩展硬件描述时,正确的做法至关重要。以I.MX6ULL平台为例,让我们深入探讨设备树节点的修改方法。
1.1 直接修改DTSI文件的隐患
在I.MX6ULL开发中,imx6ull.dtsi是SoC级别的设备树头文件,包含所有板子共用的基础配置。例如,I2C1控制器的定义如下:
c复制i2c1: i2c@021a0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C1>;
status = "disabled"; // 默认禁用I2C1
};
直接在此文件中添加设备节点(如fxls8471加速度计)会导致所有使用该DTSI的板子都强制包含此设备,这显然不合理。这种"一刀切"的修改方式会带来以下问题:
- 硬件兼容性问题:不是所有板子都搭载相同外设
- 资源浪费:内核会为不存在的设备分配资源
- 维护困难:难以追踪特定板卡的专属配置
1.2 正确的节点修改方法
正确做法是在板级DTS文件中通过标签引用(&label)进行修改。以imx6ull-alientek-emmc.dts为例:
c复制&i2c1 {
clock-frequency = <100000>; // 新增属性:配置I2C时钟频率
pinctrl-names = "default"; // 配置引脚复用
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay"; // 覆盖默认的disabled状态
// 追加磁力计节点
mag3110@0e {
compatible = "fsl,mag3110";
reg = <0x0e>;
position = <2>;
};
// 追加加速度计节点
fxls8471@1e {
compatible = "fsl,fxls8471";
reg = <0x1e>;
position = <0>;
interrupt-parent = <&gpio5>;
interrupts = <0 8>;
};
};
这种修改方式具有以下优势:
- 局部性:修改仅影响当前板卡
- 可维护性:板卡专属配置集中管理
- 灵活性:可自由添加/覆盖属性和子节点
注意:标签引用语法(&label)是设备树的重用机制,它允许我们在不复制完整节点定义的情况下,对已有节点进行扩展和修改。
2. 设备树编写实战
理解设备树语法后,让我们从零构建一个精简的I.MX6ULL设备树,掌握设备树的核心结构。
2.1 设备树框架搭建
设备树总是以根节点/开始,包含兼容性声明:
c复制/dts-v1/;
/ {
compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull";
};
兼容性字符串非常重要:
- 第一个字符串标识具体板卡
- 第二个字符串标识SoC型号
- 内核通过这些字符串匹配对应的驱动
2.2 CPU节点描述
I.MX6ULL是单核Cortex-A7处理器,CPU节点描述如下:
c复制cpus {
#address-cells = <1>; // CPU地址用1个cell表示
#size-cells = <0>; // 不需要大小信息
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0>; // CPU核心编号
};
};
关键点说明:
#address-cells和#size-cells定义子节点的地址编码方式device_type标识节点类型reg属性表示CPU核心编号
2.3 外设总线架构
I.MX6ULL的外设通过多层总线连接,我们需要正确描述这种层级关系。
2.3.1 SOC节点
c复制soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges; // 地址空间直接映射
ocram: sram@00900000 {
compatible = "fsl,lpm-sram";
reg = <0x00900000 0x20000>; // 128KB OCRAM
};
};
2.3.2 AIPS总线域
I.MX6ULL有三个AIPS总线域:
c复制aips1: aips-bus@02000000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x02000000 0x100000>;
ranges;
ecspi1: ecspi@02008000 {
compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
reg = <0x02008000 0x4000>;
status = "disabled";
};
};
aips2: aips-bus@02100000 {
compatible = "fsl,aips-bus", "simple-bus";
reg = <0x02100000 0x100000>;
ranges;
usbotg1: usb@02184000 {
compatible = "fsl,imx6ul-usb", "fsl,imx27-usb";
reg = <0x02184000 0x4000>;
status = "disabled";
};
};
aips3: aips-bus@02200000 {
compatible = "fsl,aips-bus", "simple-bus";
reg = <0x02200000 0x100000>;
ranges;
rngb: rngb@02284000 {
compatible = "fsl,imx6sl-rng", "fsl,imx-rng", "imx-rng";
reg = <0x02284000 0x4000>;
};
};
2.4 设备树编写技巧
-
节点命名规范:
- 使用<设备类型>@<地址>格式
- 例如:
usb@02184000、gpio@0209c000
-
兼容性字符串:
- 从具体到通用排列
- 例如:
"fsl,imx6ul-usb", "fsl,imx27-usb"
-
状态控制:
status = "okay"启用设备status = "disabled"禁用设备
-
地址编码:
- 寄存器地址和长度用
reg属性表示 - 注意字节序(通常为big-endian)
- 寄存器地址和长度用
3. 设备树调试技巧
设备树编写完成后,验证其正确性至关重要。以下是实用的调试方法:
3.1 查看编译后的设备树
bash复制dtc -I dtb -O dts -o decompiled.dts /boot/your_board.dtb
3.2 运行时检查
bash复制# 查看完整设备树
cat /proc/device-tree/
# 查看特定节点
ls /proc/device-tree/soc
# 查看节点属性
hexdump -C /proc/device-tree/soc/aips-bus@02000000/ecspi@02008000/reg
3.3 常见问题排查
-
设备未初始化:
- 检查status是否为"okay"
- 确认时钟和复位信号正确配置
-
地址映射错误:
- 验证reg属性与芯片手册一致
- 检查ranges属性是否正确
-
驱动匹配失败:
- 确认compatible字符串与驱动一致
- 检查内核配置是否包含对应驱动
调试提示:使用
echo 8 > /proc/sys/kernel/printk提高内核日志级别,查看驱动加载时的详细输出。
4. 进阶设备树技术
掌握基础后,可以进一步学习以下高级特性:
4.1 设备树覆盖机制
c复制// 定义覆盖片段
/dts-v1/;
/plugin/;
&i2c1 {
new_device@55 {
compatible = "vendor,new-device";
reg = <0x55>;
};
};
4.2 条件包含
c复制#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
4.3 属性继承
c复制/ {
common-props {
prop1 = "value";
prop2 = <1>;
};
node1 {
inherit = "common-props";
prop3 = "node1-specific";
};
};
5. 实战经验分享
在实际项目中积累的一些宝贵经验:
-
版本控制:
- 为每个硬件版本创建独立的DTS文件
- 使用宏定义管理硬件差异
-
文档注释:
c复制/* * 引脚功能说明: * GPIO1_IO09 - 板载LED * GPIO1_IO10 - 复位按钮 */ -
调试节点:
c复制debug_nodes { compatible = "debug-nodes"; status = "okay"; test-gpio { gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; }; }; -
性能优化:
- 按需加载设备树片段
- 合理使用status属性控制设备初始化顺序
通过本教程,您应该已经掌握了设备树的核心概念和实用技巧。在实际开发中,建议:
- 从参考设计开始,逐步修改
- 充分利用芯片厂商提供的示例
- 建立完善的测试验证流程
- 文档化所有硬件相关配置
