gpio子系统

文章目录

    • gpio子系统简介
    • I.MX6ULL的gpio子系统驱动
      • 设备树中的 gpio 信息
      • gpio子系统API函数
      • 设备树中添加gpio节点模块
      • 与 gpio 相关的 OF 函数

gpio子系统简介

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

I.MX6ULL的gpio子系统驱动

设备树中的 gpio 信息

I.MX6ULL-ALPHA 开发板上的 UART1_RTS_B 做为 SD 卡的检测引脚, UART1_RTS_B 复用为 GPIO1_IO19,通过读取这个 GPIO 的高低电平就可以知道 SD 卡有没有插入。首先肯定是将 UART1_RTS_B 这个 PIN 复用为 GPIO1_IO19,并且设置电气属性。打开dts文件,UART1_RTS_B 这个 PIN 的 pincrtl 设置如下:
== SD 卡 CD 引脚 PIN 配置参数==

316 pinctrl_hog_1: hoggrp-1 {
317	 	fsl,pins = <
318 		MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
	......
322 	>;
323 };

第 318 行,设置 UART1_RTS_B 这个 PIN 为 GPIO1_IO19。
pinctrl 配置好以后就是设置 gpio 了, SD 卡驱动程序通过读取 GPIO1_IO19 的值来判断 SD卡有没有插入,但是 SD 卡驱动程序怎么知道 CD 引脚连接的 GPIO1_IO19 呢?肯定是需要设备树告诉驱动啊!在设备树中 SD 卡节点下添加一个属性来描述 SD 卡的 CD 引脚就行了, SD卡驱动直接读取这个属性值就知道 SD 卡的 CD 引脚使用的是哪个 GPIO 了。 SD 卡连接在I.MX6ULL 的 usdhc1 接口上,在 .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>;
	cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
	keep-power-in-suspend;
	enable-sdio-wakeup;
	vmmc-supply = <&reg_sd1_vmmc>;
	status = "okay";
};

从上面看到没有指定CD引脚的pinctrl信息,那么SD卡驱动就没办法设置CD引脚定的复用功能?这个不用担心,因为在“iomuxc”节点下引用了pinctrl_hog_1这个节点,所以linux内核中的iomuxc驱动就会自动初始化pinctrl_hog_1节点下的所有PIN

属性“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信号了
gpio1节点

504 gpio1: gpio@0209c000 {
505		compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
506		reg = <0x0209c000 0x4000>;
507		interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
508			     <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
509		gpio-controller;
510		#gpio-cells = <2>;
511		interrupt-controller;
512		#interrupt-cells = <2>;
513	};

505行: 设置 gpio1 节点的 compatible 属性有两个,分别为“fsl,imx6ul-gpio”和“fsl,imx35-gpio”,在 Linux 内核中搜索这两个字符串就可以找到 I.MX6UL 的 GPIO 驱动程序。

506 行,的 reg 属性设置了 GPIO1 控制器的寄存器基地址为 0X0209C000

509 行,“gpio-controller”表示 gpio1 节点是个 GPIO 控制器。

510 行,“#gpio-cells”属性和“#address-cells”类似, #gpio-cells 应该为 2,表示一共有两个 cell,第一个 cell 为 GPIO 编号,比如“&gpio1 3”就表示 GPIO1_IO03。第二个 cell 表示GPIO 极 性 , 如 果 为 0(GPIO_ACTIVE_HIGH) 的 话 表 示 高 电 平 有 效 , 如 果 为1(GPIO_ACTIVE_LOW)的话表示低电平有效。

gpio子系统API函数

设置好设备树以后就可以使用 gpio 子系统提供的 API 函数来操作指定的 GPIO, gpio 子系统向驱动开发人员屏蔽了具体的读写寄存器过程。这就是驱动分层与分离的好处,大家各司其职,做好自己的本职工作即可。 gpio 子系统提供的常用的 API 函数有下面几个:

  1. gpio_request函数
  2. gpio_free函数
  3. gpio_direction_input函数
  4. gpio_directionn_output函数
  5. gpio_get_value函数
  6. gpio_set_value 函数

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: 要设置的值。
返回值: 无

设备树中添加gpio节点模块

  1. 创建test设备节点
    在根节点“/”下创建test设备子节点,如下所示:
    test {
    /*节点内容*/
    };
    
  2. 添加pinctrl信息
    在pinctrl子系统的笔记中,创建了pinctrl_test节点,此节点描述了test设备所使用的GPIO1_IO00 这个 PIN 的信息,我们要将这节点添加到 test 设备节点中,如下所示:
test {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_test>;
	/* 其他节点内容 */
};
  1. 添加GPIO属性信息
    我们最后需要在 test 节点中添加 GPIO 属性信息,表明 test 所使用的 GPIO 是哪个引脚,添加完成以后如下所示:
test {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_test>;
	gpio = <&gpio1 0 GPIO_ACTIVE_LOW>;
};

与 gpio 相关的 OF 函数

我们定义了一个名为“gpio”的属性, gpio 属性描述了 test 这个设备所使用的 GPIO。在驱动程序中需要读取 gpio 属性内容, Linux 内核提供了几个与 GPIO 有关的 OF 函数,常用的几个 OF 函数如下所示:

  1. of_gpio_named_count 函数
  2. of_gpio_count 函数
  3. of_get_named_gpio 函数

1.of_gpio_named_count 函数
of_gpio_named_count 函数用于获取设备树某个属性里面定义了几个 GPIO 信息,要注意的是空的 GPIO 信息也会被统计到,比如:

gpios = <0
		&gpio1 1 2
		0 &
		gpio2 3 4>;

上述代码的“gpios”节点一共定义了 4 个 GPIO,但是有 2 个是空的,没有实际的含义。通过 of_gpio_named_count 函数统计出来的 GPIO 数量就是 4 个,此函数原型如下:
int of_gpio_named_count(struct device_node *np, const char *propname)
函数参数和返回值含义如下:
np:设备节点。
propname:要统计的 GPIO 属性。
返回值: 正值,统计到的 GPIO 数量;负值,失败。

2. of_gpio_count 函数
和 of_gpio_named_count 函数一样,但是不同的地方在于,此函数统计的是“gpios”这个性的 GPIO 数量,而 of_gpio_named_count 函数可以统计任意属性的 GPIO 信息,函数原型如下所示:
int of_gpio_count(struct device_node *np)
函数参数和返回值含义如下:
np:设备节点。
返回值: 正值,统计到的 GPIO 数量;负值,失败

3.of_get_named_gpio 函数
此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号,此函数在驱动中使用很频繁!函数原型如下:
int of_get_named_gpio(struct device_node *np,const char *propname,int index)
函数参数和返回值含义如下:
np:设备节点。
propname:包含要获取 GPIO 信息的属性名。
index: GPIO 索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO 信息的话此参数为 0。
返回值: 正值,获取到的 GPIO 编号;负值,失败。

你可能感兴趣的:(linux,linux,内核,驱动程序)