本文为作者学习记录,如有错误之处欢迎指出!下文将以imx6ull芯片、Linux4.1.15内核中的led-gpio驱动作为例子对gpios属性的中的有效电平进行分析。
ps:驱动路径drivers/leds/leds-gpio.c
以单片机的思维来讲,对GPIO的控制一般是:
输入 | 输出 |
---|---|
1 | 高电平 |
0 | 低电平 |
下面两张led原理图(图片来源于网络),一张为高电平有效,一张为低电平有效。当GPIO输出对应的电平时led亮,为了实现统一的接口,让1表示灯亮、0表示灯灭,就需要用到有效电平。加入有效电平Led真值表如下所示。
Led真值表
led | 输入值 | 电平值 | 状态 |
---|---|---|---|
高电平有效LED | 1 | 高电平 | 亮 |
高电平有效LED | 0 | 低电平 | 灭 |
低电平有效LED | 1 | 低电平 | 亮 |
低电平有效LED | 0 | 高电平 | 灭 |
由内核的说明文档(Documentation/devicetree/bindings/gpio/gpio.txt
)可知,设备树中的GPIO属性主要由三部分组成:
(1)GPIO控制器
(2)第几个GPIO
(3)有效电平
如下图表示gpio4-16,高电平有效
在include/dt-bindings/gpio/gpio.h
有定义如下:
以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_LOW
,gpio_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);
}