3、设备树常用属性详解:compatible属性、reg属性和地址编码、interrupts属性、status属性
好,咱们今天来啃设备树里最常用的几个属性。说实话,我刚入行那会儿,看着设备树里一堆属性名,头都大了。后来做多了才发现,真正天天打交道的,其实就这几个核心属性。掌握了它们,你就能看懂八成以上的设备树节点。
3.1 compatible属性:设备与驱动的“身份证”
compatible属性,说白了就是告诉内核:“我这个设备是谁,该用哪个驱动来伺候我”。它是设备与驱动之间匹配的桥梁。
它的格式很简单:
compatible = "厂商,设备型号";
比如:
compatible = "ti,am335x-uart", "ns16550";
这里有两个字符串,内核匹配驱动时会按顺序尝试。先找"ti,am335x-uart"对应的驱动,找不到再找"ns16550"的。
匹配规则:内核遍历设备树时,会拿compatible里的每个字符串去和驱动里of_match_table中的字符串比对。只要有一个匹配上,就算成功。
我个人习惯,写新驱动时一定会把最具体的型号放在最前面,通用的放后面。这样既能支持特定硬件的优化,又能兼容通用驱动。
我在项目中遇到过一个问题:某款芯片的UART在旧内核上跑得好好的,升级内核后突然不工作了。查了半天,发现是compatible字符串写反了顺序,内核先匹配到了一个不兼容的通用驱动。嗯,顺序真的很重要。
3.2 reg属性和地址编码:硬件的“门牌号”
reg属性描述设备的地址空间信息。你想想看,CPU要访问一个外设,总得知道它映射到哪个地址段吧?reg就是干这个的。
基本格式:
reg = <地址1 长度1 地址2 长度2 ...>;
举个例子:
uart@44e09000 {
compatible = "ti,am335x-uart", "ns16550";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
};
这里表示UART的寄存器基地址是0x44e09000,地址范围是0x2000字节。
小技巧:地址和长度的位数由根节点的#address-cells和#size-cells决定。如果#address-cells=<1>,地址就是32位;如果是<2>,就是64位。size-cells同理。
我记得有一次调试一个PCIe设备,reg属性里写了三个地址段:配置空间、MMIO空间、IO空间。当时没注意#address-cells是2,结果地址解析全乱了。后来老老实实数了一遍cells,才把问题定位到。
多段reg的场景也很常见:
reg = <0x10000000 0x10000 /* 配置空间 */
0x20000000 0x100000 /* MMIO空间 */
0x30000000 0x1000>; /* IO空间 */
3.3 interrupts属性:设备的“门铃”
设备不能总让CPU轮询吧?那太浪费了。interrupts属性就是告诉内核:我这个设备用哪个中断号,怎么触发。
基本格式:
interrupts = <中断号 触发类型>;
触发类型常用的有:
| 值 | 含义 |
|---|---|
| 0 | 上升沿触发 |
| 1 | 下降沿触发 |
| 2 | 高电平触发 |
| 3 | 低电平触发 |
| 4 | 边沿触发(上升沿和下降沿都触发) |
实际例子:
gpio_keys {
compatible = "gpio-keys";
button@0 {
label = "Power";
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
};
};
注意:中断号的编码方式取决于中断控制器。有些控制器用全局中断号,有些用相对编号。一定要看对应中断控制器的文档。
我曾经踩过一个坑:在ARM平台上,GIC中断控制器用SPI(共享外设中断)和PPI(私有外设中断)两种编号方式。SPI中断号从32开始,而设备树里写的是硬件中断号,两者差32。当时怎么调都不对,后来查了GIC手册才明白。
3.4 status属性:设备的“开关”
status属性控制设备是否启用。说白了就是告诉内核:这个设备现在能不能用。
常用值:
- okay:设备正常,可以启用
- disabled:设备禁用,内核不初始化
- reserved:保留,不用于Linux
- fail:设备检测失败
实际应用场景:
&uart0 {
status = "okay";
pinctrl-0 = <&uart0_pins>;
pinctrl-names = "default";
};
&uart1 {
status = "disabled"; /* 这个串口不用 */
};
最佳实践:在dtsi(设备树包含文件)中,所有外设默认设为disabled。然后在具体的板级dts文件中,根据需要逐个使能。这样既清晰又安全。
我见过一些新手直接把所有外设都设成okay,结果系统启动时一堆驱动去抢资源,各种冲突。嗯,这里要注意:不用的设备一定要关掉,省电又省心。
3.5 综合示例:一个完整的设备节点
把上面几个属性串起来,就是一个完整的设备节点:
spi@10000000 {
compatible = "ti,am335x-spi", "ti,omap4-spi";
reg = <0x10000000 0x1000>;
interrupts = <0 65 4>;
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
spidev@0 {
compatible = "spidev";
reg = <0>;
spi-max-frequency = <10000000>;
};
};
这个节点包含了我们今天讲的所有核心属性。你看,compatible告诉内核用哪个驱动,reg告诉地址在哪,interrupts告诉中断怎么用,status告诉设备是否启用。
我的建议:刚开始写设备树时,先照着已有的模板改。把compatible、reg、interrupts、status这四个属性搞明白,其他属性慢慢学。别想一口吃成胖子。
好了,这节的内容就到这。下节课我们聊聊设备树里那些“看不见”的属性——它们不直接出现在节点里,但影响整个设备树的解析。到时候见。