设备树是用于描述一切硬件(包括cpu 内存 外设等)
DTS、DTB和DTC
DTS 是设备树源码文件;(.主要包括 .dts和 .dtsi这两种源文件类型)
DTC是编译工具;
DTB是编译后得到的二进制文件。
.dtbo是动态设备树:在运行时动态地修改系统的基础设备树(DTB),而无需重新编译内核或基础设备树本身(这里是linux中,其他系统如android我不太了解不敢说)
dtbo二进制文件的源码文件也是由.dts文件编译而出,语法有些许不同。
(在rk系列的板子上 使用那个动态设备树 是在/boot/uEnv/uEnv.txt(软链接)下规定的)(我手上这个板子上是,不同的板子可能不同)
基本原则
- 设备树可以将 一款SOC他的其他所有设备/平台的共有的信息提出来,作为一个通用的.dtsi文件。编译那个是通过makefile中设置(会根据芯片选)
- 是从"/"根节点开始的,从这里描述设备信息
- 多个文件下的“/”根节点的内容会合并成一个根节点
- 根节点外的& 开始的语句是“追加”:如果没有就追加、有就覆盖
- 节点名字,完整的要求 node-name@unit-address
通常是label:node-name@unit-address
前面是标签,后面是名字,“node-name”是节点名字,为 ASCII 字符串,节点名字应该能够清晰的描述出节点的功能。unit-address:一般是外设寄存器的起始地址,有时候是I2C的设备地址,或者其他含义。如果某个节点没有地址或者寄存器的话“unit-address”可以不要,比如“cpus”、“cpu@0”。
label 的目的就是为了方便访问节点,&label就是前面提到的追加 - 节点嵌套子节点,系统在启动的时候内核会读取设备树,会生成对应的路径(非常有linux一切皆文件的感觉)(在根文件系统的/proc/device/tree 目录下根据节点名字创建不同文件夹)
属性
// 文件: my-board.dts
/dts-v1/;
#include "imx8mp.dtsi" // 包含芯片通用定义,多个根节点内容将合并
/ {
// 合并后的根节点
model = "My Company i.MX8MP Development Board";
compatible = "mycompany,imx8mp-myboard", "fsl,imx8mp"; // 板子兼容性标识
// 板级内存配置
memory@40000000 {
device_type = "memory";
reg = <0x40000000 0x80000000>; // 2GB RAM
};
// 使用 & 引用并追加/覆盖节点,对应你总结的第4点
&uart1 {
status = "okay"; // 覆盖为启用,因为板子上用这个串口做调试输出
};
};
// 板子上扩展的一个I2C温度传感器
&i2c1 { // 假设i2c1控制器已在imx8mp.dtsi中定义
temperature-sensor@48 {
compatible = "ti,tmp75"; // 驱动匹配的关键!
reg = <0x48>; // I2C从设备地址,对应你总结的第5点
};
};
开始
| 概念 | 作用与含义 | 实际应用场景 |
|---|---|---|
/dts-v1/; |
版本声明:指定设备树源文件(.dts)遵循的语法规范版本。 | 通常写在.dts文件的第一行,确保编译器能正确解析后续的语法。 |
model属性 |
板卡标识:以字符串形式描述具体硬件平台的型号或名称,如"My Company i.MX8MP Development Board"。 |
帮助内核或开发人员识别正在运行的具体是哪一款板卡。 |
| 总线与设备节点位置 | 节点在设备树中的位置决定了它是什么设备类型(如 platform_device, i2c_client)。 |
temperature-sensor@48位于 &i2c1节点下,所以它是I2C设备,而非Platform设备。 |
compatible
compatible 属性也叫做“兼容性”属性,是一个字符串列表。通过在内核中(从前往后)匹配用于将设备和驱动绑定起来。一个compatible是可以放多个属性的。
compatible 属性值的一般格式
"manufacturer,model"
其中 manufacturer 表示厂商,model 一般是模块对应的驱动名字。
对应的驱动:
(一般驱动程序文件都会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果
设备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动。)
//platform虚拟总线驱动框架 代码截取
//(4)如果使用设备树 写匹配列表 就是上面的xxx_of_match
static const struct of_device_id xxx_of_match[] = {
{.compatible="xxx-gpio"},
{/*Sentinel*/}
};
# address-cells 和# size-cells 属性
首先要明确这两个属性是给子节点设置的(可以用在任何拥有子节点的设备中)
定义子节点 reg属性的格式,即地址和长度各用几个32位数表示。
address-cells决定了子节点 reg属性中地址信息所占用的字长(32 位)
size-cells属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)
reg 属性
reg 属性的值一般是(address,length)对,reg 属性一般用于描述设备地址空间资源信息或者设备地址信息,比如某个外设的寄存器地址范围信息,或者 IIC器件的设备地址等。
举例:
reg = <0x0 0xfd890000 0x0 0x100>;
uart0 节点描述了 rk3588 系列芯片的 UART0 相关信息,重点是第 10 行的 reg 属性,reg属性中“0x0 0xfd890000”这是 UART0 设备的基地址。这里使用了两个 32 位值来表示地址,这是因为#address-cells 被设置为<2>,意味着地址由两个 cells 组成。所以,UART0 的基地址是 0x00000000fd890000。“0x0 0x100”这描述了设备的大小。这里使用了两个 32 位值来表示大小,但实际上第二个值 0x100(即 256 字节)是有效的,因为#size-cells 被设置为<2>,但第一个值 0x0 通常应该是 0,表示大小只由一个 cell 组成。所以,UART0 设备的大小是0x100(即 256 字节)。查阅《Rockchip RK3588 TRM-Part1》可知,RK3588 芯片的 UART0 寄存器首地址为 0xfd890000,长度为 64KB,但是 UART0 的寄存器远远用不了 64KB,0X100 完全够了,根据此获取 UART0 寄存器首地址。
status属性
| 值 | 描述 |
|---|---|
| "okay" | 表明设备是可操作的。 |
| "disabled" | 表明设备当前不可操作,但未来可能变为可操作(例如热插拔设备插入后)。具体含义需参考设备绑定文档。 |
| "fail" | 表明设备不可操作,且检测到一系列错误,不大可能恢复可操作状态。 |
| "fail-sss" | 含义与 "fail" 相同,其中 "sss" 部分表示具体的错误内容。 |
pinctrl子系统
(在嵌入式系统和芯片手册的语境中,“pin”通常指的就是芯片上物理的“引脚”,也就是芯片外部那些看得见摸得着的金属接触点)
软件通过写入配置寄存器(如模式、方向、上拉/下拉寄存器)来设定引脚的功能(输入/输出)、工作模式(如推挽/开漏)和电气特性(如上拉电阻)
pinctrl子系统源自驱动分离与分层思想,其主要是在设备树上做响应操作,使用其可减少内核驱动.c文件的编写复杂程度。
//不看这段
正常来讲我们对引脚设置在两个方面:
(1)设置PIN的复用功能。
(2)如果PIN复用为GPIO功能,设置GPIO相关属性;如果是其他总线功能对应使用。
pinctrl子系统主要工作内容:
- 获取设备树中pin信息
- 根据获取到的pin信息来设置pin的复用功能
- 根据获取到的pin信息来设置pin的电气特性,如驱动能力
设备树里面设置好某个 pin 的相关属性即可,其他由内核源码(在drivers/pinctrl下)
这里写个MX6ULL中的例子(rk上的例子不清晰)
//imx6ull-alientek-emmc.dts
//其中的iomuxc节点
&iomuxc {
// 为第 0 组状态命名为 "default"
//当驱动加载时,可以通过名字(如 "default")来切换不同的引脚状态(例如,设备休眠时切换到一种省电的引脚状态)
pinctrl-names = "default";
//表示将 pinctrl_hog_1这个组(即 hoggrp-1)作为第 0 组状态(pinctrl-0)的配置
pinctrl-0 = <&pinctrl_hog_1>;
//子节点
imx6ull-evk
//引脚配置组
{
pinctrl_hog_1:hoggrp-1
{
//平台,引脚=<>
fsl,pins=<
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
...
>;
};
...
}
};
//为了更好理解 模拟的案例
&fec1 { // 这是以太网控制器节点
pinctrl-names = "default", "sleep"; // 第1步:声明本设备支持两种状态,名字分别为 "default" 和 "sleep"
pinctrl-0 = <&pinctrl_eth1_default>; // 第2步:状态 "default" 对应 eth1defaultgrp 配置组
pinctrl-1 = <&pinctrl_eth1_sleep>; // 第2步:状态 "sleep" 对应 eth1sleepgrp 配置组
phy-mode = "rmii";
status = "okay";
// ... 其他属性
};
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19这个宏定义是依靠厂商写的内核pinctrl子系统中的相关头文件(可能有嵌套)
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
-> ;0x17059是电气特性
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
-> NXP 官方在头文件中定义的(寄存器)固定格式?
<mux_reg conf_reg input_reg mux_mode input_val>
dtbo示例
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&i2c0>;
__overlay__ {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c0m1_xfer>;
};
};
};