3. 设备树核心属性:compatible、reg、interrupts、clocks、status、model、device_type
设备树里属性很多,但真正天天打交道的,其实就这几个。我刚开始学设备树那会儿,看着几百行的 .dts 文件头皮发麻,后来发现只要把这七个属性吃透,百分之八十的节点都能看懂。今天咱们一个一个过,每个我都会讲讲我在项目里踩过的坑。
3.1 compatible —— 驱动匹配的身份证
compatible 是设备树里最重要的属性,没有之一。它的作用就一句话:告诉内核,这个设备该用哪个驱动。
compatible = "vendor,device";
格式是「厂商,设备名」。比如:
compatible = "ti,am335x-uart", "ns16550";
这里有个细节:字符串可以写多个,内核会按顺序匹配。第一个最精确,后面的作为 fallback。我在做 AM335x 项目时,就靠这个 fallback 机制,让新板子直接复用内核自带的 ns16550 驱动,省了不少事。
3.2 reg —— 告诉驱动你的地盘在哪
reg 描述设备的地址空间。说白了,就是告诉驱动:「你的寄存器从哪个地址开始,占多大地方」。
reg = <0x48020000 0x1000>;
第一个数字是起始地址,第二个是长度。如果是 64 位地址,用两个 <>:
reg = <0x00000000 0x48020000 0x00000000 0x1000>;
嗯,这里要注意:#address-cells 和 #size-cells 决定了 reg 里每个字段占几个 cell。父节点定义这两个属性,子节点按规矩写。我见过有人把这两个属性写反,结果驱动读到的地址全是错的,查了两天才发现。
| #address-cells | #size-cells | reg 格式 |
|---|---|---|
| 1 | 1 | <addr size> |
| 2 | 1 | <addr_hi addr_lo size> |
| 1 | 0 | <addr>(无大小) |
3.3 interrupts —— 中断怎么连
中断描述比 reg 稍微复杂一点,因为它涉及中断控制器。先看个例子:
interrupts = <0 31 4>;
interrupt-parent = <&gic>;
这里三个数字分别代表:中断类型(0=SPI,1=PPI)、中断号、触发方式(1=上升沿,4=高电平)。
你想想看,如果设备树里不写 interrupt-parent,内核怎么知道这个中断发给谁?它会往父节点找,直到找到 interrupt-controller 属性。我建议每个设备节点都显式写上 interrupt-parent,别偷懒。
3.4 clocks —— 时钟的父子关系
时钟属性描述设备用哪些时钟源。格式类似中断:
clocks = <&clk_mcu 0>, <&clk_per 1>;
clock-names = "mcu", "per";
clocks 里每个条目是一个 phandle 加一个索引,clock-names 给每个时钟起个名字。驱动里通过名字找时钟,比用索引更可读。
我在做 FPGA 加速卡驱动时,设备树里挂了三个时钟:一个给 AXI 总线,一个给逻辑核心,一个给 DDR 控制器。用 clock-names 区分,驱动代码里 devm_clk_get(dev, "axi") 一目了然。
3.5 status —— 设备的开关
status 只有几个值,但用错了会出大问题:
| 值 | 含义 |
|---|---|
| okay | 设备启用,驱动正常加载 |
| disabled | 设备禁用,驱动不加载 |
| reserved | 保留给固件,OS 别碰 |
| fail | 设备检测失败,但节点保留 |
最常见的用法是在 .dtsi 里把节点设为 disabled,然后在板级 .dts 里改成 okay。比如:
// am335x.dtsi
uart0: serial@48020000 {
status = "disabled";
};
// myboard.dts
&uart0 {
status = "okay";
};
我个人习惯:所有外设默认 disabled,用到的才打开。这样既安全又清晰。
3.6 model —— 板子叫什么名字
model 就是个字符串,描述硬件平台名称。比如:
model = "TI AM335x BeagleBone Black";
它不影响驱动匹配,但内核启动时会打印出来,方便你确认当前跑的是哪个板子的设备树。我在调试时经常先看 dmesg 里的 model 信息,确认设备树有没有加载对。
3.7 device_type —— 老古董,但还得知道
device_type 是 PowerPC 时代留下的遗产,ARM 平台基本不用了。唯一还在用的地方是 cpu 节点:
cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a9";
};
新写的设备树里,除了 CPU 节点,其他地方别用这个属性。我在 review 代码时看到有人给 i2c 设备加 device_type,直接打回去重写。
3.8 核心属性关系图
下面这张图总结了七个属性各自负责什么,以及它们和驱动之间的协作关系:
3.9 实战建议
写设备树时,我建议你按这个顺序检查每个节点:
- 先写 compatible —— 确定驱动能不能认出来
- 再写 reg —— 地址范围对不对,和芯片手册一致吗
- 然后 interrupts —— 中断号、触发方式、中断控制器都配齐了吗
- 接着 clocks —— 时钟源和名字别写反了
- 最后 status —— 确认是 okay 还是 disabled