linux中的regulator

本文介绍嵌入式系统中的电源管理技术,重点讲解regulator模块如何控制电压电流供应,包括电源芯片、消费者部件和单板的概念。文章还详细解析了regulator模块的架构、关键数据结构以及注册流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Regulator模块用于控制系统中某些设备的电压/电流供应。在嵌入式系统(尤其是手机)中,控制耗电量很重要,直接影响到电池的续航时间。所以,如果系统中某一个模块暂时不需要使用,就可以通过regulator关闭其电源供应;或者降低提供给该模块的电压、电流大小。

regulator常用概念

Regulator : 电源芯片, 比如电压转换芯片
Consumer : 消费者,使用电源的部件, Regulator是给Consumer供电的
machine : 单板,上面焊接有Regulator和Consumer
Constraints : 约束, 比如某个电源管理芯片输出的电压范围
Supply : 提供电源的部件, Regulator就是一个Supply; Regulator A可以给Regulator B供电, 那么Regulator B的Supply就是A

regulaor :常用的为电压调节器,总共有以下几类
ldo :输入/输出电压差别不大时使用,电路简单,转换效率较低,如果输入/输出电压相差较大损耗就高,造成芯片发烫
dcdc:输入/输出压差别较大时使用,电路比较复杂,转换效率高
pmu : 集成了多个ldo、dcdc,可以工资每个单元电压的输出大小

regulator模块架构

主要作用: 给外设(consumer)供电
framwork层次:主要分为 machine、regulator、consumer三部分

machine
(1) 指定了regulaor和consumer的对应关系
(consumer dev的name和给consumer 引脚供电的引脚名字)
(2) 约束调节:regulator的电压范围等等

驱动要做的事情:
注册一个platform_device: 在它的私有数据里指定regulator和consume的对应关系(这个电源芯片给哪一个部件供电)
指定约束条件(比如电压范围)

regulator
电源芯片本身的驱动实现的fops
.enable
.disable
.set_votage
.set_current

regulator_register后在 regulator_list里就有了consumer dev的name和给consumer 引脚供电的引脚名字

驱动要做的事情:
注册一个platform_driver: 在它的probe函数里分配、设置、注册一个regulator
“设置”里要做的事情: 实现regulator的操作, 比如enable, disable, set_voltage

consumer
使用即可
regulator_get, regulator_enable, regulator_disable, regulator_set_voltage

regulator_get(struct device *dev, const char *id) // dev中包含了name,id指定了这个regulator给consumer那个电源引脚供电 

regulator重要的结构体

regulator_dev代表一个regulator设备


/*
 * struct regulator_dev
 *
 * Voltage / Current regulator class device. One for each
 * regulator.
 *
 * This should *not* be used directly by anything except the regulator
 * core and notification injection (which should take the mutex and do
 * no other direct access).
 */
struct regulator_dev {
    const struct regulator_desc *desc;//描述符,包括regulator的名称、ID、regulator_ops等
    int exclusive;
    u32 use_count;   // 使用计数
    u32 open_count;
    u32 bypass_count;

    /* lists we belong to */
    struct list_head list; /* list of all regulators */

    /* lists we own */
    struct list_head consumer_list; /* consumers we supply */// 此regulator负责供电的设备列表

    struct blocking_notifier_head notifier;
    struct mutex mutex; /* consumer lock */
    struct module *owner;
    struct device dev;
    struct regulation_constraints *constraints;
    struct regulator *supply;   /* for tree */
    const char *supply_name;
    struct regmap *regmap;

    struct delayed_work disable_work;
    int deferred_disables;

    void *reg_data;     /* regulator_dev data */

    struct dentry *debugfs;

    struct regulator_enable_gpio *ena_pin;
    unsigned int ena_gpio_state:1;

    /* time when this regulator was disabled last time */
    unsigned long last_off_jiffy;
};

regulator_init_data在初始化时使用,用来建立父子regulator、受电模块之间的树状结构,以及一些regulator的基本参数。


/**
 * struct regulator_init_data - regulator platform initialisation data.
 *
 * Initialisation constraints, our supply and consumers supplies.
 *
 * @supply_regulator: Parent regulator.  Specified using the regulator name
 *                    as it appears in the name field in sysfs, which can
 *                    be explicitly set using the constraints field 'name'.
 *
 * @constraints: Constraints.  These must be specified for the regulator to
 *               be usable.
 * @num_consumer_supplies: Number of consumer device supplies.
 * @consumer_supplies: Consumer device supply configuration.
 *
 * @regulator_init: Callback invoked when the regulator has been registered.
 * @driver_data: Data passed to regulator_init.
 */
struct regulator_init_data {
    const char *supply_regulator;        /* or NULL for system supply */

    struct regulation_constraints constraints;

    int num_consumer_supplies;
    struct regulator_consumer_supply *consumer_supplies;

    /* optional regulator machine specific init */
    int (*regulator_init)(void *driver_data);
    void *driver_data;  /* core does not touch this */
};

regulator_desc 结构体


/**
 * struct regulator_desc - Static regulator descriptor
 *
 * Each regulator registered with the core is described with a
 * structure of this type and a struct regulator_config.  This
 * structure contains the non-varying parts of the regulator
 * description.
 *
 * @name: Identifying name for the regulator.
 * @supply_name: Identifying the regulator supply
 * @of_match: Name used to identify regulator in DT.
 * @regulators_node: Name of node containing regulator definitions in DT.
 * @of_parse_cb: Optional callback called only if of_match is present.
 *               Will be called for each regulator parsed from DT, during
 *               init_data parsing.
 *               The regulator_config passed as argument to the callback will
 *               be a copy of config passed to regulator_register, valid only
 *               for this particular call. Callback may freely change the
 *               config but it cannot store it for later usage.
 *               Callback should return 0 on success or negative ERRNO
 *               indicating failure.
 * @id: Numerical identifier for the regulator.
 * @ops: Regulator operations table.
 * @irq: Interrupt number for the regulator.
 * @type: Indicates if the regulator is a voltage or current regulator.
 * @owner: Module providing the regulator, used for refcounting.
 *
 * @continuous_voltage_range: Indicates if the regulator can set any
 *                            voltage within constrains range.
 * @n_voltages: Number of selectors available for ops.list_voltage().
 *
 * @min_uV: Voltage given by the lowest selector (if linear mapping)
 * @uV_step: Voltage increase with each selector (if linear mapping)
 * @linear_min_sel: Minimal selector for starting linear mapping
 * @fixed_uV: Fixed voltage of rails.
 * @ramp_delay: Time to settle down after voltage change (unit: uV/us)
 * @min_dropout_uV: The minimum dropout voltage this regulator can handle
 * @linear_ranges: A constant table of possible voltage ranges.
 * @n_linear_ranges: Number of entries in the @linear_ranges table.
 * @volt_table: Voltage mapping table (if table based mapping)
 *
 * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_
 * @vsel_mask: Mask for register bitfield used for selector
 * @csel_reg: Register for TPS65218 LS3 current regulator
 * @csel_mask: Mask for TPS65218 LS3 current regulator
 * @apply_reg: Register for initiate voltage change on the output when
 *                using regulator_set_voltage_sel_regmap
 * @apply_bit: Register bitfield used for initiate voltage change on the
 *                output when using regulator_set_voltage_sel_regmap
 * @enable_reg: Register for control when using regmap enable/disable ops
 * @enable_mask: Mask for control when using regmap enable/disable ops
 * @enable_val: Enabling value for control when using regmap enable/disable ops
 * @disable_val: Disabling value for control when using regmap enable/disable ops
 * @enable_is_inverted: A flag to indicate set enable_mask bits to disable
 *                      when using regulator_enable_regmap and friends APIs.
 * @bypass_reg: Register for control when using regmap set_bypass
 * @bypass_mask: Mask for control when using regmap set_bypass
 * @bypass_val_on: Enabling value for control when using regmap set_bypass
 * @bypass_val_off: Disabling value for control when using regmap set_bypass
 * @active_discharge_off: Enabling value for control when using regmap
 *            set_active_discharge
 * @active_discharge_on: Disabling value for control when using regmap
 *           set_active_discharge
 * @active_discharge_mask: Mask for control when using regmap
 *             set_active_discharge
 * @active_discharge_reg: Register for control when using regmap
 *            set_active_discharge
 *
 * @enable_time: Time taken for initial enable of regulator (in uS).
 * @off_on_delay: guard time (in uS), before re-enabling a regulator
 *
 * @of_map_mode: Maps a hardware mode defined in a DeviceTree to a standard mode
 */
struct regulator_desc {
    const char *name;
    const char *supply_name;
    const char *of_match;
    const char *regulators_node;
    int (*of_parse_cb)(struct device_node *,
                const struct regulator_desc *,
                struct regulator_config *);
    int id;
    unsigned int continuous_voltage_range:1;
    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;
    int min_dropout_uV;

    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 csel_reg;
    unsigned int csel_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 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设备驱动直接操作的结构体
struct regulation_constraints regulator限制范围,用于初始化
struct regulator_consumer_supply regulator的consumer信息
struct regulator_map 为consumers与regulator对应表
struct regulator_config regulator的动态运行信息
struct regulator_opsregulator的操作函数集合

注册regulator

通过regulator_register函数登记生成一个regulator_dev


/**
 * regulator_register - register 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(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 &&
        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);

    /*
     * 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);
    }

    init_data = regulator_of_get_init_data(dev, regulator_desc, config,
                           &rdev->dev.of_node);
    if (!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;
    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;
    }

    if ((config->ena_gpio || config->ena_gpio_initialized) &&
        gpio_is_valid(config->ena_gpio)) {
        mutex_lock(&regulator_list_mutex);
        ret = regulator_ena_gpio_request(rdev, config);
        mutex_unlock(&regulator_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 = &regulator_class;
    rdev->dev.parent = dev;
    dev_set_name(&rdev->dev, "regulator.%lu",
            (unsigned long) atomic_inc_return(&regulator_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)
        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(&regulator_list_mutex);
        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) {
                mutex_unlock(&regulator_list_mutex);
                dev_err(dev, "Failed to set supply %s\n",
                    init_data->consumer_supplies[i].supply);
                goto unset_supplies;
            }
        }
        mutex_unlock(&regulator_list_mutex);
    }

    ret = device_register(&rdev->dev);
    if (ret != 0) {
        put_device(&rdev->dev);
        goto unset_supplies;
    }

    dev_set_drvdata(&rdev->dev, rdev);
    rdev_init_debugfs(rdev);

    /* try to resolve regulators supply since a new one was registered */
    class_for_each_device(&regulator_class, NULL, NULL,
                  regulator_register_resolve_supply);
    kfree(config);
    return rdev;

unset_supplies:
    mutex_lock(&regulator_list_mutex);
    unset_regulator_supplies(rdev);
    mutex_unlock(&regulator_list_mutex);
wash:
    kfree(rdev->constraints);
    mutex_lock(&regulator_list_mutex);
    regulator_ena_gpio_free(rdev);
    mutex_unlock(&regulator_list_mutex);
clean:
    kfree(rdev);
    kfree(config);
    return ERR_PTR(ret);
}

set_consumer_device_supply函数用于登记regulator_devcomsumer_dev(regulator负责供电的设备)之间的对应关系。对于每一个regulator_devcomsumer_dev的配对,都会有一个regulator_map结构,这些结构会被加入到全局链表regulator_map_list中。

在设备驱动使用regulator对其驱动的设备供电时,需要首先保证设备与对应regulator之间的匹配关系已经被登记到regulator框架中。
设备驱动通过regulator_get函数得到regulator结构,此函数通过regulator_map_list找到对应regulator_dev,再生成regulator结构给用户使用。

通过regulator_enable/regulator_disable打开、关闭regulator,这两个函数最终都是调用struct regulator_ops里的对应成员。
除此之外,还有regualtor_set_voltage / regulator_get_voltage等等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值