本合集分享的是,我当初学习Linux驱动的来时路——《《驱动之路》开篇:自序&前言》。
正文
在嵌入式 Linux 开发中,我们经常会在设备树(DTS)中写下这样的配置:

神奇的是,驱动程序无需手动操作寄存器、无需硬编码 GPIO 编号,就能通过内核提供的接口轻松获取这个复位 GPIO,实现设备复位控制。这背后的底层逻辑是什么?今天就扒一扒这层底层逻辑。
1 DTS 到底在配置什么?
首先我们得明确,reset-gpios = <&gpio2 RK_PB4 GPIO_ACTIVE_HIGH>; 这行代码不是给 CPU 看的,而是给内核和驱动的 “约定说明书”,每一部分都有明确含义:
reset-gpios:是 “属性名”,遵循 Linux 设备树的 GPIO 属性命名规范(后缀-gpios),告诉内核 “这是一个用于复位功能的 GPIO 配置”。
&gpio2:是 “GPIO 控制器的引用”,表示这个 GPIO 归属于gpio2控制器。
RK_PB4:是 “GPIO 引脚编号”,是芯片厂商定义的宏,本质是一个整数。
GPIO_ACTIVE_HIGH:是 “GPIO 有效电平”,表示该 GPIO 为高电平有效。
PS:GPIO_ACTIVE_HIGH会被内核解析并存储在gpio_desc中,驱动调用 gpiod_set_value() 时:若配置为GPIO_ACTIVE_HIGH,驱动传入1则内核设置 GPIO 为高电平,传入0为低电平;若配置为GPIO_ACTIVE_LOW,驱动传入1则内核自动设置为低电平(有效电平),传入0为高电平。驱动无需关心 “有效电平是高还是低”,只需按 “逻辑值”(1 = 有效,0 = 无效)调用接口,内核会自动完成电平转换。也就是说,gpiod_set_value() 设置的是“逻辑值”而非“物理值”。
简单说,这行配置的核心作用是:向内核声明 “当前设备的复位 GPIO 是 gpio2 控制器的 PB4 引脚,高电平有效”,为后续驱动获取 GPIO 打下基础。
2 DTS 配置到驱动获取的 4 个关键步骤
DTS 配置只是 “纸上约定”,驱动能真正拿到 GPIO 并使用,依赖内核的 “解析 - 注册 - 匹配 - 提供” 全流程。
步骤 1:内核解析 DTS,提取 GPIO 配置信息
Linux 内核启动时,会先执行 “设备树解析阶段”:
(1)内核的设备树解析器(dtb_parser)会遍历 DTS 文件,找到所有带-gpios后缀的属性(包括reset-gpios)。
(2)解析器会识别&gpio2对应的 GPIO 控制器(通过 DTS 中gpio2节点的compatible属性匹配内核中的 GPIO 驱动,比如rockchip,rk3576-gpio).
(3)将RK_PB4引脚编号转换为内核统一的 “GPIO 全局编号”(这是关键!)。内核为了管理所有 GPIO,会给每个 GPIO 分配一个全局唯一的整数 ID(GPIO 编号),其计算规则如下:
(4)解析器将 “GPIO 编号 + 有效电平” 封装成struct gpio_desc结构体(GPIO 描述符,内核中代表一个 GPIO 的 “身份证”),并与当前设备的struct device结构体关联(存在设备的私有数据中)。 至此,DTS 中的 “文字配置” 已被内核解析为 “可操作的 GPIO 描述符”,就像把 “地址信息” 转换成了 “快递单号”,等待驱动来 “取件”。
步骤 2:GPIO 控制器驱动初始化,提供操作接口
在解析 DTS 之前,内核已经加载了gpio2对应的 GPIO 控制器驱动(通常是芯片原厂 BSP 工程师编写的平台驱动),该驱动会完成两件关键事:
(1)向内核 GPIO 子系统注册struct gpio_chip结构体(GPIO 芯片描述符),其中包含:
GPIO 控制器的基地址(操作寄存器的物理地址,已通过ioremap映射为虚拟地址);
该控制器管理的 GPIO 数量(比如gpio2管理 32 个引脚);
操作 GPIO 的核心函数(request申请、free释放、set_value设置电平、get_value读取电平)。
(2)内核 GPIO 子系统会为该控制器分配 “GPIO 基号”(比如gpio2分配 32-63 号),确保全局编号不重复。
这一步的作用是:让内核具备操作gpio2控制器下所有 GPIO 的能力,相当于给 “快递站” 配备了 “快递员”(操作函数),能处理后续的 GPIO 申请和控制请求。
步骤 3:设备与驱动匹配,驱动申请 GPIO 资源
当内核完成 DTS 解析和 GPIO 控制器初始化后,会启动 “设备与驱动匹配” 流程:
(1)内核根据 DTS 中当前设备节点的compatible属性,在驱动链表中查找匹配的设备驱动。 (2)驱动匹配成功后,会执行probe函数,在probe函数中,驱动会通过内核提供的 GPIO 申请接口,获取reset-gpios对应的 GPIO。
比如:

这里的核心接口是devm_gpiod_get_optional(devm_前缀表示内核自动管理资源,驱动卸载时自动释放 GPIO),它接收参数"reset"对应 DTS 中的reset-gpios属性(内核会自动拼接-gpios后缀)。
步骤 4:驱动操作 GPIO,实现业务功能
通过层层函数调用,驱动最终拿到reset_gpio(gpio_desc指针)。
然后就可以通过内核提供的接口gpiod_set_value() 操作 GPIO 的电平,这里的gpiod_set_value() 接口会最终调用 GPIO 控制器驱动中的set_value函数,通过操作 GPIO 寄存器(如输出控制寄存器、数据寄存器)实现电平切换,完成硬件层面的复位控制。

3 总结
DTS 配置reset-gpios后驱动能直接获取,本质是 “三层约定 + 两层封装” 的结果:
三层约定:DTS 与内核的 “配置格式约定”、GPIO 控制器与内核的 “驱动接口约定”、驱动与内核的 “资源申请约定”;
两层封装:内核 GPIO 子系统封装硬件差异、devm_机制封装资源管理。
这种设计让嵌入式开发摆脱了 “寄存器操作的繁琐” 和 “硬件差异的困扰”,驱动开发者只需专注于业务逻辑(比如何时触发复位),无需关心底层实现,这也是 Linux 设备树 “硬件描述与驱动分离” 设计思想的核心体现。
最后留个问题: reset-gpios里的GPIO_ACTIVE_HIGH能随便写吗?我们下期分享~
(完)
审核编辑 黄宇
推荐阅读:







