linux gpio获取

最近在工作中遇到了gpio解析失败的问题,跟踪发现设备树配置的字符串不匹配,在这里再次学习并记录下。

  • of_get_named_gpio
    以前在工作中更多使用的是of_get_named_gpio这个标准函数,用以直接获取gpio。只要指定其具体的属性名,一般就能正常获取其gpio号,而本次使用另外一个接口获取的是gpio描述符结构体。就碰到问题了
  • devm_gpiod_get
    源码物位置:kernel/driver/gpio/gpiolib-devres.c
c 复制代码
/**
 * devm_gpiod_get - Resource-managed gpiod_get()
 * @dev:	GPIO consumer
 * @con_id:	function within the GPIO consumer
 * @flags:	optional GPIO initialization flags
 *
 * Managed gpiod_get(). GPIO descriptors returned from this function are
 * automatically disposed on driver detach. See gpiod_get() for detailed
 * information about behavior and return values.
 */
struct gpio_desc *__must_check devm_gpiod_get(struct device *dev,
					      const char *con_id,
					      enum gpiod_flags flags)
{
	return devm_gpiod_get_index(dev, con_id, 0, flags);
}

此接口是linux内核标准接口,下面来解析下其具体的参数函义:

  • dev 指定此gpio的使用者,一般需要拿到此dev的of_node属性节点。然后从所有的属性中查找指定的字符串。所以此参数不能为空
  • con_id 找定查找的gpio属性名,因为可能存在多个gpio属性配置,使用多个属性名。此参数可为空,如果为空则查找系统指定的字符串
  • flags gpio初始化状态,可以为以下值的任意一个
c 复制代码
enum gpiod_flags {
	GPIOD_ASIS	= 0,
	GPIOD_IN	= GPIOD_FLAGS_BIT_DIR_SET,
	GPIOD_OUT_LOW	= GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT,
	GPIOD_OUT_HIGH	= GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT |
			  GPIOD_FLAGS_BIT_DIR_VAL,
	GPIOD_OUT_LOW_OPEN_DRAIN = GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_OPEN_DRAIN,
	GPIOD_OUT_HIGH_OPEN_DRAIN = GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_OPEN_DRAIN,
};
  • 大体函义如下:
    • GPIOD_ASIS 不需要对gpio作任何修改
    • GPIOD_IN   gpio为输入
    • GPIOD_OUT_LOW gpio为输出,并拉低
    • GPIOD_OUT_HIGH gpio为输出,并拉高
    • GPIOD_OUT_LOW_OPEN_DRAIN  gpio漏极开路输出,并拉低
    • GPIOD_OUT_HIGH_OPEN_DRAIN  gpio漏极开路输出,并拉高
  • 此函数的调用流程如下:

devm_gpiod_get_index

  • Non-Exclusive :此模式允许多个进程或任务同时访问同一个GPIO引脚,需要对资源访问加锁

of_find_gpio

c 复制代码
static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" };
c 复制代码
struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id,
			       unsigned int idx, unsigned long *flags)
{
	char prop_name[32]; /* 32 is max size of property name */
	enum of_gpio_flags of_flags;
	const of_find_gpio_quirk *q;
	struct gpio_desc *desc;
	unsigned int i;

	/* Try GPIO property "foo-gpios" and "foo-gpio" */
	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 = of_get_named_gpiod_flags(np, prop_name, idx, &of_flags);

		if (!gpiod_not_found(desc))
			break;
	}

	/* Properly named GPIO was not found, try workarounds */
	for (q = of_find_gpio_quirks; gpiod_not_found(desc) && *q; q++)
		desc = (*q)(np, con_id, idx, &of_flags);

	if (IS_ERR(desc))
		return desc;

	*flags = of_convert_gpio_flags(of_flags);

	return desc;
}
  • 如果有指定属性名,那么会进行字符串拼接.拼接的规则是xxx-gpios|xxx-gpio,也就是说实际查找的字符串是添加了后辍的。会轮询查找对应的字符串,比如在驱动中使用of_find_gpio(...,"power",...),那么在DTS中就应该配置power-gpios或者power-gpio
  • 如果指定属性名为空,那么会直接查找gpios或者gpio,所以如果你不想写属性名,就直接在DTS中配置其它的一个属性就可以了
    工作中碰到的查找失败起因就是在此了。

of_get_named_gpiod_flags

  • of_parse_phandle_with_args_map是个系统函数,这里不用太关注。只需要关心其输出的结果gpiospec
    • np 其指向的gpio控制器节点
    • args_count #gpio-size配置的数据
    • args 具体的gpio配置数据
c 复制代码
struct of_phandle_args {
	struct device_node *np;
	int args_count;
	uint32_t args[MAX_PHANDLE_ARGS];
};

of_find_gpiochip_by_xlate

c 复制代码
static struct gpio_chip *of_find_gpiochip_by_xlate(
					struct of_phandle_args *gpiospec)
{
	return gpiochip_find(gpiospec, of_gpiochip_match_node_and_xlate);
}
struct gpio_chip *gpiochip_find(void *data,
				int (*match)(struct gpio_chip *gc,
					     void *data))
{
	struct gpio_device *gdev;
	struct gpio_chip *gc = NULL;
	unsigned long flags;

	spin_lock_irqsave(&gpio_lock, flags);
	list_for_each_entry(gdev, &gpio_devices, list)
		if (gdev->chip && match(gdev->chip, data)) {
			gc = gdev->chip;
			break;
		}
	spin_unlock_irqrestore(&gpio_lock, flags);
	return gc;
}

上述代码很明显,轮询所有的gpio控制器。从而找到匹配的控制器,匹配的规则也很简单就两点:

  • 设备树配置的控制器节点与其中一个控制器相等
  • 存在of_xlate回调函数且其返回值>=0
    匹配实现如下:
c 复制代码
static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data)
{
	struct of_phandle_args *gpiospec = data;

	return device_match_of_node(&chip->gpiodev->dev, gpiospec->np) &&
				chip->of_xlate &&
				chip->of_xlate(chip, gpiospec, NULL) >= 0;
}

of_xlate_and_get_gpiod_flags

c 复制代码
static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
					struct of_phandle_args *gpiospec,
					enum of_gpio_flags *flags)
{
	int ret;

	if (chip->of_gpio_n_cells != gpiospec->args_count)
		return ERR_PTR(-EINVAL);

	ret = chip->of_xlate(chip, gpiospec, flags);
	if (ret < 0)
		return ERR_PTR(ret);

	return gpiochip_get_desc(chip, ret);
}

这里的函数实现完全依赖控制器,代码逻辑比较简单,这里要搞懂这个实现。需要找一个gpio控制器来研究下,这里以gpio-sprd.c为例

gpio-sprd控制器

c 复制代码
static int sprd_gpio_probe(struct platform_device *pdev)
{
	struct gpio_irq_chip *irq;
	struct sprd_gpio *sprd_gpio;

	sprd_gpio = devm_kzalloc(&pdev->dev, sizeof(*sprd_gpio), GFP_KERNEL);
	if (!sprd_gpio)
		return -ENOMEM;

	sprd_gpio->irq = platform_get_irq(pdev, 0);
	if (sprd_gpio->irq < 0)
		return sprd_gpio->irq;

	sprd_gpio->base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(sprd_gpio->base))
		return PTR_ERR(sprd_gpio->base);

	spin_lock_init(&sprd_gpio->lock);

	sprd_gpio->chip.label = dev_name(&pdev->dev);
	sprd_gpio->chip.ngpio = SPRD_GPIO_NR;
	sprd_gpio->chip.base = -1;
	sprd_gpio->chip.parent = &pdev->dev;
	sprd_gpio->chip.request = sprd_gpio_request;
	sprd_gpio->chip.free = sprd_gpio_free;
	sprd_gpio->chip.get = sprd_gpio_get;
	sprd_gpio->chip.set = sprd_gpio_set;
	sprd_gpio->chip.direction_input = sprd_gpio_direction_input;
	sprd_gpio->chip.direction_output = sprd_gpio_direction_output;

	irq = &sprd_gpio->chip.irq;
	gpio_irq_chip_set_chip(irq, &sprd_gpio_irqchip);
	irq->handler = handle_bad_irq;
	irq->default_type = IRQ_TYPE_NONE;
	irq->parent_handler = sprd_gpio_irq_handler;
	irq->parent_handler_data = sprd_gpio;
	irq->num_parents = 1;
	irq->parents = &sprd_gpio->irq;

	return devm_gpiochip_add_data(&pdev->dev, &sprd_gpio->chip, sprd_gpio);
}

这里此控制器的定义,从上可以看出此控制器并没有定义。如果控制器没有定义此回调函数,则会使用系统默认提供的回调函数of_gpio_simple_xlate

此函数是公用的回调函数,如果控制器定义了自己的xlate函数则使用控制器自身的,如果没有就会使用此函数。到这里gpio的解析流程就基本走完了,对gpio子系统认识进一步加深。

相关推荐
dddaidai1232 小时前
深入JVM(二):字节码文件的结构
java·开发语言·jvm
郝学胜-神的一滴2 小时前
Linux C++会话编程:从基础到实践
linux·运维·服务器·开发语言·c++·程序人生·性能优化
SadSunset2 小时前
(5)spring的set注入
java·笔记·spring·架构
长而不宰2 小时前
使用 Canal实时监听数据库变化
java·数据库·spring boot
骚团长2 小时前
SQL server 配置管理器-SQL server 服务-远程过程调试失败 [0x800706be]-(Express LocalDB卸载掉)完美解决!
java·服务器·express
可爱又迷人的反派角色“yang”2 小时前
elk架构
linux·运维·elk·架构
盼哥PyAI实验室2 小时前
Python多线程实战:12306抢票系统的并发处理优化
java·开发语言·python
风月歌2 小时前
小程序项目之农业电商服务系统源代码
java·mysql·毕业设计·ssm·源码
Vect__2 小时前
Linux常见工具使用方法
linux