1. 设备树起源:为什么需要设备树?从板级文件到设备树的演进历史
1.1 那个“硬编码”的年代
做嵌入式Linux开发的老手,应该都记得那个年代。
大概十年前,我刚开始接触内核移植。那时候每换一块板子,都得改内核源码。不是改驱动,是改一个叫 board-xxx.c 的文件。这种文件,我们叫它“板级文件”。
说白了,就是把硬件信息硬生生写死在C代码里。比如:
static struct resource i2c_resources[] = {
{
.start = 0x1000,
.end = 0x10FF,
.flags = IORESOURCE_MEM,
},
{
.start = IRQ_I2C,
.end = IRQ_I2C,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device i2c_device = {
.name = "i2c-bus",
.id = 0,
.num_resources = ARRAY_SIZE(i2c_resources),
.resource = i2c_resources,
};
你看,内存地址、中断号、设备名称,全写在C语言结构体里。每次换一颗芯片,或者改一下引脚复用,就得重新编译整个内核。
我个人习惯,那时候维护一个BSP,手头至少有三四个 board-xxx.c 文件。改一个地方,得同步改好几个文件。稍不留神,就出bug。
1.2 为什么这条路走不通了?
为什么会这样?因为硬件越来越复杂了。
早期的ARM芯片,外设就那么几个。一个UART,一个I2C,一个GPIO。写死在代码里,问题不大。但到了Cortex-A系列,芯片内部集成了几十个外设。而且同一颗SoC,可以搭配不同的板子——有的用eMMC,有的用NAND Flash,有的用Nor Flash。
你想想看,如果每个板子都要改内核源码,那工作量有多大?
我在项目中遇到过最头疼的事:客户换了一颗DDR颗粒,时序参数变了。我得重新编译内核,给客户发一个新的uImage。客户那边还抱怨:“我就换个内存,你让我升级整个系统?”
嗯,这确实不合理。
核心痛点:
- 硬件信息与驱动代码强耦合
- 每款板子都需要独立的内核镜像
- 硬件变更必须重新编译内核
- 不同厂商的板级文件风格各异,难以维护
1.3 设备树:把硬件描述还给硬件
所以,Linux社区开始思考:能不能把硬件信息从C代码里抽出来?
答案就是设备树(Device Tree)。
设备树的核心思想很简单:用文本文件描述硬件,内核启动时解析这个文件,动态构建设备模型。驱动不再关心硬件接在哪根总线上、中断号是多少,它只负责“干活”。
说白了,就是“硬件描述”和“驱动逻辑”解耦。
/ {
model = "MyBoard V1.0";
compatible = "vendor,myboard";
i2c@1000 {
compatible = "vendor,i2c-bus";
reg = <0x1000 0x100>;
interrupts = <31>;
status = "okay";
};
};
你看,同样的I2C控制器,现在用文本描述。地址、中断号、状态,一目了然。换板子?改这个.dts文件就行,不用碰内核源码。
1.4 演进历史:从PowerPC到ARM
设备树其实不是Linux原创的。它最早来自PowerPC平台。PowerPC的硬件五花八门,IBM、Motorola、AMCC各搞一套,板级文件根本管不过来。所以PowerPC社区最早采用了Open Firmware的设备树概念。
ARM架构后来也遇到了同样的问题。2011年左右,ARM Linux社区开始大规模推进设备树。我记得那段时间,Linus Torvalds还发过火,说ARM的板级文件是“一团乱麻”。
从那以后,新加入的ARM SoC都强制要求使用设备树。到了2014年左右,老旧的板级文件基本被清理干净了。
一个小知识:
设备树的文件后缀名是.dts(Device Tree Source)。编译后的二进制文件是.dtb(Device Tree Blob)。内核启动时,由bootloader把.dtb加载到内存,内核解析后构建设备模型。
1.5 设备树到底解决了什么问题?
我总结一下,设备树带来的好处,其实就三点:
- 一次编译,到处运行——同一个内核镜像,配合不同的.dtb,可以适配不同的板子。
- 硬件变更无需改驱动——换DDR、改GPIO、调中断,改.dts就行。
- 社区统一标准——所有厂商都按同一套语法写设备树,维护成本大大降低。
避坑指南:
我曾经遇到过一个坑:某厂商的BSP里,设备树里写的中断号是硬件中断号,但内核驱动里用的是Linux中断号。两者差了32。结果中断一直不触发,查了两天才发现是设备树写错了。所以,设备树里的中断号,一定要看芯片手册,别想当然。
1.6 一张图看懂演进脉络
下面这张图,是我自己画的。它展示了从板级文件到设备树的演进过程:
1.7 我的个人体会
做嵌入式这么多年,我最大的感受是:设备树不是银弹。
它解决了硬件描述的问题,但带来了新的复杂度。比如设备树的覆盖(overlay)、引脚复用(pinctrl)的写法,刚开始接触时确实有点绕。但相比以前改板级文件的痛苦,这点学习成本,我觉得值。
嗯,这一章就到这里。设备树到底怎么写的?下一章我们直接上手。