Linux驱动开发(速记版)--printctl子系统

news2025/2/4 17:58:19

第102章 pinctrl 子系统的引入

        Linux中的 pinctrl子系统是管理和配置GPIO引脚的框架,提供标准化方法以适应不同硬件。

        它遵循 Linux内核设备模型,分为设备、驱动等部分。

        本章节从设备和驱动角度介绍 pinctrl子系统。

102.1 pinctrl 设备树

        在设备树中,RK3568的pinctrl配置被详细定义,

        主要包括服务端(pinctrl节点及其下的GPIO控制器)

        客户端(在rk3568-pinctrl.dtsi中定义的引脚复用配置)

服务端配置(rk3568.dtsi):

        在设备树根节点下,定义了pinctrl节点,指定了兼容性和时钟等属性。

        包含了五个GPIO控制器(gpio0-gpio4),每个都指定了地址、中断、时钟等配置。

        通过 gpio-ranges属性将GPIO引脚映射到 pinctrl系统。

pinctrl: pinctrl {  
    compatible = "rockchip,rk3568-pinctrl";  //兼容性字符串
    gpio0: gpio@fdd60000 { ... };  
    gpio1: gpio@fe740000 { ... };  
    ...  
    #include "rk3568-pinctrl.dtsi"  
};

        在设备树的最下方通过 include 包含了 rk3568-pinctrl.dtsi 设备树,该设备树中包含了所有复用功能的配置。

客户端配置(rk3568-pinctrl.dtsi):

        定义了具体的引脚复用配置,如 acodec的引脚配置。

        这些配置由瑞芯微原厂提供,用户通常只需根据需求使用。

&pinctrl {  
    acodec-pins: acodec-pins {  
        rockchip,pins = <  
            1 RK PB1 5 &pcfg_pull_none>,  
            1 RK PA1 5 &pcfg_pull_none>,  
            ...  
        >;  
    };  
    ...  
};

驱动实现:

        pinctrl 驱动根据设备树中的 compatible属性进行匹配。

        RK3568的 pinctrl驱动位于内核源码的 /driver/pinctrl/pinctrl-rockchip.c。

102.2 pinctrl 驱动

        在 /drivers/pinctrl/pinctrl-rockchip.c中,

        Rockchip pinctrl驱动的入口函数通过platform_driver_register注册了一个platform驱动。

        当设备与驱动匹配时,会调用 rockchip_pinctrl_probe()函数进行初始化。

rockchip_pinctrl_probe函数的主要作用包括:

        分配并初始化 rockchip_pinctrl结构体

        获取与平台设备相关的 rockchip_pin_ctrl结构体

        解析设备树中的"rockchip,grf"节点,获取寄存器映射基地址

        如果找不到"rockchip,grf"节点,则通过平台资源获取寄存器基地址,并配置寄存器映射。

        尝试查找可选的"rockchip,pmu"syscon引用。

        对某些SoC进行特殊处理。

        注册 rockchip_pinctrl设备。

        设置平台设备的私有数据。

        注册GPIO设备。

static struct platform_driver rockchip_pinctrl_driver = {  
    .probe = rockchip_pinctrl_probe,  
    .driver = {  
        .name = "rockchip-pinctrl",  
        .of_match_table = rockchip_pinctrl_dt_match,  
    },  
};  
  
static int __init rockchip_pinctrl_drv_register(void)  
{  
    return platform_driver_register(&rockchip_pinctrl_driver);  
}  
  
postcore_initcall(rockchip_pinctrl_drv_register);  
  
static int rockchip_pinctrl_probe(struct platform_device *pdev)  
{  
    // 分配并初始化结构体  
    // 获取并设置soc数据  
    // 解析设备树,获取寄存器基地址  
    // 查找可选的pmu syscon引用  
    // 对某些SoC进行特殊处理  
    // 注册pinctrl设备  
    // 设置私有数据  
    // 注册GPIO设备  
    return 0;  
}

第103章 pinctrl probe 函数讲解

103.1 pinctrl_desc 结构体

        pinctrl_desc 结构体用于描述引脚控制器(pinctrl)的属性和操作,

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;               // 拥有模块  
  
    // 自定义配置参数(可选)  
    unsigned int num_custom_params;  
    const struct pinconf_generic_params *custom_params;  
    const struct pin_config_item *custom_conf_items;  
};

103.2 rockchip_pinctrl 结构体

        rockchip_pinctrl 结构体是瑞芯微为了适应其芯片特定需求和功能,对标准的 pinctrl_desc 结构体进行的封装。

/*瑞芯微对pinctrl结构体的封装*/
struct rockchip_pinctrl {  
    struct regmap *regmap_base;    // 基本寄存器映射  
    int reg_size;                  // 寄存器大小  
    struct regmap *regmap_pull;    // 拉取寄存器映射  
    struct regmap *regmap_pmu;     // PMU寄存器映射  
    struct device *dev;            // 设备指针  
    struct rockchip_pin_ctrl *ctrl; // 瑞芯微引脚控制器指针  
    struct pinctrl_desc pctl;      // 引脚控制器描述符(包含标准pinctrl_desc)  
    struct pinctrl_dev *pctl_dev;  // 引脚控制器设备指针  
    struct rockchip_pin_group *groups; // 引脚组指针  
    unsigned int ngroups;          // 引脚组数量  
    struct rockchip_pmx_func *functions; // 引脚功能指针  
    unsigned int nfunctions;       // 引脚功能数量  
};

103.3 pinctrl probe 函数

probe()函数中,主要完成了以下任务:

        分配并初始化rockchip_pinctrl结构体

        获取SoC特定的配置,

        解析设备树,获取寄存器映射,

        处理可选的PMU寄存器映射。

        进行SoC特定的初始化(如果适用)。

        注册 pinctrl设备。

ret = rockchip_pinctrl_register(pdev, info);  
if (ret) return ret;

        注册GPIO设备(通过of_platform_populate)。

rockchip_pinctrl_register()函数完成了以下任务:

        初始化pinctrl_desc结构体

        为每个引脚分配内存并设置编号和名称。

        解析设备树中的pinctrl信息。

        注册 pinctrl设备。

info->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, info);  
if (IS_ERR(info->pctl_dev)) return PTR_ERR(info->pctl_dev);

devm_pinctrl_register()函数用于注册 pinctrl设备并将其与设备关联:

        分配内存以存储 pinctrl_dev指针,

        注册 pinctrl设备,

pctldev = pinctrl_register(pctldesc, dev, driver_data);  
if (IS_ERR(pctldev)) {  
    devres_free(ptr);  
    return pctldev;  
}

        将 pinctrl_dev指针存储到设备资源列表中。

pinctrl_register()函数用于注册并启用 pinctrl设备:

        初始化 pinctrl控制器,

pctldev = pinctrl_init_controller(pctldesc, dev, driver_data);  
if (IS_ERR(pctldev)) return pctldev;

        启用 pinctrl控制器。

error = pinctrl_enable(pctldev);  
if (error) return ERR_PTR(error);

        通过这一系列步骤,瑞芯微的pinctrl驱动成功注册并启用了pinctrl设备,为后续的引脚控制和配置提供了基础。

第104章 pinctrl 子系统函数操作集

        probe 函数的实际作用就是注册并启用 pinctrl 设备,pinctrl 设备由 pinctrl_desc 结构体所描述,所以在 probe() 函数中会对 pinctrl_desc 结构体中的内容进行填充。

104.1 groups 和 function

        在 Pinctrl 子系统中,关键概念包括引脚组(groups)功能(function)

以 rk3568-pinctrl.dtsi 设备树文件中的 can0 和 can1 为例:

can0 功能:

        can0m0-pins 组:

<0 RK_PB4 2 &pcfg_pull_none>, 
<0 RK_PB3 2 &pcfg_pull_none>

/*控制器编号、引脚编号、引脚模式编号(代表功能)、引脚配置选项*/
//pcfg_pull_none代表未启动上下拉,即浮空

        RK_PB4:CAN0 接收引脚(can0_rxm0)

        RK_PB3:CAN0 发送引脚(can0_txm0)

        can1m1-pins 组:

<4 RK_PC2 3 &pcfg_pull_none>, 
<4 RK_PC3 3 &pcfg_pull_none>

        RK_PC2:CAN1 接收引脚(can1_rxm1)

        RK_PC3:CAN1 发送引脚(can1_txm1)

        can0 和 can1 是两个功能,每个功能包含两个引脚组,分别用于配置 CAN0 和 CAN1 的接收和发送引脚。

104.2 函数操作集结构体

        在Linux内核中,pinctrl_desc 结构体包含三个函数操作集,

        分别用于引脚控制引脚复用引脚配置。瑞芯微在源码中对重要函数进行了实现。

        pinctrl_ops()引脚控制操作函数指针。

        pinmux_ops()引脚复用操作函数指针。

        pinconf_ops()引脚配置操作函数指针。

/*引脚控制操作函数*/
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);    
      
    // 打印引脚信息用于调试  
    void (*pin_dbg_show)(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset);    
      
    // 将设备树节点转换为引脚控制器映射  
    int (*dt_node_to_map)(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps);    
      
    // 释放由设备树节点转换来的引脚控制器映射  
    void (*dt_free_map)(struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps);    
};
/*引脚复用操作函数*/
struct pinmux_ops {    
    // 请求特定的引脚或引脚组进行复用  
    int (*request)(struct pinctrl_dev *pctldev, unsigned offset);    
      
    // 释放之前请求的引脚或引脚组  
    int (*free)(struct pinctrl_dev *pctldev, unsigned offset);    
      
    // 获取引脚控制器支持的功能数量  
    int (*get_functions_count)(struct pinctrl_dev *pctldev);    
      
    // 根据选择器获取功能名称  
    const char *(*get_function_name)(struct pinctrl_dev *pctldev, unsigned selector);    
      
    // 根据选择器获取功能对应的引脚组  
    int (*get_function_groups)(struct pinctrl_dev *pctldev, unsigned selector, const char * const **groups, unsigned *num_groups);    
      
    // 设置引脚复用功能(选择功能和引脚组)  
    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);    
      
    // 禁用并释放GPIO范围内的特定引脚  
    void (*gpio_disable_free)(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset);    
      
    // 设置GPIO引脚的方向(输入或输出)  
    int (*gpio_set_direction)(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset, bool input);    
      
    // 指示是否严格遵循某些规则(如:是否要求必须先请求再设置等)  
    bool strict;    
};
/*引脚配置操作函数*/
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)(struct pinctrl_dev *pctldev, unsigned selector, unsigned long *config);    
      
    // 设置引脚组的多个配置  
    int (*pin_config_group_set)(struct pinctrl_dev *pctldev, unsigned selector, unsigned long *configs, unsigned num_configs);    
      
    // 解析并修改引脚配置的调试参数  
    int (*pin_config_dbg_parse_modify)(struct pinctrl_dev *pctldev, const char *arg, unsigned long *config);    
      
    // 打印单个引脚配置的调试信息  
    void (*pin_config_dbg_show)(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset);    
      
    // 打印引脚组配置的调试信息  
    void (*pin_config_group_dbg_show)(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned selector);    
      
    // 打印特定配置值的调试信息  
    void (*pin_config_config_dbg_show)(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned long config);    
};

104.3 rockchip_pinctrl 分析

        在 pinctrl 的初始化过程中,rockchip_pinctrl 结构体通过一系列函数调用与 pinctrl_dev 关联。

        rockchip_pinctrl_register() 函数将rockchip_pinctrl 结构体作为参数传入。

        经过函数调用链,rockchip_pinctrl 最终作为私有数据driver_data传入pinctrl_register() 函数。

        在 pinctrl_init_controller() 函数中,driver_data 被赋值给 pctldev->driver_data,从而建立了 pinctrl_dev 和 rockchip_pinctrl 之间的关联。 

接着,解析设备树中的引脚控制器信息:

        rockchip_pinctrl_parse_dt() 函数解析设备树,

        首先计算功能数量(functions)组数量(groups)

        为功能和组分配内存空间。

        遍历每个功能节点,调用 rockchip_pinctrl_parse_functions 函数解析功能信息。

        rockchip_pinctrl_parse_functions() 函数初始化功能结构体,并为每个功能分配组指针数组。

        遍历功能节点的每个子节点(引脚组),

        调用 rockchip_pinctrl_parse_groups() 函数解析组信息,将这些信息存储在相应的结构体中。

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

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

相关文章

旅游心动盲盒:开启个性化旅行新体验

嘿&#xff0c;宝子们&#xff01;在如今这个数字化时代呀&#xff0c;文心智能体可是给咱们的生活带来了超多便利和创新呢。今天呀&#xff0c;我来给大家介绍一款超棒的智能体——旅游心动盲盒&#xff0c;它肯定能给你的旅行带来全新的惊喜和超个性化的体验哟。 一、项目背…

基于H3C环境的实验——OSPF

目录 实验设备和环境 实验设备 实验环境 实验记录 1、单区域 OSPF基本配置 步骤1:搭建实验环境并完成基本配置 步骤2:检查网络连通性和路由器路由表。 步骤3:配置OSPF 步骤4:检查路由器OSPF邻居状态及路由表 实验设备和环境 实验设备 三台路由器、两台PC、电源线、两…

GO网络编程(四):海量用户通信系统2:登录功能核心【重难点】

目录 一、C/S详细通信流程图二、消息类型定义与json标签1. 消息类型定义2. JSON标签3.结构体示例及其 JSON 表示&#xff1a;4.完整代码与使用说明 三、客户端发送消息1. 连接到服务器2. 准备发送消息3. 创建 LoginMes 并序列化4. 将序列化后的数据嵌入消息结构5. 序列化整个 M…

java 数据存储方式

1. 变量存储 这是最基本的数据存储方式&#xff0c;通过声明变量来存储数据。变量可以是基本数据类型&#xff08;如int、float、char等&#xff09;&#xff0c;也可以是引用数据类型&#xff08;如对象、数组等&#xff09;。变量存储的数据通常存储在内存中&#xff0c;随着…

有状态(Session) VS 无状态(Token)

目录 概念 JWT Token在项目中使用 概念 有状态和无状态服务是两种不同的服务架构&#xff0c;两者的不同之处在于对于服务状态的处理。 1、有状态服务 是指程序在执行过程中生成的中间数据&#xff0c;服务器端一般都要保存请求的相关信息&#xff0c;每个请求可以默认地使…

Hugging Face 任意大模型仓库劫持 - 无声的破坏

摘要 在这篇博客中&#xff0c;我们展示了攻击者如何攻击Hugging Face的Safetensors转换空间及其相关的服务机器人。这些服务是一个在Hugging Face上广受欢迎的服务&#xff0c;专门用于将不安全的机器学习模型转换为更安全的版本。我们随后展示了如何通过Hugging Face自身的服…

C0016.Clion中qDebug()打印输出中文时,都是问号??????的解决办法

问题描述 在clion中使用qDebug打印输出中文内容时&#xff0c;都是&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;如下图&#xff1a; 注意&#xff1a;修改该文件的编码格式就行&#xff0c;该文件名为apr.cpp&#xff1b; 解决办法

矩阵求解复数(aniwoth求解串扰)

所以这种求解串扰的格式是因为&#xff0c;有串扰的共轭项在方程组中 复数共轭项的作用&#xff0c;但是这是二次方程&#xff0c;

vue2集成vuex实现网站统一数据管理

文章目录 前言安装配置过程1、安装vuex依赖2、在src目录下创建store文件夹&#xff0c;创建模块site.jsgetters.jsindex.js 3、在man.js中添加vuex vuex实战&#xff1a;存储与获取网站基础数据何时去存储数据&#xff1f;&#xff08;路由前置获取数据&#xff09;如何取数据&…

windows中下载、安装、配置JDK/JDK环境配置/Java配置环境变量/Linux中安装配置JDK环境

JDK下载(官网)、安装、配置(包括系统、idea、eclipse)一篇就够了 1、问题概述? Java开发者必须掌握的JDK下载、安装、配置过程。 包括在Eclipse及IDEA中的配置使用 2、下载JDK 【注册Oracle官网账号】 下载的前天是注册orcle官网账号,作为开发者,这个必须有,随时关注…

RTX4060安装nvidia显卡驱动

文章目录 nvidia drivers下载删除原有nvidia驱动安装nvidia驱动如果报错Unable to find the kernel source tree for the currently runningbuilding kernel modules解决方法 报错成功安装!!! nvidia drivers下载 https://www.nvidia.cn/geforce/drivers/#:~:textNVIDIA%20GeF…

ESP01 AT指令学习

一 、AT指令 测试指令&#xff1a;ATCWMODE? 参数及取值范围 cwmode&#xff08;1-3&#xff09; 查询指令&#xff1a; ATCWMODE&#xff1f; 当前cwmode的取值 3 设置指令&#xff1a; ATCWMODE3 设置当前的cwmode为 3 1、station 模式 连接到其他wifi 2、softA…

【源码+文档】基于SpringBoot+Vue的酒店管理系统

&#x1f6a9;如何选题&#xff1f; 如何选题、让题目的难度在可控范围&#xff0c;以及如何在选题过程以及整个毕设过程中如何与老师沟通&#xff0c;这些问题是需要大家在选题前需要考虑的&#xff0c;具体的方法我会在文末详细为你解答。 &#x1f6ad;如何快速熟悉一个项目…

如何从 Windows 照片库恢复已删除的照片

数据丢失的主要原因之一是人为错误。更糟糕的是&#xff0c;回收站中没有备份或删除的文件。在这种情况下&#xff0c;数据恢复或适用于 Windows 的专用图片恢复工具可以为您提供帮助&#xff0c;因为它们可以帮助恢复已删除的图片。 牢记这一点&#xff0c;我们将讨论从 Wind…

基于SSM的电影院订票系统设计与实现

文未可获取一份本项目的java源码和数据库参考。 开展本课题的意义及工作内容&#xff1a; 1.意义 系统管理也都将通过计算机进行整体智能化操作&#xff0c;对于电影院订票系统系统所牵扯的管理及数据保存都是非常多的&#xff0c;例如电影信息管理、订单管理等&#xff0c;…

04-SpringBootWeb案例(中)

3. 员工管理 完成了部门管理的功能开发之后&#xff0c;我们进入到下一环节员工管理功能的开发。 基于以上原型&#xff0c;我们可以把员工管理功能分为&#xff1a; 分页查询&#xff08;今天完成&#xff09;带条件的分页查询&#xff08;今天完成&#xff09;删除员工&am…

压摆率(Slew Rate)

1. 定义 压摆率&#xff08;Slew Rate&#xff09;也叫转换速率&#xff0c;是指运算放大器输出电压的最大变化速率&#xff0c;单位通常为伏特每微秒&#xff08;V/s&#xff09;。 如果输入信号的变化速率超过运算放大器的压摆率&#xff0c;输出信号将不能即时跟随输入信号…

多线程编程实例

代码&#xff1a; #include<stdio.h> #include<pthread.h> static int run-1; static int retvalue; void *start_routine(void *arg) {int *running arg;printf("child Thread initial over,pass in parameters:%d\n",*running);while(*running){print…

Prometheus与Grafana的完美结合:打造强大的监控与可视化平台

目录 一、Prometheus简介 1.1、Prometheus基本介绍 1.2、Prometheus监控原理 1.3、Prometheus 的局限性 二、部署Prometheus 2.1、使用apt或者yum安装 2.2、基于官方提供的二进制文件安装 2.3、基于docker镜像直接启动或通过docker-compose编排 2.4、基于Operator部署在Kuberne…

使用TensorBoard可视化模型

目录 TensorBoard简介 神经网络模型 可视化 轮次-损失曲线 轮次-准确率曲线 轮次-学习率曲线 迭代-评估准确率曲线 迭代-评估损失曲线 TensorBoard简介 TensorBoard是一款出色的交互式的模型可视化工具。安装TensorFlow时,会自动安装TensorBoard。如图: TensorFlow可…