3、设备树常用属性详解:compatible、reg、interrupts、status、clocks
设备树里的属性,说白了就是给驱动看的“说明书”。每个属性告诉内核:这个设备是谁、怎么访问它、什么时候能用。我刚开始学设备树时,总觉得属性太多记不住。后来发现,真正常用的其实就那几个。今天咱们就把它们掰开揉碎了讲清楚。
3.1 compatible —— 设备的名字和身份证
compatible 是设备树里最重要的属性,没有之一。它用来匹配驱动。内核通过它找到对应的驱动程序。
格式很简单:
compatible = "厂商,型号";
比如:
compatible = "ti,am335x-rtc", "ti,da830-rtc";
这里有两个字符串,内核会按顺序尝试匹配。第一个最精确,第二个是兼容型号。我建议你永远把最具体的型号写在前面。
匹配规则其实很简单:驱动里会声明一个 of_device_id 表,内核遍历设备树节点,找到 compatible 字符串匹配的驱动,就绑定上去了。
3.2 reg —— 告诉驱动去哪里找硬件
reg 属性描述设备的地址空间。它告诉驱动:你的寄存器在哪个物理地址,占多大范围。
格式:
reg = <地址1 长度1 地址2 长度2 ...>;
举个例子:
uart@44e09000 {
compatible = "ti,am335x-uart";
reg = <0x44e09000 0x2000>;
};
这表示 UART 的寄存器从 0x44e09000 开始,占用 0x2000 字节。驱动拿到这个信息后,调用 ioremap() 就能访问了。
#address-cells 和 #size-cells 决定。默认是 1 个 cell(32位),64位系统可能是 2 个 cell。
我在项目中遇到过一个问题:某款芯片的 SPI 控制器有两个寄存器区域,一个控制寄存器,一个数据缓冲区。当时只写了一个 reg,结果驱动只能访问到控制寄存器,数据读写全乱套了。后来加上第二个 reg 才解决。
3.3 interrupts —— 让设备会“喊人”
没有中断的设备,驱动只能轮询,效率低得可怜。interrupts 属性就是告诉内核:这个设备用哪个中断号、怎么触发。
格式:
interrupts = <中断号 触发类型>;
比如:
gpio-keys {
compatible = "gpio-keys";
button@0 {
label = "power";
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
};
};
触发类型常用的有:
| 宏定义 | 值 | 含义 |
|---|---|---|
| IRQ_TYPE_EDGE_RISING | 1 | 上升沿触发 |
| IRQ_TYPE_EDGE_FALLING | 2 | 下降沿触发 |
| IRQ_TYPE_LEVEL_HIGH | 4 | 高电平触发 |
| IRQ_TYPE_LEVEL_LOW | 8 | 低电平触发 |
你想想看,如果中断类型选错了会怎样?比如设备是下降沿触发,你配成了电平触发,那中断可能会一直触发,CPU 直接跑死。嗯,我当年就犯过这个错,调试了一整天。
interrupt-parent 指定父控制器。
3.4 status —— 设备的“开关”
status 属性控制设备是否启用。说白了就是一个开关。
常用值:
okay—— 设备可用,驱动会加载disabled—— 设备禁用,驱动不会加载fail—— 设备检测失败
比如:
&uart0 {
status = "okay";
pinctrl-0 = <&uart0_pins>;
};
我习惯在板级设备树里把用不到的接口都设成 disabled。比如某个开发板上有 6 个 UART,但实际只用了 2 个,剩下的就关掉。这样能省点资源,也避免驱动误加载。
3.5 clocks —— 给设备提供“心跳”
现代 SoC 里,每个外设都需要时钟才能工作。clocks 属性就是告诉驱动:你的时钟源在哪、频率多少。
格式:
clocks = <&时钟控制器 时钟ID>;
比如:
i2c@44e0b000 {
compatible = "ti,am335x-i2c";
reg = <0x44e0b000 0x1000>;
interrupts = <70>;
clocks = <&clk_24mhz>;
clock-names = "fck";
};
clock-names 是给时钟起个名字,驱动里通过名字来获取时钟句柄。这样代码可读性更好。
我遇到过最头疼的问题:某个外设的时钟频率配错了,导致通信速率不对。比如 I2C 配成了 100KHz 的时钟,但实际给了 400KHz,结果数据全是乱码。后来用示波器一看,才发现是时钟问题。
clk_get() 和 clk_prepare_enable() 来操作。
3.6 其他常用属性一览
除了上面五个,还有几个属性也经常见到:
| 属性 | 作用 | 示例 |
|---|---|---|
| pinctrl-0 | 引脚复用配置 | pinctrl-0 = <&uart0_pins>; |
| dmas | DMA 通道配置 | dmas = <&edma 0>; |
| power-domains | 电源域管理 | power-domains = <&pd_core>; |
| resets | 复位信号 | resets = <&rst 1>; |
这些属性虽然不常用,但遇到特定硬件时,缺了它们设备就是跑不起来。
3.7 知识体系图
下面这张图帮你理清这些属性的关系:
这五个属性,基本覆盖了设备树里 80% 的配置场景。你只要把它们搞明白了,大部分设备树问题都能自己解决。
可可精品资料库