驱动开发之pinctrl 和 gpio 子系统

1.前言

不妨思考一下,为什么需要pinctrl 和 gpio 子系统??

学习过单片机的兄弟都知道,点灯有以下步骤:

(1)开启相应的GPIO时钟

(2)如果需要配置复用,就配置一下复用寄存器

(3)设置一下电气属性

(4)设置一下GPIO口的输入输出方向

(5)设置或者读取数据

按照这些步骤依次配置寄存器就可。在驱动里面,设备属性可以通过设备树获取,或者通过datasheet获取写死在驱动程序里面,这不是本次讨论重点,主要还是拿到配置信息,配置GPIO。

如果每一次使用GPIO,都需要上面这么多步骤,是不是感觉都是在写重复性代码,linux内核大佬为了解决这个问题,就推出了pinctrl 和 gpio 子系统。

2.pinctrl子系统介绍

pinctrl子系统主要是解决上面1~3步这些工作,而且还可以解决pin 功能冲突问题,也就是配置过了,想再配置会有问题。

pinctrl 子系统主要工作内容如下:

①、获取设备树中 pin 信息。

②、根据获取到的 pin 信息来设置 pin 的复用功能

③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等

是的,没错, pinctrl子系统需要借助设备树完成自动配置GPIO管脚的功能

2.1.PIN 配置信息详解

打开 imx6ull-alientek-emmc.dts,找到iomuxc 节点,该节点就是 I.MX6ULL 的 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 = <
    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
    >;
......
        };
    };
};

imx6ull的开发板属性名字一定要为 “fsl,pins”,pinctrl 驱动程序是通过读取 “fsl,pins” 属性值来获取 PIN 的配置信息。其他开发板的可以看一下设备树里面怎么写的,直接抄作业,进行修改,也可以通过compatible = "fsl,imx6ul-iomuxc";来确定需要加fsl作为前缀,最保险看一下源码怎么解析对应芯片的pin的信息的,全局搜索compatible属性值的名字,看在哪个驱动里面使用。

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 

 UART1_RTS_B 的 配 置 信 息 分 为 两 部 分 : MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 和 0x17059。

前面介绍了,对于一个 PIN 的配置主要包括两方面, 一个是设置这个 PIN 的复用功能,另一个就是设置这个 PIN 的电气特性。

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19,这是一个宏定义,定义在文件 arch/arm/boot/dts/imx6ul-pinfunc.h中,

#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。查 阅《I.MX6ULL 参考手册》可以知 UART1_RTS_B 的可选复用 IO 如图 45.1.2.1 所示:

 

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 表示将 UART1_RTS_B 这个 IO 复用为 GPIO1_IO19。

此宏定义后面跟着 5 个数字,也就是这个宏定义 的具体值,如下所示:

0x0090 0x031C 0x0000 0x5 0x0

这 5 个值的含义如下所示:

.<mux_reg conf_reg input_reg mux_mode input_val>

0x0090:mux_reg 寄存器偏移地址,设备树中的 iomuxc 节点就是 IOMUXC 外设对应的节 点 , 根 据 其 reg 属 性 可 知 IOMUXC 外 设 寄 存 器 起 始 地 址 为 0x020e0000 。 因 此 0x020e0000+0x0090=0x020e0090,IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B 寄存器地址 正好是 0x020e0090,因此可知,0x020e0000+mux_reg 就是 PIN 的复用寄存器地址。

0x031C:conf_reg 寄存器偏移地址,和 mux_reg 一样,0x020e0000+0x031c=0x020e031c, 这个就是寄存器 IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B 的地址。

0x0000:input_reg 寄存器偏移地址,有些外设有 input_reg 寄存器,有 input_reg 寄存器的 外设需要配置 input_reg 寄存器。没有的话就不需要设置,UART1_RTS_B 这个 PIN 在做 GPIO1_IO19 的时候是没有 input_reg 寄存器,因此这里 intput_reg 是无效的。

0x5 : mux_reg 寄 存 器 值 , 在 这 里 就 相 当 于 设 置 IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B 寄存器为 0x5,也即是设置 UART1_RTS_B 这 个 PIN 复用为 GPIO1_IO19。

0x0:input_reg 寄存器值,在这里无效。

这就是宏 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 的含义.

 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 

  MX6UL_PAD_UART1_RTS_B__GPIO1_IO19宏已经解释含义了,但是你会发现conf_reg 寄存器没有赋值,0x17059就是conf_reg 寄存器的值了,这个就是电气属性值,通 过此值来设置一个 IO 的上/下拉、驱动能力和速度等。

2.2.小结

对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成。如果 pinctrl 将一个 pin 脚初始化为 GPIO 而不是 IIC 或者 SPI,那么接下来就可以使用 gpio 子系统的API。gpio 子系统是基于 pinctrl 子系统的!pin controller 和 GPIO Controller 不是一回事,前者控制引脚可用于 GPIO 功能、I2C 功能等功能性切换;后者只是把引脚配置为输入、输出、设置GPIO方向、获取值等简单的功能。(pinctrl 的 api 其实可以实现所有需求,但 gpio 的函数更常用一些),而且需要设备树里面描述想要设置的功能,相对复杂很多,推荐还是使用gpio 的函数。

3.gpio 子系统介绍

gpio 子系统顾名思义,就是用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO 为输入输出,读取 GPIO 的值等。gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动 开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API 函数来操作 GPIO,Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,极大的方便了驱动开 发者使用 GPIO。

3.1.配置信息

使用GPIO需要知道GPIO的信息,需要在设备树里面进行添加:

cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;

“&gpio1”表示所使用的 IO 属于 GPIO1 组

“19” 表示 GPIO1 组的第 19 号 IO,通过这两个值 驱动程序就知道 使用了 GPIO1_IO19这个 GPIO。

“GPIO_ACTIVE_LOW”表示低电平有效,如果改为“GPIO_ACTIVE_HIGH”就表 示高电平有效。 

gpio1信息如下:

gpio1: gpio@0209c000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x0209c000 0x4000>;
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};

gpio1 节点信息描述了 GPIO1 控制器的所有信息,重点就是 GPIO1 外设寄存器基地址以及 兼 容 属 性 。一般出厂的设备树都会描述好的。

3.2.gpio 子系统 API 函数

3.2.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,申请成功;其他值,申请失败。

3.2.2.gpio_free 函数

如果不使用某个 GPIO 了,那么就可以调用 gpio_free 函数进行释放。

函数原型如下: void gpio_free(unsigned gpio)

gpio:要释放的 gpio 标号。

返回值:无。

3.2.3.gpio_direction_input 函数

此函数用于设置某个 GPIO 为输入

函数原型如下所示: int gpio_direction_input(unsigned gpio)

gpio:要设置为输入的 GPIO 标号。

返回值:0,设置成功;负值,设置失败。

3.2.4.gpio_direction_output 函数

此函数用于设置某个 GPIO 为输出,并且设置默认输出值

函数原型如下: int gpio_direction_output(unsigned gpio, int value)

gpio:要设置为输出的 GPIO 标号

value:GPIO 默认输出值

返回值:0,设置成功;负值,设置失败。 

3.2.5.gpio_get_value 函数

此函数用于获取某个 GPIO 的值(0 或 1),此函数是个宏,定义所示:

#define gpio_get_value __gpio_get_value int __gpio_get_value(unsigned gpio) 

gpio:要获取的 GPIO 标号。

返回值:非负值,得到的 GPIO 值;负值,获取失败。

3.2.6.gpio_set_value 函数

此函数用于设置某个 GPIO 的值,此函数是个宏,

定义如下 #define gpio_set_value __gpio_set_value void __gpio_set_value(unsigned gpio, int value)

gpio:要设置的 GPIO 标号。

value:要设置的值。

返回值:无 

4.实验

由于篇幅限制问题,相关实验后面统一出章节测试。

参考

嵌入式Linux中pinctrl 子系统和 gpio 子系统分析-阿里云开发者社区 (aliyun.com) 

 

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-06-15 04:20:03       14 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-15 04:20:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-15 04:20:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-15 04:20:03       18 阅读

热门阅读

  1. 使用Spring Boot设计一套BI系统

    2024-06-15 04:20:03       7 阅读
  2. Rust 异步编程

    2024-06-15 04:20:03       10 阅读
  3. 【设计模式】行为型设计模式之 模板方法模式

    2024-06-15 04:20:03       9 阅读
  4. mysql什么时候不需要建立索引

    2024-06-15 04:20:03       5 阅读
  5. 《Python程序设计(第二版)》第一二章冷门点上

    2024-06-15 04:20:03       6 阅读
  6. k8s可练习实验分享

    2024-06-15 04:20:03       7 阅读
  7. m2_python字符串-索引与切片

    2024-06-15 04:20:03       6 阅读
  8. Android10 动态修改开机动画(二)设置分区权限

    2024-06-15 04:20:03       10 阅读
  9. 【xilinx】使用vivado编译中methodology的相关介绍

    2024-06-15 04:20:03       10 阅读