一、GPIO重要概念

要想操作GPIO引脚,需要先把所用引脚配置成GPIO功能,这个通过pinctrl子系统来实现。然后可以根据设置的引脚的方向来读取引脚的值和设置输出值。GPIO子系统存在之前,我们驱动需要在代码中配置寄存器来使用GPIO引脚。再BSP工程师实现好GPIO子系统后,我们就可以在设备树中指定GPIO引脚,在驱动中使用GPIO子系统的标准函数来获取GPIO、设置GPIO方向、读取/设置GPIO的值。这样的驱动代码是于单板无关的。

二、GPIO内核相关文档

Documentationdevicetreeindingspinctrlpinctrl-bindings.txt
Documentationgpiogpio.txt
Documentationdevicetreeindingsgpiogpio.txt

三、GPIO设备树配置

1. BSP工程师实现的gpio驱动,驱动工程师直接在设备树中配置使用。

//client节点
device {
    led-gpios = <组, 哪个几个,flag>; //"组"是必须要有的元素,为gpio控制器的描述,这里除了组之外还有几个域是由组中的#gpio-cells的值决定的。
};

//service端,设备树中对一个gpio控制器的表示:
gpio1 {
    ......
    gpio-controller;
    #gpio-cells = <2>; //表示client使用gpio1这一组中的某个引脚时,除了组之外还需要使用2个整数来表示。
};

2. 举个例子:

foo_device {
    compatible = "acme,foo";
    ......
    led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH> /*red*/ //一般这里可能为&gpioX
        <&gpio 16 GPIO_ACTIVE_HIGH> /*green*/
        <&gpio 17 GPIO_ACTIVE_HIGH>; /*blue*/
    
    power-gpios = <&gpio, 1 GPIO_ACTIVE_LOW>; //注意这里使用了avtive_low属性了
};

驱动代码中:

gpiod_get_index(dev, "led", 0, GOIOD_OUT_HIGH); //取出设备树中名为led的gpio中的第0个引脚,也就是red。
gpiod_get_index(dev, "led", 1, GOIOD_OUT_HIGH); //取出第1个

若在设备树中只定义了一个引脚,就可以使用:

gpiod_get(dev, "power", GPIO_OUTPUT_HIGH); //把这个设备下名为power的那个引脚给取出来。

三、在驱动中使用GPIO

1.GPIO子系统有两套接口

(1) 一是基于描述符(descriptor-based)的,相关api函数都是以”gpiod_”为前缀,它使用gpio_desc结构来表示一个引脚。
(2) 另一种是老(legency)的,相关api函数都是以”gpio_”为前缀,它使用一个整数来表示一个引脚。

要操作一个引脚,首先要get引脚,然后设置方向,然后读取、写值。

2.列举操作GPIO常使用的函数

//1.获取GPIO
gpiod_get  //legency为gpio_request
gpiod_get_index
gpiod_get_array //legency为gpio_request_array
devm_gpiod_get
devm_gpiod_get_index
devm_gpiod_get_array

//2.设置方向
gpiod_direction_input  //legency为gpio_direction_input
gpiod_direction_output //legency为gpio_direction_input

//3.读值、写值
gpiod_get_value //legency为gpio_get_value
gpiod_set_value //legency为gpio_set_value

//4.释放GPIO
gpio_free //gpio_free
gpiod_put //gpio_free_array
gpiod_put_array
devm_gpiod_put
devm_gpiod_put_array

  前缀为”devm_”的含义是设备资源管理,这是一种自动释放资源的机制。它的思想是“资源是属于设备的,设备不存在时资源就可以自动释放”。在Linux驱动开发过程中,先申请了GPIO,再申请内存,如果内存申请失败,那么在返回之前就需要先释放GPIO资源。如果使用的是devm相关函数,在内存申请失败时可以直接返回,设备的销毁函数会自动地释放已经申请了的GPIO资源。建议使用devm相关函数操作GPIO。

3.如何通过GPIO号来使用GPIO

比如要通过gpio号来操作原理图上的GPIO5_14这个gpio引脚,那么得先知道这个gpio引脚的number是多少。那么得先找到gpio5组的基gpio number是多少。

/sys/class/gpio/# ls
export gpio30 gpiochip0 gpiochip32 gpiochip64 gpiochip96 gpiochip128 gpiochip504 unexport //gpiochip96 这一组gpio的基gpio号是96
/sys/class/gpio/gpiochip128# ls
base device label ngpio power subsystem uevent
/sys/class/gpio/gpiochip128# cat label //可以看出设备树节点名为gpio@20ac000,20ac000就是这一组gpio寄存器的基地址,可通过它查看每一组gpio的基gpio号
20ac000.gpio
/sys/class/gpio/gpiochip128# cat base //表示这一组gpio的基gpio号为128
128
/sys/class/gpio/gpiochip128# cat ngpio //表示这一组gpio的个数
32

在dtsi文件中检索20ac000就可以看到如下设备树配置,所以知道gpio5这一组gpio的基gpio号是128

gpio5: gpio@20ac000 {
    compatible = "fsl,im6ul-gpio", "fsl,imx35-gpio";
    reg = <0x020ac000 0x4000>;
    interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
    gpio-controller;
    #gpio-cells = <0x2>;
    interrupt-controller;
    #interrupt-cells = <0x2>;
};

那么GPIO5_14,其gpio号就是128+14=142,然后可以将这个gpio给导出来,设置其方向,配置其值,进行验证。

/sys/class/gpio# echo 142 > export
/sys/class/gpio# ls
export unexport gpio142  ... //此时可以看到多了一个gpio142目录
/sys/class/gpio/gpio142# ls
active_low direction power uevent device edge subsystem value //此时可以cat active_low 看是否具有active_low属性
/sys/class/gpio/gpio142# echo in > direction //设置为输入引脚
/sys/class/gpio/gpio142# cat value //读取其值
1
/sys/class/gpio# echo 142 > unexport //取消映射

如果某个引脚已经被使用了,再次export就会报错:“resource busy”。算出引脚号后就可以在驱动中使用legency函数来通过gpio号来操作gpio引脚了。

注意: Qcom平台,如果内核级驱动程序通过of_get_named_gpio()函数或类似函数获取,请求并使用该GPIO,则无法将该GPIO导出以进行sysfs控制。

五、active_low属性

  注意,设置的逻辑电平并不一定等于物理电平,因为有active_low属性,若在获取GPIO的时候指定了active_low属性,那么设置为1就是低电平,设置为0才是高电平。但是也有一些函数直接忽略active_low属性,整理如下:

unction(example)               active-low属性        物理电平
gpiod_set_raw_value(desc, 0)    don't care             0
gpiod_set_raw_value(desc, 1)    don't care             1
gpiod_set_value(desc, 0)        是                     1
gpiod_set_value(desc, 0)        否                     0
gpiod_set_value(desc, 1)        是                     0
gpiod_set_value(desc, 1)        否                     1