linux gpio获取

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

  • of_get_named_gpio
    以前在工作中更多使用的是of_get_named_gpio这个标准函数,用以直接获取gpio。只要指定其具体的属性名,一般就能正常获取其gpio号,而本次使用另外一个接口获取的是gpio描述符结构体。就碰到问题了
  • devm_gpiod_get
    源码物位置:kernel/driver/gpio/gpiolib-devres.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初始化状态,可以为以下值的任意一个
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

static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" };
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配置数据
struct of_phandle_args {
	struct device_node *np;
	int args_count;
	uint32_t args[MAX_PHANDLE_ARGS];
};

of_find_gpiochip_by_xlate

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
    匹配实现如下:
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

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控制器

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子系统认识进一步加深。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bruk_spp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值