Pinctrl 子系统简介
Posted Li-Yongjun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
>;
;
一个器件使用 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
设备树中定义了这个引脚的所有复用功能,你需要使用哪一种,只要配置到设备树中就行了,从这个角度看,貌似比单片机开发更简单便捷(一个引脚的所有复用功能,都帮你枚举出来了,你只要挑选使用哪一种就行了,不需要自己拿着数据手册去计算了,如果你不放心,可以和数据手册对照一下。所以 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
>;
;
...
设备树中的惯例:使用 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,
;
当设备树和驱动匹配成功后,就会执行驱动中的 probe 函数,在这里就是 imx6ul_pinctrl_probe
,在该函数中会完成对设备树的解析,并将设备树中的信息配置到芯片的寄存器中。
以上是关于Pinctrl 子系统简介的主要内容,如果未能解决你的问题,请参考以下文章
Linux——Linux驱动之设备树中pinctrl和gpio子系统应用实践(如何使用其在设备树中配置GPIO,驱动中如何调用?)
Linux——Linux驱动之设备树中pinctrl和gpio子系统应用实践(如何使用其在设备树中配置GPIO,驱动中如何调用?)