19.5 Regulator驱动
Regulator是Linux系统中电源管理的基础设施之一,用于稳压电源的管理,是各种驱动子系统中设置电压的标准接口。CPUFreq驱动经常使用Regulator来设定电压。
Regulator可以管理系统中的供电单元,即稳压器(Low Dropout Regulator,LDO,即低压差线性稳压器),并提供获取和设置这些供电单元电压的接口。一般在ARM电路板上,各个稳压器和设备会形成一个Regulator树形结构,如图19.6所示。
图19.6 Regulator树形结构
Linux的Regulator子系统提供如下API以用于注册/注销一个稳压器:
linux/regulator/driver.h
struct regulator_dev *regulator_register(const struct regulator_desc *regulator_desc,
const struct regulator_config *config);
void regulator_unregister(struct regulator_dev *rdev);
drivers/regulator/core.c
/**
* regulator_register - register regulator
* @regulator_desc: regulator to register
* @config: 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(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;
if (regulator_desc == NULL || cOnfig== NULL)
return ERR_PTR(-EINVAL);
dev = config->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);
init_data = regulator_of_get_init_data(dev, regulator_desc,
&rdev->dev.of_node);
if (!init_data) {
init_data = config->init_data;
rdev->dev.of_node = of_node_get(config->of_node);
}
mutex_lock(®ulator_list_mutex);
mutex_init(&rdev->mutex);
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);
/* 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;
}
/* register with sysfs */
rdev->dev.class = ®ulator_class;
rdev->dev.parent = dev;
dev_set_name(&rdev->dev, "regulator.%d",
atomic_inc_return(®ulator_no) - 1);
ret = device_register(&rdev->dev);
if (ret != 0) {
put_device(&rdev->dev);
goto clean;
}
dev_set_drvdata(&rdev->dev, rdev);
if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) {
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;
}
}
/* set regulator constraints */
if (init_data)
cOnstraints= &init_data->constraints;
ret = set_machine_constraints(rdev, constraints);
if (ret <0)
goto scrub;
/* add attributes supported by this regulator */
ret = add_regulator_attributes(rdev);
if (ret <0)
goto scrub;
if (init_data && init_data->supply_regulator)
supply = init_data->supply_regulator;
else if (regulator_desc->supply_name)
supply = regulator_desc->supply_name;
if (supply) {
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;
}
add_dev:
/* add consumers devices */
if (init_data) {
for (i = 0; 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);
mutex_unlock(®ulator_list_mutex);
rdev_init_debugfs(rdev);
rdev->proxy_cOnsumer= regulator_proxy_consumer_register(dev, config->of_node);
return rdev;
out:
mutex_unlock(®ulator_list_mutex);
return rdev;
unset_supplies:
unset_regulator_supplies(rdev);
scrub:
if (rdev->supply)
_regulator_put(rdev->supply);
regulator_ena_gpio_free(rdev);
kfree(rdev->constraints);
wash:
device_unregister(&rdev->dev);
/* device core frees rdev */
rdev = ERR_PTR(ret);
goto out;
clean:
kfree(rdev);
rdev = ERR_PTR(ret);
goto out;
}
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);
}
regulator_proxy_consumer_unregister(rdev->proxy_consumer);
rdev_deinit_debugfs(rdev);
mutex_lock(®ulator_list_mutex);
flush_work(&rdev->disable_work.work);
WARN_ON(rdev->open_count);
unset_regulator_supplies(rdev);
list_del(&rdev->list);
kfree(rdev->constraints);
regulator_ena_gpio_free(rdev);
of_node_put(rdev->dev.of_node);
device_unregister(&rdev->dev);
mutex_unlock(®ulator_list_mutex);
}
EXPORT_SYMBOL_GPL(regulator_unregister);
regulator_register()函数的两个参数分别是regulator_desc结构体和regulator_config结构体的指针。其中,regulator_desc结构体是对这个稳压器属性和操作的封装,如代码清单19.7所示。
代码清单19.7 regulator_desc结构体
linux/regulator/driver.h
struct regulator_desc {
const char *name;
const char *supply_name;
const char *of_match;
const char *regulators_node;
int id;
bool continuous_voltage_range;
unsigned n_voltages;
const struct regulator_ops *ops;
int irq;
enum regulator_type type;
struct module *owner;
unsigned int min_uV;
unsigned int uV_step;
unsigned int linear_min_sel;
int fixed_uV;
unsigned int ramp_delay;
const struct regulator_linear_range *linear_ranges;
int n_linear_ranges;
const unsigned int *volt_table;
unsigned int vsel_reg;
unsigned int vsel_mask;
unsigned int apply_reg;
unsigned int apply_bit;
unsigned int enable_reg;
unsigned int enable_mask;
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 enable_time;
unsigned int off_on_delay;
};
regulator_desc结构体中的regulator_ops指针ops是对这个稳压器硬件操作的封装,其中包含获取、设置电压等的成员函数,如代码清单19.8所示。
代码清单19.8 regulator_ops结构体
linux/regulator/driver.h
struct regulator_ops {
/* enumerate supported voltages */
int (*list_voltage) (struct regulator_dev *, unsigned selector);
int (*list_corner_voltage)(struct regulator_dev *, int corner);
/* get/set regulator voltage */
int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV,
unsigned *selector);
int (*map_voltage)(struct regulator_dev *, int min_uV, int max_uV);
int (*set_voltage_sel) (struct regulator_dev *, unsigned selector);
int (*get_voltage) (struct regulator_dev *);
int (*get_voltage_sel) (struct regulator_dev *);
/* get/set regulator current */
int (*set_current_limit) (struct regulator_dev *,
int min_uA, int max_uA);
int (*get_current_limit) (struct regulator_dev *);
/* enable/disable regulator */
int (*enable) (struct regulator_dev *);
int (*disable) (struct regulator_dev *);
int (*is_enabled) (struct regulator_dev *);
/* get/set regulator operating mode (defined in consumer.h) */
int (*set_mode) (struct regulator_dev *, unsigned int mode);
unsigned int (*get_mode) (struct regulator_dev *);
/* Time taken to enable or set voltage on the regulator */
int (*enable_time) (struct regulator_dev *);
int (*set_ramp_delay) (struct regulator_dev *, int ramp_delay);
int (*set_voltage_time_sel) (struct regulator_dev *,
unsigned int old_selector,
unsigned int new_selector);
/* report regulator status ... most other accessors report
* control inputs, this reports results of combining inputs
* from Linux (and other sources) with the actual load.
* returns REGULATOR_STATUS_* or negative errno.
*/
int (*get_status)(struct regulator_dev *);
/* get most efficient regulator operating mode for load */
unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV,
int output_uV, int load_uA);
/* control and report on bypass mode */
int (*set_bypass)(struct regulator_dev *dev, bool enable);
int (*get_bypass)(struct regulator_dev *dev, bool *enable);
/* the operations below are for configuration of regulator state when
* its parent PMIC enters a global STANDBY/HIBERNATE state */
/* set regulator suspend voltage */
int (*set_suspend_voltage) (struct regulator_dev *, int uV);
/* enable/disable regulator in suspend state */
int (*set_suspend_enable) (struct regulator_dev *);
int (*set_suspend_disable) (struct regulator_dev *);
/* set regulator suspend operating mode (defined in consumer.h) */
int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode);
};
在drivers/regulator目录下,包含大量的与电源芯片对应的Regulator驱动,同时提供一个虚拟的Regulator驱动作为参考,如代码清单19.9所示。
代码清单19.9 虚拟的Regulator驱动
#include
#include
#include
#include
#include
#include "dummy.h"
struct regulator_dev *dummy_regulator_rdev;
static struct regulator_init_data dummy_initdata = {
.cOnstraints= {
.always_on = 1,
},
};
static struct regulator_ops dummy_ops;
static struct regulator_desc dummy_desc = {
.name = "regulator-dummy",
.id = -1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.ops = &dummy_ops,
};
static int dummy_regulator_probe(struct platform_device *pdev)
{
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;
}
static struct platform_driver dummy_regulator_driver = {
.probe = dummy_regulator_probe,
.driver = {
.name = "reg-dummy",
.owner = THIS_MODULE,
},
};
static struct platform_device *dummy_pdev;
void __init regulator_dummy_init(void)
{
int ret;
dummy_pdev = platform_device_alloc("reg-dummy", -1);
if (!dummy_pdev) {
pr_err("Failed to allocate dummy regulator device\n");
return;
}
ret = platform_device_add(dummy_pdev);
if (ret != 0) {
pr_err("Failed to register dummy regulator device: %d\n", ret);
platform_device_put(dummy_pdev);
return;
}
ret = platform_driver_register(&dummy_regulator_driver);
if (ret != 0) {
pr_err("Failed to register dummy regulator driver: %d\n", ret);
platform_device_unregister(dummy_pdev);
}
}
Linux的Regulator子系统提供消费者(Consumer)API以便让其他的驱动获取、设置、关闭和使能稳压器:
linux/regulator/consumer.h
struct regulator * regulator_get(structdevice *dev, const char *id);
struct regulator * devm_regulator_get(structdevice *dev, const char *id);
struct regulator * regulator_get_exclusive(structdevice *dev, const char *id);
void regulator_put(structregulator *regulator);
void devm_regulator_put(structregulator *regulator);
int regulator_enable(structregulator *regulator);
int regulator_disable(structregulator *regulator);
int regulator_set_voltage(structregulator *regulator, intmin_uV, intmax_uV);
int regulator_get_voltage(structregulator *regulator);
这些消费者API的地位大致与GPIO子系统的gpio_request()、时钟子系统的clk_get()、dmaengine子系统的dmaengine_submit()等相当,属于基础设施。