Lazy loaded image
pinctrl专题02 - Client端使用pinctrl过程的情景分析 - 基于IMX6ULL
Words 3406Read Time 9 min
2025-12-25
📖
文章定位:本文从客户端驱动视角分析 pinctrl 使用流程。专题01侧重「Pinctrl 驱动如何构造」,本文侧重「客户端如何消费 pinctrl 服务」。两篇互为补充,建议先阅读专题01建立整体框架。

第一部分:概念框架


pinctrl的系统架构

  • 采用分层设计,将硬件细节与功能驱动分离
  • 包括客户端驱动、pinctrl API、pinctrl核心、pinctrl映射和pinctrl驱动几个层次
客户端与 pinctrl组件关系图如下:
设备树向客户端驱动和 pinctrl 驱动提供配置的方式如下:
1. 设备树向客户端驱动提供配置
客户端驱动通过在自己的设备节点中定义 pinctrl 相关属性来获取配置:
关键属性说明
  • pinctrl-names:定义状态名称列表,如 "default"、"sleep" 等
  • pinctrl-0pinctrl-1 等:通过 phandle 引用具体的引脚配置节点,数字索引与 pinctrl-names 中的名称一一对应
使用流程
  1. 驱动 probe 时调用 devm_pinctrl_get(dev) 获取 pinctrl 句柄
  1. 系统自动解析设备节点中的 pinctrl-namespinctrl-X 属性
  1. 通过 pinctrl_lookup_state() 根据状态名称查找对应的配置
  1. 使用 pinctrl_select_state() 应用相应的引脚配置
2. 设备树向 pinctrl 驱动提供配置
pinctrl 驱动从 pinctrl 子节点中获取具体的引脚配置信息。这些节点通常定义在 SoC 的 iomuxc(IO 复用控制器)节点下:
配置内容
  • fsl,pins:包含引脚配置数组,每个配置包括引脚定义(如复用功能)和配置值(如上下拉、驱动强度等)
  • 不同的 SoC 有不同的配置格式和属性名称
解析流程
  1. 当客户端驱动的 pinctrl-X 通过 phandle 引用这些配置节点时,系统调用 of_find_node_by_phandle() 找到对应节点
  1. 调用特定 SoC 的 pinctrl 驱动实现的 dt_node_to_map() 函数
  1. 该函数解析节点中的配置属性(如 fsl,pins),创建 pinctrl_map 结构
  1. 映射包括两类:
      • PIN_MAP_TYPE_MUX_GROUP:引脚复用配置
      • PIN_MAP_TYPE_CONFIGS_PIN:引脚电气特性配置
关键理解:客户端驱动通过 phandle 间接引用 pinctrl 配置,而 pinctrl 驱动负责解析这些配置节点并将其转换为可操作的映射和设置。两者通过设备树形成松耦合关系。
 
这种设计将硬件细节与功能驱动分离,使得功能驱动无需关心底层引脚配置细节,只需通过简单的 API 就能完成引脚状态管理,大大提高了代码的可维护性和可移植性。
🎯
本文侧重:我们将深入「客户端驱动」这一层,分析它如何通过 pinctrl API 获取服务、如何在运行时切换引脚状态。

第二部分:数据结构与关系


一、数据结构和关系

理解数据结构是理解代码流程的基础。本节从客户端视角出发,介绍它能「看到」和「操作」的核心结构体。

1.1 数据结构关系图

  1. 客户端与 pinctrl 的关系
      • 客户端设备通过 pinctrl 指针使用 pinctrl 服务
      • 一个客户端设备对应一个 struct pinctrl 实例
  1. pinctrl 与状态的关系
      • 一个 struct pinctrl 包含多个 struct pinctrl_state
      • 这些状态通过 states 链表连接
  1. 状态与设置的关系
      • 一个 struct pinctrl_state 包含多个 struct pinctrl_setting
      • 这些设置通过 settings 链表连接
  1. 设置与控制器的关系
      • 每个 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_settingpinctrl_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-0pinctrl-1 等对应于不同状态下的引脚配置

2.1.2 构造 pinctrl 过程

时序图
函数调用栈
  1. 客户端获取 pinctrl 句柄
      • devm_pinctrl_get(dev) - 客户端驱动调用此函数获取 pinctrl 句柄
      • 内部调用 pinctrl_get() 进行实际处理
  1. pinctrl_get() 函数处理
    1. 设备树解析
      1. pinctrl_dt_to_map() // 从设备树节点创建映射
    1. 映射创建和转化设置
      1. 映射创建过程
          • 系统从设备树中读取 pinctrl-X 属性,其中 X 是一个数字,表示不同的状态(如默认状态、睡眠状态等)
          • 对每个 pinctrl-X 属性,调用 dt_to_map_one_config() 函数处理
      2. dt_to_map_one_config() 函数
          • 通过 of_find_node_by_phandle() 找到对应的 pinctrl 节点
          • 调用特定 pinctrl 驱动实现的 dt_node_to_map() 函数创建映射
          • 不同的 SoC 有不同的实现,例如:
            • sirfsoc_dt_node_to_map() - SiRF SoC
            • imx_dt_node_to_map() - i.MX SoC
            • stm32_pctrl_dt_subnode_to_map() - STM32 SoC
      3. dt_node_to_map() 函数
          • 创建两种类型的映射:
            • PIN_MAP_TYPE_MUX_GROUP:用于配置引脚复用功能
            • PIN_MAP_TYPE_CONFIGS_PIN:用于配置引脚电气特性(如上拉/下拉、驱动强度等)
      4. pinconf_map_to_setting() 函数
          • pinctrl_map 结构转换为 pinctrl_setting 结构
          • 根据映射类型设置相应的字段
          • 复制配置数据,如您提到的代码片段:
      1. 状态查找
          • 创建 pinctrl_state 结构体表示每个状态
          • 将状态与映射关联起来

      2.2 切换 state 情景分析

      构造完成后,客户端驱动在运行时通过 pinctrl_select_state() 切换状态。本节分析状态切换的内部机制。
      总体的状态转换图

      2.2.1 切换 state 函数调用栈

      2.2.2 详细函数调用分析

      1. 查找状态
          • 客户端调用 pinctrl_lookup_state(p, "default") 查找名为 "default" 的状态
          • 内部调用 find_state() 在已创建的状态列表中查找
      1. 选择状态
          • 客户端调用 pinctrl_select_state(p, state) 应用状态配置
          • 此函数会处理两种类型的设置:复用设置和配置设置
      1. 复用设置处理
        1. 配置设置处理
          1. IMX 平台的实现
              • 对于 IMX 平台,最终会调用 imx_pmx_set() 设置引脚复用
              • 调用 imx_pinconf_set() 设置引脚配置
              • 根据 SoC 类型选择不同的后端实现(内存映射或 SCU)

          2.2.3 常见切换 state 场景

          1. 驱动初始化时
            1. 电源管理场景
              📝
              实践要点:电源管理场景是 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 属性
              🚀
              学习路径建议
              1. 先阅读专题01,理解 Pinctrl 驱动如何构造和注册
              1. 再阅读本文,理解客户端如何消费 pinctrl 服务
              1. 动手实践:为一个简单外设(如 LED GPIO)编写使用 pinctrl 的驱动
              1. 调试验证:通过 /sys/kernel/debug/pinctrl/ 观察状态变化

               
               
              上一篇
              Data Structure and Algorithm
              下一篇
              用面试拷问嵌入式技术栈

              Comments
              Loading...