Lazy loaded image
pinctrl专题01 - Pincontroller构造过程分析 -
Words 2973Read Time 8 min
2025-4-24
 

核心数据结构

pinctrl-imx.c 中,定义了 i.MX 平台的 pinctrl 实现:

引脚解析函数

pinctrl-imx.c 中,imx_pinctrl_parse_groups 函数负责解析设备树中的引脚组定义:

引脚复用设置函数

imx_pmx_set 函数负责设置引脚的复用功能:

状态图

pinctrl 子系统中引脚的状态转换:

设计理念

核心数据结构

pinctrl 子系统的核心数据结构包括:
  1. pinctrl_dev:表示一个 pinctrl 控制器设备
  1. pinctrl_desc:描述 pinctrl 控制器的特性
  1. pinctrl_ops:提供引脚组操作的回调函数
  1. pinmux_ops:提供引脚复用操作的回调函数
  1. pinconf_ops:提供引脚配置操作的回调函数
  1. pinctrl:表示一个客户端设备的 pinctrl 句柄
  1. pinctrl_state:表示一个 pinctrl 状态,如 "default"、"sleep" 等
  1. pinctrl_map:描述引脚控制映射关系

关键算法

  1. 引脚查找算法:通过引脚名称或 ID 快速查找对应的引脚
  1. 状态匹配算法:根据设备树中的状态名称匹配对应的 pinctrl 状态
  1. 引脚分配算法:确保同一时间一个引脚只能被一个功能使用
  1. 配置应用算法:将配置值正确应用到硬件寄存器

设计模式

pinctrl 子系统采用了多种设计模式:

驱动模型

工厂模式

pinctrl 子系统使用工厂模式创建不同类型的 pinctrl 控制器:

策略模式

pinctrl 子系统使用策略模式处理不同 SoC 的引脚配置:
 
 

IMX Pincontroller 功能分析

通过分析 pinctrl-imx.cpinctrl-imx6ul.c 的代码,我将详细解析 IMX 平台 pincontroller 的三个核心功能实现流程。

功能1:描述、获得引脚:解析设备树

初始化流程

  1. 驱动入口点
      • imx6ul_pinctrl_init() 开始,注册平台驱动
      • 调用 platform_driver_register(&imx6ul_pinctrl_driver)
  1. 设备匹配与探测
      • 当设备树中匹配到 fsl,imx6ul-iomuxcfsl,imx6ull-iomuxc-snvs
      • 调用 imx6ul_pinctrl_probe() 函数
      • 获取 SoC 特定信息 imx6ul_pinctrl_infoimx6ull_snvs_pinctrl_info
      • 调用通用探测函数 imx_pinctrl_probe()
  1. 设备树解析
      • imx_pinctrl_probe() 中调用 imx_pinctrl_probe_dt() 解析设备树
      • 检查设备树格式(平铺或嵌套):imx_pinctrl_dt_is_flat_functions()
      • 分配内存存储 functions 和 groups 信息
      • 解析 functions:imx_pinctrl_parse_functions()
      • 解析 groups:imx_pinctrl_parse_groups()
  1. 引脚组解析
      • imx_pinctrl_parse_groups() 解析每个引脚组
      • 从设备树节点的 fsl,pins 属性获取引脚配置
      • 根据 SoC 类型确定引脚配置大小(FSL_PIN_SIZE 或 SHARE_FSL_PIN_SIZE)
      • 为每个引脚调用 imx_pinctrl_parse_pin_mem()imx_pinctrl_parse_pin_scu()

设备树到引脚映射

  1. 设备树到映射转换
      • imx_dt_node_to_map() 将设备树节点转换为 pinctrl 子系统的映射
      • 查找对应的引脚组:imx_pinctrl_find_group_by_name()
      • 创建复用映射(mux map)和配置映射(config map)
  1. 引脚组查询
      • imx_get_groups_count() 获取引脚组数量
      • imx_get_group_name() 获取引脚组名称
      • imx_get_group_pins() 获取引脚组中的引脚

功能2:引脚复用

复用设置流程

  1. 复用操作注册
      • imx_pinctrl_probe() 中注册 imx_pmx_ops 操作集
      • 包含 get_functions_count, get_function_name, get_function_groups, set_mux 等函数
  1. 复用设置实现
      • imx_pmx_set() 设置引脚复用功能
      • 获取指定组的所有引脚
      • 遍历每个引脚,根据 SoC 类型调用:
        • imx_pmx_set_one_pin_scu() (SCU 方式)
        • imx_pmx_set_one_pin_mem() (内存映射方式)
  1. 功能查询
      • imx_pmx_get_funcs_count() 获取功能数量
      • imx_pmx_get_func_name() 获取功能名称
      • imx_pmx_get_groups() 获取功能对应的引脚组
  1. GPIO 方向设置
      • imx_pmx_gpio_set_direction() 设置 GPIO 方向
      • 根据 SoC 类型调用 imx_pmx_backend_gpio_set_direction_mem()

功能3:引脚配置

配置设置流程

  1. 配置操作注册
      • imx_pinctrl_probe() 中注册 imx_pinconf_ops 操作集
      • 包含 pin_config_get, pin_config_set 等函数
  1. 配置获取实现
      • imx_pinconf_get() 获取引脚配置
      • 根据 SoC 类型调用:
        • imx_pinconf_backend_get_scu() (SCU 方式)
        • imx_pinconf_backend_get_mem() (内存映射方式)
  1. 配置设置实现
      • imx_pinconf_set() 设置引脚配置
      • 根据 SoC 类型调用:
        • imx_pinconf_backend_set_scu() (SCU 方式)
        • imx_pinconf_backend_set_mem() (内存映射方式)
  1. 配置调试
      • imx_pinconf_dbg_show() 显示引脚配置
      • imx_pinconf_group_dbg_show() 显示引脚组配置

整体架构与调用关系

  1. 初始化流程
    1. 引脚复用流程
      1. 引脚配置流程

        总结

        IMX pincontroller 实现了三个核心功能:
        1. 引脚描述与获取:通过解析设备树,建立引脚、引脚组和功能的映射关系
        1. 引脚复用:设置引脚的功能复用,支持内存映射和 SCU 两种方式
        1. 引脚配置:设置引脚的电气特性,如上拉/下拉、驱动强度等
        这些功能通过统一的 pinctrl 子系统接口暴露给用户空间,使得驱动开发者可以通过设备树配置引脚,而无需直接操作硬件寄存器。
         
         

        IMX Pinctrl 驱动分析

        一、Pinctrl 驱动与客户端驱动的区别

        Pinctrl 驱动

        Pinctrl 驱动是一种底层硬件抽象驱动,负责管理 SoC 的引脚控制器硬件。从提供的代码中可以看出,pinctrl-imx.cpinctrl-imx6ul.c 就是典型的 pinctrl 驱动实现。
        Pinctrl 驱动主要负责:
        1. 引脚复用:配置引脚的功能模式(如 GPIO、UART、I2C 等)
        1. 引脚配置:设置引脚的电气特性(如上拉/下拉、驱动强度等)
        1. 引脚分组:将相关引脚组织成逻辑组,便于统一管理

        客户端驱动

        客户端驱动是使用 pinctrl 服务的功能驱动,如 UART、I2C、SPI 等外设驱动。
        客户端驱动主要:
        1. 通过 pinctrl_get() 获取 pinctrl 句柄
        1. 通过 pinctrl_lookup_state() 查找预定义的引脚状态
        1. 通过 pinctrl_select_state() 应用引脚配置

        二、Pinctrl 驱动与 Pinctrl 核心的关系

        Pinctrl 核心

        Pinctrl 核心(在 drivers/pinctrl/core.c 中实现)提供了统一的 API 和框架,是连接 pinctrl 驱动和客户端驱动的桥梁。

        关系模型

        从代码中可以看出:
        1. Pinctrl 驱动通过 pinctrl_register() 向核心注册自己
        1. Pinctrl 驱动实现 pinctrl_opspinmux_opspinconf_ops 接口
        1. Pinctrl 核心调用这些接口来完成客户端的请求
        例如,在 pinctrl-imx.c 中:

        三、Pinctrl 驱动开发流程

        1. 准备工作

        • 了解目标 SoC 的引脚控制器硬件规格
        • 确定引脚编号、复用功能和配置选项
        • 确定寄存器映射和位域定义

        2. 定义基础数据结构

        • 定义引脚枚举(如 imx6ul_pads 枚举)
        • 定义引脚描述符数组(如 imx6ul_pinctrl_pads
        • 定义 SoC 特定信息结构体(如 imx_pinctrl_soc_info

        3. 实现 pinctrl_ops 接口

        • get_groups_count:获取引脚组数量
        • get_group_name:获取引脚组名称
        • get_group_pins:获取引脚组中的引脚
        • dt_node_to_map:将设备树节点转换为 pinctrl 映射

        4. 实现 pinmux_ops 接口

        • get_functions_count:获取功能数量
        • get_function_name:获取功能名称
        • get_function_groups:获取功能对应的引脚组
        • set_mux:设置引脚复用功能

        5. 实现 pinconf_ops 接口

        • pin_config_get:获取引脚配置
        • pin_config_set:设置引脚配置
        • pin_config_group_get:获取引脚组配置
        • pin_config_group_set:设置引脚组配置

        6. 实现设备树解析

        • 解析设备树中的引脚组和功能定义
        • 将设备树配置转换为内部数据结构

        7. 实现驱动探测函数

        • 获取硬件资源(如寄存器映射)
        • 初始化内部数据结构
        • 注册 pinctrl 设备

        8. 注册平台驱动

        • 定义平台驱动结构体
        • 实现匹配表(compatible 字符串)
        • 注册驱动

        四、IMX Pinctrl 驱动实现示例

        从提供的代码中,我们可以看到 IMX 平台的 pinctrl 驱动实现:

        1. 驱动入口点

        2. 平台驱动定义

        3. 设备匹配表

        4. 探测函数

        5. 通用探测实现

        五、总结

        Pinctrl 驱动是连接硬件引脚控制器和 Linux 内核 pinctrl 子系统的桥梁。开发 pinctrl 驱动需要深入了解目标 SoC 的硬件特性,并按照 Linux 内核 pinctrl 子系统的框架实现相应的接口。
        IMX 平台的 pinctrl 驱动采用了分层设计:
        1. pinctrl-imx.c 提供通用的 IMX 平台 pinctrl 实现
        1. pinctrl-imx6ul.c 提供 IMX6UL 特定的引脚定义和驱动入口
        这种设计使得不同 IMX 系列 SoC 可以共享大部分代码,只需要针对特定 SoC 实现少量差异化代码。
        上一篇
        模板设计模式:让你的代码结构更清晰
        下一篇
        Guide to Linux System

        Comments
        Loading...