第一部分:概念框架pinctrl的系统架构第二部分:数据结构与关系一、数据结构和关系1.1 数据结构关系图1.2 数据结构说明1.2.1 struct pinctrl1.2.2 struct pinctrl_state1.2.3 struct pinctrl_setting1.2.4 struct pinctrl_dev1.2.5 struct client_device第三部分:使用流程分析二、使用流程分析2.1 client 节点的 pinctrl 构造过程2.1.1 设备树中的 pinctrl 定义2.1.2 构造 pinctrl 过程2.2 切换 state 情景分析2.2.1 切换 state 函数调用栈2.2.2 详细函数调用分析2.2.3 常见切换 state 场景第四部分:总结与延伸三、核心要点回顾四、与专题01的关系
文章定位:本文从客户端驱动视角分析 pinctrl 使用流程。专题01侧重「Pinctrl 驱动如何构造」,本文侧重「客户端如何消费 pinctrl 服务」。两篇互为补充,建议先阅读专题01建立整体框架。
第一部分:概念框架
pinctrl的系统架构
- 采用分层设计,将硬件细节与功能驱动分离
- 包括客户端驱动、pinctrl API、pinctrl核心、pinctrl映射和pinctrl驱动几个层次
客户端与 pinctrl组件关系图如下:
设备树向客户端驱动和 pinctrl 驱动提供配置的方式如下:
1. 设备树向客户端驱动提供配置
客户端驱动通过在自己的设备节点中定义 pinctrl 相关属性来获取配置:
关键属性说明:
pinctrl-names:定义状态名称列表,如 "default"、"sleep" 等
pinctrl-0、pinctrl-1等:通过 phandle 引用具体的引脚配置节点,数字索引与 pinctrl-names 中的名称一一对应
使用流程:
- 驱动 probe 时调用
devm_pinctrl_get(dev)获取 pinctrl 句柄
- 系统自动解析设备节点中的
pinctrl-names和pinctrl-X属性
- 通过
pinctrl_lookup_state()根据状态名称查找对应的配置
- 使用
pinctrl_select_state()应用相应的引脚配置
2. 设备树向 pinctrl 驱动提供配置
pinctrl 驱动从 pinctrl 子节点中获取具体的引脚配置信息。这些节点通常定义在 SoC 的 iomuxc(IO 复用控制器)节点下:
配置内容:
fsl,pins:包含引脚配置数组,每个配置包括引脚定义(如复用功能)和配置值(如上下拉、驱动强度等)
- 不同的 SoC 有不同的配置格式和属性名称
解析流程:
- 当客户端驱动的
pinctrl-X通过 phandle 引用这些配置节点时,系统调用of_find_node_by_phandle()找到对应节点
- 调用特定 SoC 的 pinctrl 驱动实现的
dt_node_to_map()函数
- 该函数解析节点中的配置属性(如
fsl,pins),创建pinctrl_map结构
- 映射包括两类:
PIN_MAP_TYPE_MUX_GROUP:引脚复用配置PIN_MAP_TYPE_CONFIGS_PIN:引脚电气特性配置
关键理解:客户端驱动通过 phandle 间接引用 pinctrl 配置,而 pinctrl 驱动负责解析这些配置节点并将其转换为可操作的映射和设置。两者通过设备树形成松耦合关系。
这种设计将硬件细节与功能驱动分离,使得功能驱动无需关心底层引脚配置细节,只需通过简单的 API 就能完成引脚状态管理,大大提高了代码的可维护性和可移植性。
本文侧重:我们将深入「客户端驱动」这一层,分析它如何通过 pinctrl API 获取服务、如何在运行时切换引脚状态。
第二部分:数据结构与关系
一、数据结构和关系
理解数据结构是理解代码流程的基础。本节从客户端视角出发,介绍它能「看到」和「操作」的核心结构体。
1.1 数据结构关系图
- 客户端与 pinctrl 的关系:
- 客户端设备通过
pinctrl指针使用 pinctrl 服务 - 一个客户端设备对应一个
struct pinctrl实例
- pinctrl 与状态的关系:
- 一个
struct pinctrl包含多个struct pinctrl_state - 这些状态通过
states链表连接
- 状态与设置的关系:
- 一个
struct pinctrl_state包含多个struct pinctrl_setting - 这些设置通过
settings链表连接
- 设置与控制器的关系:
- 每个
struct pinctrl_setting引用一个struct pinctrl_dev - 设置通过控制器的操作函数被应用到硬件
1.2 数据结构说明
1.2.1 struct pinctrl
功能作用:
- 代表一个客户端设备的 pinctrl 句柄
- 管理设备的所有 pinctrl 状态和映射
- 连接客户端设备与 pinctrl 子系统
主要成员:
dev:指向客户端设备
states:包含所有状态的链表
dt_maps:包含从设备树解析的映射
使用场景:
- 客户端驱动通过
devm_pinctrl_get()获取此结构
- 用于查找和选择不同的引脚状态
1.2.2 struct pinctrl_state
功能作用:
- 表示一个特定的引脚配置状态(如 "default"、"sleep")
- 包含应用此状态所需的所有设置
主要成员:
name:状态名称(如 "default"、"sleep")
settings:包含此状态下所有设置的链表
使用场景:
- 客户端通过
pinctrl_lookup_state()获取
- 通过
pinctrl_select_state()应用状态
1.2.3 struct pinctrl_setting
功能作用:
- 表示单个引脚或引脚组的具体设置
- 可以是复用设置或配置设置
主要成员:
type:设置类型(PIN_MAP_TYPE_MUX_GROUP 或 PIN_MAP_TYPE_CONFIGS_*)
pctldev:指向处理此设置的 pinctrl 控制器
data:联合体,包含具体设置数据
使用场景:
- 当应用状态时,每个设置被传递给相应的 pinctrl 驱动
1.2.4 struct pinctrl_dev
功能作用:
- 表示一个 pinctrl 控制器设备
- 提供操作引脚的接口
- 由 pinctrl 驱动注册
主要成员:
desc:控制器描述符
ops:基本操作函数集(如获取组)
pmxops:复用操作函数集
confops:配置操作函数集
使用场景:
- 由 pinctrl 驱动通过
pinctrl_register()注册
- 处理来自客户端的引脚请求
1.2.5 struct client_device
功能作用:
- 表示使用 pinctrl 服务的客户端设备
- 存储设备的 pinctrl 句柄和常用状态
主要成员:
pinctrl:指向设备的 pinctrl 句柄
default_state:默认状态句柄
sleep_state:睡眠状态句柄
使用场景:
- 客户端驱动初始化时获取 pinctrl 和状态
- 在电源管理时切换状态
关键理解:客户端驱动只需持有
pinctrl 句柄和若干 pinctrl_state 指针,无需了解 pinctrl_setting 或 pinctrl_dev 的内部细节——这正是分层抽象的价值。第三部分:使用流程分析
二、使用流程分析
有了数据结构的认知基础,本节进入动态流程分析。客户端使用 pinctrl 分为构造阶段(获取句柄)和使用阶段(切换状态)两个阶段。
客户端驱动使用 pinctrl 的过程主要包括两个阶段:
阶段 | 时机 | 核心 API | 产出 |
构造阶段 | 驱动 probe | devm_pinctrl_get(), pinctrl_lookup_state() | pinctrl 句柄 + state 指针 |
使用阶段 | 运行时 | pinctrl_select_state() | 引脚状态切换完成 |
2.1 client 节点的 pinctrl 构造过程
2.1.1 设备树中的 pinctrl 定义
客户端驱动使用 pinctrl 的第一步是在设备树中定义 pinctrl 节点。典型的客户端节点 pinctrl 定义如下:
其中:
pinctrl-names定义了不同的状态名称
pinctrl-0、pinctrl-1等对应于不同状态下的引脚配置
2.1.2 构造 pinctrl 过程
时序图
函数调用栈
- 客户端获取 pinctrl 句柄
devm_pinctrl_get(dev)- 客户端驱动调用此函数获取 pinctrl 句柄- 内部调用
pinctrl_get()进行实际处理
- pinctrl_get() 函数处理
- 设备树解析
pinctrl_dt_to_map() // 从设备树节点创建映射
- 映射创建和转化设置
- 映射创建过程:
- 系统从设备树中读取
pinctrl-X属性,其中 X 是一个数字,表示不同的状态(如默认状态、睡眠状态等) - 对每个
pinctrl-X属性,调用dt_to_map_one_config()函数处理 - dt_to_map_one_config() 函数:
- 通过
of_find_node_by_phandle()找到对应的 pinctrl 节点 - 调用特定 pinctrl 驱动实现的
dt_node_to_map()函数创建映射 - 不同的 SoC 有不同的实现,例如:
sirfsoc_dt_node_to_map()- SiRF SoCimx_dt_node_to_map()- i.MX SoCstm32_pctrl_dt_subnode_to_map()- STM32 SoC- dt_node_to_map() 函数:
- 创建两种类型的映射:
PIN_MAP_TYPE_MUX_GROUP:用于配置引脚复用功能PIN_MAP_TYPE_CONFIGS_PIN:用于配置引脚电气特性(如上拉/下拉、驱动强度等)- pinconf_map_to_setting() 函数:
- 将
pinctrl_map结构转换为pinctrl_setting结构 - 根据映射类型设置相应的字段
- 复制配置数据,如您提到的代码片段:
- 状态查找
- 创建
pinctrl_state结构体表示每个状态 - 将状态与映射关联起来
2.2 切换 state 情景分析
构造完成后,客户端驱动在运行时通过pinctrl_select_state()切换状态。本节分析状态切换的内部机制。
总体的状态转换图
2.2.1 切换 state 函数调用栈
2.2.2 详细函数调用分析
- 查找状态
- 客户端调用
pinctrl_lookup_state(p, "default")查找名为 "default" 的状态 - 内部调用
find_state()在已创建的状态列表中查找
- 选择状态
- 客户端调用
pinctrl_select_state(p, state)应用状态配置 - 此函数会处理两种类型的设置:复用设置和配置设置
- 复用设置处理
- 配置设置处理
- IMX 平台的实现
- 对于 IMX 平台,最终会调用
imx_pmx_set()设置引脚复用 - 调用
imx_pinconf_set()设置引脚配置 - 根据 SoC 类型选择不同的后端实现(内存映射或 SCU)
2.2.3 常见切换 state 场景
- 驱动初始化时
- 电源管理场景
实践要点:电源管理场景是 pinctrl 状态切换的典型用例。
sleep 状态通常将引脚配置为低功耗模式(如禁用上拉、设为输入),从而降低系统待机功耗。第四部分:总结与延伸
三、核心要点回顾
主题 | 要点 |
数据结构层次 | client_device → pinctrl → pinctrl_state → pinctrl_setting → pinctrl_dev |
构造阶段 | 设备树定义 → devm_pinctrl_get() → pinctrl_lookup_state() |
使用阶段 | pinctrl_select_state() → 核心层路由 → 驱动回调 → 硬件寄存器 |
客户端视角 | 只需操作句柄和状态指针,无需了解底层实现 |
四、与专题01的关系
维度 | 专题01(驱动视角) | 专题02(客户端视角) |
核心问题 | 如何构造 Pincontroller? | 如何使用 pinctrl 服务? |
关注结构体 | imx_pinctrl, imx_pin_group, pinctrl_desc | pinctrl, pinctrl_state, client_device |
代码路径 | pinctrl-imx.c probe 流程 | 客户端驱动 probe + 运行时切换 |
设备树关注点 | iomuxc 节点、fsl,pins 属性 | pinctrl-names、pinctrl-0/1 属性 |
学习路径建议:
- 先阅读专题01,理解 Pinctrl 驱动如何构造和注册
- 再阅读本文,理解客户端如何消费 pinctrl 服务
- 动手实践:为一个简单外设(如 LED GPIO)编写使用 pinctrl 的驱动
- 调试验证:通过
/sys/kernel/debug/pinctrl/观察状态变化




