一、前言
上一章我们编写了基于设备树的 LED 驱动,但是驱动的本质还是没变,都是配置 LED 灯所使用的 GPIO 寄存器,驱动开发方式和裸机基本没啥区别。本章我们就来学习一下如何借助 pinctrl 和 gpio 子系统来简化 GPIO 驱动开发。
Linux 内核针对 PIN 的配置推出了 pinctrl 子系统,对于 GPIO的配置推出了 gpio 子系统。
本节先讲解 pinctrl 子系统。
二、pinctrl子系统
1、pinctrl简介
传统的配置 pin 的方式就是直接操作相应的寄存器,但是这种配置方式比较繁琐、而且容易出问题(比如 pin 功能冲突)。pinctrl 子系统就是为了解决这个问题而引入的,pinctrl 子系统主要工作内容如下:
①、获取设备树中 pin 信息。
②、根根据获取到的 pin 信息来设置 pin 的复用功能
③、据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。
对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成。
2、I.MX6ULL 的 pinctrl 子系统驱动
<1> PIN 设备树配置
pinctrl 子系统要根据你提供的信息来配置 PIN 功能,因此,如果要使用 pinctrl 子系统,我们需要在设备树里面创建一个节点来描述 PIN 的配置信息。
打开 imx6ull.dtsi 文件,找到一个叫做 iomuxc 的节点:
iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6ul-iomuxc";
reg = <0x020e0000 0x4000>;
};
打开 imx6ull.dts 文件,找到 iomuxc 节点的追加内容:
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059
MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058
>;
};
......
pinctrl_wdog: wdoggrp {
fsl,pins = <
MX6UL_PAD_LCD_RESET__WDOG1_WDOG_ANY 0x30b0
>;
};
};
不同的外设使用的 PIN 不同、其配置也不同,因此一个萝卜一个坑,将某个外设所使用的所有 PIN 都组织在一个子节点里面。
这些外设子节点是没有compatible属性的,因此需要使用父节点的compatible属性 (.dtsi文件中) 对应的驱动文件对子节点进行处理。compatible 属性值为“fsl,imx6ul-iomuxc”,我们在 Linux 内核源码中全局搜索字符串“fsl,imx6ul-iomuxc”就会找到 I.MX6ULL 这颗 SOC 的 pinctrl 驱动文件。
<2> PIN 设备树配置信息详解
pinctrl 子系统如何添加 PIN 的配制信息呢?我们以 imx6ull.dts 文件中UART1_RTS_B 这个PIN为例说明:
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
配置信息分为两部分:
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19(复用功能) 和 0x17059(电气属性)
🦌 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19(复用功能)
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 其实是一个宏定义:
#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 个宏定义分别对应 UART1_RTS_B 这个 PIN 的 8 个复用 IO,上面代码的意思是将这个 IO 复用为 GPIO1_IO19:
宏定义后跟了5个数字,含义为:<mux_reg conf_reg input_reg mux_mode input_val> 。
0x0090 0x031C 0x0000 0x5 0x0
- 0x0090:mux_reg 寄存器偏移地址(PIN 的复用寄存器地址)。相对于iomuxc 节点reg属性(外设寄存器的起始地址 0x020e0000)的偏移,该PIN 的端口复用寄存器IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B (设置IO复用)地址是 “基地址” + “偏移”。
- 0x031C:conf_reg 寄存器偏移地址,和 mux_reg 一样,0x020e0000+0x031c=0x020e031c,这个就是寄存器 IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B (设置IO电气属性)的地址。
- 0x0000:input_reg 寄存器偏移地址。UART1_RTS_B 这个 PIN 在做GPIO1_IO19 的时候是没有 input_reg 寄存器,因此这里 intput_reg 是无效的。
- 0x5:mux_reg 寄存器的值。也即是设置 UART1_RTS_B 这个 PIN 复用为 GPIO1_IO19。
- 0x0:input_reg 寄存器值,在这里无效。
🦌 0x17059(电气属性)
0x17059:上面config_reg 寄存器的值,也就是设置电气属性的寄存器IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B的值,通过此值来设置一个 IO 的上/下拉、驱动能力和速度等。
<3> pinctrl驱动程序详解
所有的东西都已经准备好了,包括寄存器地址和寄存器值,Linux 内核相应的驱动文件就会根据这些值来做相应的初始化。
根据 iomuxc 节点中 compatible 属性的值 “fsl,imx6ul-iomuxc”,找到文件drivers/pinctrl/freescale/pinctrl-imx6ul.c ,具体内容不再列出,总之,该文件是一个标准的platform驱动,设备(设备树中iomuxc节点)和驱动匹配成功之后便会执行,然后按照以下函数调用路径解析设备树中提供的配置:
<4> 设备树中添加pinctrl节点步骤(总结)
对于IMX 系列SOC 的pinctrl 设备树绑定信息,可以参考文档Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt。此处,我们建立一个虚拟的设备test,test 使用了 GPIO1_IO00 这个 PIN 的 GPIO 功能,pinctrl 节点添加过程如下:
👺 创建对应的设备树节点
打开 imx6ull-alientek-emmc.dts,在 iomuxc 节点中的 “imx6ul-evk” 子节点下添加 “pinctrl_test” 节点,注意!节点前缀一定要为“pinctrl_”:
pinctrl_test: testgrp {
/* 具体的 PIN 信息 */
};
👺 添 ”fsl,pins“ 属性
对于 I.MX 系列 SOC 而言,pinctrl 驱动程序是通过读取“fsl,pins”属性值来获取 PIN 的配置信息,完成以后如下所示:
pinctrl_test: testgrp {
fsl,pins = <
/* 设备所使用的 PIN 配置信息 */
>;
};
👺 在“fsl,pins”属性中添加 PIN 配置信息
在 “fsl,pins” 属性中添加具体的 PIN 配置信息:
pinctrl_test: testgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config /*config 是具体设置值*/
>;
};
至此,我们已经在 imx6ull-alientek-emmc.dts 文件中添加好了 test 设备所使用的 PIN 配置信息。
三、注意事项
一个引脚一次只能实现一个功能,如果 A 引脚在设备树中配置为了 I2C 的 SDA 信号,那么 A 引脚就不能再配置为 GPIO,否则的话驱动程序在申请 GPIO 的时候就会失败。
检查 PIN 有没有被其他外设使用包括两个方面(pinctrl和gpio两方面的检查): 文章来源:https://www.toymoban.com/news/detail-628304.html
①、检查 pinctrl 设置。
②、如果这个 PIN 配置为 GPIO 的话,检查这个 GPIO 有没有被别的外设使用。 文章来源地址https://www.toymoban.com/news/detail-628304.html
到了这里,关于【Linux驱动开发】010 pinctrl子系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!