Bootstrap

【Linux驱动】pinctrl 和 gpio子系统(一)—— pinctrl 节点解析,引入gpio子系统

裸机开发时,如果要点亮一个 LED,我们要做如下内容:

  1. 初始化时钟
  2. 设置引脚复用为哪个功能,配置引脚的电气属性
  3. 设置引脚的 IO 方向初始值

有了设备树以后,我们可以通过 pinctrl gpio 子系统来配置上述内容。

  • pinctrl 子系统:控制引脚的复用、电气属性
  • gpio 子系统:控制GPIO初始化(如设置 IO 方向、初始值)

 为了防止自己新增的节点和设备树原本的发生冲突,建议注释原本的节点。

一、pinctrl 配置节点

pinctrl 子系统用于配置复用为不同功能时的电气属性,这就需要找到一个名为  iomuxc 的节点,这个节点会在两个地方出现:一个是通用 dtsi 设备树文件(如 imx6ull.dtsi),一个是板级设备对应的 dts 文件(如imx6ull-alientek-emmc.dts)。

1、iomuxc 通用节点

在 imx6ull.dtsi 中定义了一个 iomuxc 通用节点,该节点包含了 iomuxc 控制器的基地址,一般用于控制引脚复用。但光从这里似乎看不出来,可以复用为哪些外设,不同板级设备的外设不一样,所以外设相关的具体内容不会放在这里。

注意:IOMUXC 除了 iomuxc 外,还包含 iomuxc_snvs、gpr

2、iomuxc 外设节点

具体的外设节点会放到对应板级设备的 .dts 文件中。&iomuxc 会找到通用节点中的 iomuxc,然后实际编译的时候会将两者的内容合并。后续将在这里为不同功能对应的引脚配置电气属性

二、添加 pinctrl 节点

1、创建节点

首先在板级设备的 dts 文件中找到 &iomuxc,这里板级设备对应的 dts 文件是 imx6ull-alientek-emmc.dts。我们在 imx6ul-evk 下创建一个节点,在其他地方我们将以 " &别名 " 的方式引用节点。 

&iomuxc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog_1>;
	imx6ul-evk {
        // "别名:节点名 {}" 的格式目的是为了在其他地方通过别名引用这个节点
        // "节点名 {}" 的格式目的仅仅只是为了描述一些信息,无法在其他地方引用
        pinctrl_gpiotest: testgrp {
            // ... 
		};
    }
}

2、添加配置属性

配置属性解析

接下来我们要添加的属性名为  "fsl,pins" 。以 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 为例,MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 是一个属性名,也是宏定义,定义在 xxx-pinfunc.h 文件中(这里是 imx6ul-pinfunc.h),而 0x17059属性值

  • 前半部分 MX6UL_PAD_UART1_RTS_B 是需要配置的寄存器名称
  • 后半部分 GPIO1_IO19 是要复用成哪个功能,这里是复用成 GPIO1_IO19 功能来使用。

可以在 imx6ul-pinfunc.h 文件中找到 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 的宏定义,可以看到宏定义的内容分为了五部分:

#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19          0x0090 0x031C 0x0000 0x5 0x0
宏定义各个字段宏定义各个字段的值代表含义
<mux_reg>0x0090IOMUXC_SW_MUX_CTL 寄存器的偏移量
<conf_reg>0x031CIOMUXC_SW_PAD_CTL 寄存器的偏移量
<input_reg>0x0000input_reg 寄存器(若无该寄存器,默认为 0x0)
<mux_mode>0x5

IOMUXC_SW_MUX_CTL 寄存器的值

(即选择复用为哪个功能)

<input_val>0x0input_reg 寄存器的值

添加复用引脚配置属性

现在我们要复用为 GPIO1_IO03,因此需要在 imx6ul-pinfunc.h 文件中找到与 GPIO1_IO03 结尾的宏定义。赋予的电气属性值为 0x10b0。

&iomuxc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog_1>;
	imx6ul-evk {
        pinctrl_gpiotest: testgrp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO03__GPIO1_IO03	0x10b0 		/* GPIO1_IO03 */
			>;
		};
    }
}

三、设备树引用 pinctrl 节点

1、pinctrl-names、pinctrl-x 解析

要引用  pinctrl 节点主要用到 pinctrl-names 属性和 pinctrl-x 属性。pinctrl-names 代表当前外设状态的名字,pinctrl-0 表示了当前状态下的配置方案。以下面这个为例

  • pinctrl-names 代表状态名为 "default"
  • pinctrl-0 代表 "default" 状态下对应的配置方案选择 pinctrl_hog_1,pinctrl_hog_1 就是在 iomuxc 下的一个 pinctrl 节点

当然,一次可以根据状态不同,配置不同的方案,以下面这个为例:

  • pinctrl-names 现在有三种状态,分别是 "default"、"state_100mhz"、"state_200mhz"
  • pinctrl-0 对应 "default" 状态的配置方案
  • pinctrl-1 对应 "state_100mhz" 状态的配置方案
  • pinctrl-2 对应 "state_200mhz" 状态的配置方案

2、引用节点

我们在根节点下新增一个 gpio-led 节点,在该节点中引入 pinctrl 节点

gpio-led {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_gpio_leds>;        // pinctrl_gpio_leds 是 iomuxc下添加的节点别名
	status = "okay";
};

四、设备树引入 gpio 子系统

上面已经通过 pinctrl 子系统快速配置引脚复用,而gpio 子系统的主要目的是快捷设置 gpio 引脚的初始值(即初始状态为低电平还是高电平)

基本格式为:

属性名 = <&引脚组  引脚编号 初始状态>

假设要将 gpio1 的第 3 号引脚设置为低电平,具体写法为: 

led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>
  •  led-gpio:自定义属性名,后续在驱动代码会通过内核提供的 API 来获取到该属性
  • gpio1: 引脚所属组,定义在 imx6ull.dtsi 文件中。

  • GPIO_ACTIVE_LOW: 引脚状态的宏定义,表示低电平。高电平对应的宏定义为 GPIO_ACTIVE_HIGH

五、代码整合

最终引入 pinctrl 子系统和 gpio 子系统的节点模板为

gpio-led {
	pinctrl-names = "default";                // pinctrl 子系统
	pinctrl-0 = <&pinctrl_gpio_leds>;
	led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;    // gpio 子系统
	status = "okay";
};

其中 pinctrl_gpio_leds 的定义为: 

&iomuxc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog_1>;
	imx6ul-evk {
        pinctrl_gpiotest: testgrp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO03__GPIO1_IO03	0x10b0 		/* GPIO1_IO03 */
			>;
		};
    }
}
;