一、Pinctrl子系统核心概念总结
1、Pinctrl子系统的基本概念

a、存在的必要性
-
现代芯片引脚数量庞大(动辄数百个)
-
每个引脚可复用为多种功能(GPIO、I2C、UART等)
-
需要统一的软件框架来管理引脚复用和配置
b、两大核心对象
| 对象 | 描述 | 作用 |
|---|---|---|
| Pin Controller | 引脚控制器 | 提供引脚复用和配置服务(软件概念,对应硬件IOMUX) |
| Client Device | 客户端设备 | 使用引脚服务的设备(如UART、I2C等外设) |
c、关键区分
Pin Controller vs GPIO Controller:
-
Pin Controller :决定引脚用于什么功能(GPIO、I2C、UART等)
-
GPIO Controller :引脚已经配置为GPIO功能后,控制它的输入/输出方向、电平值等
使用顺序:先通过Pin Controller将引脚配置为GPIO功能 → 再通过GPIO Controller控制引脚
二、设备树中的关键概念
1、Pin State(引脚状态)
-
设备在不同状态下可能需要不同的引脚配置
-
常见状态:
default(默认)、sleep(休眠)、idle(空闲)等 -
示例:UART设备
default状态:引脚复用为UART功能sleep状态:引脚配置为GPIO输出高电平(省电)
2、Groups(引脚组)和Function(功能)
-
Group:一组相关的引脚(如一个UART需要TX、RX、CTS、RTS等多个引脚)
-
Function:引脚被复用的功能(如UART、I2C、SPI等)
-
一个设备可能使用多个Group,每个Group配置为不同的Function
3、设备树节点结构
cpp
/* 1. Pin Controller节点(由芯片厂商BSP工程师实现) */
pinctrl: pinctrl@地址 {
compatible = "芯片厂,pinctrl";
/* UART0的引脚复用节点 */
uart0_default: uart0_default {
mux {
groups = "uart0_tx", "uart0_rx";
function = "uart0";
};
config {
pins = "GPIOA_0", "GPIOA_1";
bias-disable; /* 无上拉下拉 */
drive-strength = <8>; /* 驱动强度 */
};
};
/* UART0休眠状态引脚配置 */
uart0_sleep: uart0_sleep {
mux {
groups = "uart0_tx", "uart0_rx";
function = "gpio";
};
config {
pins = "GPIOA_0", "GPIOA_1";
output-high; /* 输出高电平 */
bias-pull-up; /* 上拉 */
};
};
};
/* 2. Client Device节点(驱动工程师使用) */
uart0: serial@地址 {
compatible = "ns16550a";
pinctrl-names = "default", "sleep"; /* 定义状态名称 */
pinctrl-0 = <&uart0_default>; /* 默认状态使用uart0_default配置 */
pinctrl-1 = <&uart0_sleep>; /* 休眠状态使用uart0_sleep配置 */
status = "okay";
};
三、Pin Controller节点的两种类型
1、Generic pin multiplexing node
-
描述引脚复用信息
-
指定哪些引脚组(groups)复用为哪个功能(function)
2、Generic pin configuration node
-
描述引脚配置信息
-
配置电气特性:上拉/下拉、驱动强度、施密特触发、开漏等
注意:不同芯片厂商的实现方式不同,节点格式没有统一标准,但概念相通。
四、代码中使用Pinctrl(自动管理)
在标准设备驱动框架中,Pinctrl子系统会自动处理:
-
设备枚举时自动应用
default状态配置 -
系统休眠/唤醒时自动切换
sleep/default状态 -
驱动开发者通常无需手动调用
五、BSP工程师 vs 驱动工程师的分工
1、BSP工程师(芯片厂商)
a、实现Pin Controller驱动
b、在设备树中定义:
-
Pin Controller节点
-
各种引脚复用和配置节点
-
GPIO Controller节点
c、确保硬件寄存器操作正确
2、驱动工程师(设备厂商/应用开发)
a、在设备树中配置Client Device节点
b、声明设备需要使用的引脚状态
c、引用BSP工程师定义的引脚配置节点
d、编写设备驱动,无需关心引脚复用细节
六、Pin Controller 核心数据结构
1、pinctrl_desc - 控制器描述符(静态)
cpp
struct pinctrl_desc {
const char *name; // 控制器名称
const struct pinctrl_pin_desc *pins; // 引脚描述数组(描述单个引脚)
unsigned int npins; // 引脚数量
// 三大操作集
const struct pinctrl_ops *pctlops; // 基础引脚操作
const struct pinmux_ops *pmxops; // 引脚复用操作
const struct pinconf_ops *confops; // 引脚配置操作
struct module *owner; // 模块所有者
};
2、pinctrl_dev - 控制器设备(动态)
cpp
struct pinctrl_dev {
struct pinctrl_desc *desc; // 指向静态描述符
struct list_head node; // 全局链表节点
void *driver_data; // 驱动私有数据
struct radix_tree_root pin_desc_tree; // 引脚描述树
struct list_head gpio_ranges; // GPIO范围列表
};
注册流程:
cpp
// 1. 定义 pinctrl_desc
static struct pinctrl_desc my_pinctrl_desc = {
.name = "my-pinctrl",
.pins = my_pins,
.npins = ARRAY_SIZE(my_pins),
.pctlops = &my_pinctrl_ops,
.pmxops = &my_pinmux_ops,
.confops = &my_pinconf_ops,
};
// 2. 注册控制器
pctldev = pinctrl_register(&my_pinctrl_desc, dev, NULL);
// 或使用资源管理版本
pctldev = devm_pinctrl_register(dev, &my_pinctrl_desc, NULL);
3、三大操作集详解
a、pinctrl_ops - 基础引脚操作
cpp
struct pinctrl_ops {
// 引脚组管理(取出某组引脚)
int (*get_groups_count)(struct pinctrl_dev *pctldev);
const char *(*get_group_name)(struct pinctrl_dev *pctldev,
unsigned selector);
int (*get_group_pins)(struct pinctrl_dev *pctldev,
unsigned selector,
const unsigned **pins,
unsigned *num_pins);
// 设备树解析(关键!)
int (*dt_node_to_map)(struct pinctrl_dev *pctldev,
struct device_node *np_config,
struct pinctrl_map **map,
unsigned *num_maps);
};
b、pinmux_ops - 引脚复用操作
cpp
struct pinmux_ops {
// 功能管理
int (*get_functions_count)(struct pinctrl_dev *pctldev);
const char *(*get_function_name)(struct pinctrl_dev *pctldev,
unsigned selector);
// 实际复用配置(关键!)
int (*set_mux)(struct pinctrl_dev *pctldev,
unsigned func_selector,
unsigned group_selector);
// GPIO相关
int (*gpio_request_enable)(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset);
};
c、pinconf_ops - 引脚配置操作
cpp
struct pinconf_ops {
// 单个引脚配置
int (*pin_config_get)(struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *config);
int (*pin_config_set)(struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *configs,
unsigned num_configs);
// 组引脚配置
int (*pin_config_group_get)(...);
int (*pin_config_group_set)(...);
};
七、Client 设备数据结构
1、dev_pin_info - 设备的引脚信息
cpp
struct dev_pin_info {
struct pinctrl *p; // 指向设备的pinctrl(可存放自定义状态)
struct pinctrl_state *default_state; // 默认状态
struct pinctrl_state *init_state; // 初始状态
struct pinctrl_state *sleep_state; // 睡眠状态
struct pinctrl_state *idle_state; // 空闲状态
};
2、pinctrl - 设备引脚控制上下文
cpp
struct pinctrl {
struct list_head node; // 设备链表节点
struct device *dev; // 所属设备
struct list_head states; // 状态列表
struct pinctrl_state *state; // 当前状态
struct list_head dt_maps;
struct kref users;
};
3、pinctrl_state - 引脚状态
cpp
struct pinctrl_state {
struct list_head node; // 状态链表节点
const char *name; // 状态名称
struct list_head settings; // 设置列表
};
八、关键映射数据结构
1、pinctrl_map - 设备树到内核的映射
cpp
struct pinctrl_map {
const char *dev_name; // 设备名称
const char *name; // 映射名称
enum pinctrl_map_type type; // 映射类型
union {
// 引脚复用映射
struct pinctrl_map_mux {
const char *group; // 组名
const char *function; // 功能名
} mux;
// 引脚配置映射
struct pinctrl_map_configs {
const char *group_or_pin; // 组名或引脚名
unsigned long *configs; // 配置数组
unsigned num_configs; // 配置数量
} configs;
} data;
};
映射类型:
-
PIN_MAP_TYPE_MUX_GROUP- 引脚复用 -
PIN_MAP_TYPE_CONFIGS_PIN- 引脚配置 -
PIN_MAP_TYPE_CONFIGS_GROUP- 组配置
2、pinctrl_setting - 运行时设置
cpp
struct pinctrl_setting {
struct list_head node; // 设置链表节点
enum pinctrl_map_type type; // 设置类型
struct pinctrl_dev *pctldev; // 所属控制器
const char *dev_name; // 设备名称
union {
struct pinctrl_setting_mux {
unsigned group; // 组选择器
unsigned func; // 功能选择器
} mux;
struct pinctrl_setting_configs {
unsigned group_or_pin; // 组或引脚选择器
unsigned long *configs; // 配置数组
unsigned num_configs; // 配置数量
} configs;
} data;
};
九、数据流转过程
1、client节点的设备树解析流程
css
设备树节点
│
▼
dt_node_to_map() // 解析为pinctrl_map数组,放在pinctrl.dt_maps链表中
│
▼
pinctrl_maps_to_setting() // 转换为pinctrl_setting
│
▼
加入pinctrl_state.settings链表
2、设置应用流程
css
really_probe()
├── pinctrl_bind_pins()
│ ├── 解析设备树pinctrl属性
│ ├── 创建pinctrl_state
│ └── 转换为pinctrl_setting
│
└── pinctrl_select_state()
└── 遍历settings链表
├── PIN_MAP_TYPE_MUX_GROUP:
│ └── pinmux_enable_setting()
│ └── ops->set_mux() // 调用硬件设置
│
└── PIN_MAP_TYPE_CONFIGS_*:
└── pinconf_apply_setting()
└── ops->pin_config_group_set() // 调用硬件设置
十、通用 Pin Controller 驱动框架
cpp
/* ========================================
* 第一部分:基础数据结构定义
* ======================================== */
/* 1. 定义引脚描述符数组 */
static const struct pinctrl_pin_desc pins[] = {
{0, "pin0", NULL}, // 引脚编号、名称、私有数据
{1, "pin1", NULL},
{2, "pin2", NULL},
{3, "pin3", NULL},
};
/* 2. 定义配置存储数组(用于保存每个引脚的配置值) */
static unsigned long g_configs[4];
/* 3. 定义功能描述结构 */
struct virtual_functions_desc {
const char *func_name; // 功能名称
const char **groups; // 支持该功能的引脚组
int num_groups; // 组数量
};
/* 4. 定义各功能的引脚组 */
static const char *func0_grps[] = {"pin0", "pin1", "pin2", "pin3"};
static const char *func1_grps[] = {"pin0", "pin1"};
static const char *func2_grps[] = {"pin2", "pin3"};
/* 5. 定义功能表(描述硬件支持的所有功能) */
static struct virtual_functions_desc g_funcs_des[] = {
{"gpio", func0_grps, 4}, // GPIO功能:支持所有引脚
{"i2c", func1_grps, 2}, // I2C功能:支持pin0,pin1
{"uart", func2_grps, 2}, // UART功能:支持pin2,pin3
};
/* ========================================
* 第二部分:Pin Control 操作函数集
* 功能:管理引脚组信息
* ======================================== */
/* 获取引脚组总数 */
static int virtual_get_groups_count(struct pinctrl_dev *pctldev)
{
return pctldev->desc->npins; // 本例中每个引脚作为一个组
}
/* 获取指定组的名称 */
static const char *virtual_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
return pctldev->desc->pins[selector].name;
}
/* 获取指定组包含的引脚列表 */
static int virtual_get_group_pins(struct pinctrl_dev *pctldev,
unsigned selector,
const unsigned **pins,
unsigned *npins)
{
if (selector >= pctldev->desc->npins)
return -EINVAL;
*pins = &pctldev->desc->pins[selector].number; // 返回引脚编号
*npins = 1; // 每组只有一个引脚
return 0;
}
/* 调试信息显示 */
static void virtual_pin_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned offset)
{
seq_printf(s, "%s", dev_name(pctldev->dev));
}
/**
* 设备树节点转换为pinctrl映射
*
* 设备树格式示例:
* i2cgrp {
* functions = "i2c", "i2c";
* groups = "pin0", "pin1";
* configs = <0x11223344 0x55667788>;
* };
*
* 每个引脚会产生2个map:一个用于mux,一个用于config
*/
static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np,
struct pinctrl_map **map, unsigned *num_maps)
{
int i;
int num_pins = 0;
const char *pin;
const char *function;
unsigned int config;
struct pinctrl_map *new_map;
unsigned long *configs;
/* 1. 确定pin个数 */
while (1) {
if (of_property_read_string_index(np, "groups", num_pins, &pin) == 0)
num_pins++;
else
break;
}
/* 2. 分配pinctrl_map(每个pin需要2个map) */
new_map = kmalloc(sizeof(struct pinctrl_map) * num_pins * 2, GFP_KERNEL);
/* 3. 为每个pin创建map */
for (i = 0; i < num_pins; i++) {
/* 从设备树读取配置 */
of_property_read_string_index(np, "groups", i, &pin);
of_property_read_string_index(np, "functions", i, &function);
of_property_read_u32_index(np, "configs", i, &config);
/* 分配config存储空间 */
configs = kmalloc(sizeof(*configs), GFP_KERNEL);
configs[0] = config;
/* 创建MUX映射(设置引脚功能) */
new_map[i*2].type = PIN_MAP_TYPE_MUX_GROUP;
new_map[i*2].data.mux.function = function;
new_map[i*2].data.mux.group = pin;
/* 创建CONFIG映射(设置引脚配置) */
new_map[i*2+1].type = PIN_MAP_TYPE_CONFIGS_PIN;
new_map[i*2+1].data.configs.group_or_pin = pin;
new_map[i*2+1].data.configs.configs = configs;
new_map[i*2+1].data.configs.num_configs = 1;
}
*map = new_map;
*num_maps = num_pins * 2;
return 0;
}
/* 释放映射资源 */
static void virtual_dt_free_map(struct pinctrl_dev *pctldev,
struct pinctrl_map *map, unsigned num_maps)
{
while (num_maps--) {
if (map->type == PIN_MAP_TYPE_CONFIGS_PIN)
kfree(map->data.configs.configs);
kfree(map);
map++;
}
}
/* Pin Control操作函数集 */
static const struct pinctrl_ops virtual_pctrl_ops = {
.get_groups_count = virtual_get_groups_count,
.get_group_name = virtual_get_group_name,
.get_group_pins = virtual_get_group_pins,
.pin_dbg_show = virtual_pin_dbg_show,
.dt_node_to_map = virtual_dt_node_to_map,
.dt_free_map = virtual_dt_free_map,
};
/* ========================================
* 第三部分:Pin Mux 操作函数集
* 功能:管理引脚复用功能
* ======================================== */
/* 获取功能总数 */
static int virtual_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(g_funcs_des);
}
/* 获取功能名称 */
static const char *virtual_pmx_get_func_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
return g_funcs_des[selector].func_name;
}
/* 获取功能对应的引脚组 */
static int virtual_pmx_get_groups(struct pinctrl_dev *pctldev,
unsigned selector,
const char * const **groups,
unsigned * const num_groups)
{
*groups = g_funcs_des[selector].groups;
*num_groups = g_funcs_des[selector].num_groups;
return 0;
}
/*
* 设置引脚复用(核心函数)
* selector: 功能索引
* group: 引脚组索引
*
* 实际硬件中,这里需要写寄存器来配置引脚功能
*/
static int virtual_pmx_set(struct pinctrl_dev *pctldev,
unsigned selector, unsigned group)
{
printk("set %s as %s\n",
pctldev->desc->pins[group].name,
g_funcs_des[selector].func_name);
return 0;
}
/* Pin Mux操作函数集 */
static const struct pinmux_ops virtual_pmx_ops = {
.get_functions_count = virtual_pmx_get_funcs_count,
.get_function_name = virtual_pmx_get_func_name,
.get_function_groups = virtual_pmx_get_groups,
.set_mux = virtual_pmx_set,
};
/* ========================================
* 第四部分:Pin Config 操作函数集
* 功能:管理引脚配置(上拉、下拉、驱动强度等)
* ======================================== */
/* 获取引脚配置 */
static int virtual_pinconf_get(struct pinctrl_dev *pctldev,
unsigned pin_id, unsigned long *config)
{
*config = g_configs[pin_id];
return 0;
}
/*
* 设置引脚配置
* pin_id: 引脚编号
* configs: 配置值数组
* num_configs: 配置数量
*
* 实际硬件中,这里需要写寄存器配置引脚电气特性
*/
static int virtual_pinconf_set(struct pinctrl_dev *pctldev,
unsigned pin_id, unsigned long *configs,
unsigned num_configs)
{
if (num_configs != 1)
return -EINVAL;
g_configs[pin_id] = *configs;
printk("config %s as 0x%lx\n",
pctldev->desc->pins[pin_id].name, *configs);
return 0;
}
/* 调试信息显示函数 */
static void virtual_pinconf_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned pin_id)
{
seq_printf(s, "0x%lx", g_configs[pin_id]);
}
static void virtual_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned pin_id)
{
seq_printf(s, "0x%lx", g_configs[pin_id]);
}
/* Pin Config操作函数集 */
static const struct pinconf_ops virtual_pinconf_ops = {
.pin_config_get = virtual_pinconf_get,
.pin_config_set = virtual_pinconf_set,
.pin_config_dbg_show = virtual_pinconf_dbg_show,
.pin_config_group_dbg_show = virtual_pinconf_group_dbg_show,
};
/* ========================================
* 第五部分:Platform 驱动接口
* ======================================== */
/* 设备树匹配表 */
static const struct of_device_id virtual_pinctrl_of_match[] = {
{ .compatible = "virtual_pinctrl", },
{ },
};
static struct pinctrl_dev *g_pinctrl_dev;
/* 驱动probe函数 */
static int virtual_pinctrl_probe(struct platform_device *pdev)
{
struct pinctrl_desc *pctrl_desc;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
/* a. 分配pinctrl_desc */
pctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctrl_desc), GFP_KERNEL);
/* b. 设置pinctrl_desc */
pctrl_desc->name = dev_name(&pdev->dev);
pctrl_desc->owner = THIS_MODULE;
/* b.1 设置引脚和组信息 */
pctrl_desc->pins = pins;
pctrl_desc->npins = ARRAY_SIZE(pins);
pctrl_desc->pctlops = &virtual_pctrl_ops;
/* b.2 设置pin mux操作 */
pctrl_desc->pmxops = &virtual_pmx_ops;
/* b.3 设置pin config操作 */
pctrl_desc->confops = &virtual_pinconf_ops;
/* c. 注册pinctrl_desc到pinctrl子系统 */
g_pinctrl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, NULL);
return 0;
}
/* 驱动remove函数 */
static int virtual_pinctrl_remove(struct platform_device *pdev)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* Platform驱动结构 */
static struct platform_driver virtual_pinctrl_driver = {
.probe = virtual_pinctrl_probe,
.remove = virtual_pinctrl_remove,
.driver = {
.name = "virtual_pinctrl",
.of_match_table = of_match_ptr(virtual_pinctrl_of_match),
}
};
/* 模块入口函数 */
static int __init virtual_pinctrl_init(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return platform_driver_register(&virtual_pinctrl_driver);
}
/* 模块出口函数 */
static void __exit virtual_pinctrl_exit(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&virtual_pinctrl_driver);
}
module_init(virtual_pinctrl_init);
module_exit(virtual_pinctrl_exit);
MODULE_LICENSE("GPL");