核心数据结构
在
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 子系统的核心数据结构包括:
- pinctrl_dev:表示一个 pinctrl 控制器设备
- pinctrl_desc:描述 pinctrl 控制器的特性
- pinctrl_ops:提供引脚组操作的回调函数
- pinmux_ops:提供引脚复用操作的回调函数
- pinconf_ops:提供引脚配置操作的回调函数
- pinctrl:表示一个客户端设备的 pinctrl 句柄
- pinctrl_state:表示一个 pinctrl 状态,如 "default"、"sleep" 等
- pinctrl_map:描述引脚控制映射关系
关键算法
- 引脚查找算法:通过引脚名称或 ID 快速查找对应的引脚
- 状态匹配算法:根据设备树中的状态名称匹配对应的 pinctrl 状态
- 引脚分配算法:确保同一时间一个引脚只能被一个功能使用
- 配置应用算法:将配置值正确应用到硬件寄存器
设计模式
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.c 和 pinctrl-imx6ul.c 的代码,我将详细解析 IMX 平台 pincontroller 的三个核心功能实现流程。功能1:描述、获得引脚:解析设备树
初始化流程
- 驱动入口点:
- 从
imx6ul_pinctrl_init()开始,注册平台驱动 - 调用
platform_driver_register(&imx6ul_pinctrl_driver)
- 设备匹配与探测:
- 当设备树中匹配到
fsl,imx6ul-iomuxc或fsl,imx6ull-iomuxc-snvs时 - 调用
imx6ul_pinctrl_probe()函数 - 获取 SoC 特定信息
imx6ul_pinctrl_info或imx6ull_snvs_pinctrl_info - 调用通用探测函数
imx_pinctrl_probe()
- 设备树解析:
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()
- 引脚组解析:
imx_pinctrl_parse_groups()解析每个引脚组- 从设备树节点的
fsl,pins属性获取引脚配置 - 根据 SoC 类型确定引脚配置大小(FSL_PIN_SIZE 或 SHARE_FSL_PIN_SIZE)
- 为每个引脚调用
imx_pinctrl_parse_pin_mem()或imx_pinctrl_parse_pin_scu()
设备树到引脚映射
- 设备树到映射转换:
imx_dt_node_to_map()将设备树节点转换为 pinctrl 子系统的映射- 查找对应的引脚组:
imx_pinctrl_find_group_by_name() - 创建复用映射(mux map)和配置映射(config map)
- 引脚组查询:
imx_get_groups_count()获取引脚组数量imx_get_group_name()获取引脚组名称imx_get_group_pins()获取引脚组中的引脚
功能2:引脚复用
复用设置流程
- 复用操作注册:
- 在
imx_pinctrl_probe()中注册imx_pmx_ops操作集 - 包含
get_functions_count,get_function_name,get_function_groups,set_mux等函数
- 复用设置实现:
imx_pmx_set()设置引脚复用功能- 获取指定组的所有引脚
- 遍历每个引脚,根据 SoC 类型调用:
imx_pmx_set_one_pin_scu()(SCU 方式)imx_pmx_set_one_pin_mem()(内存映射方式)
- 功能查询:
imx_pmx_get_funcs_count()获取功能数量imx_pmx_get_func_name()获取功能名称imx_pmx_get_groups()获取功能对应的引脚组
- GPIO 方向设置:
imx_pmx_gpio_set_direction()设置 GPIO 方向- 根据 SoC 类型调用
imx_pmx_backend_gpio_set_direction_mem()
功能3:引脚配置
配置设置流程
- 配置操作注册:
- 在
imx_pinctrl_probe()中注册imx_pinconf_ops操作集 - 包含
pin_config_get,pin_config_set等函数
- 配置获取实现:
imx_pinconf_get()获取引脚配置- 根据 SoC 类型调用:
imx_pinconf_backend_get_scu()(SCU 方式)imx_pinconf_backend_get_mem()(内存映射方式)
- 配置设置实现:
imx_pinconf_set()设置引脚配置- 根据 SoC 类型调用:
imx_pinconf_backend_set_scu()(SCU 方式)imx_pinconf_backend_set_mem()(内存映射方式)
- 配置调试:
imx_pinconf_dbg_show()显示引脚配置imx_pinconf_group_dbg_show()显示引脚组配置
整体架构与调用关系
- 初始化流程:
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()
- 引脚复用流程:
pinctrl_select_state() // 用户空间调用 └── pinmux_enable_setting() └── imx_pmx_set() └── imx_pmx_set_one_pin_mem() 或 imx_pmx_set_one_pin_scu()
- 引脚配置流程:
pinctrl_select_state() // 用户空间调用 └── pinconf_apply_setting() └── imx_pinconf_set() └── imx_pinconf_backend_set_mem() 或 imx_pinconf_backend_set_scu()
总结
IMX pincontroller 实现了三个核心功能:
- 引脚描述与获取:通过解析设备树,建立引脚、引脚组和功能的映射关系
- 引脚复用:设置引脚的功能复用,支持内存映射和 SCU 两种方式
- 引脚配置:设置引脚的电气特性,如上拉/下拉、驱动强度等
这些功能通过统一的 pinctrl 子系统接口暴露给用户空间,使得驱动开发者可以通过设备树配置引脚,而无需直接操作硬件寄存器。
IMX Pinctrl 驱动分析
一、Pinctrl 驱动与客户端驱动的区别
Pinctrl 驱动
Pinctrl 驱动是一种底层硬件抽象驱动,负责管理 SoC 的引脚控制器硬件。从提供的代码中可以看出,
pinctrl-imx.c 和 pinctrl-imx6ul.c 就是典型的 pinctrl 驱动实现。Pinctrl 驱动主要负责:
- 引脚复用:配置引脚的功能模式(如 GPIO、UART、I2C 等)
- 引脚配置:设置引脚的电气特性(如上拉/下拉、驱动强度等)
- 引脚分组:将相关引脚组织成逻辑组,便于统一管理
客户端驱动
客户端驱动是使用 pinctrl 服务的功能驱动,如 UART、I2C、SPI 等外设驱动。
客户端驱动主要:
- 通过
pinctrl_get()获取 pinctrl 句柄
- 通过
pinctrl_lookup_state()查找预定义的引脚状态
- 通过
pinctrl_select_state()应用引脚配置
二、Pinctrl 驱动与 Pinctrl 核心的关系
Pinctrl 核心
Pinctrl 核心(在
drivers/pinctrl/core.c 中实现)提供了统一的 API 和框架,是连接 pinctrl 驱动和客户端驱动的桥梁。关系模型
客户端驱动 <---> Pinctrl 核心 <---> Pinctrl 驱动
从代码中可以看出:
- Pinctrl 驱动通过
pinctrl_register()向核心注册自己
- Pinctrl 驱动实现
pinctrl_ops、pinmux_ops和pinconf_ops接口
- 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 驱动采用了分层设计:
pinctrl-imx.c提供通用的 IMX 平台 pinctrl 实现
pinctrl-imx6ul.c提供 IMX6UL 特定的引脚定义和驱动入口
这种设计使得不同 IMX 系列 SoC 可以共享大部分代码,只需要针对特定 SoC 实现少量差异化代码。






