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_ops
regulator的操作函数集合
注册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(®ulator_list_mutex);
ret = regulator_ena_gpio_request(rdev, config);
mutex_unlock(®ulator_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 = ®ulator_class;
rdev->dev.parent = dev;
dev_set_name(&rdev->dev, "regulator.%lu",
(unsigned long) atomic_inc_return(®ulator_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(®ulator_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(®ulator_list_mutex);
dev_err(dev, "Failed to set supply %s\n",
init_data->consumer_supplies[i].supply);
goto unset_supplies;
}
}
mutex_unlock(®ulator_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(®ulator_class, NULL, NULL,
regulator_register_resolve_supply);
kfree(config);
return rdev;
unset_supplies:
mutex_lock(®ulator_list_mutex);
unset_regulator_supplies(rdev);
mutex_unlock(®ulator_list_mutex);
wash:
kfree(rdev->constraints);
mutex_lock(®ulator_list_mutex);
regulator_ena_gpio_free(rdev);
mutex_unlock(®ulator_list_mutex);
clean:
kfree(rdev);
kfree(config);
return ERR_PTR(ret);
}
set_consumer_device_supply
函数用于登记regulator_dev
与comsumer_dev
(regulator负责供电的设备)之间的对应关系。对于每一个regulator_dev
—comsumer_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
等等。