设备树与pinctrl子系统 2025-10-06

设备树是用于描述一切硬件(包括cpu 内存 外设等)

DTS、DTB和DTC

DTS 是设备树源码文件;(.主要包括 .dts.dtsi这两种源文件类型)
DTC是编译工具;
DTB是编译后得到的二进制文件。

.dtbo是动态设备树:在运行时动态地修改系统的基础设备树(DTB),而无需重新编译内核或基础设备树本身(这里是linux中,其他系统如android我不太了解不敢说)
dtbo二进制文件的源码文件也是由.dts文件编译而出,语法有些许不同。
(在rk系列的板子上 使用那个动态设备树 是在/boot/uEnv/uEnv.txt(软链接)下规定的)(我手上这个板子上是,不同的板子可能不同)

基本原则

  1. 设备树可以将 一款SOC他的其他所有设备/平台的共有的信息提出来,作为一个通用的.dtsi文件。编译那个是通过makefile中设置(会根据芯片选)
  2. 是从"/"根节点开始的,从这里描述设备信息
  3. 多个文件下的“/”根节点的内容会合并成一个根节点
  4. 根节点外的& 开始的语句是“追加”:如果没有就追加、有就覆盖
  5. 节点名字,完整的要求 node-name@unit-address
    通常是label:node-name@unit-address
    前面是标签,后面是名字,“node-name”是节点名字,为 ASCII 字符串,节点名字应该能够清晰的描述出节点的功能。unit-address:一般是外设寄存器的起始地址,有时候是I2C的设备地址,或者其他含义。如果某个节点没有地址或者寄存器的话“unit-address”可以不要,比如“cpus”、“cpu@0”。
    label 的目的就是为了方便访问节点,&label就是前面提到的追加
  6. 节点嵌套子节点,系统在启动的时候内核会读取设备树,会生成对应的路径(非常有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子系统主要工作内容:

  1. 获取设备树中pin信息
  2. 根据获取到的pin信息来设置pin的复用功能
  3. 根据获取到的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>;
        };
    };
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容