pinctrl子系统简介
linux驱动讲究驱动分离和分层,pinctrl和gpio子系统就是驱动分离和分层思想下的产物,驱动分离与分层其实就是按照面向对象编程的涉及思想而设计的设备驱动框架
Linux 内核针对 PIN 的配置推出了 pinctrl 子系统,对于 GPIO的配置推出了 gpio 子系统
pin的初始化包括:设置这个pin的复用功能、上下拉等
设置pin对应的gpio功能输入输出等
Pinctrl子系统主要工作内容如下:
①、获取设备树中 pin 信息。
②、根据获取到的 pin 信息来设置 pin 的复用功能
③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。
对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成, pinctrl 子系统源码目录为 drivers/pinctrl。
I.MX6ULL的pinctrl子系统驱动
1.PIN配置信息详解
要使用 pinctrl 子系统,我们需要在设备树里面设置 PIN 的配置信息,毕竟 pinctrl 子系统要根据你提供的信息来配置 PIN 功能,一般会在设备树里面创建一个节点来描述 PIN 的配置信息。打开 imx6ull.dtsi 文件,找到一个叫做 iomuxc 的节点,如下所示:
代码片1
756 iomuxc: iomuxc@020e0000 {
757 compatible = "fsl,imx6ul-iomuxc";
758 reg = <0x020e0000 0x4000>;
759 };
iomuxc 节点就是 I.MX6ULL 的 IOMUXC 外设对应的节点,在imx6ull-14x14-evk-emmc.dts中,有如下:
代码片2
311 &iomuxc {
312 pinctrl-names = "default";
313 pinctrl-0 = <&pinctrl_hog_1>;
314 imx6ul-evk {
315 pinctrl_hog_1: hoggrp-1 {
316 fsl,pins = <
317 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
318 MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059
319 MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059
320 MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058
321 >;
322 };
......
371 pinctrl_flexcan1: flexcan1grp{
372 fsl,pins = <
373 MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX 0x1b020
374 MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX 0x1b020
375 >;
376 };
......
587 pinctrl_wdog: wdoggrp {
588 fsl,pins = <
589 MX6UL_PAD_LCD_RESET__WDOG1_WDOG_ANY 0x30b0
590 >;
591 };
592 };
593 };
上述代码就是向iomuxc节点追加数据,不同的外设使用的PIN不同,其配置也不同,上述代码中的pinctrl_hog_1子节点就是和热拔插有关的PIN集合。如果需要在iomuxc中添加自己自定义的外设的PIN,那么需要新建一个子节点,然后将这个自定义外设的所有PIN配置信息都放到这个子节点中
757行:compatible属性值为“fsl,imx6ul-iomuxc”,linux内核会根据compatible属性值来查找对应的驱动文件。
317~320行:pinctrl_hog_1子节点所使用的PIN配置信息,以MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059为例,定义在imx6ul-pinfunc.h这个文件中
#define MX6UL_PAD_UART1_RTS_B__UART1_DCE_RTS 0x0090 0x031C 0x0620 0x0 0x3
#define MX6UL_PAD_UART1_RTS_B__UART1_DTE_CTS 0x0090 0x031C 0x0000 0x0 0x0
#define MX6UL_PAD_UART1_RTS_B__ENET1_TX_ER 0x0090 0x031C 0x0000 0x1 0x0
#define MX6UL_PAD_UART1_RTS_B__USDHC1_CD_B 0x0090 0x031C 0x0668 0x2 0x1
#define MX6UL_PAD_UART1_RTS_B__CSI_DATA05 0x0090 0x031C 0x04CC 0x3 0x1
#define MX6UL_PAD_UART1_RTS_B__ENET2_1588_EVENT1_OUT 0x0090 0x031C 0x0000 0x4 0x0
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
#define MX6UL_PAD_UART1_RTS_B__USDHC2_CD_B 0x0090 0x031C 0x0674 0x8 0x2
上述代码一共8个宏定义,对应《I.MX6ULL参考手册》可知刚好与上面的宏定义相对应
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
此宏定义的数字有如下定义:
< mux_reg conf_reg input_reg mux_mode input_val >
mux_reg: 0x0090,对应的是寄存器偏移地址,设备树中的iomuxc节点就是IOMUXC外设对应的节点,根据其reg属性可知IOMUXC外设寄存器起始地址为0x020e0000.因此0x020e0000+0x0090=0x020e0090,根据《I.MX6ULL参考手册》可得:
由此可得,x020e0000+mux_reg 就是 PIN 的复用寄存器地址。
conf_reg :0x031C conf_reg寄存器的偏移地址和mux_reg一样,0x020e0000+0x031c=0x020e031c,这个就是寄存器 IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B 的地址
input_reg: 0x0000 input_reg寄存器偏移地址,有些外设有 input_reg 寄存器,有 input_reg 寄存器的外设需要配置 input_reg 寄存器。没有的话就不需要设置, UART1_RTS_B 这个 PIN 在做GPIO1_IO19 的时候是没有 input_reg 寄存器,因此这里 intput_reg 是无效的。
mux_mode :0x5 mux_reg寄存器的值, 在这里就相当于设置IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B 寄存器为 0x5,也即是设置 UART1_RTS_B 这个 PIN 复用为 GPIO1_IO19。
input_val: 0x0: input_reg 寄存器值,在这里无效。
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
x17059 就是 conf_reg 寄存器值!此值由用户自行设置,通过此值来设置一个 IO 的上/下拉、驱动能力和速度等。在这里就相当于设置寄存器IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B 的值为 0x17059
2.设备树中添加pinctrl节点模块
这里我们虚拟一个名为“test”的设备, test 使用了 GPIO1_IO00 这个 PIN 的 GPIO 功能, pinctrl 节点添加过程如下:
- 创建对应的节点
同一个外设的PIN都放到一个节点里面,打开dts文件,在iomuxc节点中的“imx6ul-evk”子节点下添加pinctrl_test”节点,注意!节点前缀一定要为“pinctrl_”。pinctrl_test: testgrp { /*具体的PIN信息*/ }; - 添加“fsl,pins”属性
设备树是通过属性来保存信息的,因此我们需要添加一个属性,属性名字一定要为“fsl,pins”,因为对于 I.MX 系列 SOC 而言, pinctrl 驱动程序是通过读取“fsl,pins”属性值来获取 PIN 的配置信息,完成以后如下所示:
pinctrl_test: testgrp {
fsl,pin = <
/*设备所使用的PIN配置信息*/
>;
};
- 在“fsl,pins”属性中添加PIN配置信息
最后在“fsl,pins”属性中添加具体的 PIN 配置信息,完成以后如下所示:
pinctrl_test: testgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config /*config 是具体设置值*/
>;
};