基于Linux的Pinctrl子系统框架分析

文章目录

    • 1、前言
    • 2、我理解的驱动框架
    • 3、设备资源结构体——struct imx_pinctrl_soc_info
      • 3.1、struct device *dev
      • 3.2、const struct pinctrl_pin_desc *pins
      • 3.3、imx_pin_group、ngroups、imx_pmx_func、nfunctions、imx_pin_reg
    • 4、设备驱动结构体——struct pinctrl_dev
      • 4.1、引脚控制器——struct pinctrl
          • 4.1.1、struct list_head node
          • 4.1.2、struct list_head dt_maps
            • 1、最小的引脚映射单元——struct pinctrl_map
            • 2、group映射单元——struct pinctrl_maps
        • 3、struct pinctrl_dt_map
          • 4.1.3、struct list_head states
            • 1、引脚控制器状态——struct pinctrl_state
            • 2、真正的引脚配置信息——struct pinctrl_setting
          • 4.1.4、struct pinctrl_state *state
      • 4.2、struct pinctrl_desc
      • 4.3、其它的一些变量
    • 5、最终的BOSS——struct imx_pinctrl
    • 6、最后


1、前言

  最近看了很多驱动框架源码,学到了很多东西。在这过程中查阅了很多资料,耗费了很多精力,为了让后来的学习者少走一部分弯路,于是我写下这份博客,希望能给大家提供一点参考和帮助。其实在写这篇博客之前,我一直在纠结写还是不写,因为在知乎上看到有人说正是因为大部分人发了一些没有技术含量的东西,以及一些误导性的内容,导致整个博客环境很差。所以我在纠结,我这样的一个初学者,写的东西是否能给大家带来帮助,甚至害怕误导了大家。毕竟linux驱动这么庞大知识框架,对于我这样一个初学者很难把全部东西掌握。但后来,我还是决定写下这份博客。我个人觉得知乎上那些人说的对,但是有个问题就是真正大佬级别人物写的博客相比整个博客数量占比太少,以至于更多人都是查阅很多份资料,然后取其精华去其糟泊,最后融合成自己的东西。所以,我希望我的博客可以给这些人提供这样的一些有用的东西。

2、我理解的驱动框架

  在我看来,所谓的驱动框架,就是建立一个个结构体的过程。只不过这个结构体很复杂,里面包含了设备各种信息,操作方法,然后结构体里面还包含各种各样的变量、指针、结构体,以及一些链表链接。特别是如果有些结构体不知道其具体含义和定义出来后的目的,那么自然源码分析下去也会很困难。我觉得在学习驱动框架有两个很重要的东西,一个是框架的分析,一个是源码的分析。所以,在分析源码之前,我想说下这个驱动框架的结构体组成结构和里面一些重要结构体的意义。在驱动框架中,一般都有这两部分结构体,一是用来描述设备资源的结构体,二是描述设备驱动的结构体。设备资源结构体一般从设备树中获取或者自己去填充一些信息,这部分是比较自由的,就是写程序时,会根据自己的使用场景去修改配置。而设备驱动的结构体,基本都是框架定死的,里面的一部分成员是根据设备资源的结构体进行填充,另一部分是框架源码中自己去实现填充。一般来说,只要我们把设备资源的结构体填充好,设备驱动的结构体基本都是框架自己的事情,不需要我们自己去关心。

3、设备资源结构体——struct imx_pinctrl_soc_info

基于Linux的Pinctrl子系统框架分析_第1张图片

图1 struct imx_pinctrl_soc_info结构体

  上图是struct imx_pinctrl_soc_info结构体中的具体成员,里面的语法可能会有很多错误,但是大家看了应该能懂其中的含义。我这样写的目的就是为了以后看到这张图能一下子明白其中的结构是什么样子。至于注释是给我自己看的,可能有些东西只能我自己理解,你们就先当没有这些注释。接下来我会解释里面每个成员的设计意义和作用。

3.1、struct device *dev

  该结构体用来指向一个设备,在图1中,我注释写的是指iomuxc这个设备,但是这并不完全正确,因为除了iomuxc这个设备外,还有一个iomuxc-snvs设备,这两个设备节点中都定义了相关引脚的配置信息。如图2所示的设备树匹配表,这两个compatible都会在设备树中有相应的匹配,其实可以看出Pinctrl子系统注册了两个引脚控制器。所以这里我以分析iomuxc节点为主,关于iomuxc-snvs节点分析同理。
在这里插入图片描述

图2 Pinctrl子系统的设备树匹配表

3.2、const struct pinctrl_pin_desc *pins

  讲这个之前我先说下关于引脚信息和引脚配置信息这两个概念。引脚信息指的是imx6ul_pinctrl_pads这个数组中定义引脚的引脚号和引脚名,而引脚配置信息指的是在设备树iomuxc节点下的引脚配置,如配置寄存器值、输入寄存器值等配置信息。iomuxc节点下定义的引脚是imx6ul_pinctrl_pads这个数组中定义的引脚的子集。而const struct pinctrl_pin_desc *pins这个结构体就是用来专门描述引脚信息的结构体。看结构体名,pinctrl_pin_desc,也就是引脚控制器中的引脚的描述,那么引脚的描述哪来呢?这个就需要根据不同的芯片自己来定义,在imx6ull中,这个结构体就指向了imx6ul_pinctrl_pads这个结构体,imx6ul_pinctrl_pads这个结构体包含了imx6ull的所有引脚信息。如图3所示。
基于Linux的Pinctrl子系统框架分析_第2张图片

图3 imx6ul_pinctrl_pads结构体数组

  只截取了部分,至于里面的IMX_PINCTRL_PIN宏定义干了啥,就是为了填充imx6ul_pinctrl_pads这个结构体,而这个结构体里面有什么呢,看成员就知道是引脚号和引脚名。于是,引脚信息就完成了定义,这样就可以通过访问该结构体就知道该引脚控制器可以控制的引脚范围,最后会被GPIO子系统调用。该结构体中引脚,在后面将会挂接到一个叫基数树的数据结构体中,该数据结构可以通过仅仅4步操作就可以找到相应的引脚。相比一个一个遍历该数组的方式,采用基数树这种遍历方式,速度会快上很多。毕竟该数组还是挺大的。

3.3、imx_pin_group、ngroups、imx_pmx_func、nfunctions、imx_pin_reg

  这5个变量需要一起讲,因为它们都涉及到了同一个东西。前面讲了引脚信息,而现在就是讲引脚的配置信息是如何构建的。引脚配置信息相比引脚信息,就显得比较复杂,因为里面涉及了设备树的解析,以及一些组织结构上的理解。对于组织结构,Pinctrl子系统并没有直接按照设备树的引脚配置信息一条一条按顺序解析下来,而是将设备树中iomuxc中的配置引脚按功能、按组来划分,即分成了funciton组和group组。举个例子,IIC、SPI这一类就是一个funciton,代表了一组引脚的功能,但是一个芯片中,能实现IIC、SPI功能的引脚肯定不止一对,假如能实现IIC功能的引脚有两对,则就是两个group,一个group就表示这一对IIC涉及到的引脚配置。

IIC{
	pinctrl_i2c1: i2c1grp {
			fsl,pins = <
				MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
				MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
			>;
		};
		pinctrl_i2c2: i2c2grp {
			fsl,pins = <
				MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0
				MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0
			>;
		};
}

这里的IIC就是一个funciton,而i2c1grp和i2c2grp就是一个group。有人肯定想问这样安排组织结构有什么用呢?其实这就像大学的学院和专业的划分,一个学院就代表一个funciton,学院内的每个专业就代表一个group,group里面的成员就是每个专业里面的学生。正是大学这样的安排,让学校能更好的管理学生。不然各个学院和各个专业的学生混在一起,哪怕是排课都要费很大的精力。其实这里就可以将芯片的引脚类比为不同学院不同专业的学生。在Pinctrl子系统中,采用了struct imx_pin_group *groups、unsigned int ngroups和struct imx_pmx_func *functions、unsigned int nfunctions这四个类型描述了function和group。ngroups和nfunctions就不用说了,它俩描述了group组数和function组数。关键是在imx_pin_group和imx_pmx_func这两个结构体。

  先来看imx_pmx_func结构体。

struct imx_pmx_func *functions;			//用来记录iomuxc节点下的所有function组
{
    const char *name;					//function组名
    const char **groups;				//该function组下的每个group组名
    unsigned num_groups;				//该function组下的group组数
};

​ 看了这个结构体成员和注释,就很清楚了,它是用来记录iomuxc节点下所有的funtion组名和每个function组下的每个group组名以及group组数。再精简点,它就是来记录名字的。不过需要注意的点就是,这是一个指针,到时是用来指向一个function数组的,并不单纯只能存储一个数据,里面的groups同理,是个二重指针,因为它要记录function组下每个group的名字。类比大学就是,记录每个学院名字和每个学院里面每个专业的名字。

  再看imx_pin_group结构体。

struct imx_pin_group *groups;				
{
    const char *name;					//该group组名
    unsigned npins;						//该group下的引脚数量
    unsigned int *pin_ids;				//该group下的引脚id号
    struct imx_pin *pins;				//记录group组中的每个引脚配置信息
    {
        unsigned int pin;				//根据复用寄存器偏移地址计算出引脚号		
        unsigned int mux_mode;			 //复用寄存器值
        u16 input_reg;					//输入寄存器偏移地址
        unsigned int input_val;			 //输入寄存器值
        unsigned long config;			 //配置寄存器值
    };
};

  观察这个结构体成员,首当其中就是记录组名、组下的引脚数和引脚号。然后就是imx_pin这个结构体,看其成员,就知道是引脚的具体配置信息了。但是如果观察仔细,发现里面并没有复用寄存器地址和配置寄存器地址,其实有一个专门的结构体类型来描述这两个变量。

struct imx_pin_reg *pin_regs;  
{
	s16 mux_reg;							//复用寄存器地址
	s16 conf_reg;							//配置寄存器地址	
};

  至此,可以发现struct imx_pinctrl_soc_info结构体完成了对整个芯片引脚的描述和设备树中定义的各个引脚配置的描述。至于,程序中是如何完成对设备树的解析以及整个结构体的填充,我们就先别管,因为目前我们需要先对整个整体框架有所了解。等搞懂整个框架后,看源码就是一件很容易的事情。另外还有这些信息究竟会被用来干什么,现在就先别管,后面会讲,现在就只需要明白struct imx_pinctrl_soc_info结构体记录了当前的设备资源信息。

4、设备驱动结构体——struct pinctrl_dev

基于Linux的Pinctrl子系统框架分析_第3张图片基于Linux的Pinctrl子系统框架分析_第4张图片

基于Linux的Pinctrl子系统框架分析_第5张图片

基于Linux的Pinctrl子系统框架分析_第6张图片

图4 设备资源结构体

  可以看出这个结构体十分的复杂,关于注释,以及语法这些,就不要管了,这是我个人记忆框架的笔记风格。如果这个结构体叫做大BOSS的话,那么这里面的struct pinctrl结构体就叫做小BOSS。所以,我先来讲下这个结构体中的成员。

4.1、引脚控制器——struct pinctrl

  在没有深入分析Pinctrl子系统之前我一直认为引脚控制器指的是struct pinctrl_dev,并且以为引脚控制器就只有一个。后面我才发现引脚控制器有很多个。在设备树中含有pinctrl-names和pinctrl-0属性的节点都会拥有一个struct pinctrl结构体。引脚控制器,顾名思义,就是用来实现引脚的控制和配置。而设备节点中pinctrl-names和pinctrl-0属性中的内容恰恰就是我们要去控制的引脚。有时候我们会遇到多个pinctrl-names和pinctrl-0属性,如下面这段代码:

	pinctrl-names = "default", "state_100mhz", "state_200mhz";
	pinctrl-0 = <&pinctrl_usdhc1>;
	pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
	pinctrl-2 = <&pinctrl_usdhc1_200mhz>;

  这段代码中定义多个pinctrl-names和pinctrl-0属性,但是这些都能用一个struct pinctrl结构体表示。在曾经,我以为不同的属性名中代表了不同引脚的不同配置,后来我发现他们代表的是同一组引脚的不同配置。比如工作状态下与低功耗下的引脚配置都是针对同一组引脚,但是配置方式不同。但是,别搞混了,同一个pinctrl-%d属性中定义的多个引脚节点,是对应同一个pinctrl-names中的名字。其实我更想把pinctrl-names中定义名字叫做引脚的状态,至于原因后面会说。还有一点需要注意的是,pinctrl-%d属性中定义的引脚节点,就是前面我们说的group概念。

4.1.1、struct list_head node

  如果熟悉linux中的链表链接方式的朋友,就知道这个结构体的作用是什么了。该节点将会被链接到pinctrl_list全局链表上。这样以后就可以通过pinctrl_list这个链表遍历全部的引脚控制器。这里其实也能看出,引脚控制器不止一个。

4.1.2、struct list_head dt_maps

  虽然这只是一个及其简单的链表,但是这个链表头链接出来的东西,确是一个很复杂东西。这个链表专门用来链接struct pinctrl_dt_map结构体。这个结构体完成了设备树中定义的引脚节点信息的映射,说直白点,就是将设备树中定义的引脚节点信息用一个结构体来描述。可能会有人疑问,在前面的3中讲到struct imx_pinctrl_soc_info结构体不就完成了这样的一个类似功能么?我的理解就是,可能3.3中组织的结构体方式不适合在struct pinctrl结构体中进行描述。尽管这两个结构体都描述了设备树中定义的引脚节点信息,但是各自的用途是不一样的。如果仔细观察struct pinctrl_dt_map结构体,可以发现这个结构体映射的是一个group。但是,一个group中的一个引脚的映射又是用的什么结构体呢?那就是struct pinctrl_map 结构体。

1、最小的引脚映射单元——struct pinctrl_map

  struct pinctrl_map 结构体被称为最小的映射单元,即这个结构体中包含了一个引脚的所有配置信息。该结构体成员如下:

基于Linux的Pinctrl子系统框架分析_第7张图片

图5 pinctrl_map结构体成员

该结构体中首先需要注意const char *name这个成员,该成员记录的就是pinctrl-names中的属性名,也就是表明该引脚是出于一个什么状态,将来如何区分什么引脚处于什么状态,不同状态下又是什么引脚配置,就是通过该变量实现。然后是enum pinctrl_map_type type枚举类型,该枚举类型主要是用来判断该映射是什么类型。为什么会有映射类型的区分呢?这是因为在实际的引脚寄存器配置过程中,Pinctrl子系统将其分成了两部分,一部分是引脚的复用,另一部分是引脚的配置。所以,在源码中可以发现有pinconf.c和pinmux.c文件专门实现引脚的配置和复用。最后就是data共同体,学过C语言的自然明白共同体的基本性质。也就是说一个struct pinctrl_map 结构体就代表了一个引脚映射,但是一个struct pinctrl_map 结构体真的能代表一个完整的引脚映射吗?其实并不能。看这个共同体就明白,一个引脚的完整映射需要引脚复用的映射和引脚配置的映射,然而共同体的性质表明,同一时间只能拥有复用映射和配置映射的其中一个。那么就是说,需要两个struct pinctrl_map 结构体,一个结构体代表复用映射,一个结构体代表配置映射,这样就可以代表一个引脚的完整映射。这样可以吗?当然可以。但是这样何必多此一举,将两个结构体设计成一个不就得了。之所以没有这样,这是因为前面讲到的group概念,尽管struct pinctrl_map 结构体是最小的引脚映射单元,但是当我们去配置引脚时,是以group为单元的。比如我们配置一个IIC接口,肯定是需要同时配置几个引脚。于是就有了struct pinctrl_maps 结构体。该结构体看其名字,就知道该结构体能存放多个struct pinctrl_map结构体。

2、group映射单元——struct pinctrl_maps

  struct pinctrl_maps 结构体存放了一个group中所有引脚的配置信息。该结构体成员如下:

基于Linux的Pinctrl子系统框架分析_第8张图片

图6 pinctrl_maps结构成员

该结构体就很简单明了,通过struct pinctrl_map const *maps结构体指针指向了一个struct pinctrl_map结构体数组。每个struct pinctrl_map结构体就代表了一组引脚配置信息。但是这里需要注意以下这个结构体数组的排列细节,在结构体数组中的第一个元素,它的映射类型为PIN_MAP_TYPE_MUX_GROUP,即复用映射,而后面的所有元素,映射类型都为PIN_MAP_TYPE_CONFIGS_PIN,即配置映射。另外,可以看到我的注释,一个pinctrl_maps对应一个pinctrl-%d中的一个节点,比如 pinctrl-0 = <&pinctrl_gpio_leds&pinctrl_beep>;这里面就有两个引脚节点,即需要两个pinctrl_maps来表示。最后就是这里面又包含一个链表结构体,然后将会被链接到pinctrl_maps总链表上。这个链表很重要,后面就是通过遍历该链表,将该设备节点下的pinctrl_maps转换为pinctrl_setting结构体。记住啊,我说的是该设备节点下的!pinctrl_maps这个链表链接的是所有设备节点下的引脚节点。而后面说到的pinctrl_setting结构体和struct pinctrl_dt_map结构体,里面的链表结构体只会链接本设备节点下的引脚节点。

3、struct pinctrl_dt_map

  现在终于又回到了struct pinctrl_dt_map这个结构体,该结构体我已经在图4截图出来了,因为该结构体太长,所以这里我就不再截图了。该结构体可以发现几乎跟struct pinctrl_maps 结构体一样。这里又回到当初问过的一个类似问题,为什么明明有了struct pinctrl_maps结构体描述引脚配置信息,又要构建一个与之几乎相同的struct pinctrl_dt_map这个结构体来。首先看struct pinctrl_maps 结构体,我前面说了,该结构体将会被pinctrl_maps总链表链接,且pinctrl_maps总链表连接了所有设备节点下的引脚节点。但是对于一个struct pinctrl结构体来说,我只想拥有的是该引脚控制器下有的引脚节点。所以这里就创建了一个新的结构体struct pinctrl_dt_map,然后将设备节点下的所有struct pinctrl_dt_map链接到struct pinctrl结构体中的struct list_head dt_maps成员。另外再想,有没有发现,驱动框架中,总是喜欢先构建一个描述信息的结构体,如struct pinctrl_maps ,然后利用该结构体去完成驱动结构体中成员的构建,如struct list_head dt_maps。

4.1.3、struct list_head states

  这个成员跟struct list_head dt_maps一样,看似简单,但是链接出来的结构体成员同样复杂。该链表专门用来链接struct pinctrl_state结构体。在4.1中我就讲过我更想把pinctrl-names中定义名字叫做引脚的状态,而pinctrl-names中定义状态就是靠struct pinctrl_state结构体描述。

1、引脚控制器状态——struct pinctrl_state

  struct pinctrl_state结构体成员如下:

基于Linux的Pinctrl子系统框架分析_第9张图片

图7 pinctrl_state结构体成员

可以看到该结构有着struct list_head node用来实现链接,有着const char *name来表明该struct pinctrl_state结构体处于什么状态。从我写的注释中也可以看出pinctrl-names中定义了几种状态,就会有几个struct pinctrl_state结构体。该结构体有什么呢?看其成员struct pinctrl_setting就明白,该成员包含了该设备节点下同一个状态下的所有引脚配置信息。这样的话,假如我要引脚处于工作状态,我就可以通过遍历struct list_head states 找到处于工作状态的struct pinctrl_state结构体,然后将其struct pinctrl_setting结构体进行解析,就能完成对引脚的配置。如果我要引脚处于休眠状态,那么我就可以找到处于处于休眠状态的struct pinctrl_state结构体。

2、真正的引脚配置信息——struct pinctrl_setting

  接下来就是struct pinctrl_state结构体中的struct pinctrl_setting结构体了。该结构体描述了引脚的配置信息,并且我在标题上写上了真正两个字,这是因为,最终实现引脚的配置和复用利用的就是这个结构体。该结构体跟struct pinctrl_map结构体几乎类似,我就不说其中每个成员含义了。只需要知道这个结构体是通过遍历pinctrl_maps总链表来完成对自身的构建,而不是通过struct pinctrl_dt_map。另外,这里我想讲的一个细节是,在struct list_head states 中和struct list_head dt_maps中都链接了引脚配置信息相关的结构体,而这两者有什么区别,又为什么要这么设计呢?区别:首先看struct list_head dt_maps,该结构体链接的引脚配置信息,是以pinctrl-%d属性中的节点为单位,即pinctrl-%d属性中含有几个引脚节点,就有几个pinctrl_dt_map类型结构体。再看struct list_head states ,该结构体链接的引脚配置信息,是以pinctrl-names属性中的状态为单位,即pinctrl-names属性中有几个状态就有几个struct pinctrl_state结构体。原因:看下面一段代码:

pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc1        &pinctrl_usdhc2>;
pinctrl-1 = <&pinctrl_usdhc1_100mhz &pinctrl_usdhc2_100mhz>;
pinctrl-2 = <&pinctrl_usdhc1_200mhz &pinctrl_usdhc1_200mhz>;

当我们以状态为单位时,将引脚设置为 "default"状态,我们取出的引脚节点为&pinctrl_usdhc1和&pinctrl_usdhc2。而我们以pinctrl-%d属性中的节点为单位,我们则可以获取到这里面的每一个引脚节点。以状态为单位,是为了我们更加方便的设置引脚的状态,而以节点为单位,则是为了我们更好的去遍历该设备节点中每一个引脚节点。

4.1.4、struct pinctrl_state *state

  4.1.3中讲了struct list_head states 链表链接了该引脚控制器下所拥有的状态,而struct pinctrl_state *state就是用来记录此时引脚控制器正在使用其中哪一种状态。

​ 至此struct pinctrl结构体就全部讲完了。

4.2、struct pinctrl_desc

  该结构体看名字,就大概能猜出它是引脚控制器设备驱动的描述符,用来记录struct pinctrl_dev这个大BOSS的信息以及进行访问的操作集。该结构体成员如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2PIZ67bh-1668081926273)(C:\Users\LENOVO\AppData\Roaming\Typora\typora-user-images\image-20221110163047044.png)]

图8 pinctrl_desc结构体成员

该结构体中的struct pinctrl_pin_desc const *pins和unsigned int npins成员已经在struct imx_pinctrl_soc_info 结构体讲过。这里主要看imx_pmx_op、pinmux_ops、imx_pinconf_ops这3个操作集,imx_pmx_ops操作集看其结构体类型pinmux_ops就能明白是用来完成引脚的复用,而同理,imx_pinconf_ops操作集用来完成对引脚的配置,而imx_pctrl_ops操作集则是提供一些辅助操作,比如获取function和group的数量或者名字等功能。引脚控制器能实现对引脚的配置,靠的就是这3个操作集。

4.3、其它的一些变量

  这里我就将struct pinctrl_dev结构体中的其他成员放这里一起讲了。先看struct pinctrl_state *hog_default和struct pinctrl_state *hog_sleep这两个成员,至于这两个成员的变量类型已经说过,用来记录一个状态下的引脚信息。这里想给大家理一下思路,别搞混了。我前面说过每个设备节点下含有pinctrl-names和pinctrl-0属性的节点都会拥有一个struct pinctrl结构体,但iomuxc这个节点有些特殊,它不仅含有了pinctrl-names和pinctrl-0属性,并且包含了将会可能被用到所有引脚配置节点。其他设备节点pinctrl-0属性中引用的节点都是来自iomuxc设备节点中所定义的。所以对于iomuxc节点来说,多了struct pinctrl_state *hog_default和struct pinctrl_state *hog_sleep这两个成员,用来记录该设备节点下处于工作和睡眠下的两种状态。这样就不必用遍历struct list_head states 链表就能直接获取到直接想要的状态。

  struct radix_tree_root pin_desc_tree结构体在3.2中就提到一些了,它会将imx6ul_pinctrl_pads这个数组里面的引脚挂接在基数树这样的数据结构上,这样就可以很快的遍历到要搜索的引脚。其实引脚挂接到基数树的过程,也是一个引脚注册的过程。

  struct list_head node ,前面很早说过,除了iomuxc这个设备外,还有一个iomuxc-snvs设备,这个链表结构就是来实现这两者的链接的。它们都被链接到pinctrldev_list总链表上。这就有一个问题了,在其它的设备节点中的struct pinctrl结构体是如何找到对应的struct pinctrl_dev结构体。找到这个结构体很重要,因为该结构体中的struct pinctrl_desc成员含有3个重要的操作集。并且现在有着iomuxc和iomuxc-snvs两个设备,怎么能找到引脚节点相对应的struct pinctrl_dev结构体呢?其实就是通过pinctrl-0属性中的引脚节点找到它的父节点,即iomuxc节点,然后将此节点与pinctrldev_list总链表上的struct pinctrl_dev结构体的dev成员的of_node成员进行匹配,这样就能找到该引脚节点属于哪个pinctrl_dev。最后将找到的pinctrl_dev保存在struct pinctrl_dt_map结构体中的struct pinctrl_dev *pctldev成员中,并且将pinctrl_dev的设备名保存在struct pinctrl_map 结构体中的const char *ctrl_dev_name成员中,最后struct pinctrl_setting结构体中的struct pinctrl_dev *pctldev成员就是通过struct pinctrl_map 结构体中的const char *ctrl_dev_name成员获取到pinctrldev。

  最后就还剩一个void *driver_data ,其实我一直都藏了一手,我说pinctrl_dev这个结构体就是最终的大BOSS,其实不是,而是struct imx_pinctrl *ipctl。

5、最终的BOSS——struct imx_pinctrl

  该结构体之所以留到现在讲,我是怕让大家感到很乱(可能现在已经很乱了),另外就是,struct pinctrl_dev结构体是始终还是占大头。之所以会有struct imx_pinctrl结构体出现,就是表明这是一个适配I.MX系列的引脚控制器设备驱动。该结构体中除了含有struct pinctrl_dev结构体外,还有着I.MX系列特有的一些信息,比如IO控制器寄存器首地址,以及最前面讲的struct imx_pinctrl_soc_info。该结构体的具体成员如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XFmarGzZ-1668081926274)(C:\Users\LENOVO\AppData\Roaming\Typora\typora-user-images\image-20221110194431175.png)]

图9 imx_pinctrl 结构体成员

看其struct pinctrl_dev *pctl成员,通过pinctrl_register函数完成了pinctrl的注册,即构建一个struct pinctrl_dev结构体。同时该结构体有着iomuxc设备节点内存起始虚拟地址和imx_pinctrl_soc_info这个IMX独有的设备资源结构体。

6、最后

  总算是讲完了,感觉认真写一篇博客真的是一件很费精力的事情,越写到后面,思维逻辑就越乱,希望能给看到我这边篇博客的朋友带来一点帮助吧。后面还打算写关于Input子系统,USART,IIC和SPI等驱动框架的东西,但是有点害怕自己写的不好,给大家带来误导。所以看情况吧,如果后面有人支持加上时间充裕,我会加油写下去。

你可能感兴趣的:(linux,mcu,c语言)