设备树最佳实践
1. compatible 命名规范
c
/* ✅ 正确格式:vendor,device[-version] */
compatible = "ti,omap4-uart";
compatible = "arm,pl011";
compatible = "raspberrypi,rpi-backlight";
/* ❌ 错误:无 vendor 前缀 */
compatible = "my-uart";
/* 多个 compatible 实现向后兼容 */
compatible = "myvendor,my-uart-v2", "myvendor,my-uart-v1";2. 为每个 compatible 提供 binding 文档
在 Documentation/devicetree/bindings/ 下创建 YAML 格式的 binding 文档:
yaml
# Documentation/devicetree/bindings/serial/myvendor,my-uart.yaml
%YAML 1.2
---
$id: http://devicetree.org/schemas/serial/myvendor,my-uart.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MyVendor UART Controller
properties:
compatible:
enum:
- myvendor,my-uart-v1
- myvendor,my-uart-v2
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
clock-names:
const: uart_clk
required:
- compatible
- reg
- interrupts
- clocks
- clock-names验证:
bash
dt-validate -s Documentation/devicetree/bindings/ myboard.dts3. 使用 status 属性控制启用/禁用
c
/* SoC dtsi 中默认禁用所有外设 */
uart0: serial@fe201000 {
compatible = "myvendor,my-uart";
reg = <0xfe201000 0x1000>;
status = "disabled"; /* 默认禁用 */
};
/* 板级 dts 中按需启用 */
&uart0 {
status = "okay";
};驱动中检查 status:
c
/* of_device_is_available() 检查 status 是否为 "okay" 或 "ok" */
if (!of_device_is_available(np))
return -ENODEV;4. 正确使用 #address-cells 和 #size-cells
c
/* 父节点定义子节点 reg 的格式 */
soc {
#address-cells = <1>; /* reg 地址占 1 个 u32 */
#size-cells = <1>; /* reg 大小占 1 个 u32 */
uart@fe201000 {
reg = <0xfe201000 0x1000>; /* 1+1 = 2 个 cell */
};
};
/* 64 位地址 */
soc {
#address-cells = <2>; /* 地址占 2 个 u32(高32位 + 低32位) */
#size-cells = <2>;
pcie@0000000900000000 {
reg = <0x00000009 0x00000000 0x00000000 0x01000000>;
};
};5. 使用 phandle 引用其他节点
c
/* 定义时加标签 */
clk_uart: clock@fe300000 {
compatible = "myvendor,my-clk";
reg = <0xfe300000 0x1000>;
#clock-cells = <1>;
};
/* 引用 */
uart0: serial@fe201000 {
clocks = <&clk_uart CLK_UART0>; /* &标签 + 参数 */
clock-names = "uart_clk";
};6. 避免在设备树中硬编码 Linux 特有信息
c
/* ❌ 不应该出现在设备树中 */
linux,irq-type = <4>;
linux,driver = "my-uart";
/* ✅ 设备树描述硬件,驱动决定如何使用 */
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;7. 使用 ranges 进行地址转换
c
/* ranges 为空:子节点地址直接映射到父节点地址空间 */
soc {
ranges;
uart@fe201000 { reg = <0xfe201000 0x1000>; };
};
/* ranges 有值:地址转换 */
pci {
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x02000000 0 0x10000000 /* PCI 地址 */
0x10000000 /* CPU 地址 */
0 0x10000000>; /* 大小 */
};