Linux regulator子系统分析3(基于Linux6.6)---相关接口介绍
一、regulator device的注册与注销
针对regulator device的注册与注销函数主要涉及regulator_register、regulator_unregister这两个函数。而针对regulator_register函数,其实现哪些功能呢?
- 若该regulator_dev支持使用gpio控制使能与否,则完成下图标号3的gpio相关配置,并将该regulator_dev 使能控制相关的gpio参数注册到下图标号7中的链表regulator_env_gpio_list,通过函数regulator_ena_gpio_request实现;
- 将该regulator dev的所有使用者相关的信息(struct regulator_map类型变量),注册到下图标号5中的链表regulator_map_list;
- 确定该regulator_dev的约束信息(下图标号4 struct regulator_constraints类型的变量配置,通过函数set_machine_constraints实现);
- 完成该regulator_dev的注册,即将该regulator_dev注册到下图标号6中所示的regulator_list上;
除了上述四个主要的内容外,还涉及如下内容:
- 对regulator dev相关的参数进行合法性检测,如get_voltage_sel、list_voltage这两个接口是否只设置一个等;
- 为该regulator_dev创建struct device类型的变量,并注册到linux设备驱动模型子系统中,并完成与regulator_class的关联;
- 为该regulator_dev创建设备属性文件(在sysfs文件系统下创建属性文件,以便应用程序通过设备属性文件即可查看该regulator_dev相关的配置信息,如最小电压、最大电压、操作模式、使能状态、bypass、suspend相关state等),通过函数add_regulator_attributes;
- 若该regulator dev由别的regulator dev提供电压/电流输入,则需要使能其输出,通过函数regulator_enable实现;
以上即为regulator_register实现的主要功能。而针对regulator_unregister则主要实现相反的操作。
drivers/regulator/core.c
/**
* regulator_register - register regulator
* @dev: the device that drive the regulator
* @regulator_desc: regulator to register
* @cfg: runtime configuration for regulator
*
* Called by regulator drivers to register a regulator.
* Returns a valid pointer to struct regulator_dev on success
* or an ERR_PTR() on error.
*/
struct regulator_dev *
regulator_register(struct device *dev,
const struct regulator_desc *regulator_desc,
const struct regulator_config *cfg)
{
const struct regulator_init_data *init_data;
struct regulator_config *config = NULL;
static atomic_t regulator_no = ATOMIC_INIT(-1);
struct regulator_dev *rdev;
bool dangling_cfg_gpiod = false;
bool dangling_of_gpiod = false;
int ret, i;
bool resolved_early = false;
if (cfg == NULL)
return ERR_PTR(-EINVAL);
if (cfg->ena_gpiod)
dangling_cfg_gpiod = true;
if (regulator_desc == NULL) {
ret = -EINVAL;
goto rinse;
}
WARN_ON(!dev || !cfg->dev);
if (regulator_desc->name == NULL || regulator_desc->ops == NULL) {
ret = -EINVAL;
goto rinse;
}
if (regulator_desc->type != REGULATOR_VOLTAGE &&
regulator_desc->type != REGULATOR_CURRENT) {
ret = -EINVAL;
goto rinse;
}
/* 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) {
ret = -EINVAL;
goto rinse;
}
if (regulator_desc->ops->set_voltage_sel &&
!regulator_desc->ops->list_voltage) {
ret = -EINVAL;
goto rinse;
}
rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
if (rdev == NULL) {
ret = -ENOMEM;
goto rinse;
}
device_initialize(&rdev->dev);
dev_set_drvdata(&rdev->dev, rdev);
rdev->dev.class = ®ulator_class;
spin_lock_init(&rdev->err_lock);
/*
* Duplicate the config so the driver could override it after
* parsing init data.
*/
config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL);
if (config == NULL) {
ret = -ENOMEM;
goto clean;
}
init_data = regulator_of_get_init_data(dev, regulator_desc, config,
&rdev->dev.of_node);
/*
* Sometimes not all resources are probed already so we need to take
* that into account. This happens most the time if the ena_gpiod comes
* from a gpio extender or something else.
*/
if (PTR_ERR(init_data) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto clean;
}
/*
* We need to keep track of any GPIO descriptor coming from the
* device tree until we have handled it over to the core. If the
* config that was passed in to this function DOES NOT contain
* a descriptor, and the config after this call DOES contain
* a descriptor, we definitely got one from parsing the device
* tree.
*/
if (!cfg->ena_gpiod && config->ena_gpiod)
dangling_of_gpiod = true;
if (!init_data) {
init_data = config->init_data;
rdev->dev.of_node = of_node_get(config->of_node);
}
ww_mutex_init(&rdev->mutex, ®ulator_ww_class);
rdev->reg_data = config->driver_data;
rdev->owner = regulator_desc->owner;
rdev->desc = regulator_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);
BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work);
if (init_data && init_data->supply_regulator)
rdev->supply_name = init_data->supply_regulator;
else if (regulator_desc->supply_name)
rdev->supply_name = regulator_desc->supply_name;
/* register with sysfs */
rdev->dev.parent = config->dev;
dev_set_name(&rdev->dev, "regulator.%lu",
(unsigned long) atomic_inc_return(®ulator_no));
/* set regulator constraints */
if (init_data)
rdev->constraints = kmemdup(&init_data->constraints,
sizeof(*rdev->constraints),
GFP_KERNEL);
else
rdev->constraints = kzalloc(sizeof(*rdev->constraints),
GFP_KERNEL);
if (!rdev->constraints) {
ret = -ENOMEM;
goto wash;
}
if ((rdev->supply_name && !rdev->supply) &&
(rdev->constraints->always_on ||
rdev->constraints->boot_on)) {
ret = regulator_resolve_supply(rdev);
if (ret)
rdev_dbg(rdev, "unable to resolve supply early: %pe\n",
ERR_PTR(ret));
resolved_early = true;
}
/* perform any regulator specific init */
if (init_data && init_data->regulator_init) {
ret = init_data->regulator_init(rdev->reg_data);
if (ret < 0)
goto wash;
}
if (config->ena_gpiod) {
ret = regulator_ena_gpio_request(rdev, config);
if (ret != 0) {
rdev_err(rdev, "Failed to request enable GPIO: %pe\n",
ERR_PTR(ret));
goto wash;
}
/* The regulator core took over the GPIO descriptor */
dangling_cfg_gpiod = false;
dangling_of_gpiod = false;
}
ret = set_machine_constraints(rdev);
if (ret == -EPROBE_DEFER && !resolved_early) {
/* Regulator might be in bypass mode and so needs its supply
* to set the constraints
*/
/* FIXME: this currently triggers a chicken-and-egg problem
* when creating -SUPPLY symlink in sysfs to a regulator
* that is just being created
*/
rdev_dbg(rdev, "will resolve supply early: %s\n",
rdev->supply_name);
ret = regulator_resolve_supply(rdev);
if (!ret)
ret = set_machine_constraints(rdev);
else
rdev_dbg(rdev, "unable to resolve supply early: %pe\n",
ERR_PTR(ret));
}
if (ret < 0)
goto wash;
ret = regulator_init_coupling(rdev);
if (ret < 0)
goto wash;
/* add consumers devices */
if (init_data) {
for (i = 0; i < init_data->num_consumer_supplies; i++) {
ret = set_consumer_device_supply(rdev,
init_data->consumer_supplies[i].dev_name,
init_data->consumer_supplies[i].supply);
if (ret < 0) {
dev_err(dev, "Failed to set supply %s\n",
init_data->consumer_supplies[i].supply);
goto unset_supplies;
}
}
}
if (!rdev->desc->ops->get_voltage &&
!rdev->desc->ops->list_voltage &&
!rdev->desc->fixed_uV)
rdev->is_switch = true;
ret = device_add(&rdev->dev);
if (ret != 0)
goto unset_supplies;
rdev_init_debugfs(rdev);
/* try to resolve regulators coupling since a new one was registered */
mutex_lock(®ulator_list_mutex);
regulator_resolve_coupling(rdev);
mutex_unlock(®ulator_list_mutex);
/* 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);
regulator_remove_coupling(rdev);
mutex_unlock(®ulator_list_mutex);
wash:
regulator_put(rdev->supply);
kfree(rdev->coupling_desc.coupled_rdevs);
mutex_lock(®ulator_list_mutex);
regulator_ena_gpio_free(rdev);
mutex_unlock(®ulator_list_mutex);
clean:
if (dangling_of_gpiod)
gpiod_put(config->ena_gpiod);
kfree(config);
put_device(&rdev->dev);
rinse:
if (dangling_cfg_gpiod)
gpiod_put(cfg->ena_gpiod);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(regulator_register);
/**
* regulator_unregister - unregister regulator
* @rdev: regulator to unregister
*
* Called by regulator drivers to unregister a regulator.
*/
void regulator_unregister(struct regulator_dev *rdev)
{
if (rdev == NULL)
return;
if (rdev->supply) {
while (rdev->use_count--)
regulator_disable(rdev->supply);
regulator_put(rdev->supply);
}
flush_work(&rdev->disable_work.work);
mutex_lock(®ulator_list_mutex);
WARN_ON(rdev->open_count);
regulator_remove_coupling(rdev);
unset_regulator_supplies(rdev);
list_del(&rdev->list);
regulator_ena_gpio_free(rdev);
device_unregister(&rdev->dev);
mutex_unlock(®ulator_list_mutex);
}
EXPORT_SYMBOL_GPL(regulator_unregister);
二、regulator的注册与注销(regulator device的使用者)
针对regulator_dev的使用者,则主要涉及接口regulator_get、regulator_put这两个函数。而针对regulator_get接口,其主要用于创建struct regulator类型的变量,并完成与regulator_dev的关联,建立如下图的关联关系,具体说明如下:
- 根据dev_name、supply name,在regulator_map_list上进行查找与匹配,若查找到,即获取到对应的regulator_dev;
- 调用create_regulator接口创建一个regulator,并加入到regulator_dev的consumer_list链表中;
三、regulator子系统提供的接口
调节器操作接口
在调节器被成功创建并注册后,消费者可以使用各种操作接口来控制调节器的行为。这些操作通常包括启用、禁用调节器,设置和获取电压、电流,设置调节器的工作模式等。
3.1、电压与电流的设置与获取
-
regulator_set_voltage():设置调节器输出的电压。该函数允许消费者根据需要设置调节器的电压输出。
-
regulator_get_voltage():获取调节器当前的输出电压。
-
regulator_set_current_limit():设置调节器的电流上限,以防止调节器因过载而损坏。
-
regulator_get_current_limit():获取调节器的电流上限。
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);
int regulator_get_voltage(struct regulator *regulator);
int regulator_set_current_limit(struct regulator *regulator, int min_uA, int max_uA);
int regulator_get_current_limit(struct regulator *regulator);
3.2、电源管理操作
-
regulator_enable():启用调节器,使其开始提供电源。
-
regulator_disable():禁用调节器,停止其电源输出。
-
regulator_is_enabled():检查调节器是否处于启用状态。
-
regulator_force_disable():强制禁用调节器,跳过可能的依赖关系。
int regulator_enable(struct regulator *regulator);
int regulator_disable(struct regulator *regulator);
int regulator_is_enabled(struct regulator *regulator);
void regulator_force_disable(struct regulator *regulator);
3.3、调节器工作模式与电压列表
-
regulator_get_mode():获取调节器的当前工作模式(如:自动模式、强制模式等)。
-
regulator_set_mode():设置调节器的工作模式。
-
regulator_list_voltage():列出调节器支持的所有电压值。
int regulator_get_mode(struct regulator *regulator);
int regulator_set_mode(struct regulator *regulator, unsigned int mode);
int regulator_list_voltage(struct regulator *regulator, unsigned int selector);
3.4、调节器挂起与恢复
-
regulator_suspend_prepare():在系统挂起之前准备调节器,确保电源管理的平稳过渡。
-
regulator_suspend_finish():在系统恢复后完成调节器的恢复工作。
int regulator_suspend_prepare(struct regulator *regulator);
int regulator_suspend_finish(struct regulator *regulator);
3.5、批量获取与管理多个调节器
- regulator_bulk_get():同时获取多个调节器,以便同时管理多个电源轨。
int regulator_bulk_get(struct device *dev, unsigned n, struct regulator_bulk_data *bulk);
使用案例与操作流程
外设驱动通过 regulator_get()
获取 struct regulator
句柄后,可以调用上述接口进行电压、电流设置、调节器启用/禁用等操作。操作流程通常如下:
-
获取调节器:外设驱动调用
regulator_get()
获取所需调节器的句柄。 -
设置电压和电流:通过
regulator_set_voltage()
和regulator_set_current_limit()
来配置调节器的电压和电流。 -
启用调节器:调用
regulator_enable()
启用调节器,使其开始为设备供电。 -
禁用调节器:当外设不再需要电源时,调用
regulator_disable()
来禁用调节器。 -
释放资源:当外设不再需要调节器时,调用
regulator_put()
释放对调节器的引用,触发调节器的销毁。