设备树(Device Tree)
一、为什么需要设备树
- 内核支持多种板子
- 早期:硬编码
arch/arm/mach-xxx/board-xxx.c - 现在:硬件描述放在 .dts 中,内核通用化
- SoC 级代码:通用的硬件支持
- 板级代码:DTS 文件
二、DTS 语法
1. 文件类型
.dts:板级描述.dtsi:SoC 级(include 进 .dts).dtb:编译后的二进制.dts-overlay:运行时叠加(FPGA、beaglebone)
2. 基本结构
/dts-v1/;
/include/ "imx6ull.dtsi" // 老式 include
// 或
#include "imx6ull.dtsi" // C 风格(5.x 起支持)
/ {
model = "MyBoard";
compatible = "myvendor,myboard", "fsl,imx6ull";
chosen {
stdout-path = &uart1;
};
aliases {
ethernet0 = &fec1;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; // 512MB
};
leds {
compatible = "gpio-leds";
led-status {
label = "status";
gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
};
uart1: serial@40010000 {
compatible = "fsl,imx6ul-uart", "fsl,imx21-uart";
reg = <0x40010000 0x4000>;
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_UART1_IPG>,
<&clks IMX6UL_CLK_UART1_SERIAL>;
clock-names = "ipg", "per";
status = "okay";
};
};
3. 节点和属性
node@addr {
compatible = "...";
reg = <...>;
#address-cells = <1>;
#size-cells = <1>;
status = "okay"; // 或 "disabled"
phandle = <1>; // 引用
};
引用方式:
- <&node>:引用 node
- <&node arg1 arg2>:带参数
- <&{path}>:用 full path
三、常用 binding 关键属性
1. 标准属性
compatible // 匹配驱动的关键
reg // 寄存器基址 + 大小
interrupts // 中断号
clocks // 时钟引用
clock-names
resets
reset-names
pinctrl-0 // 引脚控制
pinctrl-names
power-domains
dma-names
#address-cells
#size-cells
status // "okay" / "disabled"
2. GPIO
gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>; // 通用
interrupt-gpios = <&gpio2 3 IRQ_TYPE_LEVEL_LOW>;
3. 引脚控制
pinctrl-0 = <&pinctrl_uart1>;
四、内核 API
1. 获取节点
struct device_node *np = of_find_node_by_path("/uart1");
struct device_node *np = of_find_node_by_name(root, "uart1");
struct device_node *np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-uart");
struct device_node *np = of_get_parent(np);
struct device_node *child;
for_each_child_of_node(np, child) { /* ... */ }
2. 属性读
u32 val;
of_property_read_u32(np, "clock-frequency", &val);
of_property_read_string(np, "label", &str);
of_property_read_u32_array(np, "reg", arr, ARRAY_SIZE(arr));
of_property_read_u64(np, "clock-frequency", &val64);
3. 找子节点(带 compatible)
struct device_node *child;
for_each_compatible_node(child, NULL, "myvendor,mysensor") {
/* 处理每个匹配节点 */
}
4. 与 platform 驱动集成
static const struct of_device_id my_of_match[] = {
{ .compatible = "myvendor,mydevice" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of_match);
static struct platform_driver my_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "mydevice",
.of_match_table = my_of_match,
},
};
probe 中:
static int my_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
u32 val;
of_property_read_u32(np, "myprop", &val);
// 用 devm_ API 自动清理
void __iomem *base = devm_platform_ioremap_resource(pdev, 0, NULL);
int irq = platform_get_irq(pdev, 0);
return 0;
}
五、编译
# 在内核顶层
make dtbs
make imx6ull-myboard.dtb
arch/arm/boot/dts/<soc>.dtb
六、加载
# U-Boot
fatload mmc 0:1 ${fdt_addr_r} myboard.dtb
fdt addr ${fdt_addr_r}
fdt move ${fdt_addr_r} ${fdt_addr}
bootz ${kernel_addr_r} - ${fdt_addr}
# Linux
mount -t configfs none /sys/kernel/config
mkdir /sys/kernel/config/device-tree/overlays/myspi
cat myspi.dtbo > /sys/kernel/config/device-tree/overlays/myspi/dtbo
echo 1 > /sys/kernel/config/device-tree/overlays/myspi/status
七、debugfs + device tree
mount -t debugfs none /sys/kernel/debug
ls /sys/kernel/debug/device-tree/
cat /sys/kernel/debug/device-tree/uart1/compatible
cat /sys/kernel/debug/device-tree/uart1/reg
八、常用工具
dtc(Device Tree Compiler):编译/反编译fdtdump:dump dtbfdtget / fdtput:操作 dtb- dts-check(内核
scripts/dtc/)
dtc -I dts -O dtb -o myboard.dtb myboard.dts
dtc -I dtb -O dts -o myboard.dts myboard.dtb
fdtdump myboard.dtb
fdtget myboard.dtb /uart1 compatible