本文主要是介绍Linux字符设备驱动 -- regulator子系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 环境
- regulator子系统简介:
- Regulator设备的注册
- Consumer设备的注册
环境
linux 4.9
armv8-A
regulator子系统简介:
关于regulator子系统,可以看下这这些博客:
- Linux驱动之Regulator子系统
- Linux 内核之电源篇(加载流程)
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。不过这一段我的理解可能有问题。
回顾笔记,分析都在注释中。
Regulator设备的注册
对于一个多路输出的PMIC,每一路都可以注册为一个regulator。把每一路注册为一个regulator使用的子系统接口API为:
rdev = devm_regulator_register(&pdev->dev, regulator, ®ulator_config);
需要传入参数:
- struct device *dev,这里传入&pdev->dev,所有regulator总要有一个父设备的,在设备树中我们把所有regulator节点注册为一个regulators节点的子节点。然后这个regulators节点就是这里的platform_device,但是不是在系统启动时注册的,是我们上一节通过MFD子系统注册的。
- const struct regulator_desc *regulator_desc,这是regulator的静态描述信息。这些信息是用来描述regulator设备本身的
- const struct regulator_config *config,这是regulator的动态描述信息。这些信息是用来填充一些结构关系的。
第二个参数结构如下:
struct regulator_desc {const char *name; // g, 该regulator名称const char *supply_name; // g, const char *of_match; // g, 用于与设备树中的regulator匹配,会在初测过程中与regulaotr的设备树节点名字匹配,也就是devcice_node->nameconst 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 idunsigned 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, 可能这个电压控制寄存器的某几位控制输出电压,使用该掩码控制这几位特定的bitunsigned 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, 使用的regmapbool 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, inrdev = regulator_register(regulator_desc, config);if (!IS_ERR(rdev)) {*ptr = rdev;// g, 会为dev->devres_head添加dr->node->entry// g, 也就是说新注册的regulator_dev是传入的dev的一个devresdevres_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 &®ulator_desc->type != REGULATOR_CURRENT)return ERR_PTR(-EINVAL);/* Only one of each should be implemented */WARN_ON(regulator_desc->ops->get_voltage &®ulator_desc->ops->get_voltage_sel);WARN_ON(regulator_desc->ops->set_voltage &®ulator_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, ininit_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->descif (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);
#endifBLOCKING_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_classrdev->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_devinit_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名-aldo1rdev_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);elsesearch = 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, ininit_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。
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, indrvdata->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_GETswitch (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;elseret = -EPROBE_DEFER;rdev = regulator_dev_lookup(dev, id, &ret); // g, 这个函数会负责找到对应的regulator设备,也就是找到该consumer对应的struct regulator_devif (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;elserdev->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_devif (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"的devicer = 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对应的devicereturn 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设备了。关于这些调试节点,就不说了,就是电流电压控制/开启关闭这些。
这篇关于Linux字符设备驱动 -- regulator子系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!