linux 4.9
armv8-A
关于regulator子系统,可以看下这这些博客:
regulator,翻译就是调节器。一些可以输出电流电压的设备可以使用该子系统。举个例子,一个PMIC有多路输出,每一路输出都可以给很多外设、芯片供电。那么每一路输出,我都可以认为他是一个regulator。
regulator子系统还是有一个思想就是consumer(消费者),继续刚才那个例子,PMIC的某一路输出,要给DDR供电,那么这个DDR设备就是这一路regulator的consumer,可以注册为一个consumer设备。
当然,这是真正有物理设备的consumer。有的时候为了控制regulator,不需要通过一个真正的物理设备注册的consumer,可以凭空捏造一个consumer,就是virtual consumer。后面分析的时候可以看到,对consumer的开关/电压/电流操作实际上会转化为对regulator的操作。
那为啥不直接控制regulator呢?我也不知道,看上去像是regulator子系统没有提供直接对regulator控制的API,还是要以来consumer提供的API来控制regulator,所以还是要搞个虚拟consumer。不过这一段我的理解可能有问题。
回顾笔记,分析都在注释中。
对于一个多路输出的PMIC,每一路都可以注册为一个regulator。把每一路注册为一个regulator使用的子系统接口API为:
rdev = devm_regulator_register(&pdev->dev, regulator, ®ulator_config);
需要传入参数:
第二个参数结构如下:
struct regulator_desc {
const char *name; // g, 该regulator名称
const char *supply_name; // g,
const char *of_match; // g, 用于与设备树中的regulator匹配,会在初测过程中与regulaotr的设备树节点名字匹配,也就是devcice_node->name
const char *regulators_node; // g, 一般会在设备树中把所有regulator的节点都挂在一个regulators节点下,此域表示regulators节点的名字。在注册regulator的时候需要用到
int (*of_parse_cb)(struct device_node *, // g, 自己提供,如果of_match存在且匹配,可以用这个函数来解析一些设备树中regulator节点中自己定义的属性,然后做一些自己想做的操作
const struct regulator_desc *,
struct regulator_config *);
int id; // g, regulator id
unsigned int continuous_voltage_range:1;
unsigned n_voltages; // g, 步长数,可控制的电压范围内总共有多少步
const struct regulator_ops *ops; // g, 操作集,该regulator专有的操作集,后面对consumer的控制实际上会调用到这里
int irq;
enum regulator_type type;
struct module *owner;
unsigned int min_uV; // g, 最小电压,控制regulator时不能小与该电压
unsigned int uV_step; // g, 步长,看过PMIC的芯片手册就知道,某一路的输出电压是按步长来的,bit +1 则输出电压+step.但是对于变步长的regulator来说不需要这个
unsigned int linear_min_sel;
int fixed_uV;
unsigned int ramp_delay;
int min_dropout_uV;
// g, 有些regulator的电压/电流控制是变步长的,就比如说在[a,b)这一段,vsel_reg一个bit的步长为step1,在[b,c]这一段1个bit步长为step2
// g, 此时就需要下面这个linear_ranges域,这个域是个数组,每个数组可以表示"[a,b)这一段电压范围使用步长step1, 步的范围为[c,d]"中的a, c, d, step1
// g, 则b可由公式 b = a + step1 * (d -c)计算得出
const struct regulator_linear_range *linear_ranges;
int n_linear_ranges;
const unsigned int *volt_table;
unsigned int vsel_reg; // g, 电压控制寄存器,控制该路regulator输出电压的寄存器
unsigned int vsel_mask; // g, 可能这个电压控制寄存器的某几位控制输出电压,使用该掩码控制这几位特定的bit
unsigned int csel_reg; // g, 电流控制
unsigned int csel_mask; // g, 同上
unsigned int apply_reg;
unsigned int apply_bit;
unsigned int enable_reg; // g, regulator使能寄存器地址
unsigned int enable_mask; // g, 同上
unsigned int enable_val;
unsigned int disable_val;
bool enable_is_inverted;
unsigned int bypass_reg;
unsigned int bypass_mask;
unsigned int bypass_val_on;
unsigned int bypass_val_off;
unsigned int active_discharge_on;
unsigned int active_discharge_off;
unsigned int active_discharge_mask;
unsigned int active_discharge_reg;
unsigned int enable_time;
unsigned int off_on_delay;
unsigned int (*of_map_mode)(unsigned int mode);
};
第三个参数结构如下:
struct regulator_config {
struct device *dev; // g, 一般你的regulators节点挂在哪个设备下,这里就要使用哪个设备。
const struct regulator_init_data *init_data; // g, 一些初始化数据,如果创建实例的时候初始化了这个域,并且无法从设备树中解析到对应的设备,就会使用我们初始化的域。否则,会使用设备树的信息填充该域,就算你初始化了,也会被覆盖。
void *driver_data; // g, 私有数据
struct device_node *of_node; // g, 设备节点
struct regmap *regmap; // g, 使用的regmap
bool ena_gpio_initialized;
int ena_gpio;
unsigned int ena_gpio_invert:1;
unsigned int ena_gpio_flags;
};
其中比较重要的是第二个参数,描述了该regulator本身的一些物理特性,具体配置成什么样要根据你使用的regulator来设置。举个例子:
static const struct regulator_desc pmic_regulators_desc [] = {
[0] = {
...
},
[1] = {
.name = "dcdcb", // g, 这一路regulator的名字是"dcdcb"
.supply_name = "vinb",
.of_match = of_match_ptr("dcdcb"), // g, .of_match="dcdcb",如果有需要的话,可以使用这个域来与设备树节点匹配,当然需要设备树对应的节点名字为"dcdcb"
.regulators_node = of_match_ptr("regulators"),// g, 这个regulator挂在regulators节点下,而且这个regulators节点在设备树中就叫"regulators"
.type = REGULATOR_VOLTAGE,
.id = 1,
.n_voltages = (2550 - 1000) / 50 + 1, // g, 根据dataset,范围为1v~2.55v
.owner = THIS_MODULE,
.min_uV = 1000 * 1000, // g,最小电压为1000mv,也就是1v
.uV_step = 50 * 1000(fix), // g, 步长为50mv, 这是个定步长regulator
.vsel_reg = 0x13, // g, 控制该regulator电压的寄存器地址为0x13
.vsel_mask = 0x1f, // g, 寄存器0x13的bit 0~4 控制 DCDC-B的输出voltage
.enable_reg = 0x10, // g, 开/关寄存器地址
.enable_mask = BIT(1), // g, 寄存器0x10的bit 1控制DCDC-B的on-off
.ops = &自定义, // g,自定义,但是很重要。
}
};
...
...
static int regulator_probe(struct platform_device *pdev)
{
struct regulator_config pmic_regulator_config= {
.dev = pdev->dev.parent, // g, pmu节点,因为在我的设备树下regulators节点是挂在pmu节点下面的
.regmap = 之前注册的regmap,
.driver_data = 看你自己需要什么,
};
...
...
struct regulator_dev *rdev = devm_regulator_register(&pdev->dev, pmic_regulators_desc, &pmic_regulator_config);
...
}
注册过程是什么情况呢?具体得看devm_regulator_register()的实现过程:
drivers/regulator/devres.c:
struct regulator_dev *devm_regulator_register(struct device *dev,
const struct regulator_desc *regulator_desc,
const struct regulator_config *config)
{
struct regulator_dev **ptr, *rdev;
ptr = devres_alloc(devm_rdev_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
// g, 这个函数有说法的
// g, in
rdev = regulator_register(regulator_desc, config);
if (!IS_ERR(rdev)) {
*ptr = rdev;
// g, 会为dev->devres_head添加dr->node->entry
// g, 也就是说新注册的regulator_dev是传入的dev的一个devres
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return rdev;
}
因为是devm_开头的函数,所以肯定是把资源挂在dev->devres_head下,关于devm_系列的函数,后续单独写一篇笔记分析。除此之外,这个函数就单纯的调用了regulator_register(),会注册一个regulator_dev,并返回:
struct regulator_dev *
regulator_register(const struct regulator_desc *regulator_desc,
const struct regulator_config *cfg)
{
const struct regulation_constraints *constraints = NULL;
const struct regulator_init_data *init_data;
struct regulator_config *config = NULL;
static atomic_t regulator_no = ATOMIC_INIT(-1);
struct regulator_dev *rdev;
struct device *dev;
int ret, i;
if (regulator_desc == NULL || cfg == NULL)
return ERR_PTR(-EINVAL);
dev = cfg->dev;
WARN_ON(!dev);
if (regulator_desc->name == NULL || regulator_desc->ops == NULL)
return ERR_PTR(-EINVAL);
if (regulator_desc->type != REGULATOR_VOLTAGE &&
regulator_desc->type != REGULATOR_CURRENT)
return ERR_PTR(-EINVAL);
/* Only one of each should be implemented */
WARN_ON(regulator_desc->ops->get_voltage &&
regulator_desc->ops->get_voltage_sel);
WARN_ON(regulator_desc->ops->set_voltage &&
regulator_desc->ops->set_voltage_sel);
/* If we're using selectors we must implement list_voltage. */
if (regulator_desc->ops->get_voltage_sel &&
!regulator_desc->ops->list_voltage) {
return ERR_PTR(-EINVAL);
}
if (regulator_desc->ops->set_voltage_sel &&
!regulator_desc->ops->list_voltage) {
return ERR_PTR(-EINVAL);
}
rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
if (rdev == NULL)
return ERR_PTR(-ENOMEM);
/*
* Duplicate the config so the driver could override it after
* parsing init data.
*/
config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL);
if (config == NULL) {
kfree(rdev);
return ERR_PTR(-ENOMEM);
}
// g, 这个init_data挺关键的,一般会放在config中,但是我们的config没有定义init_data
// g, 此处传入的dev为cfg->dev,也就是config->dev,也就是pdev("regulators")->dev.parent("pmu")
// g, 该函数会解析(pmu-->regulators-->子regulator)中的很多属性,赋值给init_data。同时会把子regulator结点作为regulator_dev->dev.of_node
// g, in
init_data = regulator_of_get_init_data(dev, regulator_desc, config,
&rdev->dev.of_node);
if (!init_data) { // g, 如果解析设备树失败,才会使用config->init_data,所以设备树优先级更高
init_data = config->init_data;
rdev->dev.of_node = of_node_get(config->of_node);
}
mutex_init(&rdev->mutex);
rdev->reg_data = config->driver_data;
rdev->owner = regulator_desc->owner;
rdev->desc = regulator_desc; // g,把desc表放到rdev->desc
if (config->regmap)
rdev->regmap = config->regmap;
else if (dev_get_regmap(dev, NULL))
rdev->regmap = dev_get_regmap(dev, NULL);
else if (dev->parent)
rdev->regmap = dev_get_regmap(dev->parent, NULL);
INIT_LIST_HEAD(&rdev->consumer_list);
INIT_LIST_HEAD(&rdev->list);
#if defined(CONFIG_AW_AXP)
INIT_LIST_HEAD(&rdev->axp_enable_list);
list_add(&rdev->list, &axp_regulator_list);
#endif
BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work);
/* preform any regulator specific init */
if (init_data && init_data->regulator_init) {
ret = init_data->regulator_init(rdev->reg_data);
if (ret < 0)
goto clean;
}
if ((config->ena_gpio || config->ena_gpio_initialized) &&
gpio_is_valid(config->ena_gpio)) {
mutex_lock(®ulator_list_mutex);
ret = regulator_ena_gpio_request(rdev, config);
mutex_unlock(®ulator_list_mutex);
if (ret != 0) {
rdev_err(rdev, "Failed to request enable GPIO%d: %d\n",
config->ena_gpio, ret);
goto clean;
}
}
/* register with sysfs */
rdev->dev.class = ®ulator_class; // g, 把regulator_dev注册在regulator_class
rdev->dev.parent = dev;
dev_set_name(&rdev->dev, "regulator.%lu",
(unsigned long) atomic_inc_return(®ulator_no));
/* set regulator constraints */
if (init_data)
constraints = &init_data->constraints;
if (init_data && init_data->supply_regulator)
rdev->supply_name = init_data->supply_regulator;
else if (regulator_desc->supply_name) // g, 这里会进行一个supply_name命名,如"vina", "vinb"
rdev->supply_name = regulator_desc->supply_name;
/*
* Attempt to resolve the regulator supply, if specified,
* but don't return an error if we fail because we will try
* to resolve it again later as more regulators are added.
*/
if (regulator_resolve_supply(rdev))
rdev_dbg(rdev, "unable to resolve supply\n");
ret = set_machine_constraints(rdev, constraints);
if (ret < 0)
goto wash;
/* add consumers devices */
if (init_data) {
mutex_lock(®ulator_list_mutex);
for (i = 0; i < init_data->num_consumer_supplies; i++) {
ret = set_consumer_device_supply(rdev, // g, 向全局链表regulator_map_list中添加新的节点,新结点表示新创建的regulator_dev
init_data->consumer_supplies[i].dev_name,
init_data->consumer_supplies[i].supply);
if (ret < 0) {
mutex_unlock(®ulator_list_mutex);
dev_err(dev, "Failed to set supply %s\n",
init_data->consumer_supplies[i].supply);
goto unset_supplies;
}
}
mutex_unlock(®ulator_list_mutex);
}
if (!rdev->desc->ops->get_voltage &&
!rdev->desc->ops->list_voltage &&
!rdev->desc->fixed_uV)
rdev->is_switch = true;
dev_set_drvdata(&rdev->dev, rdev);
ret = device_register(&rdev->dev);
if (ret != 0) {
put_device(&rdev->dev);
goto unset_supplies;
}
// g, 使用debugfs_create_dir在debugfs(/sys/kernel/debug/regulator)下创建调试节点
// g, 命名为:"rdev->dev.parent->name"-"rdev->name",
// g, rdev->dev.parent是传入的cfg->dev,我们设置的cfg->dev就是PMIC的dev
// g, 所以最终的debug节点为:/sys/kernel/debug/regulator/PMIC名-aldo1
rdev_init_debugfs(rdev);
/* try to resolve regulators supply since a new one was registered */
class_for_each_device(®ulator_class, NULL, NULL,
regulator_register_resolve_supply);
kfree(config);
return rdev;
unset_supplies:
mutex_lock(®ulator_list_mutex);
unset_regulator_supplies(rdev);
mutex_unlock(®ulator_list_mutex);
wash:
kfree(rdev->constraints);
mutex_lock(®ulator_list_mutex);
regulator_ena_gpio_free(rdev);
mutex_unlock(®ulator_list_mutex);
clean:
kfree(rdev);
kfree(config);
return ERR_PTR(ret);
}
注册设备,已经相应的调试节点的注册,都在注释中了。其中有个函数需要深入分析一下,这个函数决定了设备树应该怎么写,虽然一般documentation/devicetree/下会告诉你怎么写,但是还是想去看一下。这个函数是regulator_of_get_init_data(),该函数会解析设备树信息填充一个结构体变量struct regulator_init_data init_data,此变量会用来初始化一些regulator_dev的域:
struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
const struct regulator_desc *desc,
struct regulator_config *config,
struct device_node **node)
{
struct device_node *search, *child;
struct regulator_init_data *init_data = NULL;
const char *name;
if (!dev->of_node || !desc->of_match)
return NULL;
// g, desc->regulators_node = "regulators", 自己定义在静态信息表中,可以认为是当前regulator挂在哪个节点下
// g, 一般PMIC驱动,每一路输出(regulator)都会挂在一个节点下,该节点可以认为是regulator的集合(在设备树中被命名为"regulators").
if (desc->regulators_node)
search = of_get_child_by_name(dev->of_node, // g, 从dev->of_node遍历寻找"regulators节点",这个节点是PMIC节点,regulator集合节点就挂在PMIC节点下
desc->regulators_node);
else
search = dev->of_node;
if (!search) {
dev_dbg(dev, "Failed to find regulator container node '%s'\n",
desc->regulators_node);
return NULL;
}
// g, 遍历整个"regulators"结点(search),找子结点,在设备树中就是dcdca,dcdcb这些
for_each_available_child_of_node(search, child) {
name = of_get_property(child, "regulator-compatible", NULL); // g, 该节点无该属性
if (!name)
name = child->name; // g, name = child->name,也就是"dcdca", "dcdcb"
// g, 如果child->name != of_match,则continue。
// g, 806都是相等的,设备树与desc静态信息的fo_match是一致的
if (strcmp(desc->of_match, name))
continue;
// g, dev是"pmu",child是regulator小结点(pmu-->regulators-->子regulator)
// g, 该函数会分配一个struct regulator_init_data, 解析很多child中的属性填充init_data
// g, 但是我们的设备树中的regulator中有几个自定义的属性,这里无法解析,需要在外面解析
// g, in
init_data = of_get_regulator_init_data(dev, child, desc);
if (!init_data) {
dev_err(dev,
"failed to parse DT for regulator %s\n",
child->name);
break;
}
// g, 如果在静态信息表有自定义的of_parse_cb()函数,则需要在parse设备树之后调用回调
// g, 比如说,我这个regulaotr下有些单独定义的属性,希望解析这些属性做一些对应的操作,就可以自己初始化一个of_parse_cb()用来解析.
if (desc->of_parse_cb) {
if (desc->of_parse_cb(child, desc, config)) {
dev_err(dev,
"driver callback failed to parse DT for regulator %s\n",
child->name);
init_data = NULL;
break;
}
}
of_node_get(child); // g, 增加引用计数
// g, 找到的子regulator结点(pmu-->regulators-->子regulator)会作为rdev(regulator_dev)->dev.of_node
*node = child;
break;
}
of_node_put(search); // g, 减少引用计数
return init_data;
}
这样就为regulator注册好了regulator_dev,接下来需要注册consumer。
对于设备树中的virtual-consumer设备,可以使用regulator子系统提供的probe函数,匹配方法为:
drivers/regulator/virtual.c:
static struct platform_driver regulator_virtual_consumer_driver = {
.probe = regulator_virtual_probe,
.remove = regulator_virtual_remove,
.driver = {
.name = "reg-virt-consumer",
},
};
可以看到,只能通过driver->name与platform_dev->name匹配这种方式进行匹配。不过在MFD设备注册的时候我们已经把virtual_consumer对应的cell所注册而成的plat_form_dev->name设置了"reg-virt-consumer"。
所以最终是使用regulator子系统提供的这个virtual-consumer驱动的。probe过程为regulator_virtual_probe():
drivers/regulator/virtual.c:
static int regulator_virtual_probe(struct platform_device *pdev)
{
char *reg_id = dev_get_platdata(&pdev->dev); // g, 拿到dev->platform_data,也就是AXP806_DCDC1_NAME("dcdc1")这些
struct virtual_consumer_data *drvdata;
int ret;
drvdata = devm_kzalloc(&pdev->dev, sizeof(struct virtual_consumer_data),
GFP_KERNEL);
if (drvdata == NULL)
return -ENOMEM;
mutex_init(&drvdata->lock);
// g, 该函数的查找方式并不是通过reg_id和regulator_dev中的什么属性进行匹配
// g, 而是将"reg_id"拚上"-supply"变成"reg_id-supply",然后调用of_parse_phandle()函数进行搜索device_node
// g, 该函数会根据传入的结点,找到该结点中包含的引用属性"reg_id-supply"所指向的结点,
// g, 比如说该结点包含一个引用属性"dcdc1-supply",设备树中的完整格式是:dcdc1-supply = <®_dcdc1>;
// g, 最终找到的device_node就是reg_dcdc1对应的devcie_node, 这是一种隐式phandle(就是被引用的结点)
// g, 再使用container_of找到node对应的regulator_dev
// g, 下面这个函数,通过consumer_dev和reg_id,找到对应的regulator_dev,并且新建立一个struct regulator,挂在regulaot_dev->consumer_list链表下
// g, 找节点的方式比较特殊,需要设备树的配合,也就是说设备树需要按照规范来写
// g, in
drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id);
if (IS_ERR(drvdata->regulator)) {
ret = PTR_ERR(drvdata->regulator);
dev_err(&pdev->dev, "Failed to obtain supply '%s': %d\n",
reg_id, ret);
return ret;
}
// g, 创建调试结点
// g, 每个kobject,在sysfs中都会对应一个目录
ret = sysfs_create_group(&pdev->dev.kobj,
®ulator_virtual_attr_group);
if (ret != 0) {
dev_err(&pdev->dev,
"Failed to create attribute group: %d\n", ret);
return ret;
}
// 这些regulator_xx等等函数,最终都会调用到drvdata->regulator->desc->ops->xxx,也就是为regulator_dev注册的ops函数
drvdata->mode = regulator_get_mode(drvdata->regulator);
platform_set_drvdata(pdev, drvdata);
return 0;
}
最重要的工作就是进行consumer和regulator的匹配工作,也就是说给virtual-consumer找到对应的regulator。此处工作由函数devm_regulator_get()实现:
drivers/regulator/devres.c:
struct regulator *devm_regulator_get(struct device *dev, const char *id)
{
return _devm_regulator_get(dev, id, NORMAL_GET);
}
--->
static struct regulator *_devm_regulator_get(struct device *dev, const char *id,
int get_type)
{
struct regulator **ptr, *regulator;
ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
// gm 传入的是NORMAL_GET
switch (get_type) {
case NORMAL_GET: // g, 通过NORMAL_GET的方式获取
regulator = regulator_get(dev, id);
break;
case EXCLUSIVE_GET:
regulator = regulator_get_exclusive(dev, id);
break;
case OPTIONAL_GET:
regulator = regulator_get_optional(dev, id);
break;
default:
regulator = ERR_PTR(-EINVAL);
}
if (!IS_ERR(regulator)) {
*ptr = regulator;
devres_add(dev, ptr); // g, 把put函数加入到devres中,这样在remove模块的时候就可以自动调用put函数了
} else {
devres_free(ptr);
}
return regulator;
}
通过NORMAL_GET方式简历virtual-consumer和regulator_dev的联系,调用函数regulator_get():
drivers/regulator/core.c;
struct regulator *regulator_get(struct device *dev, const char *id)
{
return _regulator_get(dev, id, false, true);
}
--->
static struct regulator *_regulator_get(struct device *dev, const char *id,
bool exclusive, bool allow_dummy) // g, false, true
{
struct regulator_dev *rdev;
struct regulator *regulator = ERR_PTR(-EPROBE_DEFER);
const char *devname = NULL;
int ret;
if (id == NULL) {
pr_err("get() with no identifier\n");
return ERR_PTR(-EINVAL);
}
if (dev)
devname = dev_name(dev);
if (have_full_constraints())
ret = -ENODEV;
else
ret = -EPROBE_DEFER;
rdev = regulator_dev_lookup(dev, id, &ret); // g, 这个函数会负责找到对应的regulator设备,也就是找到该consumer对应的struct regulator_dev
if (rdev)
goto found;
regulator = ERR_PTR(ret);
/*
* If we have return value from dev_lookup fail, we do not expect to
* succeed, so, quit with appropriate error value
*/
if (ret && ret != -ENODEV)
return regulator;
if (!devname)
devname = "deviceless";
/*
* Assume that a regulator is physically present and enabled
* even if it isn't hooked up and just provide a dummy.
*/
if (have_full_constraints() && allow_dummy) {
pr_warn("%s supply %s not found, using dummy regulator\n",
devname, id);
rdev = dummy_regulator_rdev;
get_device(&rdev->dev);
goto found;
/* Don't log an error when called from regulator_get_optional() */
} else if (!have_full_constraints() || exclusive) {
dev_warn(dev, "dummy supplies not allowed\n");
}
return regulator;
found:
if (rdev->exclusive) { // g, 如果有独占标志
regulator = ERR_PTR(-EPERM);
put_device(&rdev->dev);
return regulator;
}
if (exclusive && rdev->open_count) { // g, 如果想要独占regulator,并且已经被打开过了.实际上这里传入的exclusive = false, 所以这个if不起作用
regulator = ERR_PTR(-EBUSY);
put_device(&rdev->dev);
return regulator;
}
ret = regulator_resolve_supply(rdev);
if (ret < 0) {
regulator = ERR_PTR(ret);
put_device(&rdev->dev);
return regulator;
}
if (!try_module_get(rdev->owner)) {
put_device(&rdev->dev);
return regulator;
}
// g, 如果上述检查都通过的话,会为regulator_dev创建一个struct regulator
// g, 下面这个函数会将新创建的struct regulator加入到regulator_dev->consumer_list链表中
// g, 并且初始化regulator的一些域,比如说使regulator->dev指向传入的consumer_dev
// g, 会更新/sys观测目录下的一些观测节点,比如说在regulator_dev目录(/sys/class/regulator/regulator.x)下建立指向consumer_dev(xx/reg-virt-cosumer.3-id)的链接.
// g, 会在rdev->debugfs(/sys/kernel/debug/regulator/xxx-aldo1)下建立一个调试文件夹regulator->supply_name(/sys/kernel/debug/regulator/xxx-aldo1/reg-virt-consumer.6-aldo1),里面有些调试结点
regulator = create_regulator(rdev, dev, id);
if (regulator == NULL) {
regulator = ERR_PTR(-ENOMEM);
put_device(&rdev->dev);
module_put(rdev->owner);
return regulator;
}
rdev->open_count++;
if (exclusive) {
rdev->exclusive = 1;
ret = _regulator_is_enabled(rdev);
if (ret > 0)
rdev->use_count = 1;
else
rdev->use_count = 0;
}
return regulator;
}
在创建struct regulator的过程中,会调用regulator_dev_lookup()函数,该函数根据virual-consumer的信息,来找到对应的struct regulator_dev,也就是前面注册的regulator设备。其实现过程如下:
static struct regulator_dev *regulator_dev_lookup(struct device *dev,
const char *supply,
int *ret)
{
struct regulator_dev *r;
struct device_node *node;
struct regulator_map *map;
const char *devname = NULL;
regulator_supply_alias(&dev, &supply);
/* first do a dt based lookup */
if (dev && dev->of_node) {
node = of_get_regulator(dev, supply); // g, 找到引用节点,也就是consumer节点要使用哪一个regulator节点,会在设备树中以引用节点的形式表达
if (node) {
r = of_find_regulator_by_node(node); // g, 上面找到node后,获取对应的device,再通过device找到真正的regulator节点对应的struct regulator_dev
if (r)
return r;
*ret = -EPROBE_DEFER;
return NULL;
} else {
/*
* If we couldn't even get the node then it's
* not just that the device didn't register
* yet, there's no node and we'll never
* succeed.
*/
*ret = -ENODEV;
}
}
/* if not found, try doing it non-dt way */
// g, 如果上面没有找到,则不会return, 则需要换其他途径寻找.
if (dev)
devname = dev_name(dev);
// g, 搜索方案二:从regulator_class下寻找是否有名为"supply"的device
r = regulator_lookup_by_name(supply);
if (r)
return r;
mutex_lock(®ulator_list_mutex);
// g, 搜索方案三:从全局变量regulator_map_list中搜索,是否有名字能匹配的regulator_dev
// g, 注册regulator_dev时会向regulator_map_list添加节点.
list_for_each_entry(map, ®ulator_map_list, list) {
/* If the mapping has a device set up it must match */
if (map->dev_name &&
(!devname || strcmp(map->dev_name, devname)))
continue;
if (strcmp(map->supply, supply) == 0 &&
get_device(&map->regulator->dev)) {
mutex_unlock(®ulator_list_mutex);
return map->regulator;
}
}
mutex_unlock(®ulator_list_mutex);
return NULL;
}
使用了三种匹配方案,第一种方案是通过设备树节点信息匹配,通过of_get_regulator()函数来找到对应的regulator对应的节点device_node:
drivers/regulator/core.c:
static struct device_node *of_get_regulator(struct device *dev, const char *supply)
{
struct device_node *regnode = NULL;
char prop_name[32]; /* 32 is max size of property name */
dev_dbg(dev, "Looking up %s-supply from device tree\n", supply);
snprintf(prop_name, 32, "%s-supply", supply); // g, prop_name = dcdc1-supply
// g, 该函数查找"dcdc1-supply"这个引用属性来找对应的结点
// g, 比如说设备树中的一个virtual-consumer节点中的一个属性:dcdc1-supply = <®_dcdc1>;
// g, 那么这个函数返回reg_dcdc1对应的节点.
regnode = of_parse_phandle(dev->of_node, prop_name, 0);
if (!regnode) {
dev_dbg(dev, "Looking up %s property in node %s failed",
prop_name, dev->of_node->full_name);
return NULL;
}
return regnode;
}
带of的函数都是与设备树有关的,该函数由regulator子系统实现,其中使用到了of_parse_phandle()函数。这个of函数是我第一次见,它的作用是根据引用节点名来寻找引用节点。所以为了使用第一种匹配方式,设备树中应这样实现:
regulator0: regulators@0 {
reg_dcdc1: dcdca {
xxx = <xxx>
};
...
// g, 其他regulator
...
};
virtual-dcdc1 {
...
dcdc1-supply = <®_dcdc1>; // g, 引用节点
...
};
...
// g, 其他虚拟consumer节点
...
通过of_parse_phandle(virtual-dcdc1对应的dev_node,“dcdc1-supply”),就可以获取到regulator节点了。这种方法相当于提前在设备树中对virtual-consumer和regulator进行了绑定。
找到匹配的regulator对应的dev_node后,再通过of_find_regulator_by_node()函数找到对应的regulator_dev即可:
static struct regulator_dev *of_find_regulator_by_node(struct device_node *np)
{
struct device *dev;
dev = class_find_device(®ulator_class, NULL, np, of_node_match); // g, 在regulator_class下找到node对应的device
return dev ? dev_to_rdev(dev) : NULL; // g, 找到该struct device所属的struct regulator_dev
}
找到这个virtual-consumer对应的regulator_dev之后,接下来会创建struct regulator,创建struct regulator的函数为create_regulator(),就不展开说了,该函数实现的功能写在注释中了,主要就是把新创建的struct regulator添加到regulator_dev->consumer_list中,表示该struct regulator是该regulator_dev的一个consumer;并且为该virtual-consumer创建一些/sys下的调试节点。
这样,devm_regulator_get()的工作就完成了,再次之后我们调用regulator子系统提供的接口,都需要使用到刚刚创建的struct regulator,因为struct regulator->rdev域保存着匹配到的regulator_dev。
之后,就可以用virtual.c的probe过程中创建的调试节点,通过virtual-consumer来控制真正的regulator设备了。关于这些调试节点,就不说了,就是电流电压控制/开启关闭这些。