初识imx6ull的pinctrl子系统

本篇文章是根据Linux-4.9.88内核进行分析的(imx6ull)。

使用过pinctrl的同学们都知道pinctrl系统分成了两个部分:

  • pinctrl的驱动程序,也就是用来分析内核的设备树,并将设备树转换为一系列数据结构。
  • client程序,是用户编写,使用内核设备树所提供的资源。

所以,这篇文章首先会先介绍描述pinctrl涉及的一些数据结构、pinctrl如何将设备树与数据结构挂钩、client程序如何使用内核提供的设备树资源。

首先要明确的是,pinctrl的三大作用:

  • 引脚的枚举与命名
  • 引脚复用
  • 引脚配置

一、pinctrl系统涉及到的数据结构

1、pinctrl_dev

初识imx6ull的pinctrl子系统_第1张图片

使用pinctrl_dev结构体来描述一个pincontroller,其中包含了重要的结构体pinctrl_desc,这个结构体是重点。

而在内核中,我们无需再去构造一个pinctrl_dev结构体,而是构造pinctrl_desc结构体,之后使用内核提供的函数pinctrl_register,便可以获得一个pinctrl_dev结构。

struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data);

2、pinctrl_desc

初识imx6ull的pinctrl子系统_第2张图片

这个函数中有三个重要结构体

  • pinctrl_ops:用来操作引脚。取出某组的引脚、处理设备树中的某个节点。
  • pinmux_ops:用来负责引脚的复用。
  • pinconf_ops:用来负责引脚的配置。
  • pinctrl_pin_desc:用来描述其中的一个引脚。

3、dev_pin_info

站在client的角度,我们知道,用户提供的设备树最终会被转换为一个platform_device或者是一个固定的结构设备,如i2c_client。

初识imx6ull的pinctrl子系统_第3张图片

也就是说,描述设备的结构体,总会存在一个struct device结构,其中包含有struct dev_pin_info结构,来表述设备树。

初识imx6ull的pinctrl子系统_第4张图片

这个结构主要是站在用户的角度,因为在用户编写的设备树中,会描述几个状态,来对应不同的引脚配置,比如:

初识imx6ull的pinctrl子系统_第5张图片

4、client设备树数据结构的关系

假设芯片上有多个pin controller,那么这个设备使用哪个pin controller?

这需要通过设备树来确定:

  • 分析设备树,找到pin controller

  • 对于每个状态,比如default、init,去分析pin controller中的设备树节点

    • 使用pin controller的pinctrl_ops.dt_node_to_map来处理设备树的pinctrl节点信息,得到一系列的pinctrl_map

    • 这些pinctrl_map放在pinctrl.dt_maps链表中

    • 每个pinctrl_map都被转换为pinctrl_setting,放在对应的pinctrl_state.settings链表中

初识imx6ull的pinctrl子系统_第6张图片

设备引用pin controller中的某个节点时,这个节点会被转换为一些列的pinctrl_map:

  • 转换为多少个pinctrl_map,完全由具体的驱动决定

  • 每个pinctrl_map,又被转换为一个pinctrl_setting

  • 举例,设备节点里有:pinctrl-0 = <&state_0_node_a>

    • pinctrl-0对应一个状态,会得到一个pinctrl_state

    • state_0_node_a节点被解析为一系列的pinctrl_map

    • 这一系列的pinctrl_map被转换为一系列的pinctrl_setting

    • 这些pinctrl_setting被放入pinctrl_state的settings链表

二、pinctrl驱动程序分析

drivers\pinctrl\freescale\pinctrl-imx6ul.c中是我们程序的入口。

初识imx6ull的pinctrl子系统_第7张图片

可以看出来imx_pinctrl_probe是一个通用的imx系统设备树的probe程序。

下面分析该函数

imx_pinctrl_probe:

初识imx6ull的pinctrl子系统_第8张图片

根据上述的结构体介绍可以知道,构造了一个pinctrl_desc结构,以及其中的引脚操作函数结构体,最后会调用imx_pinctrl_probe_dt函数,用来解析内核提供的设备树。调用devm_pinctrl_register函数将pinctrl_desc构造pinctrl_dev结构。

imx_pinctrl_probe_dt:

初识imx6ull的pinctrl子系统_第9张图片

将程序与设备树结合来看,其中

  • functions指的就是内核树中的"imx6ul-evk"
  • 画红色圈圈的是该functions下对应的组信息(后面还有很多...)
  • imx_pinctrl_parse_functions函数用来具体分析每个function下的每一个组了

imx_pinctrl_parse_functions:

之后解析设备树中的每一个组

imx_pinctrl_parse_groups:

初识imx6ull的pinctrl子系统_第10张图片

 

将每个组中配置信息保存起来,保存在了每一个map中,与上面的结构体对应了起来。

就这样,将内核设备树中信息一个个组的保存起来。

总结一下函数的调用过程:

imx6ul_pinctrl_probe
    imx_pinctrl_probe
        imx_pinctrl_desc->name = dev_name(&pdev->dev);
        imx_pinctrl_desc->pins = info->pins;
        imx_pinctrl_desc->npins = info->npins;
        imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
        imx_pinctrl_desc->pmxops = &imx_pmx_ops;
        imx_pinctrl_desc->confops = &imx_pinconf_ops;
        imx_pinctrl_desc->owner = THIS_MODULE;
           
        ret = imx_pinctrl_probe_dt(pdev, info);
            info->nfunctions = nfuncs;
            info->functions = devm_kzalloc...
            info->groups = devm_kzalloc...

            imx_pinctrl_parse_functions
                for_each_child_of_node(np, child) 
		            imx_pinctrl_parse_groups(child, grp, info, i++);
                        for (i = 0; i < grp->npins; i++) {
		                    if (info->flags & IMX8_USE_SCU)
			                    imx_pinctrl_parse_pin_scu(info, &grp->pin_ids[i],&grp->pins[i], list_p);
		                    else
			                    imx_pinctrl_parse_pin_mem(info, &grp->pin_ids[i],&grp->pins[i], list_p);
	                        }

        devm_pinctrl_register

三、client用户设备树调用分析

really_probe
	pinctrl_bind_pins
		dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
		
		dev->pins->p = devm_pinctrl_get(dev);
							pinctrl_get
								create_pinctrl(dev);
									ret = pinctrl_dt_to_map(p);
                                        dt_to_map_one_config
                                            ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);
									
                                    for_each_maps(maps_node, i, map) {
	                                    ret = add_setting(p, map);
                                    }
		
		dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,PINCTRL_STATE_DEFAULT);	

注意上面的调用过程,可以看到,

  • 首先是解析用户设备树
  • 调用内核引脚操作函数dt_node_to_map把每一个node转换为map结构
  • 与内核提供的内核设备树资源通过名字进行匹配
  • 再将每一个map结构,构造setting结构体。

函数返回至pinctrl_bind_pins后,

初识imx6ull的pinctrl子系统_第11张图片

初识imx6ull的pinctrl子系统_第12张图片

调用pinctrl_select_state函数,将引脚配置为default_state或者init_state状态,也即初始状态。

初识imx6ull的pinctrl子系统_第13张图片

根据是复用引脚还是配置引脚,调用对应的函数

最后就会操作到,内核提供的

各种函数结构,来处理复用或者配置的功能。

你可能感兴趣的:(Linux内核,linux,内核)