文章目录
- 概述
- 一、provider驱动
-
- [1.1 设备树](#1.1 设备树)
- [1.2 数据结构](#1.2 数据结构)
-
- [1.2.1 struct regulator_desc](#1.2.1 struct regulator_desc)
- 1.2.2
- [1.2.3 struct regulator_ops](#1.2.3 struct regulator_ops)
- [1.3 代码分析](#1.3 代码分析)
-
- [1.3.1 scmi设备树](#1.3.1 scmi设备树)
- [1.3.2 probe驱动代码](#1.3.2 probe驱动代码)
- [1.3.3 process_scmi_regulator_of_node](#1.3.3 process_scmi_regulator_of_node)
- [1.3.4 scmi_regulator_common_init](#1.3.4 scmi_regulator_common_init)
- [1.3.5 devm_regulator_register](#1.3.5 devm_regulator_register)
- 二、consumer驱动
-
- [2.1 设备树](#2.1 设备树)
- [2.2 api函数](#2.2 api函数)
-
- [2.2.1 获取regulator接口](#2.2.1 获取regulator接口)
- [2.2.2 释放regulator接口](#2.2.2 释放regulator接口)
- [2.2.3 regulator控制和状态接口](#2.2.3 regulator控制和状态接口)
- [2.2.4 多regulator控制接口](#2.2.4 多regulator控制接口)
| 平台 | 内核版本 | |
|---|---|---|
| ARM | linux 5.15 |
概述
Regulator 子系统是 Linux 内核中用于管理系统电源稳压器的框架,它为核心、驱动和用户空间提供了统一的电压/电流调节接口。该子系统抽象了各类稳压器硬件(如 PMIC、低压差稳压器),通过 consumer、provider 和 core 三层结构,实现动态电压/频率调节(DVFS)、电源域管理以及睡眠状态下的功耗优化。对于一款SOC芯片来说,精细化地控制电源开关、电压、电流输出虽然复杂度较高,但作为对功耗有显著影响的模块,还是值得细研究下。一般开发板是通过外接PMIC芯片进行电压调节的,比如瑞芯微使用的rk808,我们在这块芯片的驱动可以看到regulator的一些端倪。下面一起分析regulator设计及实现。
一、provider驱动
1.1 设备树
我们先看看固定电压和rk808的设备树:
c
vcc_sys: vcc-sys-regulator {
compatible = "regulator-fixed";
regulator-name = "vcc_sys";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
regulator-always-on;
regulator-boot-on;
};
&i2c0 {
rk808: pmic@1b {
compatible = "rockchip,rk808";
reg = <0x1b>;
vcc1-supply = <&vcc_sys>;
regulators {
vdd_cpu: DCDC_REG1 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <700000>;
regulator-max-microvolt = <1500000>;
regulator-name = "vdd_cpu";
};
vdd_log: DCDC_REG2 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <700000>;
regulator-max-microvolt = <1500000>;
regulator-name = "vdd_log";
};
...
}
}
}
1.2 数据结构
1.2.1 struct regulator_desc
struct regulator_desc 是 Linux Regulator 子系统的核心数据结构之一,用于在驱动中静态地描述一个稳压器的固有硬件特性,也可以说是一个电压或者电流源的对象。也是是为regulator_register()函数提供注册一个稳压器所需的非可变硬件信息数据结构。结构体很大,因为记录支持电压电流的方式不同,有线性的,有散列的,还有固定的。
c
struct regulator_desc {
const char *name; //名字
const char *supply_name;//如果有父电压,记录一下
const char *of_match;
bool of_match_full_name;
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; //支持的电压数量
unsigned int n_current_limits;
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 linear_range *linear_ranges;
const unsigned int *linear_range_selectors;
int n_linear_ranges;
const unsigned int *volt_table; //不规则电压设置这个
const unsigned int *curr_table;
unsigned int ramp_mask;
const unsigned int *ramp_delay_table;
unsigned int n_ramp_values;
unsigned int enable_time;
unsigned int off_on_delay;
unsigned int poll_enabled_time;
};
这个结构体最重要的成员主要是记录了支持哪些电压和操作硬件的ops。
1.2.2
regulator_config 作用类似于安装配置包,将稳压器的静态能力(regulator_desc)与具体运行环境(哪个设备、什么约束、如何访问硬件)绑定,是驱动调用 regulator_register() 时必须提供的另一个参数。
c
struct regulator_config {
struct device *dev;//指向拥有此稳压器的父设备对象
const struct regulator_init_data *init_data;//指向初始化约束数据
void *driver_data;//驱动私有数据指针
struct device_node *of_node;//指向关联的设备树节点
struct regmap *regmap;//寄存器访问抽象句柄(可选)
struct gpio_desc *ena_gpiod;//GPIO控制使能引脚(可选)
};
1.2.3 struct regulator_ops
struct regulator_ops 是 Regulator 子系统中由硬件驱动实现的核心操作函数集,它定义了稳压器所有可能的硬件控制动作(如使能、设置电压),是连接框架抽象与具体硬件的桥梁。该结构体包含一系列函数指针(如 enable, set_voltage_sel, get_voltage),驱动开发者根据芯片实际能力进行填充;框架和消费者调用这些统一接口时,驱动通过底层的寄存器读写来执行真实操作,从而实现了硬件差异的屏蔽与电源管理的标准化。
c
static const struct regulator_ops scmi_reg_discrete_ops = {
.enable = scmi_reg_enable,//启用稳压器输出
.disable = scmi_reg_disable,//禁用稳压器输出
.is_enabled = scmi_reg_is_enabled,//查询稳压器当前启用状态
.get_voltage_sel = scmi_reg_get_voltage_sel,//获取当前电压选择器索引值
.set_voltage_sel = scmi_reg_set_voltage_sel,//设置电压选择器索引值
.list_voltage = regulator_list_voltage_table,//查询所有支持的电压值
.map_voltage = regulator_map_voltage_iterate,//将电压值转换为对应的索引值
};
这里是比较重要的几个成员,其中要注意的是get_voltage_sel和set_voltage_sel成员传入参数并不是电压值,而已电压在regulator的索引值。
1.3 代码分析
1.3.1 scmi设备树
c
firmware {
scmi: scmi {
compatible = "arm,scmi-smc";
arm,smc-id = <0x82000010>;
shmem = <&scmi_shmem>;
#address-cells = <1>;
#size-cells = <0>;
scmi_regu: regulators {
#address-cells = <1>;
#size-cells = <0>;
scmi_reg_dcdc1: scmi_dcdc1 {
reg = <1>;
regulator-name = "dcdc1";
};
scmi_reg_dcdc2: scmi_dcdc2 {
reg = <2>;
regulator-name = "dcdc2";
};
};
};
};
1.3.2 probe驱动代码
在代码分析这一块,我们还是看scmi regulator驱动,因为这里没有哪些复杂的硬件初始化和操作,方便我们分析,代码在drivers/regulator/scmi-regulator.c。
下面是驱动的probe伪代码:
c
static int scmi_regulator_probe(struct scmi_device *sdev)
{
int d, ret, num_doms;
struct device_node *np, *child;
const struct scmi_handle *handle = sdev->handle;
struct scmi_regulator_info *rinfo;
struct scmi_protocol_handle *ph;
num_doms = voltage_ops->num_domains_get(ph);
rinfo = devm_kzalloc(&sdev->dev, sizeof(*rinfo), GFP_KERNEL);
rinfo->sregv = devm_kcalloc(&sdev->dev, num_doms,
sizeof(void *), GFP_KERNEL);
rinfo->num_doms = num_doms;
np = of_find_node_by_name(handle->dev->of_node, "regulators");
for_each_child_of_node(np, child) {
ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo);
}
for (d = 0; d < num_doms; d++) {
struct scmi_regulator *sreg = rinfo->sregv[d];
/* Skip empty slots */
if (!sreg)
continue;
ret = scmi_regulator_common_init(sreg);
sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc,
&sreg->conf);
}
dev_set_drvdata(&sdev->dev, rinfo);
return 0;
}
probe函数很简单,主要是做了以下几件事:
- 调用scmi协议函数voltage_ops->num_domains_get查询host支持regulator的数量,
- 调用函数of_find_node_by_name找到该设备下的regulators节点,
- 调用函数遍历regulators节点下的每一个子节点,也就是设备树中提供的电压节点,对每一个节点调用函数process_scmi_regulator_of_node初始化,
- 遍历scmi支持的regulator,如果我们设备树没有使用到这个regulator,就跳过他;否则就调用函数scmi_regulator_common_init函数初始化,最后调用函数devm_regulator_register注册。
后面我们再看看process_scmi_regulator_of_node、scmi_regulator_common_init和devm_regulator_register函数。
1.3.3 process_scmi_regulator_of_node
process_scmi_regulator_of_node函数主要是根据regulator节点通过分配私有成员的内存和记录一些简单的信息:
c
static int process_scmi_regulator_of_node(struct scmi_device *sdev,
struct scmi_protocol_handle *ph,
struct device_node *np,
struct scmi_regulator_info *rinfo)
{
u32 dom, ret;
ret = of_property_read_u32(np, "reg", &dom);
if (ret)
return ret;
if (dom >= rinfo->num_doms)
return -ENODEV;
if (rinfo->sregv[dom]) {
dev_err(&sdev->dev,
"SCMI Voltage Domain %d already in use. Skipping: %s\n",
dom, np->full_name);
return -EINVAL;
}
rinfo->sregv[dom] = devm_kzalloc(&sdev->dev,
sizeof(struct scmi_regulator),
GFP_KERNEL);
if (!rinfo->sregv[dom])
return -ENOMEM;
rinfo->sregv[dom]->id = dom;
rinfo->sregv[dom]->sdev = sdev;
rinfo->sregv[dom]->ph = ph;
/* get hold of good nodes */
of_node_get(np);
rinfo->sregv[dom]->of_node = np;
dev_dbg(&sdev->dev,
"Found SCMI Regulator entry -- OF node [%d] -> %s\n",
dom, np->full_name);
return 0;
}
process_scmi_regulator_of_node函数做了下面几件事:
- 调用函数of_property_read_u32获取regulator节点下reg属性的数值,记录在dom中,
- 如果获取不到reg的值,或者reg的值超出host支持的数量,有或者reg的值重复,都会返回错误,
- 分配struct scmi_regulator的内存,并且记录在rinfo的sregv数组下标为dom的成员中,其中包括了struct regulator_desc的内存,
- 设置scmi_regulator结构体的成员,
1.3.4 scmi_regulator_common_init
scmi_regulator_common_init函数是真的regulator节点处理函数,需要通过scmi协议获取相关信息,方便后面注册regulator的:
c
static int scmi_regulator_common_init(struct scmi_regulator *sreg)
{
int ret;
struct device *dev = &sreg->sdev->dev;
const struct scmi_voltage_info *vinfo;
vinfo = voltage_ops->info_get(sreg->ph, sreg->id);
if (!vinfo) {
dev_warn(dev, "Failure to get voltage domain %d\n",
sreg->id);
return -ENODEV;
}
/*
* Regulator framework does not fully support negative voltages
* so we discard any voltage domain reported as supporting negative
* voltages: as a consequence each levels_uv entry is guaranteed to
* be non-negative from here on.
*/
if (vinfo->negative_volts_allowed) {
dev_warn(dev, "Negative voltages NOT supported...skip %s\n",
sreg->of_node->full_name);
return -EOPNOTSUPP;
}
sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name);
if (!sreg->desc.name)
return -ENOMEM;
sreg->desc.id = sreg->id;
sreg->desc.type = REGULATOR_VOLTAGE;
sreg->desc.owner = THIS_MODULE;
sreg->desc.of_match_full_name = true;
sreg->desc.of_match = sreg->of_node->full_name;
sreg->desc.regulators_node = "regulators";
if (vinfo->segmented)
ret = scmi_config_linear_regulator_mappings(sreg, vinfo);
else
ret = scmi_config_discrete_regulator_mappings(sreg, vinfo);
if (ret)
return ret;
/*
* Using the scmi device here to have DT searched from Voltage
* protocol node down.
*/
sreg->conf.dev = dev;
/* Store for later retrieval via rdev_get_drvdata() */
sreg->conf.driver_data = sreg;
return 0;
}
scmi_regulator_common_init做了以下几件事:
- 调用scmi协议函数voltage_ops->info_get获取对应regulator节点的信息,
- struct regulator_desc内存已经在上一个函数分配了,这里根据scmi获取到的信息设置struct regulator_desc的成员,
- 最后根据电压的类型,如果是线性电压,调用函数scmi_config_linear_regulator_mappings设置设置struct regulator_desc中支持电压的成员和ops方法集;如果是散列电压,则调用函数scmi_config_discrete_regulator_mappings设置struct regulator_desc中支持电压的成员和ops方法集,
1.3.5 devm_regulator_register
devm_regulator_register最关键还是通过调用函数regulator_register进行注册的:
c
struct regulator_dev *
regulator_register(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;
struct device *dev;
int ret, i;
if (cfg == NULL)
return ERR_PTR(-EINVAL);
if (cfg->ena_gpiod)
dangling_cfg_gpiod = true;
if (regulator_desc == NULL) {
ret = -EINVAL;
goto rinse;
}
dev = cfg->dev;
WARN_ON(!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);
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);
/* 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_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 clean;
}
/* The regulator core took over the GPIO descriptor */
dangling_cfg_gpiod = false;
dangling_of_gpiod = false;
}
/* register with sysfs */
rdev->dev.class = ®ulator_class;
rdev->dev.parent = dev;
dev_set_name(&rdev->dev, "regulator.%lu",
(unsigned long) atomic_inc_return(®ulator_no));
dev_set_drvdata(&rdev->dev, rdev);
/* 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 (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;
ret = set_machine_constraints(rdev);
if (ret == -EPROBE_DEFER) {
/* 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;
}
二、consumer驱动
2.1 设备树
我们先找一个消费者的设备树看看:
c
&sdmmc {
bus-width = <4>;
cap-sd-highspeed;
cd-gpios = <&gpio0 RK_PA7 GPIO_ACTIVE_LOW>;
disable-wp;
max-frequency = <150000000>;
pinctrl-names = "default";
pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_bus4>;
sd-uhs-sdr104;
vmmc-supply = <&vcc3v0_sd>;
vqmmc-supply = <&vcc_sdio>;
status = "okay";
};
关键是vmmc-supply = <&vcc3v0_sd>;和vqmmc-supply = <&vcc_sdio>;,驱动会通过api获取到对应的regulator设备。
我们也可以看看cpu的设备树:
c
&cpu0 {
cpu-supply = <&vdd_arm>;
};
&cpu1 {
cpu-supply = <&vdd_arm>;
};
&cpu2 {
cpu-supply = <&vdd_arm>;
};
&cpu3 {
cpu-supply = <&vdd_arm>;
};
cpu-supply = <&vdd_arm>;会被regulator_get函数获取到对应的regulator。
2.2 api函数
2.2.1 获取regulator接口
c
struct regulator *regulator_get(struct device *dev, const char *id)
struct regulator *devm_regulator_get(struct device *dev, const char *id)
struct regulator *regulator_get_exclusive(struct device *dev, const char *id)
struct regulator *regulator_get_optional(struct device *dev, const char *id)
regulator_get为普通的regulator句柄获取接口,内存的资源在申请时分配。
devm_regulator_get是regulator封装了一层的devres机制接口,便于管理内存资源。
regulator_get_exclusive,重点在最后一个单词"exclusive",这里的专有、专用对于consumer来说是有先后顺序的,第一个获取regulator的consumer有了优先使用权,后续consumer再想获取是不可能了。
regulator_get_optional,可选,用于获取regulator。
上面这些函数其实都是调用_regulator_get函数而已,只有第三个参数有所不同,我们先看看第三个参数:
c
enum regulator_get_type {
NORMAL_GET, //普通获取
EXCLUSIVE_GET, //独占获取
OPTIONAL_GET, //可选获取
MAX_GET_TYPE //边界标记
};
_regulator_get函数我们后面再分析。
2.2.2 释放regulator接口
驱动注销或异常时,需要释放已获取的regulator,因为获取接口申请了不少内存资源。
c
static void devm_regulator_release(struct device *dev, void *res)
void devm_regulator_put(struct regulator *regulator)
2.2.3 regulator控制和状态接口
c
int regulator_enable(struct regulator *regulator) //启用稳压器
int regulator_disable(struct regulator *regulator) //禁用稳压器
int regulator_force_disable(struct regulator *regulator) //强制禁用稳压器,无视引用计数
int regulator_disable_deferred(struct regulator *regulator, int ms)//延迟禁用稳压器
int regulator_is_enabled(struct regulator *regulator)//查询稳压器当前硬件启用状态
上述接口的作用是控制电源的打开、关闭,以及查询当前状态。下面的接口主要用于获取电源电压、电流,配置电压、电流,同时获取电压列表、压差步长、电流限制、模式等。
c
int regulator_count_voltages(struct regulator *regulator)
int regulator_list_voltage(struct regulator *regulator, unsigned selector)
int regulator_is_supported_voltage(struct regulator *regulator,
int min_uV, int max_uV)
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)
int regulator_suspend_enable(struct regulator_dev *rdev,
suspend_state_t state)
int regulator_suspend_disable(struct regulator_dev *rdev,
suspend_state_t state)
int regulator_set_suspend_voltage(struct regulator *regulator, int min_uV,
int max_uV, suspend_state_t state)
int regulator_get_voltage(struct regulator *regulator)
int regulator_set_mode(struct regulator *regulator, unsigned int mode)
unsigned int regulator_get_mode(struct regulator *regulator)
- regulator_count_voltages:查询该稳压器支持的电压档位总数
- regulator_list_voltage:查询指定选择器索引 (selector) 对应的电压值
- regulator_is_supported_voltage:检查稳压器是否能提供指定范围内的某个电压
- regulator_set_voltage:请求将输出电压设置在 [min_uV, max_uV] 微伏范围内
- regulator_suspend_enable:启用指定休眠状态 (state) 下的稳压器输出
- regulator_suspend_disable:禁用指定休眠状态下的稳压器输出
- regulator_set_suspend_voltage:设置稳压器在指定休眠状态下的输出电压
- regulator_get_voltage:查询稳压器当前实际配置的输出电压
- regulator_set_mode:设置稳压器工作模式
- regulator_get_mode:获取稳压器当前工作模式
2.2.4 多regulator控制接口
还有一类特殊接口,需要一次控制多个regulator,如MMC需要1.9 V和1.0 V两路电源一起供电才能正常工作。打开两路电源有两种方法,即通过regulator_get获取两路电源后控制,或者通过regulator bulk接口控制电源。
这里有个重要的结构体regulator_bulk_data,明白该结构体及regulator_bulk_get实现就可以很轻易地明白bulk接口的实现原理。regulator_bulk_data本质对应一个电源(regulator),supply为获取regulator的参数。
c
struct regulator_bulk_data {
const char *supply;
struct regulator *consumer;
/* private: Internal use */
int ret;
};
consumer通过regulator_bulk_get一次获取多个regulator,通过regulator_bulk_get实现可知,bulk相关接口通过struct regulator_bulk_data数组控制了多个regulator。目前bulk接口只支持获取、释放、使能、去使能,不支持配置电压:
c
int regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers)
int regulator_bulk_enable(int num_consumers,
struct regulator_bulk_data *consumers)
int regulator_bulk_disable(int num_consumers,
struct regulator_bulk_data *consumers)
int regulator_bulk_force_disable(int num_consumers,
struct regulator_bulk_data *consumers)
void regulator_bulk_free(int num_consumers,
struct regulator_bulk_data *consumers)