天天看点

GPIO系列(2)——Linux的GPIO控制“gpiod_”和“gpio_”浅析Devicetree获取GPIO控制GPIO总结

Linux的GPIO控制“gpiod_”和“gpio_”浅析

  • Devicetree
  • 获取GPIO
    • of函数获取
    • gpiod_函数获取
  • 控制GPIO
    • gpio_函数控制
    • gpiod_函数控制
  • 总结

平台:MT6739

Kernel:4.4

新linux内核对于gpio的控制,从以前的旧的“gpio_”开头的函数,已经变为“gpiod_”开头的函数,本文简单对比分析下。

具体文档可以查看:

kernel-4.4\Documentation\gpio\consumer.txt

kernel-4.4\Documentation\gpio\gpio-legacy.txt

Devicetree

设备树的变化不大,基本还是下面的结构:

gpio1: gpio1 {
		gpio-controller
		#gpio-cells = <2>;
	};
	gpio2: gpio2 {
		gpio-controller
		#gpio-cells = <1>;
	};
	[...]
	//设备节点内要声明的gpio属性示例
	enable-gpios = <&gpio2 2>;
	data-gpios = <&gpio1 12 0>,
		     <&gpio1 13 0>,
		     <&gpio1 14 0>,
		     <&gpio1 15 0>;
           

设备节点内声明的gpio属性(gpios property),格式保持没变,每个list的第一个元素依然是具体IO的controller的句柄,剩下的元素个数,由对应controller的“#gpio-cells”属性的值决定,常用的是“#gpio-cells = < 2 >;”。一个为IO的number,一个为IO需要配置的状态,0 = GPIO_ACTIVE_HIGH,具体定义见dts目录下的 include/dt-bindings/gpio/gpio.h 文件。

有变化的地方在gpio属性的命名,就是上面示例的“enable-gpios”和“data-gpios”。之前旧的版本,建议命名方式为“[< name >-]gpios”,其实也可以不用按这种方式命名,在源码获取gpio的时候,用比如“of_get_named_gpio”这种可以传name进去的函数,把自己的gpio属性名字传进去,也可以获取到gpio。例如:

gpio-pin-id = <&gpio 1 GPIO_ACTIVE_HIGH>;
//将gpio属性的名字“gpio-pin-id”传入下面函数
id_gpio = of_get_named_gpio(pdev->dev.of_node, "gpio-pin-id", 0);
           

但是,用新的函数的话,建议按照推荐的命名方式,这样可以不用“of”函数,直接获取到需要的gpio,例如:

enable-gpios = <&gpio2 2>;
//将gpio属性的名字前缀“enable”传入下面函数
enable = gpiod_get(&(pdev->dev), "enable", GPIOD_IN);
           

以上内容可以查看文档:

kernel-4.4\Documentation\devicetree\bindings\gpio\gpio.txt

kernel-4.4\Documentation\gpio\board.txt

获取GPIO

之前旧的方式从dts获取gpio,基本都要用of函数,现在新的方式可以直接使用“gpiod_”开头的函数,例如,有如下3种命名方式的gpio属性:

gpios = <&gpio1 12 0>;
enable-gpios = <&gpio2 2>;
gpio-pin-id = <&gpio 1 GPIO_ACTIVE_HIGH>;
           

of函数获取

头文件:kernel-4.4\include\linux\of_gpio.h

int of_get_gpio(struct device_node *np, int index);

int of_get_gpio_flags(struct device_node *np, int index,enum of_gpio_flags *flags);

上面这两个函数只能获取的“gpios”的这个gpio,因为这两个函数,最终都是调用“of_get_named_gpio_flags”,并默认传入了“gpios”这个name。

static inline int of_get_gpio(struct device_node *np, int index)
{
	return of_get_gpio_flags(np, index, NULL);
}
static inline int of_get_gpio_flags(struct device_node *np, int index,
		      enum of_gpio_flags *flags)
{
	return of_get_named_gpio_flags(np, "gpios", index, flags);
}
           

int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index, enum of_gpio_flags *flags);

这个函数可以获取到以上3种gpio,只要将对应的name传入这个函数就行。

gpiod_函数获取

头文件:kernel-4.4\include\linux\gpio\consumer.h

源文件:kernel-4.4\drivers\gpio\gpiolib.c

使用“gpiod_”函数,只能直接获取到“gpios”和“enable-gpios”这两个gpio,例如函数“gpiod_get_index”:

struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
					       const char *con_id,
					       unsigned int idx,
					       enum gpiod_flags flags)
{
	......
	if (dev) {
		/* Using device tree? */
		if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
			dev_dbg(dev, "using device tree for GPIO lookup\n");
			//注意这个函数“of_find_gpio”
			desc = of_find_gpio(dev, con_id, idx, &lookupflags);
		} else if (ACPI_COMPANION(dev)) {
			dev_dbg(dev, "using ACPI for GPIO lookup\n");
			desc = acpi_find_gpio(dev, con_id, idx, &lookupflags);
		}
	}
	......
}
           

这个函数调用到了下面一个函数“of_find_gpio”:

static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
				      unsigned int idx,
				      enum gpio_lookup_flags *flags)
{
	......
	for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
		//这里处理“gpiod_”函数传进来的name
		if (con_id)
			snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id,
				 gpio_suffixes[i]);
		else
			snprintf(prop_name, sizeof(prop_name), "%s",
				 gpio_suffixes[i]);

		desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
						&of_flags);
		if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
			break;
	}
	......
}
           

可以看到,这里将“gpiod_”函数传进来的name加了后缀,这个后缀就是“gpios”,如果name为空的话,就只有一个后缀。所以,当“gpiod_”函数传入的name为“enable”时,得到“enable-gpios”这个gpio,当传入的name为“”时,得到“gpios”这个gpio。

那么还有一个“gpio-pin-id”其实也是可以用“gpiod_”函数去控制的,不过先要用旧方式of函数获取到旧类型的gpio,然后使用函数:

struct gpio_desc *gpio_to_desc(unsigned gpio);

来获取到新类型的gpio。

控制GPIO

gpio_函数控制

一般的流程是,先请求一个gpio,判断是否获取到了:

int gpio_request(unsigned gpio, const char *label);

ret = gpio_request(gpio_num, "gpio");
if (ret) {
	pr_err("Could not get GPIO %d", gpio_num);
	return ret;
}
           

然后初始化为输入或者输出:

int gpio_direction_input(unsigned gpio);

int gpio_direction_output(unsigned gpio, int value);

if (input) {
	if (gpio_direction_input(gpio_num)) {
		pr_err("Could not set direction of GPIO %d to input", gpio_num);
		goto err;
	}
} else {
	if (gpio_direction_output(gpio_num, 0)) {
		pr_err("Could not set direction of GPIO %d to output", gpio_num);
		goto err;
	}
}
           

然后单独控制输出改变,或者获取输入状态:

int gpio_get_value(unsigned gpio);

void gpio_set_value(unsigned gpio, int value);

使用完之后,再释放gpio:

void gpio_free(unsigned gpio);

gpiod_函数控制

依然以函数“gpiod_get_index”为例:

struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
					       const char *con_id,
					       unsigned int idx,
					       enum gpiod_flags flags)
{
	struct gpio_desc *desc = NULL;
	int status;
	enum gpio_lookup_flags lookupflags = 0;

	dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id);

	if (dev) {
		/* Using device tree? */
		if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
			dev_dbg(dev, "using device tree for GPIO lookup\n");
			desc = of_find_gpio(dev, con_id, idx, &lookupflags);
		} else if (ACPI_COMPANION(dev)) {
			dev_dbg(dev, "using ACPI for GPIO lookup\n");
			desc = acpi_find_gpio(dev, con_id, idx, &lookupflags);
		}
	}

	/*
	 * Either we are not using DT or ACPI, or their lookup did not return
	 * a result. In that case, use platform lookup as a fallback.
	 */
	if (!desc || desc == ERR_PTR(-ENOENT)) {
		dev_dbg(dev, "using lookup tables for GPIO lookup\n");
		desc = gpiod_find(dev, con_id, idx, &lookupflags);
	}

	if (IS_ERR(desc)) {
		dev_dbg(dev, "lookup for GPIO %s failed\n", con_id);
		return desc;
	}

	status = gpiod_request(desc, con_id);
	if (status < 0)
		return ERR_PTR(status);

	status = gpiod_configure_flags(desc, con_id, lookupflags, flags);
	if (status < 0) {
		dev_dbg(dev, "setup of GPIO %s failed\n", con_id);
		gpiod_put(desc);
		return ERR_PTR(status);
	}

	return desc;
}
EXPORT_SYMBOL_GPL(gpiod_get_index);
           

可以看到,在这个函数里,已经做了3个动作,分别是函数“of_find_gpio”,“gpiod_request”,“gpiod_configure_flags”;

这3个函数,分别对应旧代码里面的“of_get_named_gpio_flags”,“gpio_request”,“gpio_direction_input/gpio_direction_output”;

所以,这一个函数就已经整合了,从devicetree获取gpio,请求gpio,初始化gpio这3步,非常方便:

struct gpio_desc *enable;
//获取到设备树里面的“enable-gpios”这个gpio,并初始化为输出低
enable = gpiod_get_index(&(pdev->dev), "enable", 0, GPIOD_OUT_LOW);
if (IS_ERR(enable ))
	printk("Cannot find enable-gpios! \n");
           

获取到gpio之后,就可以和以前一样对gpio单独操作:

int gpiod_get_value(const struct gpio_desc *desc);

void gpiod_set_value(struct gpio_desc *desc, int value);

上面两个函数依然是不能休眠,要休眠用下面:

int gpiod_get_value_cansleep(const struct gpio_desc *desc);

void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);

gpio使用之后,也要释放,不过是用:

void gpiod_put(struct gpio_desc *desc);

和“gpio_free”功能类似。

总结

新的“gpiod_”方式,单纯从API的使用上来看,主要就是把之前要分几步去做的事情,整合到了一起,使用起来更方便一些,也更规范了一下;对于更复杂的内部实现的机制,比如,之前获取到的gpio是一个“int”类型,现在获取到的是“struct gpio_desc *”,这个后面有机会再深入研究了!

继续阅读