Pinctrl子系统_04_Pinctrl子系统主要数据结构

2024-03-09 14:52

本文主要是介绍Pinctrl子系统_04_Pinctrl子系统主要数据结构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

引言

本节说明Pinctrl子系统中主要的数据结构,对这些数据结构有所了解,也就是对Pinctrl子系统有所了解了。

前面说过,要使用Pinctrl子系统,就需要去配置设备树。

以内核面向对象的思想,设备树可以分为两部分,一部分(左边)用来描述Controller,另一部分(右边)则是描述使用引脚(使用controller)的device

对于controller的部分,内核会抽象出一个 pinctrl_dev 结构体;对于 device 的部分,内核会抽象出一个 device 结构体,device 结构体中会有 pinctrl 方面的成员。

显然,这个 pinctrl 方面的成员肯定会和左边的 pinctrl_dev 结构体产生联系。

那么,它们之间是什么样的关系呢?

答:在我们了解完Pinctrl子系统的数据结构之后,他们之间的关系也就清晰了。

首先,在了解Pinctrl子系统的数据结构前,先回忆一下Pinctrl子系统的三大作用

  1. 引脚枚举与命名(Enumerating and naming ):这个pinctrl支持哪些引脚,这些引脚叫什么名字;
  2. 引脚复用(Multiplexing ):用作什么功能,比如用作GPIO、I2C或其他功能;
  3. 引脚配置(Configuration):配置具体的引脚属性,比如上拉、下拉、open drain、驱动强度等;

记住这三大作用,就可以比较形象的去理解相关的结构体了。

那么,在Pinctrl子系统中,是怎么去实现这三大作用的呢?

pinctrl_desc和pinctrl_dev

我们刚刚说,controller 的部分,内核会抽象出一个 pinctrl_dev 结构体,但是事实上我们并不需要自己构造出这个 pinctrl_dev 结构体,而是使用内核提供的注册接口(pinctrl_register函数),我们只需要提供一个pinctrl_desc,然后调用这个接口,接口的返回值就是一个指向 pinctrl_dev 结构体的指针

通过 pinctrl_devpinctrl_desc 这两个结构体,就可以描述一个 pincontroller

controller相关结构体说明

imx6ull 为例,来了解pinctrl子系统中,controller 主要的数据结构,看看代码中是如何通过这些数据结构,实现 pinctrl 的三大作用的。

以下是 imx6ull 的pinctrl节点:

在设备启动后,这个节点会被转换成一个平台设备,和对应的平台驱动匹配完成后,就会调用对应的 probe 函数。

probe函数的大致流程

根据 compatible 属性值("fsl,imx6ul-iomuxc"),可以找到对应的驱动文件是 pinctrl-imx6ul.c,对应的 probe 函数是 imx6ul_pinctrl_probe,imx6ul_pinctrl_probe中则会调用 imx_pinctrl_probe函数。

imx_pinctrl_probe 函数中,会定义一个结构体指针 imx_pinctrl_desc,指向一个 pinctrl_desc 结构体。

在之后的代码中,首先申请一段内存,用来保存 pinctrl_desc,然后填充 pinctrl_desc 结构体,最后调用 devm_pinctrl_register,进行注册。

三大作用的具体实现

上面是probe函数中相关操作的大致流程,下面来具体说一下,在 Pinctrl 子系统中,是怎么去实现上面说的三大作用(引脚枚举与命名,引脚复用,引脚配置)的。

实现的关键就在于 pinctrl_desc 结构体,下面依次说明。

引脚的枚举与命名

首先看第一个功能,引脚的枚举与命名。

需要注意一下,引脚的枚举与命名分为两种情况

  1. 单个引脚 的枚举与命名;
  2. 多个引脚 的枚举与命名;
单个引脚

单个引脚的枚举和命名,主要是通过 pinctrl_desc 结构体的 pins 成员和 npins 成员来实现的。

其中,pins 成员是一个结构体指针,指向一个 pinctrl_pin_desc 结构体,主要负责引脚的枚举与命名;而 npins 成员则是一个无符号的整型数据,用来记录引脚的总个数

在 probe 函数中,会对 pins 和 npins 赋值,大致流程如下:

其中,imx6ul_pinctrl_info 变量的类型是 imx_pinctrl_soc_info 结构体,他也有 pins 和 npins 成员:

可以看到,他的 pins 成员指向了一个 imx6ul_pinctrl_pads 变量,这个变量是一个结构体数组,我们稍后再说。

综上,相当于执行了:

imx_pinctrl_desc->pins = imx6ull_snvs_pinctrl_pads;imx_pinctrl_desc->npins = ARRAY_SIZE(imx6ull_snvs_pinctrl_pads);

下面,看一下 imx6ul_pinctrl_pads 变量,他是一个结构体数组。通过对 IMX_PINCTRL_PIN 宏的分析,可以看到,这里主要定义了两个成员:

  1. number(第几个引脚,引脚的枚举)
  2. name (引脚的名字,引脚的命名)

 

总结一下,单个引脚的枚举与命名,主要的相关结构体是:

  1. pinctrl_pin_desc结构体;

多个引脚

上面说 pins 和 npins,他们是描述单个引脚。而在实际使用中,有时候会需要同时操作多个引脚group),比如i2c中我们要用到一组引脚,要如何同时操作多个引脚呢?

答:这个时候就要用到 pinctrl_ops 结构体了:

可以看到, pinctrl_ops结构体的成员全部都是函数指针,它们的功能如下:

struct pinctrl_ops {/* 返回已注册的group数*  - struct pinctrl_dev *pctldev:引脚控制设备结构体指针,表示引脚控制器设备。*/int (*get_groups_count) (struct pinctrl_dev *pctldev);/* 返回指定group的名字*  - struct pinctrl_dev *pctldev:引脚控制设备结构体指针,表示引脚控制器设备。*  - unsigned selector:group选择器,表示选择哪个group。*/const char *(*get_group_name) (struct pinctrl_dev *pctldev,unsigned selector);/* 返回指定group的引脚数组,并在num_pins中返回数组大小。*  - struct pinctrl_dev *pctldev:引脚控制设备结构体指针,表示引脚控制器设备。*  - unsigned selector:group选择器,表示选择哪个group。*  - const unsigned **pins:指向存储引脚数组的指针的指针。*  - unsigned *num_pins:指向存储引脚数组大小的变量的指针。*/int (*get_group_pins) (struct pinctrl_dev *pctldev,unsigned selector,const unsigned **pins,unsigned *num_pins);/* 可选的debugfs钩子函数,用于在debugfs中为特定引脚提供每个设备的信息。*  - struct pinctrl_dev *pctldev:引脚控制设备结构体指针,表示引脚控制器设备。*  - struct seq_file *s:序列文件结构体指针,用于在debugfs中显示信息。*  - unsigned offset:偏移量,表示特定引脚的偏移量。*/void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,unsigned offset);/* 解析设备树中的“引脚配置节点”,并为其创建映射表条目。这些通过`map`和`num_maps`输出参数返回。此函数是可选的,对于不支持设备树的引脚控制驱动程序可以省略。*  - struct pinctrl_dev *pctldev:引脚控制设备结构体指针,表示引脚控制器设备。*  - struct device_node *np_config:设备树中的引脚配置节点。*  - struct pinctrl_map **map:指向映射表指针的指针,用于返回映射表条目。*  - unsigned *num_maps:指向存储映射表条目数量的变量的指针。*/int (*dt_node_to_map) (struct pinctrl_dev *pctldev,struct device_node *np_config,struct pinctrl_map **map, unsigned *num_maps);/* 释放通过`dt_node_to_map`创建的映射表条目。必须释放顶层`map`指针,以及映射表条目本身的任何动态分配成员。此函数是可选的,对于不支持设备树的引脚控制驱动程序可以省略。*  - struct pinctrl_dev *pctldev:引脚控制设备结构体指针,表示引脚控制器设备。*  - struct pinctrl_map *map:映射表指针,需要释放。*  - unsigned num_maps:映射表条目数量。*/void (*dt_free_map) (struct pinctrl_dev *pctldev,struct pinctrl_map *map, unsigned num_maps);
};

这里说明一下,pinctrl_ops结构体成员中,有一个很关键的函数指针 dt_node_to_map,他是用来处理设备树的,我们以后再说,这里先点一下。

所以,对于多个引脚(group),相关的结构体是:

  1. pinctrl_ops 结构体;

引脚复用

类似的,引脚的复用也是由一个结构体来实现:pinmux_ops

pinmux_ops 就是 "Pin Multiplexing Operations"的缩写,表示引脚复用操作。

可以看到,结构体内部主要也是一堆函数指针

其中,引脚的复用主要是通过 set_mux 成员来实现的,他也是一个函数指针:

这里目前只对set_mux成员做说明,后面如果有用到其他函数指针,到时候再补充。

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);/* 启用特定的muxing功能与特定的group。驱动程序无需确定启用此功能是否与该组中pin的其他用途冲突,这种冲突由pinmux子系统处理。*  - struct pinctrl_dev *pctldev: 指向pinctrl设备结构的指针,用于表示设置mux的pinctrl设备。*  - unsigned func_selector: 无符号整数,表示选择什么功能。*  - unsigned group_selector: 无符号整数,表示选择哪个group。*/int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector,unsigned group_selector);int (*gpio_request_enable) (struct pinctrl_dev *pctldev,struct pinctrl_gpio_range *range,unsigned offset);void (*gpio_disable_free) (struct pinctrl_dev *pctldev,struct pinctrl_gpio_range *range,unsigned offset);int (*gpio_set_direction) (struct pinctrl_dev *pctldev,struct pinctrl_gpio_range *range,unsigned offset,bool input);bool strict;
};

引脚配置

我们还可以将一个或一组引脚,设置成不同的配置,比如上拉,下拉,open drain(开漏)等等。

这是怎么操作的呢?

答:同样是通过一个结构体:pinconf_ops

pinconf_ops 就是"Pin Configuration Options"的缩写,表示引脚的配置操作。

可以看到,结构体内部主要还是一堆函数指针。

这里主要说明以下四个函数指针:

  1. pin_config_get:获取某个pin的配置;
  2. pin_config_set:设置某个pin的配置;
  3. pin_config_group_get:获取某个group的配置;
  4. pin_config_group_set:设置某个group的配置;

注册pinctrl_dev

填充完 pinctrl_desc 结构体之后,调用 devm_pinctrl_register pinctrl_register,就可以根据 pinctrl_desc 构造出 pinctrl_dev,并且把 pinctrl_dev 放入链表

devm_pinctrl_registerpinctrl_registerstruct pinctrl_dev *pctldev;pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);pctldev->owner = pctldesc->owner;pctldev->desc = pctldesc;pctldev->driver_data = driver_data;/* check core ops for sanity */ret = pinctrl_check_ops(pctldev);/* If we're implementing pinmuxing, check the ops for sanity */ret = pinmux_check_ops(pctldev);/* If we're implementing pinconfig, check the ops for sanity */ret = pinconf_check_ops(pctldev);/* Register all the pins */ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);list_add_tail(&pctldev->node, &pinctrldev_list);

总结

综上,对于controller,涉及的结构体主要有5个:

  1. pinctrl_desc:用于描述一个特定的引脚控制器(pinctrl)的配置信息,包括该控制器管理的引脚数量、引脚的功能、引脚的默认状态等。
  2. pinctrl_dev:代表一个具体的引脚控制器设备,它与`pinctrl_desc`结构体相关联,用于在系统中表示和管理一个特定的引脚控制器。
  3. pinctrl_ops :定义了对引脚控制器进行操作的一组函数指针,包括引脚的配置、引脚的状态读取、引脚的状态设置等操作。
  4. pinmux_ops:定义了对引脚复用(pin multiplexing)进行操作的一组函数指针,用于配置引脚的不同功能模式,例如将一个引脚配置为GPIO模式或者特定外设的模式。
  5. pinconf_ops:定义了对引脚配置(pin configuration)进行操作的一组函数指针,用于设置和获取引脚的一些特定属性,例如引脚的电压、上下拉设置等。

device相关结构体说明

上面主要是对左边controller相关的结构体说明,下面来讨论一下右边device相关的结构体。

dev_pin_info 结构体

在设备树中,使用pinctrl时,device节点格式如下:

/* For a client device requiring named states */
device {pinctrl-names = "active", "idle";pinctrl-0 = <&state_0_node_a>;pinctrl-1 = <&state_1_node_a &state_1_node_b>;
};

设备节点要么被转换为 platform_device,要么转换为其他结构体(比如i2c_client),但是里面都会有一个 device 结构体。在 device 结构体中会有一个 pins 成员,这个 pins 成员是一个结构体指针,指向一个 dev_pin_info 结构体。

dev_pin_info 结构体保存的就是这个 device 的 pinctrl 信息

下面是 dev_pin_info 结构体的定义:

可以看到,主要定义了:

  1. 一个指向 pinctrl 结构体的结构体指针p;
  2. 4个指向 pinctrl_state 结构体的结构体指针 default_state,init_state,sleep_state,idle_state。

结合device节点,分析理解一下 dev_pin_info 结构体,以下面的device节点为例:

右边的device节点中,定义了两种状态:

  1. 状态0:default(对应 controller 节点 state_0_node_a) ;
  2. 状态1:sleep(对应 controller 节点 state_1_node_a 和 state_1_node_b)。

那么,就会用这些节点,来构造 dev_pin_info 结构体中的 default_state sleep_state

可以看到,dev_pin_info 结构体中已经定义了 4 个pinctrl_state指针,如果要添加我们自定义的state,要怎么记录呢?

答:dev_pin_info 结构体中有一个 pinctrl 结构体,我们自定义的 state 就存放在这个 pinctrl 结构体中。

假设要添加一个自定义的state,名字叫做“plane”,意为飞行模式,那么节点会变成这样:

pinctrl 结构体中有一个 states 成员,这个成员就会以链表的形式保存所有state,根据状态的序号,依次分别是default,sleep,plane。

并且 dev_pin_info 结构体中原先就有定义的 default_state 和 sleep_state,他们也会指向 states 成员中保存的 default 和 sleep 状态信息。

综上,对应device节点,最重要的结构体当属 pinctrl_state 结构体。

当设备进入某种状态时,就要把引脚配置成对应的 state。

那么,我们如何构造 pinctrl_state 呢?

如何构造pinctrl_state?

以下图为例,device 节点中的 pinctrl-0(default状态) 使用的是 state_0_node_a 节点,那么自然就要根据 state_0_node_a 节点来构造出 default_state。

那么,怎么根据pinctrl节点构造state呢?

答:需要用到 pinctrl_ops 结构体中的 dt_node_to_map 成员了。

dt_node_to_map 就是 "device tree node ==》map",顾名思义,就是将设备树的节点转换成一系列的map结构体(即pinctrl_map结构体)的意思。

通过 dt_node_to_map,将 pinctrl 节点转换为 pinctrl_map,再由 pinctrl_map 转换为 pinctrl_setting,最后,pinctrl_setting 会被存入 pinctrl_state 中的 settings 链表。

 

那么,pinctrl_map pinctrl_setting 中,需要保存哪些信息呢?

答:对于 pinctrl 节点,他主要有两个作用:

  1. pin mux,引脚复用;
  2. pin cfg,引脚配置;

那么,显然,pinctrl_map pinctrl_setting 就需要将这两个信息(引脚的复用信息,引脚的配置信息)保存记录下来。

首先,看一下 pinctrl_map 结构体:

可以看到,pinctrl_map 内部有一个联合体(union)data,这个联合体有两个成员:

  1. pinctrl_map_mux,记录复用信息;
  2. pinctrl_map_configs,记录配置信息;

所以,pinctrl_map 既可以记录引脚的复用信息,也可以记录引脚的配置信息;

刚刚说了,pinctrl_map 还会转换出 pinctrl_setting,来看一下 pinctrl_setting 结构体:

可以看到,他也有一个联合体(union) data,并且这个data也有两个成员:

  1. pinctrl_setting_mux,记录复用信息;
  2. pinctrl_setting_configs,记录配置信息;

所以,与 pinctrl_map 一样,pinctrl_setting 也是既可以记录引脚的复用信息,也可以记录引脚的配置信息。

对比 pinctrl_map pinctrl_setting,可以发现两者高度类似:都可以保存引脚的复用信息,配置信息。

综上,我们知道了, 驱动程序会把 pinctrl子节点 转换成一系列(为什么说一系列?因为一个pinctrl子节点可能包含多个引脚)的 pinctrl_map pinctrl_setting 结构体,在 pinctrl_map pinctrl_setting 结构体中会保存引脚的配置信息,复用信息

并且 pinctrl_setting 结构体还会被存入 pinctrl_state,以后我们选择让这个设备进入某种状态时,就会根据这些setting,来设置那些引脚,选择引脚的功能,配置引脚的上下拉,驱动强度等等。

 

使用pinctr_setting

最后,我们来看一下 pinctrl_state 中的这一系列 setting,是如何被调用的,又是如何去配置引脚的

这主要会涉及 pinmux_ops 结构体中的 set_mux(设置复用)和 pinconf_ops 结构体中的 pin_config_set(设置引脚配置),pin_config_group_set(设置group配置)。

调用的流程如下:

really_probepinctrl_bind_pinspinctrl_select_state/* Apply all the settings for the new state */list_for_each_entry(setting, &state->settings, node) {switch (setting->type) {/* 引脚复用 */case PIN_MAP_TYPE_MUX_GROUP:ret = pinmux_enable_setting(setting);ret = ops->set_mux(...);break;/* 引脚配置:单引脚,多引脚 */case PIN_MAP_TYPE_CONFIGS_PIN:case PIN_MAP_TYPE_CONFIGS_GROUP:ret = pinconf_apply_setting(setting);switch (setting->type) {case PIN_MAP_TYPE_CONFIGS_PIN:ret = ops->pin_config_set(...);break;case PIN_MAP_TYPE_CONFIGS_GROUP:ret = ops->pin_config_group_set(...);break;default:return -EINVAL;}break;default:ret = -EINVAL;break;}		

这样,左右两边的结构体(controller和device)就产生了联系,当设备进入某种状态时,就可以将引脚设置为对应的配置。

当我们对上述的结构体都有了初步的了解之后,后面就可以开始进行更深入的分析了。

以上就是本节全部内容。

这篇关于Pinctrl子系统_04_Pinctrl子系统主要数据结构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/791013

相关文章

便携式气象仪器的主要特点

TH-BQX9】便携式气象仪器,也称为便携式气象仪或便携式自动气象站,是一款高度集成、低功耗、可快速安装、便于野外监测使用的高精度自动气象观测设备。以下是关于便携式气象仪器的详细介绍:   主要特点   高精度与多功能:便携式气象仪器能够采集多种气象参数,包括但不限于风速、风向、温度、湿度、气压等,部分高级型号还能监测雨量和辐射等。数据采集与存储:配备微电脑气象数据采集仪,具有实时时钟、数据存

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

《数据结构(C语言版)第二版》第八章-排序(8.3-交换排序、8.4-选择排序)

8.3 交换排序 8.3.1 冒泡排序 【算法特点】 (1) 稳定排序。 (2) 可用于链式存储结构。 (3) 移动记录次数较多,算法平均时间性能比直接插入排序差。当初始记录无序,n较大时, 此算法不宜采用。 #include <stdio.h>#include <stdlib.h>#define MAXSIZE 26typedef int KeyType;typedef char In

取得 Git 仓库 —— Git 学习笔记 04

取得 Git 仓库 —— Git 学习笔记 04 我认为, Git 的学习分为两大块:一是工作区、索引、本地版本库之间的交互;二是本地版本库和远程版本库之间的交互。第一块是基础,第二块是难点。 下面,我们就围绕着第一部分内容来学习,先不考虑远程仓库,只考虑本地仓库。 怎样取得项目的 Git 仓库? 有两种取得 Git 项目仓库的方法。第一种是在本地创建一个新的仓库,第二种是把其他地方的某个

分布式系统的主要考虑

异构性:分布式系统由于基于不同的网路、操作系统、计算机硬件和编程语言来构造,必须要考虑一种通用的网络通讯协议来屏蔽异构系统之间的禅意。一般交由中间件来处理这些差异。缺乏全球时钟:在程序需要协作时,它们通过交换消息来协调它们的动作。紧密的协调经常依赖于对程序动作发生时间的共识,但是,实际上网络上计算机同步时钟的准确性受到极大的限制,即没有一个正确时间的全局概念。这是通过网络发送消息作为唯一的通信方式

【408数据结构】散列 (哈希)知识点集合复习考点题目

苏泽  “弃工从研”的路上很孤独,于是我记下了些许笔记相伴,希望能够帮助到大家    知识点 1. 散列查找 散列查找是一种高效的查找方法,它通过散列函数将关键字映射到数组的一个位置,从而实现快速查找。这种方法的时间复杂度平均为(

浙大数据结构:树的定义与操作

四种遍历 #include<iostream>#include<queue>using namespace std;typedef struct treenode *BinTree;typedef BinTree position;typedef int ElementType;struct treenode{ElementType data;BinTree left;BinTre

Python 内置的一些数据结构

文章目录 1. 列表 (List)2. 元组 (Tuple)3. 字典 (Dictionary)4. 集合 (Set)5. 字符串 (String) Python 提供了几种内置的数据结构来存储和操作数据,每种都有其独特的特点和用途。下面是一些常用的数据结构及其简要说明: 1. 列表 (List) 列表是一种可变的有序集合,可以存放任意类型的数据。列表中的元素可以通过索