Linux内核的pinctrl子系统驱动框架详解

目录

1 引入

2 设备树中的pinctrl

2.1 iomuxc节点

2.2 其他节点怎么使用pinctrl控制器

3 pin controller代码流程以及相关数据结构

4 device client代码流程以及相关数据结构

5 阅读内核源码时的相关疑问

6 费曼学习法:于是我自己录制了一个讲解pinctrl子系统的视频


1 引入

无论STM32还是IMX6ULL,如果我们想用某个引脚控制LED灯的亮灭,那么一般来说我们要对引脚进行上图所示的配置,在Linux内核中配置引脚的功能和电气属性是由pinctrl子系统来做的。pinctrl子系统主要有三个作用:

  • 功能1:引脚的枚举和命名(Enumerating and naming)
  • 功能2:引脚复用(Multiplexing)
  • 功能3:设置电气属性(Configuration)

另外,Linux内核中的pinctrl子系统相关代码,芯片厂家都已经实现好了,使用者不需要写代码,只需要修改设备树文件即可。 

2 设备树中的pinctrl

不同的厂家的pinctrl设备树格式不一样,这里以imx6ull平台为例,后面的代码阅读是以imx6ull提供的Linux4.9.88内核源码为例进行阅读讲解。

2.1 iomuxc节点

先看iomuxc节点

iomuxc: iomuxc@020e0000 {
    compatible = "fsl,imx6ul-iomuxc";
    reg = <0x020e0000 0x4000>;
    imx6ul-evk {
        pinctrl_hog_1: hoggrp-1 {
			fsl,pins = <
				MX6UL_PAD_UART1_RTS_B__GPIO1_IO19	0x17059 /* SD1 CD */
				MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT	0x17059 /* SD1 VSELECT */
				MX6UL_PAD_GPIO1_IO09__GPIO1_IO09        0x17059 /* SD1 RESET */
			>;
		};
        
        pinctrl_csi1: csi1grp {
			fsl,pins = <
				MX6UL_PAD_CSI_MCLK__CSI_MCLK		0x1b088
				MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK	0x1b088
                               ...没写全...
				MX6UL_PAD_CSI_DATA06__CSI_DATA08	0x1b088
				MX6UL_PAD_CSI_DATA07__CSI_DATA09	0x1b088
			>;
		};

        pinctrl_i2c1: i2c1grp {
			fsl,pins = <
				MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
				MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
			>;
		};
                    
    }
};

fsl,pins里面的信息就是寄存器的偏移地址和值,用来配置某个引脚的复用和电气属性的,比如以为例

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19    0x17059 /* SD1 CD */

其中MX6UL_PAD_UART1_RTS_B__GPIO1_IO19是一个宏定义,

#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19       0x0090 0x031c 0x0000 5 0

如果把宏定义替换一下,那么MX6UL_PAD_UART1_RTS_B__GPIO1_IO19    0x17059 /* SD1 CD */就是

0x0090 0x031c 0x0000 5 0  0x17059

这六个值分别是

mux_reg    conf_reg    input_reg    mux_mode    input_val    conf_reg 

根据这6个值就可以配置一个引脚的复用和电气属性了,

2.2 其他节点怎么使用pinctrl控制器

比如i2c1

&i2c1 {
    clock-frequency = <100000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c1>;
    status = "okay";

};

这样i2c1节点就使用了pinctrl提供的引脚配置。

到此为止其实pinctrl子系统就可以算讲完了,因为可以用了,后面的讲解是为了理解,然后阅读了Linux内核源码。

3 pin controller代码流程以及相关数据结构

我直接画了一个图用于讲解pin controller的代码流程和相关数据结构。

 首先设备树中的iomuxc节点会被转化成一个platform_device,然后根据compatible = "fsl,imx6ul-iomuxc"进行匹配时,imx6ul_pinctrl_probe函数会被调用,在这个函数中,先是取出了pinctrl_info = (struct imx_pinctrl_soc_info *) match->data;这里面其实就是pins的枚举,然后又调用了 imx_pinctrl_probe(pdev, pinctrl_info);函数,在这个函数中分配了一个pinctrl_desc结构体,

    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;

然后在这个结构体中,分别包含了pins,还有一些操作函数,然后又调用了imx_pinctrl_probe_dt(pdev, info);在这里面解析设备树得到了那些寄存器地址和寄存器值。然后调用ipctl->pctl = devm_pinctrl_register(&pdev->dev, imx_pinctrl_desc, ipctl);函数,这个函数里面构造了struct pinctrl_dev *pctldev;结构体,这个struct pinctrl_dev *pctldev;结构体里面就包含了pins,操作函数,还有寄存器值和寄存器地址等所有的信息。

4 device client代码流程以及相关数据结构

首先看一下device client的数据结构,我画了一个图表示

然后接下来看一下Linux内核源码,看看上面的这些数据结构他的构造过程,我也直接画了一个图。

 5 阅读内核源码时的相关疑问

 您好!您的理解是正确的。在这段代码中,platform_set_drvdata(pdev, ipctl); 调用了 platform_set_drvdata 函数,将 ipctl 与平台设备 pdev 关联起来。这样,当您在设备驱动程序中需要访问设备的私有数据时,您就可以使用 platform_get_drvdata 函数来获取 ipctl

ipctl 是一个指向 imx_pinctrl 结构体的指针,它包含了从设备树中解析出来的引脚寄存器信息。这些信息被保存在 ipctl->info->pin_regs 数组中,每个元素都是一个 imx_pin_reg 结构体,表示一个引脚的寄存器信息。

因此,在这段代码中,从设备树中解析出来的引脚寄存器信息被保存在了 pdev->dev.driver_data 中。希望这些信息对您有所帮助!

6 费曼学习法:于是我自己录制了一个讲解pinctrl子系统的视频

15分钟讲解Linux内核的pinctrl子系统_哔哩哔哩_bilibili 

猜你喜欢

转载自blog.csdn.net/u013171226/article/details/132431972