前言
原始字符设备驱动,patform框架,设备树这三种方法来点亮led灯的原理都是配置gpio寄存器。但在实际开发中,需要用到的引脚非常多。都是用前面讲到的方法不显示,麻烦。实际开发中我们常用pinctrl和gpio子系统来进行GPIO驱动开发。
一、pinctrl子系统
传统的配置 pin 的方式就是直接操作相应的寄存器,但是这种配置方式比较繁琐、而且容易出问题(比如 pin 功能冲突)。而pinctrl子系统就是为了解决这种问题而引入的,pinctrl子系统的主要工作内容:
1.获取设备树中pin的信息
2.依据获取到的pin信息设置pin的复用功能
3.依据获取到的pin信息设置pin的电气属性。
使用时我们只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成。例如:
&iomuxc { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hog_1>; imx6ul-evk { pinctrl_led: ledgrp { fsl,pins = < MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0 */ >; };
搜索“pinctrl_led”就会找到 pin引脚的gpio设置:
gpioled { #address-cells = <1>; #size-cells = <1>; compatible = "atkalpha-gpioled"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_led>; led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; status = "okay"; };
注意:pinctrl_led节点和gpioled 节点是我自己添加上去的,不是原来就有的。
我们先说明pinctrl子系统。gpio1_3在这里我们用来点亮led灯:
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03
这是一个宏定义,在arch/arm/boot/dts/imx6ul-pinfunc.h中可以找到:
#define MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x0068 0x02F4 0x0000 0x5 0x0
后面跟着5个数字,为该宏定义的具体值。
<mux_reg conf_reg input_reg mux_mode input_val>
0x0068 :mux_reg 寄存器偏移地址,设备树中的 iomuxc 节点就是 IOMUXC 外设对应的节点 , 根 据 其 reg 属 性 可 知 IOMUXC 外 设 寄 存 器 起 始 地 址 为 0x020e0000 。 因 此0x020e0000+0x0068 =0x020e0068, MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 寄存器地址正 好 是 0x020e0068,
0x020e0000+mux_reg 就是 PIN 的复用寄存器地址。
0x02F4 : conf_reg 寄存器偏移地址,和 mux_reg 一样, 0x020e0000+0x02F4 =0x020e02F4c,这个就是寄存器 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03的地址。
0x0000: input_reg 寄存器偏移地址,有些外设有 input_reg 寄存器,有 input_reg 寄存器的外设需要配置 input_reg 寄存器。没有的话就不需要设置, GPIO1_IO03这个 PIN 在做GPIO1_IO03的时候是没有 input_reg 寄存器,因此这里 intput_reg 是无效的。
0x5 : mux_reg 寄 存 器 值 , 在 这 里 就 相 当 于 设 置MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 寄存器为 0x5,也即是设置 GPIO1_IO03这个 PIN 复用为 GPIO1_IO03。
0x0: input_reg 寄存器值,在这里无效。至于pin引脚的电气属性,由我们自行设置:
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0 */
0x10B0 设置的就是 conf_reg 寄存器值,在这里就相当于设置寄存器MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 的值为 0x10B0 。
二、gpio子系统
pinctrl 子系统重点是设置 PIN(有的 SOC 叫做 PAD)的复用和电气属性,如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话,那么接下来就要用到 gpio 子系统,gpio 子系统就是用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。
gpioled { #address-cells = <1>; #size-cells = <1>; compatible = "atkalpha-gpioled"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_led>; led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; status = "okay"; };
pinctrl-0 属性设置 LED 灯所使用的 PIN 对应的 pinctrl 节点。led-gpio 属性指定了 LED 灯所使用的 GPIO,在这里就是 GPIO1 的 IO03,低电平有效。稍后编写驱动程序的时候会获取 led-gpio 属性的内容来得到 GPIO 编号,因为 gpio 子系统的 API 操作函数需要 GPIO 编号。
三、pinctrl与gpio子系统的使用
pinctrl使用模板
1、创建对应的节点
同一个外设的 PIN 都放到一个节点里面,打开 imx6ull-alientek-emmc.dts,在 iomuxc 节点中的“imx6ul-evk”子节点下添加“pinctrl_test”节点,注意!节点前缀一定要为“pinctrl_”。添加完成以后如下所示:
pinctrl_test: testgrp { /* 具体的 PIN 信息 */ };
2、添加“fsl,pins”属性
设备树是通过属性来保存信息的,因此我们需要添加一个属性,属性名字一定要为“fsl,pins”,因为对于 I.MX 系列 SOC 而言, pinctrl 驱动程序是通过读取“fsl,pins”属性值来获取 PIN 的配置信息,完成以后如下所示:
pinctrl_test: testgrp { fsl,pins = < /* 设备所使用的 PIN 配置信息 */ >; };
3、在“fsl,pins”属性中添加 PIN 配置信息
最后在“fsl,pins”属性中添加具体的 PIN 配置信息,完成以后如下所示:
pinctrl_test: testgrp { fsl,pins = < MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config /*config 是具体设置值*/ >;
gpio模板
1、创建 test 设备节点
在根节点“/”下创建 test 设备子节点,如下所示:
test { /*节点内容*/ };
2、添加 pinctrl 信息
在 前面中我们创建了 pinctrl_test 节点,此节点描述了 test 设备所使用的 GPIO1_IO00 这个 PIN 的信息,我们要将这节点添加到 test 设备节点中,如下所示:
test { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_test>; /* 其他节点内容 */ };
添加 pinctrl-names 属性,此属性描述 pinctrl 名字为“default”。
添加 pinctrl-0 节点,此节点引用 前面中创建的 pinctrl_test 节点,表示 tset 设备的所使用的 PIN 信息保存在 pinctrl_test 节点中。
3、添加 GPIO 属性信息
我们最后需要在 test 节点中添加 GPIO 属性信息,表明 test 所使用的 GPIO 是哪个引脚,添加完成以后如下所示:
test { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_test>; gpio = <&gpio1 0 GPIO_ACTIVE_LOW>; };