3. 设备树常用属性详解:compatible、reg、interrupts、status、clocks、resets 等
设备树(Device Tree)中的属性(Property)是描述硬件设备特征、连接关系和资源需求的“数据字典”。驱动在 probe 阶段通过解析这些属性来获取硬件地址、中断号、时钟频率等关键信息,从而实现与物理设备的正确映射。本章将逐一拆解最核心的 6 个属性,并结合实际案例说明其配置规则与常见陷阱。
学习提示: 设备树属性是驱动与硬件之间的“契约”。属性值写错或缺失,轻则驱动 probe 失败,重则导致系统崩溃。建议在阅读本章时,对照实际开发板上的 .dts 文件进行验证。
3.1 compatible —— 驱动匹配的“身份证”
compatible 属性是设备树中最关键的字符串属性,用于将设备节点与对应的 Linux 驱动进行绑定。其值通常采用 "厂商,设备型号" 的格式,遵循从具体到通用的降序排列原则。
3.1.1 属性格式与匹配规则
- 格式:
compatible = "vendor,device", "generic-device"; - 匹配优先级: 内核的
of_match_table会按字符串顺序依次匹配,第一个匹配成功的驱动将被使用。 - 回退机制: 当没有精确匹配时,内核会尝试匹配更通用的字符串(如
"simple-bus")。
// 示例:I2C 控制器节点
i2c@ff160000 {
compatible = "snps,designware-i2c", "simple-bus";
reg = <0x0 0xff160000 0x0 0x1000>;
interrupts = <0 20 4>;
status = "okay";
};
驱动端匹配代码(of_match_table):
static const struct of_device_id dw_i2c_of_match[] = {
{ .compatible = "snps,designware-i2c", .data = &dw_i2c_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
3.1.2 常见错误与避坑
| 错误类型 | 现象 | 解决方案 |
|---|---|---|
| 字符串拼写不一致 | 驱动 probe 失败,dmesg 无匹配信息 | 严格检查设备树与驱动中的字符串是否完全一致(包括大小写) |
| 缺少通用回退字符串 | 更换内核版本后驱动失效 | 始终保留一个通用兼容字符串(如 "simple-bus") |
| 逗号后缺少空格 | 解析器报语法错误 | 遵循 DTS 语法:逗号后必须加空格 |
3.2 reg —— 硬件地址与内存映射
reg 属性定义了设备在系统总线上的地址空间,包括起始地址和长度。驱动通过 of_iomap() 或 platform_get_resource() 来获取这些信息,并建立虚拟地址映射。
3.2.1 属性结构
reg = <address1 length1 address2 length2 ...>;
- address: 设备的基地址(物理地址)。
- length: 地址空间的长度(字节数)。
- 多段地址: 当设备有多个不连续的地址空间时,可以依次列出多组
<address length>。
3.2.2 地址格式与 #address-cells / #size-cells
reg 的解析依赖于父节点中的 #address-cells 和 #size-cells 属性:
| 属性 | 含义 | 常见值 |
|---|---|---|
#address-cells |
地址字段占用的 32 位单元数 | 1(32位地址)或 2(64位地址) |
#size-cells |
长度字段占用的 32 位单元数 | 1(32位长度)或 2(64位长度) |
// 64位地址示例
soc@0 {
#address-cells = <2>;
#size-cells = <2>;
uart@ff100000 {
reg = <0x0 0xff100000 0x0 0x1000>;
// 地址:0xff100000(高32位=0,低32位=0xff100000)
// 长度:0x1000(高32位=0,低32位=0x1000)
};
};
常见错误: 当父节点
#address-cells = <2> 时,子节点 reg 中的地址必须写 2 个 32 位值(即使高 32 位为 0)。漏写会导致地址解析错位,驱动读取到错误的基地址。
3.3 interrupts —— 中断资源描述
interrupts 属性用于描述设备使用的中断号、触发类型和中断控制器层级关系。它是驱动实现异步事件处理的关键。
3.3.1 属性格式
interrupts = <interrupt-specifier>;
interrupt-parent = <&phandle>;
- interrupt-specifier: 由中断控制器定义的 1~3 个 32 位值,通常包含中断号、触发类型等。
- interrupt-parent: 指向中断控制器的 phandle,指定该设备连接到哪个中断控制器。
3.3.2 中断触发类型
| 宏定义 | 值 | 触发方式 |
|---|---|---|
IRQ_TYPE_NONE |
0 | 未指定 |
IRQ_TYPE_EDGE_RISING |
1 | 上升沿触发 |
IRQ_TYPE_EDGE_FALLING |
2 | 下降沿触发 |
IRQ_TYPE_LEVEL_HIGH |
4 | 高电平触发 |
IRQ_TYPE_LEVEL_LOW |
8 | 低电平触发 |
// 实际配置示例
gpio-keys {
compatible = "gpio-keys";
interrupt-parent = &gpio1;
interrupts = <5 IRQ_TYPE_EDGE_FALLING>,
<6 IRQ_TYPE_EDGE_RISING>;
};
驱动端获取中断号:
int irq = platform_get_irq(pdev, 0); // 获取第一个中断
if (irq < 0) {
dev_err(&pdev->dev, "failed to get IRQ\n");
return irq;
}
ret = devm_request_irq(&pdev->dev, irq, handler,
IRQF_TRIGGER_FALLING, "mydev", data);
3.4 status —— 设备状态控制
status 属性用于控制设备节点的启用或禁用状态,是设备树中实现“硬件裁剪”和“调试开关”的常用手段。
3.4.1 可选值
| 值 | 含义 | 驱动行为 |
|---|---|---|
"okay" |
设备启用 | 正常 probe,注册设备 |
"disabled" |
设备禁用 | 跳过 probe,不注册设备 |
"reserved" |
保留状态 | 通常由固件使用,内核不处理 |
"fail" |
设备故障 | 标记为不可用,驱动不加载 |
3.4.2 覆盖机制
在 .dtsi 文件中通常将设备默认设为 disabled,然后在具体的 .dts 板级文件中通过 &node 引用将其改为 okay:
// soc.dtsi(默认禁用)
uart1: serial@ff100000 {
compatible = "snps,dw-apb-uart";
reg = <0xff100000 0x1000>;
status = "disabled";
};
// myboard.dts(板级启用)
&uart1 {
status = "okay";
clocks = <&clk_uart1>;
};
调试建议: 当驱动 probe 失败时,首先检查
status 是否为 "okay"。很多移植问题源于 .dtsi 中默认 disabled 而板级文件忘记覆盖。
3.5 clocks —— 时钟资源绑定
clocks 属性用于指定设备使用的时钟源,驱动通过时钟框架(CCF)来获取、使能或调整时钟频率。
3.5.1 属性格式
clocks = <&phandle1>, <&phandle2>;
clock-names = "bus", "core";
- clocks: 指向时钟提供者节点的 phandle 列表。
- clock-names: 为每个时钟指定名称,驱动通过名称来获取对应的时钟句柄。
3.5.2 驱动端使用
struct clk *bus_clk, *core_clk;
bus_clk = devm_clk_get(&pdev->dev, "bus");
if (IS_ERR(bus_clk))
return PTR_ERR(bus_clk);
core_clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(core_clk))
return PTR_ERR(core_clk);
clk_prepare_enable(bus_clk);
clk_prepare_enable(core_clk);
3.6 resets —— 复位信号控制
resets 属性用于描述设备的复位信号,驱动可以通过复位框架在初始化或错误恢复时对设备进行硬件复位。
3.6.1 属性格式
resets = <&phandle1 reset-specifier>;
reset-names = "ctrl";
- resets: 指向复位控制器节点的 phandle,以及复位标识符。
- reset-names: 为每个复位信号命名,便于驱动引用。
3.6.2 驱动端使用
struct reset_control *rst;
rst = devm_reset_control_get(&pdev->dev, "ctrl");
if (IS_ERR(rst))
return PTR_ERR(rst);
// 执行硬件复位
reset_control_assert(rst);
udelay(10);
reset_control_deassert(rst);
3.7 属性组合实战案例
以下是一个完整的 SPI 控制器设备树节点,展示了多个属性的协同使用:
spi@ff110000 {
compatible = "snps,dw-apb-ssi";
reg = <0x0 0xff110000 0x0 0x1000>;
interrupts = <0 36 4>;
clocks = <&clk_spi_mst>;
clock-names = "mst";
resets = <&rstgen RST_SPI0>;
reset-names = "ctrl";
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
spi-flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <50000000>;
};
};
驱动 probe 流程总结:
- 通过
compatible匹配驱动 - 通过
reg获取地址并 ioremap - 通过
interrupts注册中断处理函数 - 通过
clocks使能时钟 - 通过
resets释放复位 - 检查
status确认设备可用
3.8 本章小结
| 属性 | 核心作用 | 驱动获取方式 | 常见错误 |
|---|---|---|---|
compatible |
驱动匹配 | of_match_table |
字符串拼写不一致 |
reg |
地址映射 | of_iomap() |
address/size-cells 不匹配 |
interrupts |
中断绑定 | platform_get_irq() |
中断号与控制器不匹配 |
status |
设备开关 | of_device_is_available() |
忘记覆盖 disabled |
clocks |
时钟使能 | devm_clk_get() |
时钟名称与驱动不匹配 |
resets |
复位控制 | devm_reset_control_get() |
复位顺序错误导致死锁 |