Skip to content

设备树最佳实践

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.dts

3. 使用 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>;             /* 大小 */
};

褚成志的笔记