GPIO子系统
GPIO子系统的作用
管理GPIO,既能支持芯片本身的GPIO,也能支持扩展的GPIO。提供统一的、简便的访问接口,实现:输入、输出、中断。
GPIO通用功能
可以设为输出:让它输出高低电平;
可以设为输入,读取引脚当前电平;
可以用来触发中断
对于芯片自带的GPIO,它的访问时很快的,可以在获得spinlocks的情况下操作它。但是,对于通过I2C、SPI等接口扩展的GPIO,访问它们时可能导致休眠,所以这些GPIO Expander就不能在获得spinlocks的情况下使用。
GPIO子系统的重要概念
GPIO Controller
芯片GPIO一般分为几组,例如GPIO1、GPIO2又或者GPIOA、GPIOB等等,而每一组GPIO下面又有多个引脚,比如GPIO1这组引脚,下面有32个脚可以用,定义为GPIO1_0~GPIO1_31 。
我们这里所说的GPIO Controller 就是GPIO组的概念,每个组就是一个GPIO Controller,举例说明如下图所示:



以imx6ull为例说明,gpio子系统中如何在设备树种定义:

在设备树中一般芯片原厂已经指定GPIO Controller,我们只需使用即可,比如上面的ds18b20设备
设备驱动程序使用GPIO子系统
GPIO子系统有两套接口:
基于描述符的(descriptor-based):函数都有前缀
gpiod_,它使用gpio_desc结构体来表示一个引脚需要包含头文件
#include <linux/gpio/consumer.h> // descriptor-based
老的(legacy):函数都有前缀
gpio_,它使用一个整数来表示一个引脚#include <linux/gpio.h> // legacy
两套驱动API接口如下对比如下
| descriptor-based | legacy | 说明 |
|---|---|---|
| 获得GPIO | ||
| gpiod_get | gpio_request | |
| gpiod_get_index | ||
| gpiod_get_array | gpio_request_array | |
| devm_gpiod_get | ||
| devm_gpiod_get_index | ||
| devm_gpiod_get_array | ||
| 设置方向 | ||
| gpiod_direction_input | gpio_direction_input | |
| gpiod_direction_output | gpio_direction_output | |
| 读值、写值 | ||
| gpiod_get_value | gpio_get_value | |
| gpiod_set_value | gpio_set_value | |
| 释放GPIO | ||
| gpio_free | gpio_free | |
| gpiod_put | gpio_free_array | |
| gpiod_put_array | ||
| devm_gpiod_put | ||
| devm_gpiod_put_array |
有前缀
devm_的含义是“设备资源管理”(Managed Device Resource),这是一种自动释放资源的机制。它的思想是资源是属于设备的,设备不存在时资源就可以自动释放。比如在Linux开发过程中,先申请了GPIO,再申请内存;如果内存申请失败,那么在返回之前就需要先释放GPIO资源。如果使用devm的相关函数,在内存申请失败时可以直接返回:设备的销毁函数会自动地释放已经申请了的GPIO资源。建议使用
devm_版本的相关函数
使用说明:
假如有下面的设备节点
foo_device {
compatible = "acme,foo";
...
led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */
<&gpio 16 GPIO_ACTIVE_HIGH>, /* green */
<&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */
power-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
};
在驱动中如何获取red green blue和power三个引脚的gpio呢
struct gpio_desc *red, *green, *blue, *power;
// 取led-gpios 里面的0 1 2索引的gpio 分别对应red green blue
red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH);
green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH);
blue = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH);
//取名字为power的gpio 对应为power
power = gpiod_get(dev, "power", GPIOD_OUT_HIGH);
注意:如果设置了GPIO_ACTIVE_LOW属性,说明激活的时候是低电平,也就是使用gpiod_set_value(gpio,1),对应的实际物理电平是低电平,gpiod_set_value(gpio,0)对应的实际物理电平是高电平。
实际如下:
sysfs中访问方法
在sysfs中访问GPIO,实际上用的就是引脚号,老的方法。
先确定引脚的所在组的基准引脚号
开发板的/sys/class/gpio目录下,找到各个gpiochipXXX目录

然后进入某个gpiochip目录,查看文件label的内容
根据label的内容对比设备树
所以gpio4这组引脚的基准引脚号就是96,这也可以
cat base来再次确认。

在命令行操作gpio
假如要读取那么GPIO4_14,号码是96+14=110,可以如下操作读取值
echo 110 > /sys/class/gpio/export echo in > /sys/class/gpio/gpio110/direction cat /sys/class/gpio/gpio110/value #读取值 echo 110 > /sys/class/gpio/unexport
假如设置GPIO4_14,号码是96+14=110,可以如下操作设置值
echo 110 > /sys/class/gpio/export echo out > /sys/class/gpio/gpio110/direction echo 1 > /sys/class/gpio/gpio110/value #设置high echo 0 > /sys/class/gpio/gpio110/value #设置低 echo 110 > /sys/class/gpio/unexport
C编程使用方法
#include <stdio.h> #include <unistd.h> #include <sys/stat.h> #include <linux/fs.h> #include <fcntl.h> #include <string.h> #include <termios.h> #include <sys/ioctl.h> // 等价于 echo 110 > /sys/class/gpio/export int gpio_fd; gpio_fd = open("/sys/class/gpio/export",O_WRONLY); write(gpio_fd,"110",strlen("110")); close(gpio_fd); //等价于echo out > /sys/class/gpio/gpio110/direction gpio_fd = open("/sys/class/gpio/gpio110/direction",O_RDWR); write(gpio_fd,"out",strlen("out")); close(gpio_fd); gpio_fd = open("/sys/class/gpio/gpio24/value",O_RDWR); write(gpio_fd,"1",strlen("1")); //等价于 echo 1 > /sys/class/gpio/gpio110/value write(gpio_fd,"0",strlen("0")); //等价于 echo 0 > /sys/class/gpio/gpio110/value close(gpio_fd);
GPIO子系统的层次

GPIO lib向上提供API接口
| descriptor-based | legacy | 说明 |
|---|---|---|
| 获得GPIO | ||
| gpiod_get | gpio_request | |
| gpiod_get_index | ||
| gpiod_get_array | gpio_request_array | |
| devm_gpiod_get | ||
| devm_gpiod_get_index | ||
| devm_gpiod_get_array | ||
| 设置方向 | ||
| gpiod_direction_input | gpio_direction_input | |
| gpiod_direction_output | gpio_direction_output | |
| 读值、写值 | ||
| gpiod_get_value | gpio_get_value | |
| gpiod_set_value | gpio_set_value | |
| 释放GPIO | ||
| gpio_free | gpio_free | |
| gpiod_put | gpio_free_array | |
| gpiod_put_array | ||
| devm_gpiod_put | ||
| devm_gpiod_put_array |
GPIO lib向下提供API接口

GPIO子系统核心数据结构
gpio_device

gpio_chip
控制引脚的函数
中断相关的函数
引脚信息:支持多少个引脚?各个引脚的名字?

gpio_desc
我们去使用GPIO子系统时,首先是获得某个引脚对应的gpio_desc,gpio_device表示一个GPIO Controller,里面支持多个GPIO,在gpio_device中有一个gpio_desc数组,每一引脚有一项gpio_desc。

如何编写GPIO Controller驱动程序
分配、设置、注册gpioc_chip结构体,示例:drivers\gpio\gpio-74x164.c

