• Pinctrl 子系统简介


    活动地址:CSDN21天学习挑战赛

    Linux 驱动开发

    在 Linux 下开发驱动,精髓就两个字:框架
    同样一个功能,在单片机中简简单单就实现了。而在 Linux 下绕来绕去。
    比方说 I2C 驱动,
    在单片机中,实现 I2C_Start()、I2C_Stop()、I2C_Send_Byte()、I2C_Read_Byte() 等几个函数,就能够操作 I2C,底层就基本完成了。上层再根据具体器件手册,读写若干寄存器,整个功能就实现了。
    而到了 Linux 里面,什么平台、总线、控制器、控制器驱动、设备、设备驱动等等,光是理解这些概念就要耗费很大功夫,甚至超过 I2C 驱动本身的工作量。
    那 Linux 是傻吗?避简就繁?答案是否定的,至少 linux 驱动代码质量应该是信得过的。那 Linux 为啥要搞那么复杂?我认为主要原因是通用性和扩展性。
    用单片机写好一份设备驱动后,一般写的时候就认为就只在当前单片机上使用,就不太会考虑兼容性,硬件也是确定的,也不怎么去考虑扩展性。
    但 Linux 下就不一样了,那么多人共同使用 Linux,相同的驱动代码肯定要复用,要减少冗余,增加通用性。兼容绝大多数场景,扩展性就要好。所以 Linux 设计了一套框架,当开发驱动时,就在这套框架下完成具体的配置、实现具体的函数功能接口就行了。核心思想是使用框架,通俗来讲就是按照 Linux 驱动的套路开发。

    Pinctrl 子系统

    Pinctrl 子系统就是上述框架的一种。
    在单片机中,使用一个 GPIO,一般先设置引脚的复用模式、上下拉等,然后再进行具体的引脚读写操作。
    在 Linux 驱动中,将上述步骤分成了两个子系统,
    Pinctrl 子系统:

    获取设备树中 pin 信息
    根据获取到的 pin 信息设置 pin 的复用功能
    根据获取到的 pin 信息来设置 pin 的电气特性,比如上下拉、速度、驱动能力等。

    GPIO 子系统:

    控制 GPIO 输入、输出,也就是使用 GPIO

    Linux 通篇都是这种驱动分层、分离的思想。
    不管怎样,我们既然做 Linux 驱动开发,就要尝试理解其意图,说不定就尝到其甜头了呢。

    Pinctrl 设备树

    先看个例子

    pinctrl_enet2: enet2grp {
       fsl,pins = <
        MX6UL_PAD_GPIO1_IO07__ENET2_MDC  0x1b0b0
        MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0
        MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0
        MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0
        MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0
        MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0
        MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0
        MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0
        MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0
        MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031
       >;
      };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    一个器件使用 pinctrl 描述了自己所使用的全部引脚。拿其中一个引脚(GPIO1_IO07)举例,

    #define MX6UL_PAD_GPIO1_IO07__ENET1_MDC                           0x0078 0x0304 0x0000 0x0 0x0
    #define MX6UL_PAD_GPIO1_IO07__ENET2_MDC                           0x0078 0x0304 0x0000 0x1 0x0
    #define MX6UL_PAD_GPIO1_IO07__USB_OTG_HOST_MODE                   0x0078 0x0304 0x0000 0x2 0x0
    #define MX6UL_PAD_GPIO1_IO07__CSI_PIXCLK                          0x0078 0x0304 0x0528 0x3 0x0
    #define MX6UL_PAD_GPIO1_IO07__USDHC2_CD_B                         0x0078 0x0304 0x0674 0x4 0x1
    #define MX6UL_PAD_GPIO1_IO07__GPIO1_IO07                          0x0078 0x0304 0x0000 0x5 0x0
    #define MX6UL_PAD_GPIO1_IO07__CCM_STOP                            0x0078 0x0304 0x0000 0x6 0x0
    #define MX6UL_PAD_GPIO1_IO07__UART1_DCE_RTS                       0x0078 0x0304 0x0620 0x8 0x1
    #define MX6UL_PAD_GPIO1_IO07__UART1_DTE_CTS                       0x0078 0x0304 0x0000 0x8 0x0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    设备树中定义了这个引脚的所有复用功能,你需要使用哪一种,只要配置到设备树中就行了,从这个角度看,貌似比单片机开发更简单便捷(一个引脚的所有复用功能,都帮你枚举出来了,你只要挑选使用哪一种就行了,不需要自己拿着数据手册去计算了,如果你不放心,可以和数据手册对照一下。所以 Linux 驱动框架从一定程度上帮助开发人员阅读了一部分数据手册,减轻了开发人员的工作量,提高了编码质量)。
    所以,虽然 Linux 驱动很复杂,我们只要理解它就行,实际写代码时按照它的框架写就行了。由于框架的加持,往往能写出高内聚、低耦合、性能更好、扩展性更好的驱动。

    Pinctrl 驱动

    Pinctrl 子系统分为两个部分,设备树和驱动,上面介绍了设备树,下面就介绍下驱动。
    首先想到的是,驱动是如何使用设备树中的参数的。下面结合一个具体的例子看下,在设备树中会有一个 pin controller 节点,具体名字会有所不同,IMX6U 芯片中叫 iomuxc

    iomuxc: iomuxc@020e0000 {
    	compatible = "fsl,imx6ul-iomuxc";
    	reg = <0x020e0000 0x4000>;
    };
    
    &iomuxc {
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_hog_1>;
    	imx6ul-evk {
    		...
    		pinctrl_enet2: enet2grp {
    			fsl,pins = <
    				MX6UL_PAD_GPIO1_IO07__ENET2_MDC		0x1b0b0
    				MX6UL_PAD_GPIO1_IO06__ENET2_MDIO	0x1b0b0
    				MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN	0x1b0b0
    				MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER	0x1b0b0
    				MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00	0x1b0b0
    				MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01	0x1b0b0
    				MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN	0x1b0b0
    				MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00	0x1b0b0
    				MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01	0x1b0b0
    				MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2	0x4001b031
    			>;
    		};
    		...
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    设备树中的惯例:使用 compatible 属性值来匹配设备树和驱动,全局搜索 ““fsl,imx6ul-iomuxc””,就可以找到对应的驱动
    drivers/pinctrl/freescale/pinctrl-imx6ul.c

    static struct of_device_id imx6ul_pinctrl_of_match[] = {
    	{ .compatible = "fsl,imx6ul-iomuxc", .data = &imx6ul_pinctrl_info, }
    };
    
    static struct platform_driver imx6ul_pinctrl_driver = {
    	.driver = {
    		.name = "imx6ul-pinctrl",
    		.owner = THIS_MODULE,
    		.of_match_table = of_match_ptr(imx6ul_pinctrl_of_match),
    	},
    	.probe = imx6ul_pinctrl_probe,
    	.remove = imx_pinctrl_remove,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    当设备树和驱动匹配成功后,就会执行驱动中的 probe 函数,在这里就是 imx6ul_pinctrl_probe,在该函数中会完成对设备树的解析,并将设备树中的信息配置到芯片的寄存器中。

  • 相关阅读:
    保险丝的工作原理
    计算机通识——多媒体参数
    深度学习——Bounding Box预测
    IP证书怎么申请,如何实现加密保护
    第16章_多版本并发控制MVCC
    yarn安装详细教程说明、升级教程、修改yarn的全局和缓存目录、yarn基本命令
    zip4j压缩使用总结
    linux 安装mysql8.0 超详细教程(实战多次)
    卷积神经网络(CNN)实现服装图像分类
    闲聊vue版本差异和开发中不太容易注意的点(基础篇)
  • 原文地址:https://blog.csdn.net/lyndon_li/article/details/126203457