4. 设备树常用属性详解:compatible、reg、interrupts、status、model 等核心属性
设备树中的属性(Property)是描述硬件节点特征的关键载体。每个节点通过一组属性向内核传递硬件配置信息,驱动则通过解析这些属性来初始化设备。本章将深入剖析最常用的五个核心属性:compatible、reg、interrupts、status 和 model,并结合实战案例说明其编写规范与驱动适配要点。
4.1 compatible 属性:驱动匹配的“身份证”
compatible 属性是设备树中最关键的属性之一,它指定了设备与驱动之间的匹配关系。内核通过该属性查找对应的驱动程序。
4.1.1 属性格式与规范
compatible 属性是一个字符串列表,通常遵循 "厂商,设备型号" 的命名规范。列表中的第一个字符串是精确匹配,后续字符串是兼容的设备型号。
// 标准格式
compatible = "vendor,device", "vendor,device-family", "generic-driver";
// 实际示例:I2C控制器
compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
// 实际示例:串口设备
compatible = "ns16550a", "ns16550";
| 字段 | 说明 | 示例 |
|---|---|---|
| 厂商前缀 | 设备制造商的缩写,通常与内核中的 vendor 前缀一致 | fsl (飞思卡尔)、ti (德州仪器) |
| 设备型号 | 具体的芯片或模块型号 | imx6q-i2c、omap4-uart |
| 兼容列表 | 按优先级从高到低排列,驱动按顺序匹配 | "fsl,imx6q-i2c", "fsl,imx21-i2c" |
4.1.2 驱动匹配机制
Linux 内核的设备驱动模型通过 of_match_table 实现设备树与驱动的匹配。驱动编写者需要在驱动中声明一个 of_device_id 结构体数组,内核在设备树解析时会遍历该数组进行匹配。
// 驱动中的匹配表
static const struct of_device_id imx_i2c_of_match[] = {
{ .compatible = "fsl,imx6q-i2c", .data = &imx6q_i2c_hw },
{ .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hw },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_i2c_of_match);
// 平台驱动结构体
static struct platform_driver imx_i2c_driver = {
.driver = {
.name = "imx-i2c",
.of_match_table = imx_i2c_of_match,
},
.probe = imx_i2c_probe,
.remove = imx_i2c_remove,
};
compatible = "myvendor,myuart", "ns16550a",这样即使没有专用驱动,也能使用通用 16550 驱动。
4.1.3 compatible 匹配流程图
4.2 reg 属性:地址空间与资源映射
reg 属性用于描述设备在总线上的地址空间,包括寄存器基地址、内存区域、I/O 端口等资源。它是驱动获取硬件资源的核心依据。
4.2.1 属性格式
reg 属性由一组 <地址, 长度> 对组成,每个对称为一个 reg 条目。地址和长度的位数由父节点的 #address-cells 和 #size-cells 属性决定。
// 基本格式
reg = <address1 length1> [, <address2 length2> ...];
// 示例1:32位地址空间,单个区域
#address-cells = <1>;
#size-cells = <1>;
reg = <0x0209c000 0x4000>; // 基地址0x0209c000,长度0x4000
// 示例2:64位地址空间,两个区域
#address-cells = <2>;
#size-cells = <2>;
reg = <0x0 0x4a000000 0x0 0x1000>, // 第一个区域
<0x0 0x4a001000 0x0 0x100>; // 第二个区域
// 示例3:带片选的 SPI 设备
spi@0 {
compatible = "m25p80";
reg = <0>; // 片选号0
spi-max-frequency = <20000000>;
};
4.2.2 address-cells 与 size-cells
| 属性 | 含义 | 常见值 | 说明 |
|---|---|---|---|
#address-cells |
地址字段占用的 32 位单元数 | 1 或 2 | 1 表示 32 位地址,2 表示 64 位地址 |
#size-cells |
长度字段占用的 32 位单元数 | 0 或 1 | 0 表示无长度(如片选),1 表示 32 位长度 |
#address-cells 和 #size-cells 会导致 reg 属性解析错误。例如,在 64 位 ARM 平台上,如果父节点未正确设置这两个属性,子节点的 reg 地址可能被截断。
4.2.3 驱动中读取 reg 资源
在驱动中,通常使用 platform_get_resource() 或 of_iomap() 函数来获取 reg 属性定义的资源。
// 方法1:使用 platform_get_resource
static int my_driver_probe(struct platform_device *pdev)
{
struct resource *res;
void __iomem *base;
// 获取第0个reg条目(索引从0开始)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get memory resource\n");
return -ENXIO;
}
// 映射寄存器地址
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
// ... 使用 base 操作硬件寄存器
return 0;
}
// 方法2:使用 of_iomap(更简洁)
static int my_driver_probe(struct platform_device *pdev)
{
void __iomem *base;
// 直接映射第0个reg条目
base = of_iomap(pdev->dev.of_node, 0);
if (!base) {
dev_err(&pdev->dev, "failed to iomap\n");
return -ENOMEM;
}
// ... 使用 base
return 0;
}
4.2.4 reg 属性地址映射示意图
4.3 interrupts 属性:中断资源描述
interrupts 属性用于描述设备的中断请求线,包括中断号、触发类型等信息。它是设备与中断控制器之间的桥梁。
4.3.1 属性格式
interrupts 属性的格式依赖于父节点 interrupt-parent 指定的中断控制器。通常包含中断号和触发类型两个字段。
// 基本格式(依赖 interrupt-parent)
interrupt-parent = <&gic>; // 指定中断控制器
interrupts = <0 89 4>; // 中断号 89,触发类型 4
// 多中断描述
interrupts = <0 89 4>, <0 90 4>; // 两个中断
// 无中断控制器引用(直接使用父节点)
interrupts = <14 2>; // 中断号14,触发类型2
4.3.2 中断触发类型
| 值 | 宏定义 | 触发方式 | 适用场景 |
|---|---|---|---|
| 0 | IRQ_TYPE_NONE |
未指定 | 由硬件默认决定 |
| 1 | IRQ_TYPE_EDGE_RISING |
上升沿触发 | 按键、边沿敏感设备 |
| 2 | IRQ_TYPE_EDGE_FALLING |
下降沿触发 | 下降沿中断设备 |
| 3 | IRQ_TYPE_EDGE_BOTH |
双边沿触发 | 编码器、脉冲计数器 |
| 4 | IRQ_TYPE_LEVEL_HIGH |
高电平触发 | 电平敏感设备 |
| 8 | IRQ_TYPE_LEVEL_LOW |
低电平触发 | 低电平有效设备 |
4.3.3 中断控制器与 interrupts-extended
当设备连接到多个中断控制器时,可以使用 interrupts-extended 属性为每个中断指定不同的中断控制器。
// 使用 interrupts-extended 指定多个中断控制器
interrupts-extended = <&gic 0 89 4>, <&gpio1 15 2>;
// 第一个中断来自 GIC,第二个中断来自 GPIO1 控制器
4.3.4 驱动中获取中断号
static int my_driver_probe(struct platform_device *pdev)
{
int irq;
int ret;
// 获取第0个中断号
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, my_irq_handler,
IRQF_TRIGGER_RISING, "my_device", dev);
if (ret) {
dev_err(&pdev->dev, "failed to request irq\n");
return ret;
}
return 0;
}
4.3.5 中断路由结构图
4.4 status 属性:设备状态控制
status 属性用于控制设备节点的启用或禁用状态。它允许在不修改设备树文件的情况下,通过 overlay 或 bootloader 动态调整设备状态。
4.4.1 可选值
| 值 | 含义 | 说明 |
|---|---|---|
"okay" |
设备启用 | 设备正常初始化,驱动可以 probe |
"disabled" |
设备禁用 | 设备不会被初始化,驱动不会 probe |
"reserved" |
保留状态 | 设备被保留,不用于操作系统 |
"fail" |
设备故障 | 设备检测到故障,不启用 |
"fail-sss" |
故障原因 | 如 "fail-gpio" 表示 GPIO 检测失败 |
4.4.2 典型用法
// 在 dtsi 中默认禁用
&uart1 {
status = "disabled";
};
// 在板级 dts 中启用
&uart1 {
status = "okay";
pinctrl-0 = <&pinctrl_uart1>;
pinctrl-names = "default";
};
// 条件启用(通过 bootloader 修改)
// bootloader 根据硬件版本设置 status
disabled,板级 dts 文件中按需设置为 okay。这样可以避免未使用的设备占用资源,同时保持设备树的可维护性。
4.4.3 内核处理 status 的流程
4.5 model 属性:设备型号标识
model 属性用于描述设备或板卡的型号名称。它通常作为人类可读的标识,用于区分不同硬件版本或配置。
4.5.1 属性格式
// 根节点中的 model 描述整板型号
/ {
model = "Freescale i.M