Linux电源管理-Linux Regulator Framework代码分析
Linux电源管理-Linux Regulator Framework代码分析 static struct regulator_dev *dummy_regulator_rdev; }; static struct regulator_init_data dummy_initdata = { }; static struct regulator_ops dummy_ops; static struct regulator_desc dummy_desc = { }; static int regulator_driver_probe(struct platform_device *pdev) } static struct platform_driver regulator_driver = { }; static struct platform_device *regulator_pdev; static int regulator_driver_init(void) } static void regulator_driver_exit(void) } module_init(regulator_driver_init); regulator的作用就是管理consumer设备,给consumer设备提供voltage, current。所以必须实现一个consumer设备,代码如下: static struct regulator* my_regulator; static int regulator_consumer_probe(struct platform_device *pdev) } static int regulator_consumer_remove(struct platform_device *pdev) } static struct platform_driver regulator_consumer_driver = { }; static struct platform_device *regulator_consumer; static int regulator_consumer_init(void) } static void regulator_consumer_exit(void) } module_init(regulator_consumer_init); 在/sys/kernel/debug/regulator/my_regulator_constrains下有该regulator的统计信息 if (regulator_desc == NULL || config == NULL) dev = config->dev; if (regulator_desc->name == NULL || regulator_desc->ops == NULL) if (regulator_desc->type != REGULATOR_VOLTAGE && /* Only one of each should be implemented */ WARN_ON(regulator_desc->ops->set_voltage && /* If we're using selectors we must implement list_voltage. */ } } rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); init_data = regulator_of_get_init_data(dev, regulator_desc, if (!init_data) { } mutex_lock(®ulator_list_mutex); mutex_init(&rdev->mutex); else if (dev_get_regmap(dev, NULL)) else if (dev->parent) INIT_LIST_HEAD(&rdev->consumer_list); /* preform any regulator specific init */ } /* register with sysfs */ ret = device_register(&rdev->dev); } dev_set_drvdata(&rdev->dev, rdev); if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) { } /* set regulator constraints */ ret = set_machine_constraints(rdev, constraints); /* add attributes supported by this regulator */ if (init_data && init_data->supply_regulator) else if (regulator_desc->supply_name) if (supply) { } add_dev: out: unset_supplies: scrub: wash: clean: } 添加consumer设备,通过init_data中的num_consumer_supplies个数,添加consumer的信息。 { if (supply == NULL) if (consumer_dev_name != NULL) else list_for_each_entry(node, ®ulator_map_list, list) { } node = kzalloc(sizeof(struct regulator_map), GFP_KERNEL); node->regulator = rdev; if (has_dev) { } list_add(&node->list, ®ulator_map_list); 然后将此regulator_dev加入到regulator_list中。 在debugfs下创建regulator的属性。 { if (id == NULL) { } if (dev) if (have_full_constraints()) else mutex_lock(®ulator_list_mutex); rdev = regulator_dev_lookup(dev, id, &ret); regulator = ERR_PTR(ret); /* if (!devname) /* even if it isn't hooked up and just provide a dummy. rdev = dummy_regulator_rdev; mutex_unlock(®ulator_list_mutex); found: out: } 找到之后跳到found标号处,创建regulator结构。设置统计参数。 { regulator_supply_alias(&dev, &supply); /* first do a dt based lookup */ } /* if not found, try doing it non-dt way */ list_for_each_entry(r, ®ulator_list, list) list_for_each_entry(map, ®ulator_map_list, list) { } return NULL; 首先会在dt中查找,如果查找不到就使用no-dt的方法。 在regulator_list中通过比较"supply"和rdev->constraints->name或者rdev->desc->name的名字比较。其中supply等于"VCC",而rdev->constraints->name的名字是"my_regulator_constrains",显然是不匹配的。 最后会在regulator_map_list中通过在regulator_register中注册的dev_name和supply匹配,最终会找到注册的regulator_dev。 最终的注册查找可以用如下图概述: 当然了一个regulator可以存在多个consumer设备的,具体情况具体分析。详细信息可以查看文档Documentation/power/regulator/machine.txt
本文链接:https://blog.csdn.net/longwang155069/article/details/53161468
示例分析
在内核kernel/drivers/regulator/dummy.c文件中构造了一个虚拟的regulator,参考此文件编写一个虚拟的regulator driver。
#include
#include
#include
#include
#include
#include
#include
static struct regulator_consumer_supply relate={.dev_name = "reg-consumer",
.supply = "VCC",
.constraints = {
.name ="my_regulator_constrains",
.always_on = 1,
},
.num_consumer_supplies = 1,
.consumer_supplies = &relate,
.name = "regulator-driver",
.id = -1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.ops = &dummy_ops,
{struct regulator_config config = { };
int ret;
config.dev = &pdev->dev;
config.init_data = &dummy_initdata;
dummy_regulator_rdev = regulator_register(&dummy_desc, &config);
if (IS_ERR(dummy_regulator_rdev)) {
ret = PTR_ERR(dummy_regulator_rdev);
pr_err("Failed to register regulator: %d\n", ret);
return ret;
}
return 0;
.probe = regulator_driver_probe,
.driver = {
.name = "reg-driver",
.owner = THIS_MODULE,
},
{int ret;
regulator_pdev = platform_device_alloc("reg-driver", -1);
if (!regulator_pdev) {
pr_err("Failed to allocate dummy regulator device\n");
return -1;
}
ret = platform_device_add(regulator_pdev);
if (ret != 0) {
pr_err("Failed to register dummy regulator device: %d\n", ret);
platform_device_put(regulator_pdev);
return -1;
}
ret = platform_driver_register(®ulator_driver);
if (ret != 0) {
pr_err("Failed to register dummy regulator driver: %d\n", ret);
platform_device_unregister(regulator_pdev);
return -1;
}
return 0;
{printk(KERN_EMERG "regulator: regulator_consumer_exit\n");
regulator_unregister(dummy_regulator_rdev);
platform_device_unregister(regulator_pdev);
platform_driver_unregister(®ulator_driver);
module_exit(regulator_driver_exit);
MODULE_LICENSE("GPL");
此函数通过分配一个名字为"reg-driver"的platform device,同时注册一个名字为"reg-driver"的platform driver,这时候probe函数就会调用。在probe函数中通过调用regulator_register函数注册一个regulator。在注册的时候需要设置regulator的静态配置regulator_desc和动态配置regulator_config。
#include
#include
#include
#include
#include
#include
{int ret;
printk(KERN_EMERG "regulator: regulator_consumer_probe\n");
my_regulator = regulator_get(&pdev->dev,"VCC");
ret = regulator_enable(my_regulator);
return 0;
{printk(KERN_EMERG "regulator: regulator_consumer_remove\n");
regulator_put(my_regulator);
return 0;
.probe = regulator_consumer_probe,
.remove = regulator_consumer_remove,
.driver = {
.name = "reg-consumer",
.owner = THIS_MODULE,
},
{int ret;
printk(KERN_EMERG "regulator: regulator_consumer_init\n");
regulator_consumer = platform_device_alloc("reg-consumer", -1);
if (!regulator_consumer) {
pr_err("Failed to allocate dummy regulator consumer!\n");
return -1;
}
ret = platform_device_add(regulator_consumer);
if (ret != 0) {
pr_err("Failed to register dummy regulator consumer: %d\n", ret);
platform_device_put(regulator_consumer);
return -1;
}
ret = platform_driver_register(®ulator_consumer_driver);
if (ret != 0) {
pr_err("Failed to register dummy regulator consumer: %d\n", ret);
platform_device_unregister(regulator_consumer);
}
return ret;
{printk(KERN_EMERG "regulator: regulator_consumer_exit\n");
platform_device_unregister(regulator_consumer);
platform_driver_unregister(®ulator_consumer_driver);
module_exit(regulator_consumer_exit);
MODULE_LICENSE("GPL");
因为没有真实的consumer设备,所以创建一个虚拟的platform设备"reg-consumer",在probe函数中通过regulator_get就可以获得regulator设备。
测试结果
test:/sys/devices/platform/reg-driver # ls -l
total 0
lrwxrwxrwx 1 root root 0 2012-01-01 13:13 driver -> ../../../bus/platform/drivers/reg-driver
-rw-r--r-- 1 root root 4096 2012-01-01 13:13 driver_override
-r--r--r-- 1 root root 4096 2012-01-01 13:13 modalias
drwxr-xr-x 2 root root 0 2012-01-01 13:13 power
drwxr-xr-x 3 root root 0 2012-01-01 13:13 regulator
lrwxrwxrwx 1 root root 0 2012-01-01 13:13 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4096 2012-01-01 13:13 uevent
test:/sys/class/regulator/regulator.27 # ls
device num_users subsystem suspend_mem_state type
name power suspend_disk_state suspend_standby_state uevent
test:/sys/class/regulator/regulator.27 # cat name
my_regulator_constrains
test:/sys/devices/platform/reg-consumer # ls -l
total 0
lrwxrwxrwx 1 root root 0 2012-01-01 13:29 driver -> ../../../bus/platform/drivers/reg-consumer
-rw-r--r-- 1 root root 4096 2012-01-01 13:29 driver_override
-r--r--r-- 1 root root 4096 2012-01-01 13:29 modalias
drwxr-xr-x 2 root root 0 2012-01-01 13:29 power
lrwxrwxrwx 1 root root 0 2012-01-01 13:29 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4096 2012-01-01 13:29 uevent
test:/sys/class/regulator/regulator.27 # ls
device power suspend_disk_state type
name reg-consumer-VCC suspend_mem_state uevent
num_users subsystem suspend_standby_state
test:/sys/kernel/debug/regulator # cat regulator_summary
regulator use open bypass voltage current min max
-------------------------------------------------------------------------------
....
my_regulator_constrains 0 1 0 0mV 0mA 98mV 987mV
reg-consumer 0mV 0mV
test:/sys/kernel/debug/regulator/my_regulator_constrains # ls -l
total 0
-r--r--r-- 1 root root 0 2012-01-01 13:13 bypass_count
-r--r--r-- 1 root root 0 2012-01-01 13:13 open_count
drwxr-xr-x 2 root root 0 2012-01-01 13:28 reg-consumer-VCC
-r--r--r-- 1 root root 0 2012-01-01 13:13 use_count
代码分析
基于上述的测试结果,首先分析整个regulator的注册过程,已经regulator和consumer之间关系的匹配过程。
regulator_register分析
struct regulator_dev *regulator_register(const struct regulator_desc *regulator_desc,const struct regulator_config *config)
{
const struct regulation_constraints *constraints = NULL;
const struct regulator_init_data *init_data;
static atomic_t regulator_no = ATOMIC_INIT(0);
struct regulator_dev *rdev;
struct device *dev;
int ret, i;
const char *supply = NULL;return ERR_PTR(-EINVAL);
WARN_ON(!dev);return ERR_PTR(-EINVAL);
regulator_desc->type != REGULATOR_CURRENT)
return ERR_PTR(-EINVAL);
WARN_ON(regulator_desc->ops->get_voltage &®ulator_desc->ops->get_voltage_sel);
regulator_desc->ops->set_voltage_sel);
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);
if (rdev == NULL)return ERR_PTR(-ENOMEM);
&rdev->dev.of_node);
init_data = config->init_data;
rdev->dev.of_node = of_node_get(config->of_node);
rdev->reg_data = config->driver_data;
rdev->owner = regulator_desc->owner;
rdev->desc = regulator_desc;
if (config->regmap)rdev->regmap = config->regmap;
rdev->regmap = dev_get_regmap(dev, NULL);
rdev->regmap = dev_get_regmap(dev->parent, NULL);
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->regulator_init) {ret = init_data->regulator_init(rdev->reg_data);
if (ret < 0)
goto clean;
rdev->dev.class = ®ulator_class;
rdev->dev.parent = dev;
dev_set_name(&rdev->dev, "regulator.%d", atomic_inc_return(®ulator_no) - 1);
if (ret != 0) {put_device(&rdev->dev);
goto clean;
ret = regulator_ena_gpio_request(rdev, config);
if (ret != 0) {
rdev_err(rdev, "Failed to request enable GPIO%d: %d\n",
config->ena_gpio, ret);
goto wash;
}
if (init_data)constraints = &init_data->constraints;
if (ret < 0)goto scrub;
ret = add_regulator_attributes(rdev);
if (ret < 0)goto scrub;
supply = init_data->supply_regulator;
supply = regulator_desc->supply_name;
struct regulator_dev *r;
r = regulator_dev_lookup(dev, supply, &ret);
if (ret == -ENODEV) {
/*
* No supply was specified for this regulator and
* there will never be one.
*/
ret = 0;
goto add_dev;
} else if (!r) {
dev_err(dev, "Failed to find supply %s\n", supply);
ret = -EPROBE_DEFER;
goto scrub;
}
ret = set_supply(rdev, r);
if (ret < 0)
goto scrub;
/* Enable supply if rail is enabled */
if (_regulator_is_enabled(rdev)) {
ret = regulator_enable(rdev->supply);
if (ret < 0)
goto scrub;
}
/* 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;
}
}
}
list_add(&rdev->list, ®ulator_list);
rdev_init_debugfs(rdev);
mutex_unlock(®ulator_list_mutex);
return rdev;
unset_regulator_supplies(rdev);
if (rdev->supply)
_regulator_put(rdev->supply);
regulator_ena_gpio_free(rdev);
kfree(rdev->constraints);
device_unregister(&rdev->dev);
/* device core frees rdev */
rdev = ERR_PTR(ret);
goto out;
kfree(rdev);
rdev = ERR_PTR(ret);
goto out;
代码有点长,只关注重点部分。
static int set_consumer_device_supply(struct regulator_dev *rdev, const char *consumer_dev_name,
const char *supply)
struct regulator_map *node;
int has_dev;return -EINVAL;
has_dev = 1;
has_dev = 0;
if (node->dev_name && consumer_dev_name) {
if (strcmp(node->dev_name, consumer_dev_name) != 0)
continue;
} else if (node->dev_name || consumer_dev_name) {
continue;
}
if (strcmp(node->supply, supply) != 0)
continue;
pr_debug("%s: %s/%s is '%s' supply; fail %s/%s\n",
consumer_dev_name,
dev_name(&node->regulator->dev),
node->regulator->desc->name,
supply,
dev_name(&rdev->dev), rdev_get_name(rdev));
return -EBUSY;
if (node == NULL)return -ENOMEM;
node->supply = supply;node->dev_name = kstrdup(consumer_dev_name, GFP_KERNEL);
if (node->dev_name == NULL) {
kfree(node);
return -ENOMEM;
}
return 0;
}
此函数先在regulator_map_list链表中通过consumer_dev_name和supply查找是否存在,如果存在返回EBUSY。否则重新分配一个regulator_map变量,使用传入的参数初始化,然后将此regulator_map加入到regulator_map_list链表中。
regulator_get分析
在consumer函数中,会通过regulator_get函数得到该consumer的regulator。接下来分析是如何得到consumer的regulator。
static struct regulator *_regulator_get(struct device *dev, const char *id, bool exclusive, bool allow_dummy)
struct regulator_dev *rdev;
struct regulator *regulator = ERR_PTR(-EPROBE_DEFER);
const char *devname = NULL;
int ret;pr_err("get() with no identifier\n");
return ERR_PTR(-EINVAL);
devname = dev_name(dev);
ret = -ENODEV;
ret = -EPROBE_DEFER;
if (rdev)goto found;
*/
if (ret && ret != -ENODEV)
goto out;devname = "deviceless";
*/
if (have_full_constraints() && allow_dummy) {
pr_warn("%s supply %s not found, using dummy regulator\n",devname, id);
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;if (rdev->exclusive) {
regulator = ERR_PTR(-EPERM);
goto out;
}
if (exclusive && rdev->open_count) {
regulator = ERR_PTR(-EBUSY);
goto out;
}
if (!try_module_get(rdev->owner))
goto out;
regulator = create_regulator(rdev, dev, id);
if (regulator == NULL) {
regulator = ERR_PTR(-ENOMEM);
module_put(rdev->owner);
goto out;
}
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;
}
mutex_unlock(®ulator_list_mutex);
return regulator;
此函数的重点就是查找regulator_dev的过程。
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;
if (dev && dev->of_node) {node = of_get_regulator(dev, supply);
if (node) {
list_for_each_entry(r, ®ulator_list, list)
if (r->dev.parent &&
node == r->dev.of_node)
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 (dev)devname = dev_name(dev);
if (strcmp(rdev_get_name(r), supply) == 0)
return r;
/* 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)
return map->regulator;
}
————————————————
版权声明:本文为CSDN博主「Loopers」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/longwang155069/article/details/53161468