Linux version: 4.14
Code link: Linux source code (v4.14) - Bootlin
1 devm_fwnode_get_gpiod_from_child 函数
static inline
struct gpio_desc *devm_fwnode_get_gpiod_from_child(struct device *dev,
const char *con_id,
struct fwnode_handle *child,
enum gpiod_flags flags,
const char *label)
{
return devm_fwnode_get_index_gpiod_from_child(dev, con_id, 0, child,
flags, label);
}
2 devm_fwnode_get_index_gpiod_from_child 函数
struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev,
const char *con_id, int index,
struct fwnode_handle *child,
enum gpiod_flags flags,
const char *label)
{
char prop_name[32]; /* 32 is max size of property name */
struct gpio_desc **dr;
struct gpio_desc *desc;
unsigned int i;
dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *),
GFP_KERNEL);
if (!dr)
return ERR_PTR(-ENOMEM);
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
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 = fwnode_get_named_gpiod(child, prop_name, index, flags,
label);
if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT))
break;
}
if (IS_ERR(desc)) {
devres_free(dr);
return desc;
}
*dr = desc;
devres_add(dev, dr);
return desc;
}
EXPORT_SYMBOL(devm_fwnode_get_index_gpiod_from_child);
可以看到 gpio 的属性名 prop_name 是由 con_id (如果有的话)和 gpio_suffixes[i] 拼接而成,然后调用 fwnode_get_named_gpiod 函数来获取 gpiod。gpio_suffixes 的定义如下,后缀用 gpio 或者 gpios 都可以。
/* gpio suffixes used for ACPI and device tree lookup-用于ACPI和设备树查找的gpio后缀 */
static const char * const gpio_suffixes[] = { "gpios", "gpio" };
3 devm_fwnode_get_index_gpiod_from_child 函数
struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
const char *propname, int index,
enum gpiod_flags dflags,
const char *label)
{
struct gpio_desc *desc = ERR_PTR(-ENODEV);
unsigned long lflags = 0;
bool active_low = false;
bool single_ended = false;
bool open_drain = false;
int ret;
if (!fwnode)
return ERR_PTR(-EINVAL);
if (is_of_node(fwnode)) {
enum of_gpio_flags flags;
desc = of_get_named_gpiod_flags(to_of_node(fwnode), propname,
index, &flags);
if (!IS_ERR(desc)) {
active_low = flags & OF_GPIO_ACTIVE_LOW;
single_ended = flags & OF_GPIO_SINGLE_ENDED;
open_drain = flags & OF_GPIO_OPEN_DRAIN;
}
} else if (is_acpi_node(fwnode)) {
struct acpi_gpio_info info;
desc = acpi_node_get_gpiod(fwnode, propname, index, &info);
if (!IS_ERR(desc)) {
active_low = info.polarity == GPIO_ACTIVE_LOW;
ret = acpi_gpio_update_gpiod_flags(&dflags, info.flags);
if (ret)
pr_debug("Override GPIO initialization flags\n");
}
}
if (IS_ERR(desc))
return desc;
ret = gpiod_request(desc, label);
if (ret)
return ERR_PTR(ret);
if (active_low)
lflags |= GPIO_ACTIVE_LOW;
if (single_ended) {
if (open_drain)
lflags |= GPIO_OPEN_DRAIN;
else
lflags |= GPIO_OPEN_SOURCE;
}
ret = gpiod_configure_flags(desc, propname, lflags, dflags);
if (ret < 0) {
gpiod_put(desc);
return ERR_PTR(ret);
}
return desc;
}
EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
上述代码中的 is_of_node(fwnode) 判断 fwnode 是否为设备树结点,而 is_acpi_node(fwnode) 判断 fwnode 是否为 ACPI 结点。这里我们使用了设备树,所以 is_of_node(fwnode) 函数返回为真。
补充:fwnode 指的是固件结点,通常表示设备树或 ACPI(通常是DSDT表)中的条目。设备树和 ACPI是定义设备及其属性和它们之间的互联的两种不同的方式。他们都使用树结构来编码这些信息。给定 struct_device 上的 fwnode 成员是该设备的相应固件表中的结点。ACPI 在基于 x86/UEFI 的系统中很常见,设备树在 ARM 系统中很常见。fwnode 可以与接受 fwnode 句柄的内核 API 一起使用。一些不错的引用文档:
设备树:https://www.kernel.org/doc/Documentation/devicetree/usage-model.txt
_DSDT 中的 DSD 图:https://www.kernel.org/doc/Documentation/acpi/dsd/graph.txt
4 of_get_named_gpiod_flags 函数
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
const char *propname, int index, enum of_gpio_flags *flags)
{
struct of_phandle_args gpiospec;
struct gpio_chip *chip;
struct gpio_desc *desc;
int ret;
ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
&gpiospec);
if (ret) {
pr_debug("%s: can't parse '%s' property of node '%pOF[%d]'\n",
__func__, propname, np, index);
return ERR_PTR(ret);
}
// 遍历 gpio_devices 链表中的每一个 gpio_device,
// 将每一个 gpio_device->chip 与 gpiospec 进行对比,并返回匹配的 chip */
chip = of_find_gpiochip_by_xlate(&gpiospec);
if (!chip) {
desc = ERR_PTR(-EPROBE_DEFER);
goto out;
}
// 根据 chip 和 gpiospec 描述的信息返回对应引脚的 gpio_desc 结构体
desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
if (IS_ERR(desc))
goto out;
pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n",
__func__, propname, np, index,
PTR_ERR_OR_ZERO(desc));
out:
of_node_put(gpiospec.np);
return desc;
}
(1)函数 of_parse_phandle_with_args 参考
of_parse_phandle_with_args 函数_Vane Zhang的博客-CSDN博客
(2)函数 of_find_gpiochip_by_xlate 参考
of_find_gpiochip_by_xlate 函数_Vane Zhang的博客-CSDN博客
(3)函数 of_xlate_and_get_gpiod_flags 参考
of_xlate_and_get_gpiod_flags 函数_Vane Zhang的博客-CSDN博客
5 总结
static inline
struct gpio_desc *devm_fwnode_get_gpiod_from_child(struct device *dev,
const char *con_id,
struct fwnode_handle *child,
enum gpiod_flags flags,
const char *label)
函数 devm_fwnode_get_gpiod_from_child 根据传入的 device 结构体变量,con_id 字符串以及 fwnode_handle 结构体变量,返回对应引脚的描述符 gpio_desc 结构体变量。