目录
简介:
1、GPIO 子系统有两套API:
一、GPIO新、旧版互相兼容转换 API
1、转化函数
二、基于描述符接口(descriptor-based) (以"gpiod_"为前缀)
1、获取 GPIO
2.1 struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,enum gpiod_flags flags)
2.2 struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx, enum gpiod_flags flags)
2.3 struct gpio_descs *__must_check gpiod_get_array(struct device *dev, const char *con_id, enum gpiod_flags flags)
2、释放 GPIO
2.1 void gpiod_put(struct gpio_desc *desc)
3、设置/获取 GPIO 方向
3.1 int gpiod_direction_input(struct gpio_desc *desc)
3.2 int gpiod_direction_output(struct gpio_desc *desc, int value)
3.3 int gpiod_get_direction(struct gpio_desc *desc)
4、设置/获取 GPIO 值
4.1 原子方式
4.1.1 int gpiod_get_value(const struct gpio_desc *desc)
4.1.2 void gpiod_set_value(struct gpio_desc *desc, int value)
4.2 队列方式
4.2.1 int gpiod_get_value_cansleep(const struct gpio_desc *desc)
4.2.2 void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
4.2.3 int gpiod_cansleep(const struct gpio_desc *desc)
5、GPIO IRQ 设为中断
5.1 int gpiod_to_irq(const struct gpio_desc *desc)
5.2 static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
三、legency接口(以"gpio_"为前缀)
1、申请 / 释放 GPIO
1.1 int gpio_request(unsigned gpio, const char *label)
1.2 void gpio_free(unsigned gpio)
2、设置 GPIO 方向
2.1 int gpio_direction_input(unsigned gpio)
2.2 int gpio_direction_output(unsigned gpio, int value)
3、设置 / 获取 GPIO 值
3.1 gpio_get_value
3.2 gpio_set_value
4、判断 GPIO 是否有效
4.1 static inline bool gpio_is_valid(int number)
5、导出 GPIO 到 sysfs
5.1 static inline int gpio_export(unsigned gpio, bool direction_may_change)
5.2 static inline void gpio_unexport(unsigned gpio)
6、GPIO IRQ
6.1 int gpio_to_irq(unsigned gpio)
6.2 int irq_to_gpio(unsigned irq)(尽量避免使用)
6.3 static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
7、示例
7.1 输出
7.2 输入
7.3 中断
四、gpio_set_value获取物理电平,gpiod_set_value据 active-low 设置逻辑电平
简介:
在 Linux 内核中,GPIO(General Purpose Input/Output,通用输入/输出)的管理方式经历了从 旧版 gpio_*
接口 到 新版 gpiod_*
接口 的演变
1、GPIO 子系统有两套API:
- 前缀为 gpiod_。
- 使用 gpio_desc 几个题来表示一个引脚。
- 参考文档:Documentation/gpio/consumer.txt
- 前缀为 gpio_。
- 使用一个整数来表示一个引脚。
- 参考文档:Documentation/gpio/gpio-legacy.txt
特性 | gpio_* 旧接口 | gpiod_* 新接口 |
---|---|---|
标识方式 | 全局 GPIO 编号(如 120 ) | 描述符(struct gpio_desc ) |
设备树支持 | 不友好 | 原生支持 |
资源管理 | 手动 gpio_request/free | 自动释放(devm_* 可选) |
休眠安全 | 无 | 提供 _cansleep 版本 |
适用场景 | 旧内核或简单硬件 | 现代驱动开发(推荐) |
一、GPIO新、旧版互相兼容转换 API
-
旧版API是使用整数标记引脚的;
-
新版API是使用字符标记引脚的。
但是引脚都是唯一的,所以两者可以相互转化。
1、转化函数
源码路径:drivers\gpio\gpiolib.c
功能:通过引脚 gpio_desc 结构体指针 获取引脚 GPIO 编号;
功能:通过引脚 GPIO 编号获取引脚 gpio_desc 结构体指针;
二、基于描述符接口(descriptor-based) (以"gpiod_"为前缀)
参考文档:Documentation/gpio/consumer.txt
desc_to_gpio
devm_get_gpiod_from_child
devm_fwnode_get_gpiod_from_child
devm_gpiod_get
devm_gpiod_get_array
devm_gpiod_get_array_optional
devm_gpiod_get_index
devm_gpiod_get_index_optional
devm_gpiod_get_optional
devm_gpiod_put
devm_gpiod_put_array
fwnode_get_named_gpiod
gpio_to_desc
gpiod_cansleep
gpiod_count
gpiod_direction_input
gpiod_direction_output
gpiod_direction_output_raw
gpiod_export
gpiod_export_link
gpiod_get
gpiod_get_array
gpiod_get_array_optional
gpiod_get_direction
gpiod_get_index
gpiod_get_index_optional
gpiod_get_optional
gpiod_get_raw_value
gpiod_get_raw_value_cansleep
gpiod_get_value
gpiod_get_value_cansleep
gpiod_is_active_low
gpiod_put
gpiod_put_array
gpiod_set_array_value
gpiod_set_array_value_cansleep
gpiod_set_debounce
gpiod_set_raw_array_val
gpiod_set_raw_array_val
gpiod_set_raw_value
gpiod_set_raw_value_cansleep
gpiod_set_value
gpiod_set_value_cansleep
gpiod_to_irq
gpiod_unexport
1、获取 GPIO
- gpiod_get / devm_gpiod_get
- gpiod_get_index / devm_gpiod_get_index
- gpiod_get_array / devm_gpiod_get_array
前缀为"devm_"表示自动释放资源机制。资源是属于设备的,设备不存在时资源就可以自动释放
背景:在Linux驱动开发过程中,先申请了GPIO,再申请内存,如果内存申请失败,那么在返回之前就需要先释放GPIO资源。如果使用的是devm相关函数,在内存申请失败时可以直接返回,设备的销毁函数会自动地释放已经申请了的GPIO资源。
因此,建议使用devm相关函数操作GPIO。
2.1 struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,enum gpiod_flags flags)
- 功能:获取 dev 设备,con_id 的第 0 个引脚信息,并做 flags 初始化。 等价于 gpiod_get_index 的idx 为0。gpiod_get_index 函数调用了gpiod_request
- 参数:
- dev:设备指针。从该设备获取引脚信息;
- con_id:引脚组名称(不包含前缀 "-gpios")。如引脚组名为 reset-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>。则 con_id = "reset";
- flags:初始化标志
GPIOD_ASIS或0根本不初始化GPIO。稍后必须使用专用功能之一设置方 GPIOD_IN将GPIO初始化为输入。 GPIOD_OUT_LOW将GPIO初始化为值为0的输出。 GPIOD_OUT_HIGH将GPIO初始化为值为1的输出。 GPIOD_OUT_LOW_OPEN_DRAIN与GPIOD_OUT_LOW相同,但也强制该线路与开漏极电连接。 GPIOD_OUT_HIGH_OPEN_DRAIN与GPIOD_OUT_HIGH相同,但也强制线路与漏极开路电连接。
- 返回:
- 成功:gpio_desc 结构体指针。
- 失败:-ENOENT。具体可通过 IS_ERR() 获取返回码。
例一:GPIO输出
// 设备树示例:
// mygpio {
// compatible = "my-gpio-driver";
// led-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>, // GPIO1
// button-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>; // GPIO2
// };
在设备树中,"xxx-gpios"中的 "xxx" 代表定义的GPIO名(label)。
static int gpio_probe(struct platform_device *pdev) {... ...static struct gpio_desc *mygpio_led = NULL;int ret = 0;/* GPIO输出,"led"是对应设备树文件中name-gpios中的name */mygpio_led = gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW); //将GPIO初始化为输出值为0的if (IS_ERR(mygpio_led)) { //判断指针是否错误ret = PTR_ERR(mygpio_led); //返回错误码goto error_gpio;}gpiod_set_value(mygpio_led, 1); // 设置 GPIO 为高电平error_gpio:if (mygpio_led)gpiod_put(mygpio_led); // 释放 GPIOreturn ret;
}
例二:GPIO输入
static int gpio_probe(struct platform_device *pdev) {... ...static struct gpio_desc *mygpio_button = NULL;int ret = 0;/* GPIO输入 */mygpio_button = gpiod_get(&pdev->dev, "button", GPIOD_IN); //将GPIO初始化为输入if (IS_ERR(mygpio_button)) { //判断指针是否错误ret = PTR_ERR(mygpio_button); //返回错误码goto error_gpio;}// 读取 GPIO 值int value = gpiod_get_value(mygpio_button);
error_gpio:gpiod_put(mygpio_button); // 释放 GPIOreturn ret;
}
2.2 struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx, enum gpiod_flags flags)
- 功能:获取 dev 设备,con_id 的第 idx 个引脚信息,并做 flags 初始化。gpiod_get_index 函数调用了gpiod_request
- 举例:
// 设备树示例:
// leds {
// compatible = "my-led-driver";
// led-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>, // LED1
// <&gpio2 3 GPIO_ACTIVE_HIGH>; // LED2
// };
static int led_probe(struct platform_device *pdev) {... ...int num_leds = gpiod_count(&pdev->dev, "led"); //获取 led-gpios 中 GPIO 数量for (int i = 0; i < num_leds; i++) {struct gpio_desc *led = gpiod_get_index(&pdev->dev, "led", i, GPIOD_OUT_LOW);// 控制每个 LED...}... ...
}
2.3 struct gpio_descs *__must_check gpiod_get_array(struct device *dev, const char *con_id, enum gpiod_flags flags)
- 功能:获取 dev 设备 con_id 的所有引脚信息,并做 flags 初始化
例一: 获取 GPIO 数组,并控制输出电平
//leds {
// compatible = "gpio-leds";
// led-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>, // LED0
// <&gpio 11 GPIO_ACTIVE_HIGH>, // LED1
// <&gpio 12 GPIO_ACTIVE_HIGH>; // LED2
//};
static int led_probe(struct platform_device *pdev)
{... ...struct gpio_descs *leds = NULL;//获取 GPIO 数组("led"是对应设备树文件中name-gpios中的name)leds = gpiod_get_array(&pdev->dev, "led", GPIOD_OUT_LOW); if (IS_ERR(leds)) { //判断指针是否错误ret = PTR_ERR(leds); //返回错误码goto error_gpios;}// 设置所有 GPIO 为高电平(点亮 LED)for (int i = 0; i < leds->ndescs; i++) {gpiod_set_value(leds->desc[i], 1);}error_gpios:if (leds) gpiod_put_array(leds); // 使用完后释放return ret;
}
例二:获取 GPIO 数组,读取buttons输入状态
//buttons {
// buttons-gpios = <&gpio 5 GPIO_ACTIVE_LOW>, // BTN0
// <&gpio 6 GPIO_ACTIVE_LOW>; // BTN1
//};
static int led_probe(struct platform_device *pdev)
{... ...struct gpio_descs *buttons;buttons = gpiod_get_array(&pdev->dev, "buttons", GPIOD_IN);if (IS_ERR(buttons)) { //判断指针是否错误ret = PTR_ERR(buttons); //返回错误码goto error_gpios;}// 读取所有按钮状态for (int i = 0; i < buttons->ndescs; i++) {int val = gpiod_get_value(buttons->desc[i]);printk("Button %d: %d\n", i, val);}error_gpios:if (buttons)gpiod_put_array(buttons); // 释放return ret;
}
其它获取GPIO的函数:
-
gpiod_get_optional:和 gpiod_get 差不多。不同的是该函数返回 gpio_desc 结构体指针 或 NULL。如果 GPIO 不存在或无法获取,函数返回 NULL(而不是错误),驱动可以安全地忽略该 GPIO。例如:
struct gpio_desc *desc;desc = gpiod_get_optional(&pdev->dev, "debug-enable", GPIOD_OUT_HIGH);
if (!desc) {dev_info(&pdev->dev, "Debug GPIO not available, skipping\n");// 可以继续执行,不报错
} else {// 如果 GPIO 存在,则使用gpiod_set_value(desc, 1);
}
-
devm_xxx:以上函数均可添加 devm_ 前缀。比以上函数多了绑定设备dev,设备被删除时,自动释放引脚。
2、释放 GPIO
-
gpiod_put / devm_gpiod_put
-
gpiod_put_array / devm_gpiod_put_array
2.1 void gpiod_put(struct gpio_desc *desc)
- 功能:释放 desc 引脚
3、设置/获取 GPIO 方向
- gpiod_direction_input
- gpiod_direction_output
- gpiod_get_direction
3.1 int gpiod_direction_input(struct gpio_desc *desc)
- 功能:设置该引脚为输入方向
- 返回值:成功返回值为零,否则返回值为负的错误码
3.2 int gpiod_direction_output(struct gpio_desc *desc, int value)
- 功能:设置该引脚为输入方向。
- 参数:
- value:设置方向后的初始值。
- 返回值:成功返回值为零,否则返回值为负的错误码
3.3 int gpiod_get_direction(struct gpio_desc *desc)
- 功能:获取引脚方向。
- 返回值:
- 0(GPIOF_DIR_OUT):输出
- 1(GPIOF_DIR_IN):输入
4、设置/获取 GPIO 值
注意:"gpiod_"方式设置 GPIO 值需考虑 DTS 中 active-low状态。当 GPIO 设置为 GPIO_ACTIVE_LOW 低电平有效时,即是低电平为逻辑 1,高电平为逻辑 0。gpiod_set_value 设置 1,就是将gpio拉低(物理电平)。这里一定要区分逻辑电平和物理电平的差别。
led-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>, //低有效,低电平为逻辑1
使用方法
gpiod_set_value_cansleep(led_gpio, 1); //输出逻辑有效,设备树active 有效状态是 GPIO_ACTIVE_LOW,因此输出低电平
因为DTS里面的active 状态是 GPIO_ACTIVE_LOW,所以这个代码输出的是低电平。
当然,也有直接控制物理电平的方式,内核 Documentation/driver-api/gpio/consumer.rst 文件有相关介绍
Function (example) | line property | physical line |
gpiod_set_raw_value(desc, 0); | don't care | low |
gpiod_set_raw_value(desc, 1); | don't care | high |
gpiod_set_value(desc, 0); | default (active high) | low |
gpiod_set_value(desc, 1); | default (active high) | high |
gpiod_set_value(desc, 0); | active low | high |
gpiod_set_value(desc, 1); | active low | low |
gpiod_set_value(desc, 0); | default (active high) | low |
gpiod_set_value(desc, 1); | default (active high) | high |
gpiod_set_value(desc, 0); | open drain | low |
gpiod_set_value(desc, 1); | open drain | high impedance |
gpiod_set_value(desc, 0); | open source | high impedance |
gpiod_set_value(desc, 1); | open source | high |
gpiod_set_raw-value:忽略 DTS 中的 ACTIVE,直接输出物理电平
int gpiod_get_raw_value(const struct gpio_desc *desc);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
设置 / 获取GPIO 有两种访问方式:
-
原子方式:一种是通过储存器读写实现的,这种操作属于原子操作,不需要等待,所以可以在中断处理程序中使用
int gpiod_get_value(const struct gpio_ _desc *desc);
void gpiod_set_value(struct gpio_ desc *desc, int value);
-
队列方式:访问必须通过消息总线比如I2C或者SPI,这种访问需要在总线访问队列中等待,所以可能进入睡眠,此类访问不能出现在IRQ handler
int gpiod_cansleep(const struct gpio_desc *desc) //分辨该引脚是否需要通过消息总线访问
int gpiod_get_value_cansleep(const struct gpio_desc *desc)
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
4.1 原子方式
4.1.1 int gpiod_get_value(const struct gpio_desc *desc)
- 功能:获取引脚逻辑值
- 返回值:
- 成功:非负数:0-逻辑无效, 非0-逻辑有效
- 失败:负数
4.1.2 void gpiod_set_value(struct gpio_desc *desc, int value)
- 功能:设置引脚逻辑值,1-有效,0-无效,例如设备树 active 为 GPIO_ACTIVE_LOW 时,1有效表示低电平
4.2 队列方式
队列方式:在队列中等待访问引脚,可能会进入睡眠,不能作用于中断。访问必须通过消息总线比如I2C或者SPI,这些需要在队列中访问。
4.2.1 int gpiod_get_value_cansleep(const struct gpio_desc *desc)
- 功能:获取gpio逻辑值,支持休眠
4.2.2 void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
- 功能:设置gpio逻辑值,支持休眠
4.2.3 int gpiod_cansleep(const struct gpio_desc *desc)
- 功能:可以使用 gpiod_cansleep() 函数分辨该引脚是否需要通过消息总线访问
5、GPIO IRQ 设为中断
request_irq(gpiod_to_irq(gpio_desc)...); //将gpio转为对应的irq,然后注册该irq的中断handlerint gpio_probe(struct platform_device *pdev)
{... ...gpio_desc = devm_gpiod_get(&pdev->dev, "button", GPIOD_IN); //配置成输入if (gpio_desc) {ret = request_irq(gpiod_to_irq(gpio_desc), gpio_test_irq, IRQF_TRIGGER_FALLING, "gpio Detect", dev);}... ...
}//中断服务函数
static irqreturn_t gpio_test_irq(int irq, void *devid)
{schedule_work(&bat_work);return IRQ_HANDLED;
}
5.1 int gpiod_to_irq(const struct gpio_desc *desc)
- 功能:获取该引脚对应的 IRQ number。
- 返回:
- 成功:中断号。
- 失败:负数。
5.2 static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
- 功能:请求中断,需先配置为输入GPIO
- 参数:
- irq:中断号。
- handler:中断回调函数。
- flags:中断类型。
- IRQF_TRIGGER_RISING 上升沿触发 按键、信号检测
- IRQF_TRIGGER_FALLING 下降沿触发 按键释放检测
- IRQF_TRIGGER_HIGH 高电平触发 电平敏感设备
- IRQF_TRIGGER_LOW 低电平触发 低电平有效设备
- IRQF_SHARED 共享中断 多个设备共享同一条中断线
- IRQF_ONESHOT 单次触发 线程化中断需要保持禁用直到线程完成
- IRQF_NO_THREAD 禁止线程化 要求快速处理的中断
- IRQF_NOBALANCING 禁止CPU负载均衡 需要固定CPU处理的中断
- name:请求中断的设备名称。
- dev:可取任意值。
- 但必须唯一能够代表发出中断请求的设备。
- 通常取描述该设备的结构体,或NULL。
- 用于共享中断时。(若中断被共享,则不能为 NULL)
三、legency接口(以"gpio_"为前缀)
- 申请、释放:gpio_request、gpio_free
- 设置GPIO方向:gpio_direction_input、gpio_direction_output
- 获取设置GPIO值:gpio_get_value、gpio_set_value
- 设置为中断:gpio_to_irq
- 导出到sys文件系统:gpio_export
1、申请 / 释放 GPIO
1.1 int gpio_request(unsigned gpio, const char *label)
- 功能:申请 GPIO
- 参数:
- gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信息,此函数会返回这个 GPIO 的标号。
- label:给 gpio 设置个名字。
- 返回值:
- 0,申请成功;其他值,申请失败
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
标志 | 含义 |
GPIOF_IN | 设置为输入方向 |
GPIOF_OUT_INIT_LOW | 设置为输出,初始低电平 |
GPIOF_OUT_INIT_HIGH | 设置为输出,初始高电平 |
GPIOF_OPEN_DRAIN | 开漏输出 |
GPIOF_OPEN_SOURCE | 开源输出 |
GPIOF_EXPORT | 同时导出到 sysfs |
1.2 void gpio_free(unsigned gpio)
- 功能:释放 GPIO
- 参数:
- gpio:要释放的 gpio 标号。
2、设置 GPIO 方向
2.1 int gpio_direction_input(unsigned gpio)
- 功能:把 gpio 引脚设置为为输入方向
- 参数:
- gpio:要设置为输入的 GPIO 标号。
- 返回值:
- 0,设置成功;负值,设置失败
2.2 int gpio_direction_output(unsigned gpio, int value)
- 功能:把 gpio 引脚设置为为输出方向
- 参数:
- gpio:要设置为输出的 GPIO 标号。
- value: GPIO 默认输出值。
- 返回值:
- 0,设置成功;负值,设置失败
3、设置 / 获取 GPIO 值
3.1 gpio_get_value
#define gpio_get_value __gpio_get_valuestatic inline int __gpio_get_value(unsigned gpio)
{return gpiod_get_raw_value(gpio_to_desc(gpio)); //这里也是用的 "gpiod_" 的 gpiod_get_raw_value 获取物理电平
}
- 功能:调用 gpiod_get_raw_value 获取GPIO物理电平
- 参数
- gpio:要获取的 gpio编号
- 返回值:
- 非负值,得到的 GPIO 值;负值,获取失败。
3.2 gpio_set_value
#define gpio_set_value __gpio_set_valuestatic inline void __gpio_set_value(unsigned gpio, int value)
{return gpiod_set_raw_value(gpio_to_desc(gpio), value); //这里用 "gpiod_" 的 gpiod_get_raw_value 设置物理电平
}
- 功能:设置GPIO物理电平
- 参数:
- gpio:要设置的 GPIO 标号
- value: 要设置的值,1-高电平,0-低电平
4、判断 GPIO 是否有效
4.1 static inline bool gpio_is_valid(int number)
- 功能:判断 GPIO number 是否有效。
- 返回值:
- 有效:1
- 无效:0
5、导出 GPIO 到 sysfs
将 gpio 通过 sys 文件系统导出,应用层可以通过文件操作gpio。如查看状态、设置状态等等。
主要用于调试,导出后访问路径:/sys/class/gpio 下
源码路径:include\linux\gpio.h
5.1 static inline int gpio_export(unsigned gpio, bool direction_may_change)
- 功能:把该 gpio 导出到 sys。
- 参数:
- gpio:GPIO number。
- direction_may_change:表示用户是否可以改变方向。
5.2 static inline void gpio_unexport(unsigned gpio)
- 功能:取消导出
- 参数:
- gpio:GPIO number
6、GPIO IRQ
6.1 int gpio_to_irq(unsigned gpio)
- 功能:获取该引脚对应的 IRQ number。
- 返回:
- 成功:中断号
- 失败:负数
6.2 int irq_to_gpio(unsigned irq)(尽量避免使用)
- 功能:获取该引脚对应的 GPIO number。
- 返回:
- 成功:gpio号
- 失败:负数
6.3 static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
- 功能:请求中断
- 参数:
- irq:中断号。
- handler:中断回调函数。
- flags:中断类型。
- name:请求中断的设备名称。
- dev:可取任意值。
- 但必须唯一能够代表发出中断请求的设备。
- 通常取描述该设备的结构体,或NULL。
- 用于共享中断时。(若中断被共享,则不能为 NULL)
7、示例
7.1 输出
static int gpio_out_probe(struct platform_device *pdev)
{int ret = 0;int gpio;/* 从设备树中获取GPIO */gpio = of_get_named_gpio(pdev->dev.of_node, "reset-gpios", 0);if(!gpio_is_valid(gpio)) {dev_err(dev, "%s: of_get_named_gpio (%d)\n", __func__, ret);return -EINVAL;}/* 申请使用GPIO */ret = gpio_request(gpio, "reset");if(ret < 0) {dev_err(dev, "%s: unable to request reset_gpio (%d)\n", __func__, ret);return ret;}/* 将GPIO设置为输出模式并设置GPIO初始化物理电平状态为低电平 */gpio_direction_output(gpio, 0);#if 1if (gpio) {if (gpiod_cansleep(gpio_to_desc(gpio)))gpio_set_value_cansleep(gpio, 0);elsegpio_set_value(gpio, 0); //输出低电平}
#elsegpio_set_value(gpio, 1); //输出高电平
#endifgpio_free(gpio); //释放gpioreturn 0;
}
7.2 输入
static int gpio_in_probe(struct platform_device *pdev)
{int ret = 0;int gpio;int value;/* 从设备树中获取GPIO */gpio = of_get_named_gpio(pdev->dev.of_node, "key-gpios", 0);if(!gpio_is_valid(gpio)) {dev_err(dev, "%s: of_get_named_gpio (%d)\n", __func__, ret);return -EINVAL;}/* 申请使用GPIO */ret = gpio_request(gpio, "key");if(ret < 0) {dev_err(dev, "%s: unable to request reset_gpio (%d)\n", __func__, ret);return ret;}/* 将GPIO设置为输入模式 */ret = gpio_direction_input(gpio);if(ret < 0) {printk("can't set gpio!\r\n");return ret;}/* 获取gpio电平状态 */value = gpio_get_value(gpio);gpio_free(gpio); //释放gpioreturn 0;
}
7.3 中断
static int gpio_in_probe(struct platform_device *pdev)
{priv->ser_errb = of_get_named_gpio(ser, "ser-errb", 0);ret = devm_gpio_request_one(&client->dev, priv->ser_errb, GPIOF_DIR_IN, "GPIO_MAXIM_SER");if (ret < 0) {dev_err(dev, "%s: GPIO request failed\n ret: %d", __func__, ret);return ret;}if (gpio_is_valid(priv->ser_errb)) {priv->ser_irq = gpio_to_irq(priv->ser_errb);ret = request_threaded_irq(priv->ser_irq, NULL,gpio_irq_handler,IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "SER", priv);if (ret < 0) {dev_err(dev, "%s: Unable to register IRQ handler ret: %d\n", __func__, ret);return ret;}}return ret;
}static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{return IRQ_HANDLED;
}
四、gpio_set_value设置物理电平,gpiod_set_value根据 active-low 设置逻辑电平
gpioled {compatible = "led-gpio";led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; //gpio1组第3号引脚flag为低电平有效default-state = "on";status = "okay";
};
gpio_set_value、gpio_get_value 设置引脚物理电平(与active-low无关)。gpiod_set_value、gpiod_get_value 根据 active-low 设置逻辑电平
分析内核源码可以看到gpio_set_value、gpio_get_value最终分别调用 gpiod_set_raw_value 和 gpiod_get_raw_value
//arch/arm/include/asm/gpio.h
#define gpio_get_value __gpio_get_value
#define gpio_set_value __gpio_set_value//include/asm-generic/gpio.h
static inline void __gpio_set_value(unsigned gpio, int value)
{return gpiod_set_raw_value(gpio_to_desc(gpio), value);
}//drivers/gpio/gpiolib.c
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
{VALIDATE_DESC_VOID(desc);/* Should be using gpiod_set_raw_value_cansleep() */WARN_ON(desc->gdev->chip->can_sleep);gpiod_set_raw_value_commit(desc, value);
}//include/asm-generic/gpio.h
static inline int __gpio_get_value(unsigned gpio)
{return gpiod_get_raw_value(gpio_to_desc(gpio));
}//drivers/gpio/gpiolib.c
int gpiod_get_raw_value(const struct gpio_desc *desc)
{VALIDATE_DESC(desc);/* Should be using gpiod_get_raw_value_cansleep() */WARN_ON(desc->gdev->chip->can_sleep);return gpiod_get_raw_value_commit(desc);
}
gpiod_get_value 调用 gpiod_set_value_nocheck 函数中会判断 flags 为 FLAG_ACTIVE_LOW 就翻转 value。gpiod_set_value函数同理。
//get同理
int gpiod_get_value(const struct gpio_desc *desc)
{int value;VALIDATE_DESC(desc);/* Should be using gpiod_get_value_cansleep() */WARN_ON(desc->gdev->chip->can_sleep);value = gpiod_get_raw_value_commit(desc);if (value < 0)return value;if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))value = !value;return value;
}
参考链接:
在Linux驱动中使用gpio子系统 - schips - 博客园
嵌入式Linux中的 gpio、gpiod基本分析_51CTO博客_linux gpio操作
详解GPIO子系统_gpio子系统 设备树讲解-CSDN博客