3. 设备树节点与属性详解:compatible属性、reg属性、interrupts属性、status属性、自定义属性的使用规范
设备树(Device Tree)是嵌入式Linux系统中描述硬件拓扑结构的核心机制。对于网关驱动部署而言,正确理解并配置设备树节点与属性,是确保CAN控制器、SPI总线、GPIO中断等外设正常工作的前提。本章将逐一拆解五大核心属性的语法规范、使用场景与常见陷阱。
3.1 设备树节点基础结构
设备树由一系列节点(node)和属性(property)组成,每个节点代表一个硬件设备或总线,属性则描述设备的配置参数。节点的基本语法如下:
/dts-v1/;
/ {
model = "Gateway CAN Board";
compatible = "vendor,gateway-can-board";
soc {
#address-cells = <1>;
#size-cells = <1>;
can0: can@1c00000 {
compatible = "vendor,can-controller";
reg = <0x1c00000 0x1000>;
interrupts = <0 55 4>;
status = "okay";
clock-frequency = <8000000>;
};
};
};
节点路径由根节点(/)开始,通过层级关系定位。节点名称通常采用“设备名@地址”的格式,如 can@1c00000。每个节点可以包含子节点和属性,属性值可以是字符串、整数、数组或二进制数据。
下图展示了设备树节点的层级结构与属性挂载关系:
3.2 compatible 属性:驱动匹配的核心
compatible 属性是设备树中最重要的属性之一,它建立了硬件设备与内核驱动之间的绑定关系。内核通过设备树中的 compatible 字符串,在驱动注册表中查找匹配的驱动。
3.2.1 语法规范
compatible = "manufacturer,device-model", "generic-device";
属性值是一个或多个字符串,按优先级从高到低排列。内核优先使用第一个匹配的驱动,若未找到则尝试后续字符串。
| 组成部分 | 格式要求 | 示例 | 说明 |
|---|---|---|---|
| 厂商前缀 | 小写字母、数字、连字符 | ti, nxp, microchip | 标识设备制造商 |
| 设备型号 | 小写字母、数字、连字符 | m_can, flexcan, sja1000 | 具体芯片或IP核型号 |
| 通用兼容 | 小写字母、数字、连字符 | can-controller, serial | 通用设备类别,用于回退匹配 |
3.2.2 CAN控制器兼容性配置示例
/* MCP2515 SPI转CAN控制器 */
can0: can@0 {
compatible = "microchip,mcp2515", "microchip,mcp251x", "can-controller";
reg = <0>;
clocks = <&clk16m>;
interrupt-parent = <&gpio1>;
interrupts = <3 2>;
spi-max-frequency = <10000000>;
status = "okay";
};
/* NXP FlexCAN控制器 */
can1: can@2090000 {
compatible = "fsl,imx6q-flexcan", "fsl,imx6ul-flexcan", "fsl,flexcan";
reg = <0x02090000 0x4000>;
interrupts = <0 110 4>;
clocks = <&clks 1>, <&clks 2>;
clock-names = "ipg", "per";
status = "okay";
};
下图展示了 compatible 属性驱动匹配的完整流程:
3.3 reg 属性:地址空间与资源映射
reg 属性用于描述设备在总线上的地址空间,包括基地址和地址范围。对于内存映射设备,它定义了寄存器区域的起始地址和大小;对于SPI/I2C设备,它定义了片选号或从设备地址。
3.3.1 语法规范
reg = <address1 length1 address2 length2 ...>;
每个地址-长度对由 #address-cells 和 #size-cells 属性决定其单元大小。这两个属性通常在父节点中定义。
| 场景 | #address-cells | #size-cells | reg 示例 | 含义 |
|---|---|---|---|---|
| 32位内存映射 | 1 | 1 | <0x1c00000 0x1000> | 基地址0x1c00000,范围0x1000字节 |
| 64位内存映射 | 2 | 2 | <0x0 0x1c00000 0x0 0x1000> | 64位基地址,64位范围 |
| SPI设备 | 1 | 0 | <0> | 片选号0 |
| I2C设备 | 1 | 0 | <0x50> | 从设备地址0x50 |
3.3.2 CAN控制器reg配置示例
/* 内存映射CAN控制器 - 32位地址 */
can0: can@1c00000 {
compatible = "vendor,can-controller";
reg = <0x1c00000 0x1000>; /* 基地址0x1c00000,寄存器范围4KB */
interrupts = <0 55 4>;
status = "okay";
};
/* SPI连接CAN控制器 - 片选地址 */
&spi1 {
status = "okay";
can0: can@0 {
compatible = "microchip,mcp2515";
reg = <0>; /* SPI片选0 */
spi-max-frequency = <10000000>;
interrupts = <3 2>;
status = "okay";
};
};
3.4 interrupts 属性:中断信号绑定
interrupts 属性描述设备的中断请求信号,包括中断控制器、中断号和触发类型。中断系统是CAN控制器实时响应报文的关键,正确配置中断属性至关重要。
3.4.1 语法规范
interrupts = <interrupt-specifier>;
interrupt-parent = <&interrupt-controller>;
interrupt-specifier 的格式由中断控制器的 #interrupt-cells 属性决定。常见的中断控制器类型如下:
| 中断控制器 | #interrupt-cells | 中断说明符格式 | 示例 |
|---|---|---|---|
| GIC (ARM通用中断控制器) | 3 | <中断类型 中断号 触发类型> | <0 55 4> |
| GPIO控制器 | 2 | <GPIO引脚 触发类型> | <3 2> |
| 简单中断控制器 | 1 | <中断号> | <10> |
3.4.2 GIC中断类型与触发类型
对于ARM GIC控制器,中断说明符的三个字段含义如下:
| 字段 | 值 | 含义 |
|---|---|---|
| 中断类型 | 0 | SPI(共享外设中断),用于外设 |
| 中断类型 | 1 | PPI(私有外设中断),用于CPU内部 |
| 触发类型 | 1 | 上升沿触发 |
| 触发类型 | 2 | 下降沿触发 |
| 触发类型 | 4 | 高电平触发 |
| 触发类型 | 8 | 低电平触发 |
3.4.3 CAN控制器中断配置示例
/* GIC中断控制器 - SPI中断 */
interrupt-parent = <&gic>;
interrupts = <0 55 4>; /* SPI中断号55,高电平触发 */
/* GPIO中断控制器 - 外部中断 */
interrupt-parent = <&gpio1>;
interrupts = <3 2>; /* GPIO1_3引脚,下降沿触发 */
/* 多中断源配置 */
interrupts = <0 55 4>, <0 56 4>; /* 两个中断源 */
interrupt-names = "rx", "tx"; /* 中断名称,用于驱动识别 */
3.5 status 属性:设备启用控制
status 属性控制设备的启用状态,是设备树中实现设备热插拔、电源管理、调试控制的重要机制。
3.5.1 可选值及含义
| 值 | 含义 | 使用场景 |
|---|---|---|
| "okay" | 设备启用,驱动正常加载 | 正常部署的设备 |
| "disabled" | 设备禁用,驱动不加载 | 未使用的硬件、调试阶段 |
| "reserved" | 设备保留,不由Linux管理 | 由固件或安全世界管理的设备 |
| "fail" | 设备检测失败 | 硬件故障或检测错误 |
3.5.2 使用规范
/* 在dtsi文件中默认禁用 */
&can0 {
status = "disabled";
};
/* 在板级dts文件中启用 */
&can0 {
status = "okay";
/* 其他板级特定配置 */
clock-frequency = <8000000>;
};
/* 条件编译示例 - 通过宏控制 */
#ifdef CONFIG_CAN_SUPPORT
&can0 {
status = "okay";
};
#else
&can0 {
status = "disabled";
};
#endif
3.6 自定义属性:扩展硬件描述能力
设备树允许开发者定义自定义属性,用于描述芯片特有的配置参数。自定义属性是设备树灵活性的重要体现,但必须遵循命名规范和使用约束。
3.6.1 命名规范
| 规则 | 说明 | 正确示例 | 错误示例 |
|---|---|---|---|
| 厂商前缀 | 自定义属性应包含厂商前缀 | vendor,clock-frequency | clock-frequency(标准属性除外) |
| 小写连字符 | 使用小写字母和连字符 | can-bus-speed | can_bus_speed / canBusSpeed |
| 避免标准属性名 | 不要覆盖标准属性含义 | vendor,can-mode | compatible(标准属性) |
3.6.2 常见自定义属性示例
/* CAN控制器自定义属性 */
can0: can@1c00000 {
compatible = "vendor,can-controller";
reg = <0x1c00000 0x1000>;
interrupts = <0 55 4>;
status = "okay";
/* 自定义属性:CAN总线速率 */
vendor,can-bus-speed = <500000>; /* 500Kbps */
/* 自定义属性:采样点位置 */
vendor,sample-point = <875>; /* 87.5% */
/* 自定义属性:工作模式 */
vendor,operation-mode = "loopback"; /* 回环模式 */
/* 自定义属性:滤波器配置 */
vendor,filter-config = <0x12345678 0x0FFFFFFF>;
/* 自定义属性:DMA支持 */
vendor,dma-enable;
};
3.6.3 驱动中读取自定义属性
/* 内核驱动中读取自定义属性示例 */
#include <linux/of.h>
#include <linux/of_device.h>
static int can_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
u32 bus_speed, sample_point;
const char *mode;
int ret;
/* 读取整数属性 */
ret = of_property_read_u32(np, "vendor,can-bus-speed", &bus_speed);
if (ret) {
dev_err(&pdev->dev, "Failed to read can-bus-speed\n");
return ret;
}
/* 读取字符串属性 */
ret = of_property_read_string(np, "vendor,operation-mode", &mode);
if (!ret) {
if (strcmp(mode, "loopback") == 0) {
/* 配置回环模式 */
}
}
/* 读取布尔属性 */
if (of_property_read_bool(np, "vendor,dma-enable")) {
/* 启用DMA */
}
return 0;
}
3.7 属性配置综合示例:CAN控制器完整设备树节点
以下是一个完整的CAN控制器设备树节点,综合运用了本章介绍的所有属性:
/* 完整CAN控制器设备树节点 - MCP2515 SPI转CAN */
/ {
/* 根节点属性 */
model = "Industrial Gateway CAN Board";
compatible = "vendor,gateway-v2", "vendor,gateway";
/* 时钟源 */
clk16m: oscillator {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <16000000>;
};
/* SPI总线 */
&spi1 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi1>;
/* CAN控制器节点 */
can0: can@0 {
compatible = "microchip,mcp2515", "microchip,mcp251x", "can-controller";
reg = <0>; /* SPI片选0 */
clocks = <&clk16m>; /* 外部时钟 */
interrupt-parent = <&gpio1>; /* 中断控制器 */
interrupts = <3 2>; /* GPIO1_3,下降沿触发 */
spi-max-frequency = <10000000>; /* SPI最大频率 */
status = "okay"; /* 设备启用 */
/* 自定义属性 */
microchip,oscillator-frequency = <16000000>;
microchip,can-bus-speed = <500000>;
microchip,sample-point = <875>;
microchip,operation-mode = "normal";
};
};
/* 内存映射CAN控制器 */
can1: can@2090000 {
compatible = "fsl,imx6q-flexcan", "fsl,flexcan";
reg = <0x02090000 0x4000>; /* 寄存器基地址和范围 */
interrupts = <0 110 4>; /* GIC SPI中断110,高电平触发 */
clocks = <&clks 1>, <&clks 2>;
clock-names = "ipg", "per";
status = "okay";
/* 自定义属性 */
fsl,stop-mode = <&gpr 0x34 28>;
fsl,clk-source = <0>;
};
};
下图展示了CAN控制器设备树节点与内核驱动之间的完整数据流: