Linux 内核学习(2) --- regulator 框架
目录
Regulator 介绍
Regulator 指的是稳定器(调压器),有电压稳定器及电流稳定器两种,能够自动维持恒定电流或者电压,
其中,电压稳定器 voltage regulator
在电路中比较常见。
从设备驱动的角度来看,regulator
的控制比较简单,主要有 enable/disable/ 输出电压或电流大小的控制。
Linux利用 regulator framework 对 regulator
进行管理和控制。
由调节器供电的设备被称为消费者(regualtor consumer
)。它们消耗调节器提供的电力。大多数调节器可以启用和禁用他们的输出,一些也可以控制他们的输出电压或电流。
Linux regulator 也遵循 provider,framwork,consumer
体系,regulator provider
向系统注册称为 regulator device,并提供它们硬件参数和对应的操作接口函数集合,regulator framework
负责管理系统中的 regulator 设置并提供统一的 consumer 接口,regulator consumer
使用统一的 regulator_get()
等函数获取 compatible 对应的 regulator,并用 regulator_enable() regulator_disbale()
和可选的 regulator_set_voltage
操作 regulator
Linux regulator framework 的主要目的:提供标准的内核接口,控制系统的 voltage/current regulators
,并提供相应的开关、电压/电流设置的机制。
在系统运行的过程中,根据具体的需要动态改变 regulators
的输出,从而达到省电的目的。在系统中如果配错 Regulator 是比较危险的,可能会造成硬件器件的损坏。因此,需要在 regulator framework 中对电流或者电压的大小做强制限定,并且不能被 Regulator 的 consumer 或者 provider 更改。
硬件上提供这些物理调节电压和电流的芯片称为 电源管理集成芯片(PMIC)
Linux Regualtor framework 被设计用于开关和控制电压和电流调节器,主要主要分为下面的流程:
Regulator provider
的 registerRegulator consumer
的获取和操作device tree
的配置和解析的接口- user interface sysfs interface
Regulator provider 注册
Regulator 向内核注册的核心函数如下,注册的参数中包含 struct regulator_desc
和 struct regulator_config
两个主要的结构
相对应的,也存在 regulator_unregister
函数,当模块卸载的时候使用该函数
// include/linux/regulator/driver.h
// drivers/regulator/core.c
struct regulator_dev * regulator_register(const struct regulator_desc *regulator_desc,
const struct regulator_config *cfg)
void regulator_unregister(struct regulator_dev *rdev)
现在主要分析注册到 regulator_register 要用到的两个主要结构
struct regulator_desc
struct regulator_desc
的定义如下所示:
struct regulator_desc {
// regulator name
const char *name;
const char *supply_name;
// 保存了用于识别 devicetree 中的 regulator 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;
// 表示该调节器可用的选择器数量。它表示调节器能输出的值的数量。输出电压固定时,n_voltage 应设为 1。
unsigned n_voltages;
// regulator 的操作函数
const struct regulator_ops *ops;
// regulator interrupt name
int irq;
// 指示调节器是电压调节器还是电流调节器。它可以是 REGULATOR_VOLTAGE 或 REGULATOR_CURRENT
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;
.....
}
主要参数的含义如下:
name | 含义 |
---|---|
name | regulator name |
id | regulator 的数字标志符 |
owner | 代表提供调节器的模块。将该字段设置为 THIS_MODULE |
type | 指示调节器是电压调节器还是电流调节器,它可以是 REGULATOR_VOLTAGE 或 REGULATOR_CURRENT |
n_voltage | 表示该调节器可用的选择器数量。它表示调节器能输出的值的数量。输出电压固定时,n_voltage 应设为 1 |
min_uV | 表示该稳压器能提供的最小电压值。它是由最低的选择器给出的电压 |
uV_step | 表示每个选择器增加的电压 |
ops | ops表示调节器操作表。它是一个指向调节器可以支持的一组操作回调的结构 |
min_uV | regualtor 的中断号 |
struct regualtor_config
struct regulator_config {
struct device *dev;
const struct regulator_init_data *init_data;
void *driver_data;
struct device_node *of_node;
struct regmap *regmap;
bool ena_gpio_initialized;
int ena_gpio;
unsigned int ena_gpio_invert:1;
unsigned int ena_gpio_flags;
};
struct regulator_init_data {
const char *supply_regulator; /* or NULL for system supply */
// regulator 的限制或者硬件特性,
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_configs 相关参数的含义如下:
Name | 说明 |
---|---|
dev | 表示调节器所属的 struct device 结构 |
init_data | 是结构中最重要的字段,因为它包含一个包含 regulation_constraints 的元素 |
driver_data | 表示持有的 regulator 的私有数据,可以通过 consumer 传递 |
regulator_configs
包含了 const struct regulator_init_data *init_data
这个关键的结构,其余是 reg 和 gpio 的定义
regulator_init_data
中各个结构的含义如下:
struct regulation_constraints {
const char *name;
int min_uV;
int max_uV;
int uV_offset;
int min_uA;
int max_uA;
int ilim_uA;
int system_load;
unsigned int valid_modes_mask;
unsigned int valid_ops_mask;
int input_uV;
/* regulator suspend states for global PMIC STANDBY/HIBERNATE */
struct regulator_state state_disk;
struct regulator_state state_mem;
struct regulator_state state_standby;
suspend_state_t initial_state; /* suspend state to set at init */
// 启动时设置的模式
unsigned int initial_mode;
.....
regulation_constraints
中各个参数的含义如下:
Name | 说明 |
---|---|
min_uV, min_uA, max_uA 和 max_uV | 是用户可以设置的最小电压/电流值 |
uV_offset | 是应用于电压从消费者补偿电压下降的偏移量 |
valid_modes_mask 和 valid_ops_mask | 分别是模式和操作的掩码,可以由消费者配置/执行 |
always_on | 应该被设置, 如果调节器不能被禁用 |
boot_on | 应该被设置如果在系统初始启动时启用了调节器。如果调节器没有被硬件或引导加载程序启用,那么它将在应用约束时启用 |
name | 是用于显示目的的约束的描述性名称 |
apply_uV | 在初始化时应用电压约束 |
input_uV | 表示该调节器由另一个调节器提供时的输入电压 |
state_disk, state_mem 和 state_standby | 定义了当系统在磁盘模式、mem模式或备用模式下挂起时调节器的状态 |
initial_state | 表示默认设置挂起状态 |
initial_mode | 是在启动时设置的模式 |
struct regulation_constraints
结构是 init data 的一部分。
这可以用以下事实来解释: 在调节器初始化时,它的约束直接应用于它,远远早于任何消费者可以使用它
DTS 配置和解析
struct regulation_constraints
和 init_data
中的成员,都可以从 device-tree 中读取并且配置好
下面给出一个参考配置的 DTS 节点:
// 以 pmic is16271a 作为参考
isl6271a@3c {
compatible = "isl6271a";
reg = <0x3c>;
interrupts = <0 86 0x4>;
/* supposing our regulator is powered by another regulator */
in-v1-supply = <&some_reg>;
[...]
regulators {
reg1: core_buck {
regulator-name = "Core Buck";
regulator-min-microvolt = <850000>;
regulator-max-microvolt = <1600000>;
};
reg2: ldo1 {
regulator-name = "LDO1";
regulator-min-microvolt = <1100000>;
regulator-max-microvolt = <1100000>;
regulator-always-on;
};
reg3: ldo2 {
regulator-name = "LDO2";
regulator-min-microvolt = <1300000>;
regulator-max-microvolt = <1300000>;
regulator-always-on;
};
};
};
DTS 中的每个 PMIC 节点都应该有一个名为 regulators
的子节点,在这个子节点中,我们必须声明PMIC提供的每个调节器为专用子节点。
PMIC的每个调节器都被定义为 regulators 节点的子节点,而 regulators 节点又是 DTS 中 PMIC 节点的子节点。
在 DTS PMIC 中的 regulators
的子节点中可以定义一些标准化的属性
regulator-name
: 这是一个字符串,用作调节器输出的描述性名称。regulator-min-microvolt
: 这是用户可以设定的最小电压。regulator-max-microvolt
: 这是用户可设定的最大电压。regulator-microvolt-offset
: 这是施加在电压上以补偿电压下降的偏移量。regulator-min-microamp
: 这是用户可以设定的最小电流。regulator-max-microamp
: 这是用户可以设定的最大电流。regulator-always-on
: 这是一个布尔值,表示不应该禁用调节器。regulator-boot-on
: 这是一个bootloader/firmware 阶段使能的调节器。<name>-supply:
这是一个指向父供应/调节节点的phandle。regulator-ramp-delay
: 这是调节器的斜坡延迟(用uV/uS表示)
为了提取从 dts 内部传递的 init 数据,我们需要引入一个新的数据类型,struct of_regulator_match,
,它的结构如下:
struct of_regulator_match {
const char *name;
void *driver_data;
struct regulator_init_data *init_data;
struct device_node *of_node;
const struct regulator_desc *desc;
};
同时内核中还有一个同名的函数 of_regulator_match(),
将 regulators
子节点作为参数,
该函数将遍历每个调节器设备节点,并为每个设备构建一个 struct regulator_init_data
结构体, 这里匹配方法实际就是通过名称来匹配的:
int of_regulator_match(struct device *dev, struct device_node *node,
struct of_regulator_match *matches,
unsigned int num_matches)
比如在下面的 ab3100.c 中,就使用了 of_regualtor_match
来匹配当前的 regulators 节点,
通过 of_regualtor_match 函数直接 填充了 of_regulator_match 的 struct regulator_init_data
指针 init_data
再以 struct regulator_init_data
指针 init_data
向平台注册:
// ab3100.c 中的regulator 注册方法
static struct of_regulator_match ab3100_regulator_matches[] = {
{ .name = "ab3100_ldo_a", .driver_data = (void *) AB3100_LDO_A, },
{ .name = "ab3100_ldo_c", .driver_data = (void *) AB3100_LDO_C, },
.....
};
if (np) {
// 传入当前的节点 np, 和 of_regulator_match 结构
// 如果两者相匹配,就会初始化 init_data
err = of_regulator_match(&pdev->dev, np,
ab3100_regulator_matches,
ARRAY_SIZE(ab3100_regulator_matches));
if (err < 0) {
dev_err(&pdev->dev,"Error parsing regulator init data: %d\n", err);
return err;
}
return ab3100_regulator_of_probe(pdev, np);
}
static int ab3100_regulator_of_probe(struct platform_device *pdev, struct device_node *np)
{
int err, i;
.....
for (i = 0; i < ARRAY_SIZE(ab3100_regulator_matches); i++) {
// 将init_data 作为参数 相 regualtor framework 进行注册
// 注意使用了 ab3100_regulator_matches[i].init_data 作为参数
// 将 driver_data 作为私有参数
err = ab3100_regulator_register(
pdev, NULL, ab3100_regulator_matches[i].init_data,
ab3100_regulator_matches[i].of_node,
(unsigned long)ab3100_regulator_matches[i].driver_data);
if (err) {
ab3100_regulators_remove(pdev);
return err;
}
}
return 0;
}
On BoardConfig 配置
这种方式 legacy 的方法,从驱动程序或板级文件中定义约束数组,并将其用作平台数据的一部分:
// 定义 regulator_init_data 结构
static const struct regulator_init_data arizona_ldo1_dvfs = {
.constraints = {
.min_uV = 1200000,
.max_uV = 1800000,
.valid_ops_mask = REGULATOR_CHANGE_STATUS |
REGULATOR_CHANGE_VOLTAGE,
},
.num_consumer_supplies = 1,
};
switch (arizona->type) {
case WM5102:
case WM8997:
case WM8998:
case WM1814:
desc = &arizona_ldo1_hc;
ldo1->init_data = arizona_ldo1_dvfs;
....
}
config.init_data = &ldo1->init_data;
// 以 config.init_data 作为参数,注册 regulator
ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config);
regulator_ops
regulator_desc 结构中包含一个 const struct regulator_ops *ops;
表示 regulator 的硬件操作接口:
struct regulator_ops {
/* enumerate supported voltages */
int (*list_voltage) (struct regulator_dev *, unsigned selector);
/* 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 *);
......
// 回调名称很好地解释了它们的作用, 对于这些回调函数,您必须在调节器的约束valid_ops_mask或valid_modes_mask中启用适当的掩码,然后消费者才能使用它们。
// 可用的操作掩码标志在 include/linux/regulator/machine.h 中定义。
总结
regulator_provider
注册的一般步骤如下:
-
为PMIC提供的所有调节器定义一个
struct regulator_desc
对象数组,定义一个有效的struct regulator_ops
,以链接到适当的regulator_desc
,所有的regulator_ops
都可以是相同的,假设它们都支持相同的操作。 -
从平台数据(dts node 或者 boarconfig)中获取适当的
struct regulator_init_data
结构体(其中必须已经包含有效的struct regulator_constraints
结构体,或者从DTS Node 中构建一个struct regulator_constraints
结构体,以便构建一个新的struct regulator_init_data
对象 -
使用前面的
struct regulator_init_data
来设置struct regulator_config
结构体。如果驱动程序支持 DTS,你可以让regulator_config.of_node
指向用于提取调节器属性的节点 -
regulator_register()
或者devm_regulator_register())
将调节器注册到regulator framework ,并给出之前的struct regulator_desc
和struct regulator_config
作为参数。
Regulator Consumer 使用
Regulator consumer 的接口定义在 include/linux/regulator/consumer.h
常用的接口如下:
/* regulator get and put */
struct regulator *__must_check regulator_get(struct device *dev,const char *id);
struct regulator *__must_check devm_regulator_get(struct device *dev, const char *id);
int __must_check regulator_enable(struct regulator *regulator);
int regulator_disable(struct regulator *regulator);
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);
int regulator_get_voltage(struct regulator *regulator);
使用比较简单,一般的使用方法如下:
// 定义一个 strutc regulator 结构
// 使用 regulator_get 获取
int ret;
struct regulator *reg;
const char *supply = "vdd1";
int min_uV, max_uV;
reg = regulator_get(dev, supply);
其中的关键结构就是 struct regulator
,定义如下:
struct regulator {
struct device *dev;
struct list_head list;
unsigned int always_on:1;
unsigned int bypass:1;
int uA_load;
int min_uV;
int max_uV;
char *supply_name;
struct device_attribute dev_attr;
struct regulator_dev *rdev;
struct dentry *debugfs;
};
struct regulator 获取
获取 struct regulator 的 函数主要为:
struct regulator *__must_check regulator_get(struct device *dev, const char *id);
struct regulator *__must_check devm_regulator_get(struct device *dev, const char *id);
传入的 驱动设备的 dev 类型和 Regulator name 的字符串指针,Regulatr framework 通过 DTS 注册进来的 regulator 设备根据名称进行查找,
关联到正确的操作函数,返回 struct regulator
结构
释放 regulator 的操作函数是:
void regulator_put(struct regulator *regulator);
在调用这个函数之前,驱动程序应该确保对这个调节器源的所有 regulator_enable() 调用都被 regulator_disable() 调用平衡
regulator 操作
Regulator 的操作函数如下:
int __must_check regulator_enable(struct regulator *regulator);
int regulator_disable(struct regulator *regulator)
通过 regulator_enable 和 regulator_disable 函数来开启或者禁用电源
对于共享调节器,regulator_disable() 实际上只在启用的引用计数为零时禁用调节器。
也就是说,你可以在紧急情况下强制禁用。例如,通过调用 regulator_force_disable()
每个函数实际上都是对 注册时的regulator_ops 操作的包装。
例如,regulator_set_voltage() 在内部调用 regulator_ops.set_voltage 在检查了相应的掩码后允许设置此操作。
要检查 regulator 是否启动,可以使用下面的函数:
int regulator_is_enabled(regulator);
于需要根据操作模式调整电源的用户,内核提供了如下功能:
int regulator_set_voltage(regulator, min_uV, max_uV);
min_uV 和 max_uV 是以微伏为单位的最小和最大可接受电压。
如果在稳压器被禁用时调用该功能,则该功能将改变电压配置,以便在稳压器下次启用时物理设置电压
也就是说,消费者可以通过调用 regulator_get_voltage() 获得调节器配置的电压输出,无论调节器是否启用,它都会返回配置的输出电压:
我们在电压部分所讨论的问题在这里也适用。例如,USB驱动程序可能希望在供电时将限制设置为500 mA。
int regulator_set_current_limit(regulator, min_uA, max_uA);
min_uA 和 max_uA 是以微安为单位的最小和最大可接受电流限制。
同样,消费者可以通过调用 regulator_get_current_limit()
得到调节器配置的电流限制,无论调节器是否启用,调节器都会返回电流限制的值:
int regulator_get_current_limit(regulator);
使用Multi Regulator
消费者节点可以使用以下绑定引用一个或多个供应/调节器:
使用 name-supply : phandle to regualtor name 的方式进行引用
gpu:gpu@304000000 {
mali-supply = <&S3A_LEVEL>
vdd-supply = <&gpu_gdsc>
}
因为属性的名称中包含了 regulator 的name,已经足够有意义了,必须匹配 regulator_get() 函数的 id参数,以便驱动程序可以在请求调节器时轻松地引用它
这里的 regulator dts 注册方法有可能是这样的:
twl_reg1: regulator@0 {
[...]
};
twl_reg2: regulator@1 {
[...]
};
mmc: mmc@0x0 {
[...]
vmmc-supply = <&twl_reg1>;
vmmcaux-supply = <&twl_reg2>;
};
参考博客
Linux regulator 框架理解和使用
代码位置:
driver/regulator/core.c
// dummy regulator 的参考实现
driver/regulator/dummy.c
include/linux/regulator/consumer.h
原文地址:https://blog.csdn.net/chenwang1824/article/details/137636959
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!