设备树实战指南

本文是 嵌入式 Linux 学习路径 第五阶段:从 原理图 → DTS 节点 → 内核 probe 完成闭环。完成后你应能:读 SoC .dtsi、写板级 .dts、编译 dtb、用 dmesg 验证 compatible 匹配


学习目标

  • 理解 DTS 语法标签/引用/include/.dtsi 分层
  • GPIO 控制的 LED 编写节点,并与 现有或最小驱动 匹配。
  • 使用 dtc、内核 make dtbs 编译;在运行系统上读 /proc/device-tree
  • 查阅 Documentation/devicetree/bindings 核对属性合法性。

设备树在启动中的位置

  • Bootloader 将 dtb 指针 传给内核(ARM64 booti 第三参数等)。
  • 内核 early init 解析 DTB 为 unflattened device-tree;驱动 probe 时匹配 compatible

语法基础

节点与属性

/ {
	model = "My Board";
	compatible = "vendor,myboard", "vendor,socfamily";
 
	leds {
		compatible = "gpio-leds";
		status = "okay";
 
		led_user: led-user {
			label = "user";
			gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
			default-state = "off";
		};
	};
};

常见属性

属性含义
compatible驱动匹配字符串列表(从具体到通用 fallback)
reg地址与长度(MMIO/总线地址)
address-cells / size-cells子节点 reg 格式
interrupts中断 specifier(依赖 interrupt-parent)
gpiosGPIO specifier(依赖 gpio-controller)
status"okay" / "disabled"
phandle / &label引用其他节点

引用 SoC 已有控制器

板级 .dts 通常:

/dts-v1/;
#include "soc.dtsi"
 
/ {
	compatible = "vendor,myboard", "vendor,soc";
	/* 覆盖或追加 */
};
 
&gpio0 {
	status = "okay";
};
 
&i2c1 {
	status = "okay";
	/* 挂外设节点 */
};

实战:GPIO LED(对照原理图)

步骤

  • 在原理图找到 LED 网络名、所接 GPIO 端口与引脚有效电平(高亮还是低亮)。
  • SoC dtsi 确认 gpio controller 节点标签(如 gpio0)。
  • 板级 dts 增加 gpio-leds 子节点(内核自带 leds-gpio 驱动,compatible gpio-leds)。

编译 dtb

在内核树:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs
# 或单独:
dtc -I dts -O dtb -o board.dtb board.dts

部署与验证

  • 替换 boot 分区 dtb 或通过 U-Boot fdt 命令 修改(高级)。
  • 启动后:
dmesg | grep -i led
ls /sys/class/leds/
echo 1 > /sys/class/leds/user/brightness

probe 失败排障

现象检查
无 led 相关 dmesgstatuscompatible、dtb 是否真正加载
deferred probe依赖的 clock/regulator/gpio 未就绪
GPIO 错误引脚号bankpinctrl 是否配置

pinctrl(多数 SoC 必配)

  • GPIO 常需 pin mux 从复用功能切到 GPIO;在 pinctrl 节点 定义 state,在设备节点 pinctrl-names / pinctrl-0 引用。
&pinctrl {
	led_pins: led-pins {
		pins = "GPIO12";
		function = "gpio";
	};
};
 
&led_user {
	pinctrl-names = "default";
	pinctrl-0 = <&led_pins>;
};

(具体属性名 必须以 SoC binding 为准。)


调试工具

# 运行中查看(路径随内核配置)
ls /proc/device-tree/
hexdump -C /proc/device-tree/leds/led-user/gpios
of_node - 通过 sysfs /sys/firmware/devicetree/...(较新内核)
dtc -I fs -O dts /proc/device-tree > live.dts  # 反编译查看(教学用)

libfdt / fdtdump:离线检查 dtb。


与驱动的关系

  • platform_drivergpio-leds 平台驱动 在注册时遍历 DT 匹配 compatible
  • 匹配成功后 probe 解析 gpios 属性,注册 led_classdev
  • 自定义驱动of_match_table 与 DTS compatible 字符串 完全一致(包括 vendor 前缀)。

实践练习

  • 两个 LED 增加节点,default-state 一 on 一 off。
  • 故意写错 gpio 编号,记录 dmesg,再改对。
  • Documentation/devicetree/bindings/leds/leds-gpio.yaml(或旧 .txt),列出必填属性。
  • 设备树插件 dtbo(若平台支持)动态加载(选做)。

阶段验收

  • 从原理图 标出 gpio 与 active level,与 dts 一致。
  • 解释 &gpio0 12 GPIO_ACTIVE_HIGH 每个 cell 含义(查 gpio binding)。
  • 能在 5 步内 完成 dts 改 → dtb 编 → 刷入 → sysfs 点灯。

下一阶段衔接

  • 字符设备驱动:自定义 compatibleplatform_driver 替代 gpio-leds 框架(教学)。
  • I2C/SPI 传感器:同一套 reg/interrupts 思维扩展到总线设备。

参考

  • 内核 devicetree-specification(规范)
  • Documentation/devicetree/bindings/
  • Device Tree for Dummies(elinux.org,入门)

一个 dts 错误可能导致 boot hang;保留 U-Boot fdt 回退双 dtb 分区