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

核心数据结构

pinctrl-imx.c 中,定义了 i.MX 平台的 pinctrl 实现:
struct imx_pinctrl { struct device *dev; struct pinctrl_dev *pctldev; void __iomem *base; const struct imx_pinctrl_soc_info *info; }; struct imx_pin_group { const char *name; unsigned int npins; struct imx_pin *pins; unsigned int *pin_ids; }; struct imx_pmx_func { const char *name; const char **groups; unsigned int num_groups; };

引脚解析函数

pinctrl-imx.c 中,imx_pinctrl_parse_groups 函数负责解析设备树中的引脚组定义:
static int imx_pinctrl_parse_groups(struct device_node *np, struct imx_pin_group *grp, struct imx_pinctrl_soc_info *info, u32 index) { int size, pin_size; const __be32 *list, **list_p; int i; dev_dbg(info->dev, "group(%d): %s\\n", index, np->name); if (info->flags & IMX8_USE_SCU) pin_size = FSL_IMX8_PIN_SIZE; else if (info->flags & SHARE_MUX_CONF_REG) pin_size = SHARE_FSL_PIN_SIZE; else pin_size = FSL_PIN_SIZE; /* Initialise group */ grp->name = np->name; /* * the binding format is fsl,pins = <PIN_FUNC_ID CONFIG ...>, * do sanity check and calculate pins number */ list = of_get_property(np, "fsl,pins", &size); if (!list) { dev_err(info->dev, "no fsl,pins property in node %s\\n", np->full_name); return -EINVAL; } list_p = &list; /* we do not check return since it's safe node passed down */ if (!size || size % pin_size) { dev_err(info->dev, "Invalid fsl,pins property in node %s\\n", np->full_name); return -EINVAL; } grp->npins = size / pin_size; grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(struct imx_pin), GFP_KERNEL); grp->pin_ids = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), GFP_KERNEL); if (!grp->pins || ! grp->pin_ids) return -ENOMEM; for (i = 0; i < grp->npins; i++) { if (info->flags & IMX8_USE_SCU) imx_pinctrl_parse_pin_scu(info, &grp->pin_ids[i], &grp->pins[i], list_p); else imx_pinctrl_parse_pin_mem(info, &grp->pin_ids[i], &grp->pins[i], list_p); } return 0; }

引脚复用设置函数

imx_pmx_set 函数负责设置引脚的复用功能:
static int imx_pmx_set(struct pinctrl_dev *pctldev, unsigned selector, unsigned group) { struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); const struct imx_pinctrl_soc_info *info = ipctl->info; unsigned int npins; int i, err; struct imx_pin_group *grp; /* * Configure the mux mode for each pin in the group for a specific * function. */ grp = &info->groups[group]; npins = grp->npins; dev_dbg(ipctl->dev, "enable function %s group %s\\n", info->functions[selector].name, grp->name); for (i = 0; i < npins; i++) { if (info->flags & IMX8_USE_SCU) err = imx_pmx_set_one_pin_scu(ipctl, &grp->pins[i]); else err = imx_pmx_set_one_pin_mem(ipctl, &grp->pins[i]); if (err) return err; } return 0; }

状态图

pinctrl 子系统中引脚的状态转换:
stateDiagram-v2 [*] --> 未初始化 未初始化 --> 已注册: pinctrl_register() 已注册 --> 已获取: pinctrl_get() 已获取 --> 状态查找: pinctrl_lookup_state() 状态查找 --> 状态选择: pinctrl_select_state() 状态选择 --> 功能模式: set_mux() 状态选择 --> 电气配置: pin_config_set() 功能模式 --> 工作状态 电气配置 --> 工作状态 工作状态 --> 已获取: 切换状态 已获取 --> [*]: pinctrl_put()

设计理念

核心数据结构

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 子系统采用了多种设计模式:

驱动模型

classDiagram class Core { +注册控制器 +管理状态 +提供API } class Driver { +实现硬件操作 +提供回调函数 } class Client { +获取pinctrl句柄 +选择状态 } Core <-- Driver : 注册 Core <-- Client : 使用 Driver <-- Core : 回调

工厂模式

pinctrl 子系统使用工厂模式创建不同类型的 pinctrl 控制器:
classDiagram class pinctrl_register { +创建pinctrl_dev } class pinctrl_dev { +struct pinctrl_desc *desc +void *driver_data } class imx_pinctrl_probe { +创建imx_pinctrl +调用pinctrl_register } class qcom_pinctrl_probe { +创建qcom_pinctrl +调用pinctrl_register } pinctrl_register --> pinctrl_dev : 创建 imx_pinctrl_probe --> pinctrl_register : 调用 qcom_pinctrl_probe --> pinctrl_register : 调用

策略模式

pinctrl 子系统使用策略模式处理不同 SoC 的引脚配置:
classDiagram class pinctrl_ops { <<interface>> +get_groups_count() +get_group_name() +get_group_pins() } class imx_pctrl_ops { +imx_get_groups_count() +imx_get_group_name() +imx_get_group_pins() } class qcom_pctrl_ops { +qcom_get_groups_count() +qcom_get_group_name() +qcom_get_group_pins() } pinctrl_ops <|.. imx_pctrl_ops : 实现 pinctrl_ops <|.. qcom_pctrl_ops : 实现
 
 

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. imx6ul_pinctrl_init() └── platform_driver_register() └── imx6ul_pinctrl_probe() └── imx_pinctrl_probe() ├── imx_pinctrl_probe_dt() │ ├── imx_pinctrl_dt_is_flat_functions() │ └── imx_pinctrl_parse_functions() │ └── imx_pinctrl_parse_groups() └── pinctrl_register()
  1. 引脚复用流程
    1. pinctrl_select_state() // 用户空间调用 └── pinmux_enable_setting() └── imx_pmx_set() └── imx_pmx_set_one_pin_mem() 或 imx_pmx_set_one_pin_scu()
  1. 引脚配置流程
    1. pinctrl_select_state() // 用户空间调用 └── pinconf_apply_setting() └── imx_pinconf_set() └── imx_pinconf_backend_set_mem() 或 imx_pinconf_backend_set_scu()

总结

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 驱动和客户端驱动的桥梁。

关系模型

客户端驱动 <---> Pinctrl 核心 <---> Pinctrl 驱动
从代码中可以看出:
  1. Pinctrl 驱动通过 pinctrl_register() 向核心注册自己
  1. Pinctrl 驱动实现 pinctrl_opspinmux_opspinconf_ops 接口
  1. Pinctrl 核心调用这些接口来完成客户端的请求
例如,在 pinctrl-imx.c 中:
static const struct pinctrl_ops imx_pctrl_ops = { .get_groups_count = imx_get_groups_count, .get_group_name = imx_get_group_name, .get_group_pins = imx_get_group_pins, .pin_dbg_show = imx_pin_dbg_show, .dt_node_to_map = imx_dt_node_to_map, .dt_free_map = imx_dt_free_map, };

三、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. 驱动入口点

static int __init imx6ul_pinctrl_init(void) { return platform_driver_register(&imx6ul_pinctrl_driver); } arch_initcall(imx6ul_pinctrl_init);

2. 平台驱动定义

static struct platform_driver imx6ul_pinctrl_driver = { .driver = { .name = "imx6ul-pinctrl", .of_match_table = of_match_ptr(imx6ul_pinctrl_of_match), }, .probe = imx6ul_pinctrl_probe, };

3. 设备匹配表

static struct of_device_id imx6ul_pinctrl_of_match[] = { { .compatible = "fsl,imx6ul-iomuxc", .data = &imx6ul_pinctrl_info, }, { .compatible = "fsl,imx6ull-iomuxc-snvs", .data = &imx6ull_snvs_pinctrl_info, }, { /* sentinel */ } };

4. 探测函数

static int imx6ul_pinctrl_probe(struct platform_device *pdev) { const struct of_device_id *match; struct imx_pinctrl_soc_info *pinctrl_info; match = of_match_device(imx6ul_pinctrl_of_match, &pdev->dev); if (!match) return -ENODEV; pinctrl_info = (struct imx_pinctrl_soc_info *) match->data; return imx_pinctrl_probe(pdev, pinctrl_info); }

5. 通用探测实现

int imx_pinctrl_probe(struct platform_device *pdev, struct imx_pinctrl_soc_info *info) { // 1. 获取硬件资源 // 2. 解析设备树 // 3. 注册 pinctrl 设备 ipctl->pctl = devm_pinctrl_register(&pdev->dev, imx_pinctrl_desc, ipctl); return 0; }

五、总结

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...