[驱动之路(七)——Pinctrl子系统]学习总结,万字长篇,一文彻底搞懂Pinctrl子系统(含Pin Controller驱动框架解析)

一、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");
相关推荐
用户6135411460162 小时前
Linux 麒麟系统安装 gcc-7.3.0 rpm 包步骤
linux
小尧嵌入式2 小时前
Linux网络介绍网络编程和数据库
linux·运维·服务器·网络·数据库·qt·php
山川而川-R2 小时前
在香橙派5pro上的ubuntu22.04系统烧录镜像
linux·运维·服务器
Ancelin安心3 小时前
关于代理的一些网络知识复盘
linux·运维·网络·计算机网络·web安全·ubuntu·网络安全
chenyuhao20243 小时前
Linux系统编程:多线程互斥以及死锁问题
linux·运维·服务器·c++·后端
Norach3 小时前
Ubuntu升级opencv版本至4.9.0
linux·经验分享·opencv·yolo·ubuntu·dnn
默|笙3 小时前
【Linux】进程(6)虚拟地址空间
linux·运维·服务器
linzihahaha3 小时前
vmware-ubuntu 虚拟机共享文件及复制拖动配置
linux·运维·ubuntu
韩金群3 小时前
centos离线安装配置clickhouse
linux·clickhouse·centos