驱动之路#20:Pinctrl 在手,引脚复用很顺手

智驭车手

欢迎关注,每周更新!

本合集分享的是,我当初学习Linux驱动的来时路——《《驱动之路》开篇:自序&前言》。

正文

嵌入式 Linux 开发中,你是否遇到过这些困惑:

为什么同样一个 GPIO引脚,既能做普通输入输出,又能做 I2CSDA、SPI 的 CS 等功能?

驱动中没写引脚配置代码,芯片却能自动切换引脚功能,底层是怎么实现的?

设备树中pinctrl-0 pinctrl-names这些属性到底有什么用?

这一切的答案,都藏在 Linux 内核的Pinctrl 子系统里。它是管理芯片引脚的 “总管家”,负责引脚的复用配置、电气属性设置,让驱动开发者彻底摆脱 “寄存器操作引脚” 的繁琐。

1 为什么需要 Pinctrl 子系统?

如果没有 Pinctrl 子系统,嵌入式引脚配置全靠 “硬编码”,痛点极其明显:

(1)引脚复用冲突

芯片的引脚资源有限,一个引脚往往对应多种功能(如 GPIO、I2C、UART、SPI)。比如 RK3576 的GPIO2_PB4,支持如下功能复用。

wKgZPGnVBIKABAtIAAGAIr8UqrE187.png

如果驱动直接操作寄存器配置功能,很容易出现 “多个驱动抢占同一引脚” 的冲突。

(2)配置繁琐且易出错

引脚配置不仅要选 “功能”,还要设置电气属性:

上拉 / 下拉电阻(如 I2C 引脚需要上拉);

驱动能力(如大电流引脚需要增强驱动能力);

电平标准(如 LVCMOS3.3V、LVDS)。

这些都需要操作复杂的寄存器,不同芯片的寄存器地址还不一样,移植性极差。

(3)驱动与硬件耦合严重

驱动代码中硬编码引脚配置,更换硬件平台或修改引脚时,必须修改驱动代码,违背了 “硬件描述与驱动分离” 的设计思想。

而 Pinctrl 子系统的核心目标,就是解决这些问题:

统一管理引脚:集中处理引脚复用、电气属性配置,避免冲突;

设备树驱动解耦:引脚配置写在设备树中,驱动无需关心硬件细节;

标准化接口:提供统一的内核 API,驱动通过设备树即可获取引脚配置。

2 Pinctrl 的 3 个关键术语

想要理解 Pinctrl,必须先搞懂这 3 个核心概念,否则后续流程会 confusion:

(1)Pin(引脚)

指芯片物理引脚的抽象,每个引脚都有唯一的编号(如 RK3576 的GPIO2_PB4对应引脚编号52)。Pinctrl 子系统通过 “引脚编号” 识别和管理硬件引脚。

(2)Pinmux(引脚复用)

“Pin Multiplexing” 的缩写,核心作用是选择引脚的功能模式。比如:

把GPIO2_PB4配置为 “普通 GPIO 模式”;

把GPIO2_PB4配置为 “UART7_CTSN 模式”;

把GPIO2_PB4配置为 “SPI4_MOSI 模式”。

每个引脚的可用模式,由芯片的 datasheet 明确规定(如瑞芯微提供的 RK3576_PinOut 表格)。

(3)Pinctrl(引脚配置)

“Pin Control” 的缩写,核心作用是设置引脚的电气属性,常见配置包括:

上拉电阻(pull-up):引脚默认拉高,如 I2C、按键引脚;

下拉电阻(pull-down):引脚默认拉低,如复位引脚;

驱动能力(drive strength):引脚输出电流大小,如 LED 引脚需要 10mA 驱动;

电平标准(voltage level):如 LVCMOS3.3V、LVCMOS1.8V;

slew rate(转换速率):引脚电平变化的快慢,高频场景需调整。

简单总结:Pinmux 负责 “引脚做什么功能”,Pinctrl 负责 “引脚的电气特性是什么”,两者结合完成引脚的完整配置。

3 Pinctrl 工作流程

Pinctrl 子系统的工作,本质是 “设备树配置 → 内核解析 → 驱动使用” 的流程。

步骤 1:设备树中定义引脚配置

开发者需要在设备树中,为每个设备(如 I2C、SPI、传感器)定义 “引脚复用 + 电气属性”,核心是 3 类节点:

(1)Pinctrl 控制器节点

描述芯片的引脚控制器硬件信息,由芯片厂商在 dtsi 文件中预定义(无需开发者修改)。例如 RK3576 的 rk3576.dtsi(如下图)。

核心作用:告诉内核 “引脚控制器的硬件位置、支持的功能”,内核通过该节点初始化 Pinctrl 驱动。

(2)引脚配置子节点

在&pinctrl节点下(rk3576-pinctrl.dtsi),定义具体的引脚配置(复用 + 电气属性),通常以 “功能 + 场景”命名。例如 I2C0 的引脚配置:

wKgZPGnVBIKAEeDhAAC5avd7Anw388.png

(3)设备节点引用引脚配置

在具体设备节点(如 I2C0)中,通过pinctrl-names和pinctrl-0引用上述配置:

步骤 2:内核解析设备树,初始化引脚配置

Linux 内核启动时,Pinctrl 子系统会执行以下操作:

解析&pinctrl控制器节点,加载对应的 Pinctrl 驱动(如drivers/pinctrl/pinctrl-rockchip.c);

解析引脚配置子节点(如i2c0_default),提取pinmux(功能)和pinconf(电气属性)信息;

检查引脚是否被其他设备占用(避免复用冲突);

若引脚空闲,通过 Pinctrl 驱动操作寄存器,配置引脚的复用模式和电气属性;

把配置好的引脚与设备节点(如&i2c0)绑定,等待驱动使用。

步骤 3:驱动匹配成功,直接使用配置好的引脚

当设备节点(如&i2c0)与驱动(如 I2C 驱动)匹配成功后,驱动无需再配置引脚 ——Pinctrl 子系统已提前完成所有工作:

I2C 驱动直接通过 I2C 控制器收发数据,SDA/SCL 引脚已被配置为 I2C 功能,且启用了上拉电阻;

传感器驱动通过devm_gpiod_get获取复位引脚时,该引脚已被配置为 GPIO 功能,无需手动设置复用。

这就是为什么驱动中不用写引脚配置代码 ——Pinctrl 子系统已经 “提前打理好一切”。

总结

Pinctrl 子系统的本质是 “内核提供的引脚管理框架”,核心作用是统一处理引脚复用和电气属性配置,实现 “硬件描述与驱动分离”。

关键要点回顾:

3 个核心概念:Pin(引脚)、Pinmux(复用)、Pinconf(配置);

工作流程:设备树定义配置 → 内核解析初始化 → 驱动直接使用;

开发重点:在设备树中正确定义引脚组、功能、电气属性,并让设备节点引用。

记住:嵌入式 Linux 中,所有引脚的复用和配置,都应该通过 Pinctrl 子系统实现,而非驱动硬编码 —— 这是规范,也是避免踩坑的关键。

(完)

本人专注 Linux 驱动 & Linux/Android BSP 开发调试,可接外包项目/技术支持/问题定位。有需求或交个朋友可加微信:【Chen_WeChat2026】。

更多原创技术文章:《README 2026》。

审核编辑 黄宇