Linux的GPIO控制“gpiod_”和“gpio_”浅析
平台: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 *”,这个后面有机会再深入研究了!