提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
使用了一个外部电路,是通过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引脚。