Linux驱动学习day20(pinctrl子系统驱动大全)

一、Pinctrl作用

Pinctrl(Pin Controller):控制引脚

引脚的枚举与命名、引脚复用、引脚配置。Pinctrl驱动一般由芯片原厂的BSP工程师来写,一般驱动工程师只需要在设备树中指明使用哪个引脚,复用为哪个功能、配置为哪些状态。

二、Pin Controller重要结构体

由于面向对象的思想,在Linux系统中使用pinctrl_desc和 pinctrl_dev描述pinController。

左边是controller(抽象出pinctrl_dev结构体)部分,右边是设备(抽象出device结构体,该结构体含有.pinctrl会和左边pinctrl_dev挂钩)部分。

2.1 pinctrl_dev结构体 

我们并不需要自己构建pinctrl_dev,只需要描述它,提供一个pinctrl_desc结构体使用pinctrl_register(函数原型如下)可以构建。

struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,struct device *dev, void *driver_data)
{struct pinctrl_dev *pctldev;int error;pctldev = pinctrl_init_controller(pctldesc, dev, driver_data);if (IS_ERR(pctldev))return pctldev;error = pinctrl_enable(pctldev);if (error)return ERR_PTR(error);return pctldev;}

当设备树中的pinctrl节点compatible属性和驱动中的compatible匹配上之后 ,会调用驱动程序的probe函数。在probe函数中,会分配设置pinctrl_desc结构体,并且使用这个结构体来注册pinctrl_dev。使用pinctrl_desc中的pins 和 npins来描述一个引脚,使用pctlops结构体来描述一组引脚。

在pctlops结构体中必须要构造下面这些函数:

2.2 client(dev_pin_info)

设备树上的设备节点不一定都会被构建成dev下的设备,比如I2C的设备节点,会被构建成I2C_client,但是不论构建成哪一个,结构体中都会含有dev结构体。每个device结构体里面都有一个 dev_pin_info结构体,用来保存设备的pinctrl信息。内核已经给出了几个默认的状态,每种状态对应的信息会被保存到对应的pinctrl_state结构体中,也可以自己指定,但是自己指定的状态会被保存到pinctrl指针的list链表中。

在pinctrl这个大节点下面会有很多节点,节点会对应某个状态,在pinctrl的驱动程序中会使用pinctrl_ops的dt_node_to_map函数,这个函数会把每个子节点转换成一系列的pinctrl_map,pinctrl_map又会被转换成一系列的pinctrl_setting,这些pinctrl_setting会被存放在某一个pinctrl.state.settings链表里面。

三、设备树解析流程

在rk3568.dtsi文件中存在pinctrl这个节点,该节点会被转化成一个dev,当dev的compatible属性和驱动程序中的 compatible匹配,驱动程序中的probe会被调用,我们根据相关的关系可以找到rk3568的pinctrl的驱动代码。

dd@dd-NP3020M3:~/RK3568/SDK/linux/rk3568_linux_sdk/kernel/drivers$ grep "rockchip,rk3568-pinctrl" -nr
Binary file pinctrl/pinctrl-rockchip.o matches
pinctrl/pinctrl-rockchip.c:4120:	{ .compatible = "rockchip,rk3568-pinctrl",

作用1. 描述获得引脚,解析设备树

1. 单个引脚

可以在开发板上查看

/sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl

关于rk3568,下图是其打印信息

2. 引脚组信息

cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pingroups

 3. 引脚组功能

3.2 client节点的pinctrl构造过程

3.2.1 设备树转换成pinctrl_map 

设备节点和驱动匹配之后调用really_probe,really_probe给每个dev设置绑定pins,并将设备树信息转化成pinctrl_map。(pinctrl_bind_pins() 是在 really_probe()且在 probe 之前调用的,这就是内核的 pinctrl 绑定机制。)借用chatgpt回答:

__device_attach_driver()└─ really_probe(dev, drv)└─ dev->driver = drv;└─ pinctrl_bind_pins(dev);  ← 设置 pins└─ drv->probe(dev);         ← 调用你的驱动 probe

四、编写虚拟的pinctrl驱动程序

首先先来回顾一下pinctrl子系统的三大左右:引脚枚举与命名、引脚复用、引脚配置。

pinctrl驱动的核心是分配设置构造一个pinctrl_desc结构体

 需要做的事情1、创建设备树pinctrller节点,编写驱动程序 2、创建client节点,编写驱动程序

4.1 设备树节点 

/{virtual_pincontroller {compatible = "xupt,virtual_pinctrl";i2cgrp{pins{functions = "i2c","i2c";groups = "pin0" , "pin1";configs = <0x11224455 0x8746446>;}}}
}virtual_i2c{compatible = "xupt,virtual_i2c";pinctrl_name = "default";pinctrl_0 = <&i2cgrp>;
}

4.2 virtual_pinctrl_client_drv.c

#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/slab.h>
#include <linux/regmap.h>static const struct of_device_id virtual_client_of_match[] = {{ .compatible = "xupt,virtual_i2c", },{ },
};static int virtual_client_probe(struct platform_device *pdev)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
static int virtual_client_remove(struct platform_device *pdev)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static struct platform_driver virtual_client_driver = {.probe		= virtual_client_probe,.remove		= virtual_client_remove,.driver		= {.name	= "xupt,virtual_i2c",.of_match_table = of_match_ptr(virtual_client_of_match),}
};/* 1. 入口函数 */
static int __init virtual_client_init(void)
{	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 1.1 注册一个platform_driver */return platform_driver_register(&virtual_client_driver);
}/* 2. 出口函数 */
static void __exit virtual_client_exit(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 2.1 反注册platform_driver */platform_driver_unregister(&virtual_client_driver);
}module_init(virtual_client_init);
module_exit(virtual_client_exit);MODULE_LICENSE("GPL");

  4.3 virtual_pinctrl_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/gpio.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/rockchip/cpu.h>
#include <dt-bindings/pinctrl/rockchip.h>#include "core.h"static struct pinctrl_dev *g_pctldev;static const struct pinctrl_pin_desc pins[] = {{ 0 ,  "pin0" , NULL},{ 1 ,  "pin1" , NULL},{ 2 ,  "pin2" , NULL},{ 3 ,  "pin3" , NULL},
};static unsigned long g_configs[4];struct virtual_function_desc{const char *func_name;const char **groups;int num_group;
};static const char *func0_pins[] = {"pin0" , "pin1" , "pin2" , "pin3"};
static const char *func1_pins[] = {"pin0" , "pin1"};
static const char *func2_pins[] = {"pin2" , "pin3"};static struct virtual_function_desc g_func_des[] = {{"gpio" , func0_pins , 4},{"i2c" , func1_pins , 2},{"uart" , func2_pins , 2},
};static int virtual_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
{return pctldev->desc->npins;
}static const char *virtual_pinctrl_get_group_name(struct pinctrl_dev *pctldev,unsigned selector)
{return pctldev->desc->pins[selector].name;
}static int virtual_pinctrl_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;
}/*	
i2cgrp{pins{functions = "i2c","i2c";groups = "pin0" , "pin1";driver-open-drain;}}one pin ==> two pinctrl_map (one for mux , one for config)*/static int virtual_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,struct device_node *np,struct pinctrl_map **map, unsigned *num_maps)
{/* alloc pinctrl_map and num_pin */int num_pins = 1;int i;const char *pins;const char *functions;unsigned int config;struct pinctrl_map *new_map;unsigned long *configs;while(1){if(of_property_read_string_index(np, "groups", num_pins, &pins) == 0)num_pins++;else {break;}}new_map = kmalloc( sizeof(*new_map) *num_pins* 2, GFP_KERNEL);*map = new_map;*num_maps = num_pins * 2;for(i = 0 ; i < num_pins; i++){/* get pin / function / config  (from decice tree)*/of_property_read_string_index(np, "functions", i, &pins);of_property_read_string_index(np, "functions", i, &functions);of_property_read_u32_index(np, "configs", i, &config);configs = kmalloc(sizeof(*configs) , GFP_KERNEL);/* save pinctrl_map */new_map[i*2].type = PIN_MAP_TYPE_MUX_GROUP;new_map[i*2].data.mux.function = functions;new_map[i*2].data.mux.group = pins;new_map[i*2 + 1].type = PIN_MAP_TYPE_CONFIGS_PIN;new_map[i*2 + 1].data.configs.group_or_pin = pins;configs[i] = config;new_map[i*2 + 1].data.configs.configs = configs;new_map[i*2 + 1].data.configs.num_configs = 1;}return 0;
}static void virtual_pinctrl_dt_free_map(struct pinctrl_dev *pctldev,struct pinctrl_map *map, unsigned num_maps)
{while(num_maps--){if(map->type == PIN_MAP_TYPE_MUX_GROUP){kfree(map->data.configs.configs);}kfree(map);map++;}
}static const struct pinctrl_ops virtual_pctrl_ops = {.get_groups_count	= virtual_pinctrl_get_groups_count,.get_group_name		= virtual_pinctrl_get_group_name,.get_group_pins		= virtual_pinctrl_get_group_pins,.dt_node_to_map		= virtual_pinctrl_dt_node_to_map,.dt_free_map		= virtual_pinctrl_dt_free_map,
};static int virtual_pinctrl_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
{return ARRAY_SIZE(g_func_des);
}static const char *virtual_pinctrl_pmx_get_func_name(struct pinctrl_dev *pctldev,unsigned selector)
{return g_func_des[selector].func_name;
}static int virtual_pinctrl_pmx_get_groups(struct pinctrl_dev *pctldev,unsigned selector, const char * const **groups,unsigned * const num_groups)
{*groups = g_func_des[selector].groups;*num_groups = g_func_des[selector].num_group;return 0;
}static int virtual_pinctrl_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,unsigned group)
{printk("virtual_pinctrl_pmx_set %s as %s\n" , pctldev->desc->pins[group].name , g_func_des[selector].func_name);return 0;
}/* 实现引脚复用功能,获取功能数量、功能名、功能包含组,及设置复用。 */
static const struct pinmux_ops virtual_pinctrl_pmx_ops = {.get_functions_count	= virtual_pinctrl_pmx_get_funcs_count,.get_function_name	= virtual_pinctrl_pmx_get_func_name,.get_function_groups	= virtual_pinctrl_pmx_get_groups,.set_mux		= virtual_pinctrl_pmx_set,
};static int virtual_pinctrl_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,unsigned long *config)
{*config = g_configs[pin];return 0;
}static int virtual_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,unsigned long *configs, unsigned num_configs)
{if(num_configs != 1)return -EINVAL;g_configs[pin] = *configs;printk("config %s as 0x%lx\n" , pctldev->desc->pins[pin].name , *configs);return 0;
}/* 实现引脚配置功能,保存引脚配置状态并打印配置日志。 */
static const struct pinconf_ops virtual_pinctrl_pinconf_ops = {.pin_config_get			= virtual_pinctrl_pinconf_get,.pin_config_set			= virtual_pinctrl_pinconf_set,.is_generic			= true,
};static int virtual_pinctrl_probe(struct platform_device *pdev)
{/* alloc pinctrl_desc  */struct pinctrl_desc  * desc;desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);/* setting */desc->pins = pins;desc->npins = ARRAY_SIZE(pins);desc->owner = THIS_MODULE;desc->name = "virtual-pinctrl";desc->pctlops = &virtual_pctrl_ops;desc->pmxops = &virtual_pinctrl_pmx_ops;desc->confops = &virtual_pinctrl_pinconf_ops;g_pctldev = devm_pinctrl_register(&pdev->dev, desc, NULL);return 0;
}static const struct of_device_id virtual_pinctrl_dt_match[] = {{ .compatible = "xupt,virtual_pinctrl", .data = NULL },{},
};static struct platform_driver virtual_pinctrl_driver = {.probe		= virtual_pinctrl_probe,.driver = {.name	= "vitrual-pinctrl",.of_match_table = virtual_pinctrl_dt_match,},
};static int __init virtual_pinctrl_drv_init(void)
{return platform_driver_register(&virtual_pinctrl_driver);
}static void __exit virtual_pinctrl_drv_exit(void)
{platform_driver_unregister(&virtual_pinctrl_driver);
}module_init(virtual_pinctrl_drv_init);
module_exit(virtual_pinctrl_drv_exit);
MODULE_LICENSE("GPL");

五、总结

这个子系统主要还是分配设置注册 pinctrl_desc  结构体,里面需要提供将设备树信息转换成所需要的引脚信息,复用信息,引脚配置等函数,全部加起来就是一大块内容,涉及超级多的结构体和函数,设计这个的人一定是个大聪明,能把所有的链接到一块去。太厉害了。

对于这几天每个子系统都是很庞大的一块知识,听的云里雾里的。 太难了。

六、问题

pinctrl是在加载pinctrl驱动时设置的引脚,还是在加载模块时设置的引脚复用呢?还是说会
设置成俩次呢?是在加载使用这些引脚的设备驱动时设置的引脚复用。

__device_attach_driver()└─ really_probe(dev, drv)└─ dev->driver = drv;└─ pinctrl_bind_pins(dev);  ← 设置 pins└─ drv->probe(dev);         ← 调用你的驱动 probe

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/90430.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/90430.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Debiased All-in-one Image Restoration with Task Uncertainty Regularization

Abstract 一体化图像恢复是一项基础的底层视觉任务&#xff0c;在现实世界中有重要应用。主要挑战在于在单个模型中处理多种退化情况。虽然当前方法主要利用任务先验信息来指导恢复模型&#xff0c;但它们通常采用统一的多任务学习&#xff0c;忽略了不同退化任务在模型优化中的…

逆向 qq 音乐 sign,data, 解密 response 返回的 arraybuffer

解密 arraybuffer python requests 请求得到 arraybuffer&#xff0c;转为 hex 传递给 js res_data sign ctx.call("decrypt", response.content.hex())function decrypt(hex) {const bytes new Uint8Array(hex.length / 2);for (let i 0; i < hex.length; i …

PPT处理控件Aspose.Slides教程:在 C# 中将 ODP 转换为 PPTX

您是否正在寻找可靠的 PowerPoint SDK 来以编程方式开发ODP到PPTX转换器&#xff1f;本篇博文演示了如何使用 C# 将 ODP 转换为 PPTX。ODP是一种基于 XML 的演示文稿文件&#xff0c;可能包含图像、视频、文本等。但是&#xff0c;将打开的文档演示文稿转换为 PowerPoint 格式可…

[746] 使用最小花费爬楼梯

可以从下标0或者1作为起始位置————dp[0] dp[1] 0。一次性可以选择移动1次或者2次&#xff0c;故当下标>2的时候&#xff0c;到达2有可能是从下标0开始或者下标1开始&#xff0c;cost[0] or cost[1]&#xff1b;到达n&#xff0c;有可能是花费cost[n-1]到达&#xff0c…

树莓派vsftpd文件传输服务器的配置方法

在树莓派上安装和配置 vsftpd&#xff08;Very Secure FTP Daemon&#xff09;服务器的步骤如下&#xff1a; 1. 安装 vsftpd 打开终端&#xff0c;执行以下命令安装 vsftpd&#xff1a; sudo apt update sudo apt install vsftpd安装完成后&#xff0c;vsftpd 会自动启动。可以…

4.服务注册发现:微服务的神经系统

在微服务架构中,服务之间不再是固定连接,而是高度动态、短暂存在的。如何让每个服务准确找到彼此,是分布式系统治理的核心问题之一。服务注册发现机制,正如神经系统之于人体,承担着连接、协调、感知变化的关键角色。 本文将围绕 Netflix 开源的服务注册发现组件 Eureka 展…

基于Docker Compose部署Traccar容器与主机MySQL的完整指南

Traccar Docker镜像内嵌了H2数据库&#xff0c;该数据库容量有限&#xff0c;当达到一定容量时&#xff0c;定位数据无法写入会导致无法定位显示。为此有必要为Traccar 配置外部数据库。根据官网文档和自身经验我选择了MySQL。 参考的官方文档 软件环境为ubuntu server 24.04版…

paddlehub环境搭建和测试

目录1.环境搭建1.1 创建conda环境1.2 安装paddlepaddle和paddlehub1.3 安装依赖2. 移动端模型部署2.1 安装移动端模型2.2 测试3. 服务部署3.1 启动PaddleHub Serving3.2 发送预测请求1.环境搭建 1.1 创建conda环境 conda create --name paddlehub python3.8 conda activate p…

408第三季part2 - 计算机网络 - ip地址II

理解路由聚合就是从第一个不一样的往后全置为0题目这里一般来说会到达2个目的地址&#xff0c;但中间有个路由&#xff0c;所以路由聚合一下就行了聚合出来这个然后下一跳就是跳到下一个路由器d前面一样的不动&#xff0c;不一样的开始全置为0c再次理解题目这个先匹配169.96.40…

【Unity】MiniGame编辑器小游戏(十一)消消乐【Crush】

更新日期:2025年7月9日。 项目源码:获取项目源码 索引 消消乐【Crush】一、游戏最终效果二、玩法简介三、正式开始1.定义游戏窗口类2.规划游戏窗口、视口区域3.方块 Block①.定义方块类②.生成方块所有类型③.生成消消乐棋盘④.绘制收集栏⑤.绘制方块阵列4.查看方块挡住的其他…

RK3588 Android SDK 实战全解析 —— 架构、原理与开发关键点

&#x1f4d6; 推荐阅读&#xff1a;《Yocto项目实战教程:高效定制嵌入式Linux系统》 &#x1f3a5; 更多学习视频请关注 B 站&#xff1a;嵌入式Jerry RK3588 Android SDK 实战全解析 —— 架构、原理与开发关键点 作者&#xff1a;嵌入式 Jerry 一、前言 随着 AIoT、工业智…

从救火到赋能:运维的职责演进与云原生时代的未来图景

引言:刻板印象的瓦解 提起"运维工程师",许多人脑海中可能仍会浮现这样的画面:深夜里守着闪烁的监控屏幕、手忙脚乱地重启服务器、在布满网线的机房里穿梭…这曾是运维工作的真实片段,但绝非全貌,更非未来。 在云计算、DevOps、SRE理念和云原生技术栈的冲击下,…

UDP的socket编程

socket接口int socket(int domain, int type, int protocol);参数说明​​参数说明domain协议族&#xff08;地址族&#xff09;&#xff0c;如 AF_INET&#xff08;IPv4&#xff09;、AF_INET6&#xff08;IPv6&#xff09;type套接字类型&#xff0c;UDP 使用 SOCK_DGRAM&…

基于SD-WAN的管件制造数字化产线系统集成方案

1. 背景与目标随着制造业向智能化、数字化方向转型&#xff0c;传统产线面临着数据割裂、协同效率低下等问题。管件制造作为典型场景&#xff0c;涉及多环节的设计、制造与质检流程&#xff0c;亟需一套高效的系统集成方案&#xff0c;保障全流程数据贯通与实时协同。本方案基于…

学习open62541 --- [79] 在docker中运行open62541工程

docker是非常流行的容器技术&#xff0c;解决了部署环境不一致的问题&#xff0c;open62541的工程也可以在docker容器中运行&#xff0c;本文讲述如何把open62541工程放到docker容器中运行。 本文使用WSL ubuntu 22.04作为宿主环境&#xff0c;其它linux也是一样。一 拉取debia…

Spring Boot微服务中集成gRPC实践经验分享

Spring Boot微服务中集成gRPC实践经验分享 一、业务场景描述 在某电商系统中&#xff0c;推荐服务、库存服务、订单服务等微服务需要高效、双向流式通信&#xff0c;RESTHTTP已无法满足低延迟、高并发和严格类型安全的需求。为此&#xff0c;我们选择在Spring Boot微服务中集成…

springboot项目编写测试类,乱码问题解决

​MockMvc 的默认行为​ MockMvc ​默认使用 ISO-8859-1 解码响应&#xff0c;而服务端实际返回 UTF-8 编码数据 。 Postman 无乱码是因浏览器自动识别编码&#xff0c;但 MockMvc 需显式配置。 ​过滤器失效场景​ Spring 的 CharacterEncodingFilter ​默认只对 POST 请求生效…

打破传统,开启 AR 智慧课堂​

在教育领域&#xff0c;AR 智慧课堂宛如一场及时雨&#xff0c;为传统教育模式带来了革命性的变革&#xff0c;让学习变得更加生动有趣、高效互动。通过 AR 技术&#xff0c;抽象的知识瞬间变得鲜活起来&#xff0c;学生们可以在虚拟与现实交织的世界中&#xff0c;探索历史的长…

热烈祝贺 Flink 2.0 存算分离入选 VLDB 2025

VLDB 2025 论文热烈祝贺 Apache Flink 2.0 的重磅研究成果《Disaggregated State Management in Apache Flink 2.0 》被数据库领域顶级会议 VLDB 2025 正式接收&#xff01;这项工作由 Apache Flink 社区 联合 阿里巴巴实时计算 Flink 团队 以及多位学术界研究人员共同完成&…

蓄电池能量管理matlab的simulink仿真

蓄电池能量管理matlab的simulink仿真模型 AlternatorTableData.mat , 7395 Battery_Management_Lib.mdl , 577258 Readme.txt , 1293 license.txt , 1551 ssc_battery_management.mdl , 221248 ssc_lead_acid_battery_50Ah_ini.m , 1760 ssc_lead_acid_battery_80Ah_ini.m , 1…