Linux驱动开发———GPIO有效电平解析

目录

          • 前言
          • 1、什么是有效电平
          • 2、设备树中的GPIO属性
          • 3、有效电平的定义
          • 4、GPIO有效电平的实现
          • 总结

前言

        本文为作者学习记录,如有错误之处欢迎指出!下文将以imx6ull芯片、Linux4.1.15内核中的led-gpio驱动作为例子对gpios属性的中的有效电平进行分析。
ps:驱动路径drivers/leds/leds-gpio.c

1、什么是有效电平

        以单片机的思维来讲,对GPIO的控制一般是:

输入 输出
1 高电平
0 低电平

        下面两张led原理图(图片来源于网络),一张为高电平有效,一张为低电平有效。当GPIO输出对应的电平时led亮,为了实现统一的接口,让1表示灯亮、0表示灯灭,就需要用到有效电平。加入有效电平Led真值表如下所示。
Linux驱动开发———GPIO有效电平解析_第1张图片Linux驱动开发———GPIO有效电平解析_第2张图片
Led真值表

led 输入值 电平值 状态
高电平有效LED 1 高电平
高电平有效LED 0 低电平
低电平有效LED 1 低电平
低电平有效LED 0 高电平
2、设备树中的GPIO属性

        由内核的说明文档(Documentation/devicetree/bindings/gpio/gpio.txt)可知,设备树中的GPIO属性主要由三部分组成:
(1)GPIO控制器
(2)第几个GPIO
(3)有效电平
如下图表示gpio4-16,高电平有效
Linux驱动开发———GPIO有效电平解析_第3张图片

3、有效电平的定义

        在include/dt-bindings/gpio/gpio.h有定义如下:
Linux驱动开发———GPIO有效电平解析_第4张图片

4、GPIO有效电平的实现

        以led-gpios驱动为例,设备树中的led节点匹配驱动成功后,会调用drivers/leds/leds-gpio.c中的gpio_led_probe函数:

// 判断是否使用设备树
if (pdata && pdata->num_leds) {
		......
} else {
		priv = gpio_leds_create(pdev);
		if (IS_ERR(priv))
			return PTR_ERR(priv);
}

        在gpio_leds_create函数中会调用device_for_each_child_node函数遍历led节点下的每一个子节点,一个子节点表示一个led:

device_for_each_child_node(dev, child) {
	...... 
		led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);
		if (IS_ERR(led.gpiod)) {
			fwnode_handle_put(child);
			ret = PTR_ERR(led.gpiod);
			goto err;
		}
	......
}

        在devm_get_gpiod_from_child函数中会去获取gpios属性的值:

	for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
	    /* 构造led属性的字段名,比如在设备树定义了 led-gpios属性,con-id可传入"led" */
		if (con_id)
			snprintf(prop_name, sizeof(prop_name), "%s-%s",
					    con_id, suffixes[i]);
		else
			snprintf(prop_name, sizeof(prop_name), "%s",
							       suffixes[i]);

		/* 构造gpiod结构体 */
		desc = fwnode_get_named_gpiod(child, prop_name);
		if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
			break;
	}

        在fwnode_get_named_gpiod函数中会去获取gpios的flag并记录,当设置了GPIO_ACTIV_LOWgpio_desc中的flags就会被设置

	.......
	if (is_of_node(fwnode)) {
		enum of_gpio_flags flags;

		desc = of_get_named_gpiod_flags(of_node(fwnode), propname, 0,
						&flags);
		if (!IS_ERR(desc))
			active_low = flags & OF_GPIO_ACTIVE_LOW;
	}
	.......
	if (active_low)
		set_bit(FLAG_ACTIVE_LOW, &desc->flags);

        调用gpiod_set_value设置gpio的输出时,会先判断flags的值:

void gpiod_set_value(struct gpio_desc *desc, int value)
{
	if (!desc)
		return;
	/* Should be using gpio_set_value_cansleep() */
	WARN_ON(desc->chip->can_sleep);
	if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
		value = !value;
	_gpiod_set_raw_value(desc, value);
}

        gpiod_direction_output配置GPIO为输出时会设置其默认状态,也会去判断flags的值:

int gpiod_direction_output(struct gpio_desc *desc, int value)
{
	if (!desc || !desc->chip) {
		pr_warn("%s: invalid GPIO\n", __func__);
		return -EINVAL;
	}
	if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
		value = !value;
	return _gpiod_direction_output_raw(desc, value);
}
总结

        以上就是设备树中gpios属性配置有效电平的分析过程,欢迎关注我的微信公众号一起讨论!
Linux驱动开发———GPIO有效电平解析_第5张图片

你可能感兴趣的:(驱动开发,linux,学习记录,驱动开发,linux,设备树,gpio)