linux 通过激活gpio去使能对应regulator的引脚的电分析

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

使用了一个外部电路,是通过gpio的高低对这个外部电路的固定电压进行一个使能,在linux如何通过regulator_enable这个regulator使能的func与对应gpio关联起来?


提示:以下是本篇文章正文内容,下面案例可供参考

一、先从原理图和设备树开始分析

在这里插入图片描述
上图使用的是一个稳压IC,经GPIO_CAM_LDO_EN1(GPIO 2)使能后,可以稳定输出2.8V的电压。

下面直接贴相关设备树内容

	CAM0_AVDD28_EN: CAM0-AVDD28-EN {
		compatible = "regulator-fixed";					
		regulator-name = "CAM0-AVDD28-EN";
		regulator-min-microvolt = <2800000>;//min和max都是一样的,因为是一个固定电压2.8V
		regulator-max-microvolt = <2800000>;
		gpio = <&pio 2 GPIO_ACTIVE_HIGH>;	//使能脚 GPIO2
		vin-supply = <&VDD_3V3>;			
		enable-active-high;					//点屏为高则激活
		//regulator-always-on;		//这个如果打开,会常开,在一些场景不会被关闭,这个电
	};

由上面的设备树compatible的信息,我们可以找到如下代码文件kernel-5.15/drivers/regulator/fixed.c
下面,我们就从代码去分析做了什么事

二、源码分析

1.kernel-5.15/drivers/regulator/fixed.c

...

struct fixed_voltage_data {
	struct regulator_desc desc;
	struct regulator_dev *dev;

	struct clk *enable_clock;
	unsigned int enable_counter;
	int performance_state;
};

...

static int reg_domain_enable(struct regulator_dev *rdev)
{
	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
	struct device *dev = rdev->dev.parent;
	int ret;

	ret = dev_pm_genpd_set_performance_state(dev, priv->performance_state);
	if (ret)
		return ret;

	priv->enable_counter++;

	return ret;
}

static int reg_domain_disable(struct regulator_dev *rdev)
{
	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
	struct device *dev = rdev->dev.parent;
	int ret;

	ret = dev_pm_genpd_set_performance_state(dev, 0);
	if (ret)
		return ret;

	priv->enable_counter--;

	return 0;
}

static int reg_is_enabled(struct regulator_dev *rdev)
{
	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);

	return priv->enable_counter > 0;
}

...
static const struct regulator_ops fixed_voltage_domain_ops = {
	.enable = reg_domain_enable,
	.disable = reg_domain_disable,
	.is_enabled = reg_is_enabled,
};

//这里删除了一些代码,在分析中是不必要的
static int reg_fixed_voltage_probe(struct platform_device *pdev)
{
	/**
	*	在这里
	*	config->init_data = of_get_regulator_init_data(dev, dev->of_node, desc);
	*		if (init_data->constraints.min_uV == init_data->constraints.max_uV) 
	*			config->microvolts = init_data->constraints.min_uV;
	*	已经通过读取设备树的配置确认一个固定的电压
	*/
	...
	//从这里开始分析gpio相关的内容,在本章 2小节可以查看具体分析
	cfg.ena_gpiod = gpiod_get_optional(&pdev->dev, NULL, gflags);
	if (IS_ERR(cfg.ena_gpiod))
		return dev_err_probe(&pdev->dev, PTR_ERR(cfg.ena_gpiod),
				     "can't get GPIO\n");
	
	//这个在本章 3小节查看具体分析
	drvdata->dev = devm_regulator_register(&pdev->dev, &drvdata->desc, &cfg);
	...
}
...

2.关于gpiod_get_optional的分析

kernel-5.15/drivers/gpio/gpiolib.c

gpiod_get_optional
	->gpiod_get_index(dev, NULL, 0, flags)
		->desc = of_find_gpio(dev, NULL, 0, &lookupflags/*不是上一个func的flags*/);
			->desc = of_get_named_gpiod_flags(dev->of_node, prop_name, 0, &of_flags/*不是上一个func的lookupflags*/);
				->of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags/*这个flag是of_flags*/);
					->chip->of_xlate(chip, gpiospec, flags);//会将设备树中gpio的第二个参数传递到flags中
						->of_gpio_flags_quirks(np, propname, flags, index);//下面会详细对这个func进行分析
		->gpiod_request(desc, con_id ? con_id : devname);			//这两个在本文中不是关键
		->gpiod_configure_flags(desc, con_id, lookupflags, flags);	//这个主要是设置gpio的flags,使用setbit

kernel-5.15/drivers/gpio/gpiolib-of.c

static void of_gpio_flags_quirks(const struct device_node *np,
				 const char *propname,
				 enum of_gpio_flags *flags,
				 int index)
{
	/*
	 * Some GPIO fixed regulator quirks.
	 * Note that active low is the default.
	 */
	if (IS_ENABLED(CONFIG_REGULATOR) &&
	    (of_device_is_compatible(np, "regulator-fixed") ||
	     of_device_is_compatible(np, "reg-fixed-voltage") ||
	     (!(strcmp(propname, "enable-gpio") &&
		strcmp(propname, "enable-gpios")) &&
	      of_device_is_compatible(np, "regulator-gpio")))) {
	    //已知第一节中关于设备树的描述是enable-active-high
	    //所以这里active_low == false
		bool active_low = !of_property_read_bool(np,
							 "enable-active-high");
		/*
		 * The regulator GPIO handles are specified such that the
		 * presence or absence of "enable-active-high" solely controls
		 * the polarity of the GPIO line. Any phandle flags must
		 * be actively ignored.
		 */
		if ((*flags & OF_GPIO_ACTIVE_LOW) && !active_low) {
			pr_warn("%s GPIO handle specifies active low - ignored\n",
				of_node_full_name(np));
			//这里给做了一个低电平激活的反转的与运算
			*flags &= ~OF_GPIO_ACTIVE_LOW;
		}
		if (active_low)
			*flags |= OF_GPIO_ACTIVE_LOW;
	}
	...
}
-------------------------------------------------------------------------------------

//再回到of_find_gpio(dev, NULL, 0, &lookupflags/*不是上一个func的flags*/);
struct gpio_desc *of_find_gpio(struct device *dev, 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;
	struct gpio_desc *desc;
	unsigned int i;

	/* Try GPIO property "foo-gpios" and "foo-gpio" */
	for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
		...
		desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
						&of_flags);
		//最终传回来的of_flags就是这个gpio激活的flag,还有对应的gpio_desc desc
	}
	...
	//这里对这个of_flags进行一个判断,符合要求就做一个|=运算合入flags中
	if (of_flags & OF_GPIO_ACTIVE_LOW)
		*flags |= GPIO_ACTIVE_LOW;

	if (of_flags & OF_GPIO_SINGLE_ENDED) {
		if (of_flags & OF_GPIO_OPEN_DRAIN)
			*flags |= GPIO_OPEN_DRAIN;
		else
			*flags |= GPIO_OPEN_SOURCE;
	}

	if (of_flags & OF_GPIO_TRANSITORY)
		*flags |= GPIO_TRANSITORY;

	if (of_flags & OF_GPIO_PULL_UP)
		*flags |= GPIO_PULL_UP;
	if (of_flags & OF_GPIO_PULL_DOWN)
		*flags |= GPIO_PULL_DOWN;

	return desc;
}

到这里gpiod_get_optional这个func就分析完了,最终会返回一个获取到的gpio desc 和 对应的 flags

3.关于devm_regulator_register的分析

devm_regulator_register

drvdata->dev = devm_regulator_register(&pdev->dev, &drvdata->desc, &cfg);
	->rdev = regulator_register(regulator_desc, config);
		->regulator_ena_gpio_request(rdev, config);	//如果这个gpio没被使用,主要是做一个增加regulator_ena_gpio_list中list_add的操作

从上述代码分析,cfg中包含了cfg.ena_gpiod,在CAM0-AVDD28-EN这个regulator引脚注册时,就可以说明可以通过cfg.ena_gpiod去控制CAM0-AVDD28-EN的使能输出。

下面需要使能对应引脚的时候会调用regulator_enable

regulator_enable(struct regulator *regulator)
	->_regulator_enable(regulator);
		->_regulator_is_enabled(rdev);
			->return rdev->ena_gpio_state;//有线使用gpio控制,如果返回0说明,当前是disable可以被enable,如果=1,说明已经使能了,没必要再使能,调用regulator_enable是重复了。
			->_regulator_do_enable(rdev);//这里假设判断=0,就会执行这个func

关于_regulator_do_enable(rdev)核心的代码,有如下

	if (rdev->ena_pin) {
		if (!rdev->ena_gpio_state) {
			
			ret = regulator_ena_gpio_ctrl(rdev, true);
				/**
				*	regulator_ena_gpio_ctrl(rdev, true);
				*		->gpiod_set_value_cansleep(pin->gpiod, 1); 
				*			->gpiod_set_value_nocheck(desc, 1);
				*				->if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value;	//这里会先判断desc->flags是否是低电平激活,如果是,就会把value进行一个反转
				*				->然后会根据实际配置的flag进行高低电平的设置
				*/
			if (ret < 0)
				return ret;
			//这里,对ena_gpio_state进行设置,可以注意上面分析的return rdev->ena_gpio_state
			rdev->ena_gpio_state = 1;
		}
	} 
	...

总结

根据上述分析,可知如何通过gpio去控制一个regulator引脚。

部分内容分析不清晰,请指正,谢谢。

  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值