1、pinctrl 子系统主要工作内容如下:
①、获取设备树中 pin 信息。
②、根据获取到的 pin 信息来设置 pin 的复用功能
③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。
对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始
化工作均由 pinctrl 子系统来完成,pinctrl 子系统源码目录为 drivers/pinctrl。
iomuxc 节点就是 I.MX6ULL 的 IOMUXC 外设对应的节点,但这个节点在dtsi里面不能修改,若要添加设备需打开 imx6ull-alientek-emmc.dts,修改&iomuxc 节点(本质就是在iomuxc下添加)。
2、 如何添加一个PIN的信息
pinctrl_hog_1: hoggrp-1 {fsl,pins = ;
};
我们在imx6ul-pinfunc.h中找到:
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
0x0090 0x031C 0x0000 0x5 0x0
IOMUXC父节点首地址0x020e0000,因此UART1_RTS_B这个PIN的mux寄存器地址 就是:0x020e0000+0x0090=0x020e 0090。
conf_reg:0x020e0000+0x031C=0x020e 031C,这个寄存器就是UART1_RTS_B的电气属性配置寄存器。
input_reg,便宜为0,表示UART1_RTS_B这个PIN没有input功能。
mux_mode:5表示复用为GPIO1_IO19,将其写入0x020e 0090
input_val:就是写入input_reg寄存器的值。
0x17059:为PIN的电气属性配置寄存器值,0x17059 就是 conf_reg 寄存器值,此值由用户自行设置,通过此值来设置一个 IO 的上/下拉、驱动能力和速度等。
3、完整的 iomuxc 节点
iomuxc: iomuxc@020e0000 {compatible = "fsl,imx6ul-iomuxc";reg = <0x020e0000 0x4000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_hog_1>;imx6ul-evk {pinctrl_hog_1: hoggrp-1 {fsl,pins = ;......};};
};
如何找到IMX6UL对应的pinctrl子系统驱动,设备树里面的设备节点是如何跟驱动匹配的呢?通过compatible,此属性是字符串列表。驱动文件里面有一个描述驱动兼容性的东西,当设备树节点的compatible属性和驱动里面的兼容性字符串匹配,也就是一模一样的时候就表示设备和驱动匹了。
所以我们只需要全局搜索,设备节点里面的compatible属性的值,看看在哪个.c文件里面有,那么此.c文件就是驱动文件。找到pinctrl-imx6ul.c文件,那么此文件就是6UL/6ULL的pinctrl驱动文件。当驱动和设备匹配以后执行,probe函数。也就是
imx6ul_pinctrl_probe->imx_pinctrl_probe-> 此函数会初始化imx_pinctrl_desc,为pinctrl_desc类型的结构体。重点是:imx_pinctrl_desc->pctlops = &imx_pctrl_ops;imx_pinctrl_desc->pmxops = &imx_pmx_ops;imx_pinctrl_desc->confops = &imx_pinconf_ops;最后通过pinctrl_register函数向系统注册一个imx_pinctrl_desc
->imx_pinctrl_probe_dt->imx_pinctrl_parse_functions->imx_pinctrl_parse_groups-> pin_reg->mux_reg = mux_reg;pin_reg->conf_reg = conf_reg;pin->input_reg = be32_to_cpu(*list++);pin->mux_mode = be32_to_cpu(*list++);pin->input_val = be32_to_cpu(*list++);config = be32_to_cpu(*list++);if (config & IMX_PAD_SION)pin->mux_mode |= IOMUXC_CONFIG_SION;pin->config = config & ~IMX_PAD_SION;
imx_pinconf_set函数设置PIN的电气属性
imx_pmx_set函数设置PIN的复用
设备树中添加 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 = ;
};
至此,我们已经在 imx6ull-alientek-emmc.dts 文件中添加好了 test 设备所使用的 PIN 配置信
息
二、gpio 子系统
gpio 子系统顾名思义,就是用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO
为输入输出,读取 GPIO 的值等。
1、gpio子系统驱动
打开 imx6ull-alientek-emmc.dts, UART1_RTS_B 这个 PIN 的 pincrtl 设置如下:
pinctrl_hog_1: hoggrp-1 {fsl,pins = ;
};
pinctrl 配置好以后就是设置 gpio 了
在 imx6ull-alientek-emmc.dts 中找到名为“usdhc1”的节点,这个
节点就是 SD 卡设备节点,如下所示:
&usdhc1 {pinctrl-names = "default", "state_100mhz", "state_200mhz";pinctrl-0 = <&pinctrl_usdhc1>;pinctrl-1 = <&pinctrl_usdhc1_100mhz>;pinctrl-2 = <&pinctrl_usdhc1_200mhz>;/* pinctrl-3 = <&pinctrl_hog_1>; */cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;keep-power-in-suspend;enable-sdio-wakeup;vmmc-supply = <®_sd1_vmmc>;status = "okay";
};
“cd-gpios”描述了 SD 卡的 CD 引脚使用的哪个 IO。属性值一共有三个,我们来看一下这三个属性值的含义,“&gpio1”表示 CD 引脚所使用的 IO 属于 GPIO1 组,“19”表示 GPIO1 组的第 19 号 IO,通过这两个值 SD 卡驱动程序就知道 CD 引脚使用了 GPIO1_IO19这 GPIO。“GPIO_ACTIVE_LOW”表示低电平有效,如果改为“GPIO_ACTIVE_HIGH”就表示高电平有效。根据上面这些信息,SD 卡驱动程序就可以使用 GPIO1_IO19 来检测 SD 卡的 CD 信号了
2、gpio 子系统 API 函数
1、gpio_request 函数
gpio_request 函数用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request
进行申请,函数原型如下:
int gpio_request(unsigned gpio, const char *label)
函数参数和返回值含义如下:
gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信
息,此函数会返回这个 GPIO 的标号。
label:给 gpio 设置个名字。
返回值:0,申请成功;其他值,申请失败。
2、gpio_free 函数
如果不使用某个 GPIO 了,那么就可以调用 gpio_free 函数进行释放。函数原型如下:
void gpio_free(unsigned gpio)
函数参数和返回值含义如下:
gpio:要释放的 gpio 标号。
返回值:无。
3、gpio_direction_input 函数
此函数用于设置某个 GPIO 为输入,函数原型如下所示:
int gpio_direction_input(unsigned gpio)
函数参数和返回值含义如下:
gpio:要设置为输入的 GPIO 标号。
返回值:0,设置成功;负值,设置失败。
4、gpio_direction_output 函数
此函数用于设置某个 GPIO 为输出,并且设置默认输出值,函数原型如下:
int gpio_direction_output(unsigned gpio, int value)
函数参数和返回值含义如下:
gpio:要设置为输出的 GPIO 标号。
value:GPIO 默认输出值。
返回值:0,设置成功;负值,设置失败。
5、gpio_get_value 函数
此函数用于获取某个 GPIO 的值(0 或 1),此函数是个宏,定义所示:
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
函数参数和返回值含义如下:
gpio:要获取的 GPIO 标号。
返回值:非负值,得到的 GPIO 值;负值,获取失败。
6、gpio_set_value 函数
此函数用于设置某个 GPIO 的值,此函数是个宏,定义如下
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
函数参数和返回值含义如下:
gpio:要设置的 GPIO 标号。
value:要设置的值。
返回值:无
3、设备树中添加 gpio 节点模板
1、创建 test 设备节点
在根节点“/”下创建 test 设备子节点,如下所示:
test {/* 节点内容 */
};
2、添加 pinctrl 信息
在前面我们创建了 pinctrl_test 节点,此节点描述了 test 设备所使用的 GPIO1_IO00 这
个 PIN 的信息,我们要将这节点添加到 test 设备节点中,如下所示:
test {pinctrl-names = "default";pinctrl-0 = <&pinctrl_test>;/* 其他节点内容 */
};
第 2 行,添加 pinctrl-names 属性,此属性描述 pinctrl 名字为“default”。
第 3 行,添加 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>;
};
第 4 行,test 设备所使用的 gpio
关于 pinctrl 子系统和 gpio 子系统就讲解到这里,接下来就使用 pinctrl 和 gpio 子系统来驱
动 I.MX6ULL-ALPHA 开发板上的 LED灯。