第19章 Linux电源管理的系统架构和驱动之Regulator驱动

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(&regulator_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 = &regulator_class;
        rdev->dev.parent = dev;
        dev_set_name(&rdev->dev, "regulator.%d",
                     atomic_inc_return(&regulator_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 < 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, &regulator_list);

        mutex_unlock(&regulator_list_mutex);
        rdev_init_debugfs(rdev);
        rdev->proxy_consumer = regulator_proxy_consumer_register(dev, config->of_node);
        return rdev;

out:
        mutex_unlock(&regulator_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(&regulator_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(&regulator_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 <linux/err.h>
#include <linux/export.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>

#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()等相当,属于基础设施。

猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80619304
今日推荐