大多数SOC的PIN都是支持复用的,比如 I.MX6ULL 的 GPIO1_IO03 既可以作为普通的GPIO 使用,也可以作为 I2C1 的 SDA 等等。所以在配置时要考虑复用的设置,此外还要配置PIN的电气特性,比如上下拉、速度、驱动等。
pinctrl子系统的主要工作内容:
获取设备树中pin信息
根据获得到的pin信息来设置pin的复用功能
根据获得到的pin信息来设置pin的电气特性,比如上下拉、速度、驱动能力
对于我们使用者来说,只需要在设备树里面设置好某个pin的相关属性即可,其他的初始化工作均由pinctrl子系统来完成,pinctrl 子系统源码目录drivers/pinctrl。
下面我们来看一下pinctrl子系统在imx6ull的设备树中是如何实现并使用的。
先来看一下官方手册Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt中关于pinctrl的语法实例:
Examples:
usdhc@0219c000 { /* uSDHC4 */
non-removable;
vmmc-supply = <®_3p3v>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usdhc4_1>;
};
iomuxc@020e0000 {
compatible = "fsl,imx6q-iomuxc";
reg = <0x020e0000 0x4000>;
/* shared pinctrl settings */
usdhc4 {
pinctrl_usdhc4_1: usdhc4grp-1 {
fsl,pins = <
MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059
MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059
MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059
MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059
MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059
MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059
MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059
MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059
MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059
MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059
>;
};
....
};
在usdhc设备节点中引用了&pinctrl_usdhc4_1,而pinctrl_usdhc4_1定义在了iomuxc设备节点下。在官方手册中是以imx6q平台为例,我们看一下在topeet_emmc_4_3.dts文件中具体的定义:
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1
&pinctrl_fec1_reset>;
phy-mode = "rmii";
phy-handle = <ðphy0>;
phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;
phy-reset-duration = <200>;
status = "okay";
};
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
......
pinctrl_enet1: enet1grp {
fsl,pins = <
MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031
>;
};
......
};
在设备节点fec1下通过label引用了pinctrl_enet1,而pinctrl_enet1的定义在iomuxc设备节点下。下面来具体分析一下关于pin引脚的配置。
对于PIN的配置分为两个部分“MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0”
MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN这部分配置的是引脚的复用
0x1b0b0 这部分是对引脚的初始化,配置速度、上下拉、驱动能力等
MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN定义在arch/arm/boot/dts/imx6ul-pinfunc.h文件中,具体内容如下:
#ifndef __DTS_IMX6UL_PINFUNC_H
#define __DTS_IMX6UL_PINFUNC_H
#define MX6UL_PAD_BOOT_MODE0__GPIO5_IO10 0x0014 0x02A0 0x0000 0x5 0x0
#define MX6UL_PAD_BOOT_MODE1__GPIO5_IO11 0x0018 0x02A4 0x0000 0x5 0x0
#define MX6UL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x001C 0x02A8 0x0000 0x5 0x0
#define MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x0020 0x02AC 0x0000 0x5 0x0
#define MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x0024 0x02B0 0x0000 0x5 0x0
#define MX6UL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x0028 0x02B4 0x0000 0x5 0x0
#define MX6UL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x002C 0x02B8 0x0000 0x5 0x0
......
#define MX6UL_PAD_CSI_DATA06__USDHC1_RESET_B 0x01FC 0x0488 0x0000 0x8 0x0
#define MX6UL_PAD_CSI_DATA07__CSI_DATA09 0x0200 0x048C 0x04E8 0x0 0x1
#define MX6UL_PAD_CSI_DATA07__USDHC2_DATA7 0x0200 0x048C 0x0698 0x1 0x2
#define MX6UL_PAD_CSI_DATA07__SIM2_PORT1_TRXD 0x0200 0x048C 0x0000 0x2 0x0
#define MX6UL_PAD_CSI_DATA07__ECSPI1_MISO 0x0200 0x048C 0x0538 0x3 0x1
#define MX6UL_PAD_CSI_DATA07__EIM_AD07 0x0200 0x048C 0x0000 0x4 0x0
#define MX6UL_PAD_CSI_DATA07__GPIO4_IO28 0x0200 0x048C 0x0000 0x5 0x0
#define MX6UL_PAD_CSI_DATA07__SAI1_TX_DATA 0x0200 0x048C 0x0000 0x6 0x0
#define MX6UL_PAD_CSI_DATA07__USDHC1_VSELECT 0x0200 0x048C 0x0000 0x8 0x0
#endif /* __DTS_IMX6UL_PINFUNC_H */
imx6ul-pinfunc.h文件中的每一个宏定义是某一个引脚的某一个复用功能。
imx6ull实现IO复用的使用IOMUXC来实现的,通过查阅《I.MX6ULL参考手册》,我们看两个寄存器:
在SW_MUX_CTL_PAD_ENET1_RX_EN的MUX_MODE位即0~3位,定义了该引脚的8种复用功能。
HYS:迟滞比较器,IO作为输入功能时有效,用于设置输入接收器的施密特触发器是否使能,用于输入波形整型
PUS:设置上下拉电阻
PUE:上下拉还是状态保持,保持就是断电保持以前的状态
PKE:用来使能或者静止上下拉/状态保持器功能
ODE:禁止或使能开路输出
SPEED:速度
DSE:驱动能力
SRE:压摆率,就是IO电平跳变所需要的时间,要过EMC可以使用低压摆率,波形缓和要进行高速通信可以使用高压摆率。
我们以imx6ul-pinfunc.h文件中某个引脚的复用功能为例:
#define MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x00CC 0x0358 0x0000 0x0 0x0
这个引脚复用功能的宏定义后有5个值,这5个值得定义如下:
0x00CC:mux_reg复用寄存器的偏移地址,设备树中的 iomuxc 节点就是 IOMUXC 外设对应的节点,根据其 reg 属性可知 IOMUXC 外设寄存器起始地址为 0x020e0000 。因此 0x020e0000+0x00CC=0x020e00CC,SW_MUX_CTL_PAD_ENET1_RX_EN 寄存器地址正好是0x020e00CC ,如图 1.4所示:
因此可知,0x020e0000+mux_reg 就是 PIN 的复用寄存器地址。
0x0358:conf_reg 寄存器偏移地址,和 mux_reg 一样,0x020e0000+0x0358=0x020e0358,
这个就是寄存器 SW_PAD_CTL_PAD_ENET1_RX_EN 的地址。
0x0000:input_reg 寄存器偏移地址,有些外设有 input_reg 寄存器,有 input_reg 寄存器的外设需要配置 input_reg 寄存,没有的话就不需要设置.
0x0:mux_reg寄存器值,用于设置IO复用模式,这里设置为0x0,就相当于设置复用功能为ENET1_RX_EN。
0x0:input_reg 寄存器值,在这里无效。
当我们使用这个引脚的复用功能时,后面会在跟一个值,如下所示:
MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
这里的0x1b0b0值是设置conf_reg 寄存器,config_reg 寄存器是设置一个 PIN 的电气特性的,此值由用户自行设置,通过此值来设置一个 IO 的上/下拉、驱动能力和速度等。
通过以上的操作我们就可以完成对一个引脚的复用配置以及初始化。
调用pinctrl一般在设备树中进行,在上一小节的举例中也体现出了如何调用pinctrl。下面在来回顾一下:
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1
&pinctrl_fec1_reset>;
phy-mode = "rmii";
phy-handle = <ðphy0>;
phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;
phy-reset-duration = <200>;
status = "okay";
};
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
......
pinctrl_enet1: enet1grp {
fsl,pins = <
MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031
>;
};
......
};
在这个例子中我们只关注fec1设备节点下的“pinctrl-names ”和“pinctrl-0”两条语句,这两句的意思就是引用在iomuxc中配置的pinctrl节点。
因此总结一下,想要配置某个外设而需要配置某一个引脚为GPIO时,一般的配置流程是这样的:
在IOMUXC/pinctrl中对某一个引脚进行配置,两个部分(复用、初始化),在外设节点中调用
在驱动中获取设备节点以及GPIO
对GPIO进行配置