基本介绍
Regulator指的是稳定器,有电压稳定器及电流稳定器两种,能够自动维持恒定电流或者电压。其中,电压稳定器voltage regulator在电路中比较常见。从设备驱动的角度来看,regulator的控制比较简单,主要有enable/disable/输出电压或电流大小的控制。Linux利用regulator framework对regulator进行管理和控制。
Linux regulator framework的主要目的:提供标准的内核接口,控制系统的voltage/current regulators,并提供相应的开关、大小设置的机制。在系统运行的过程中,根据具体的需要动态改变regulators的输出,从而达到省电的目的。在系统中如果配错regulator是比较危险的,可能会造成硬件器件的损坏。因此,需要在regulator framework中对电流或者电压的大小做限定,并且不能被ragulator的consumer或者provider更改。
框架说明
regulator属于电源管理部分,主要实现电压、电流的输出操作,本篇文章主要介绍regulator子系统的代码实现,因此我们先熟悉一些regulator子系统的相关概念。对于regulator子系统,我们可以讲其分为三部分
- 供电端的regulator, 负责电源芯片本身的驱动程序,比如enable/disable函数,使能或者禁止电源芯片,有一些强悍的电源芯片,是可以动态的调整电压或电流的
- 耗电端的consumer: 比如声卡,网卡, lcd==, 在这些驱动程序里,只是去引用regulator的函数,不关心电源函数如何实现,想使能就调用enable函数
- 不同类型的单板,regulator和consumer的对应关系是不一致的。
- regulator和consumer的对应关系,哪一个regulator对哪一个consumer供电
- 约束条件,在模块单板里可以输出xxx的电压或者电流,在其他的单板里可能就输出xxx的电压或者电流,单板相关的条件,比如电压范围
machine(表示一个单板)
machin使用struct regulator_init_data,静态的描述regulator在板级的硬件连接情况,这些限制通过驱动或dts配置,涉及到系统供电安全,因此必须小心,这些配置主要包括:
1)用于描述regulator在板级的级联关系:前级regulator(即该regulator的输出是另一个regulator的输入,简称supply regulator)和后级regulator(即该regulator的输入是其它regulator的输出,简称consumer regulator)。
2)利用struct regulation_constraints 描述regulator的物理限制,比如:
- 输出电压的最大值和最小值(voltage regulator);
- 输出电流的最大值和最小值(current regulator);
- 允许的操作(修改电压值、修改电流限制、enable、disable等);
- 输入电压是多少(当输入是另一个regulator时);
- 是否不允许关闭(always_on);
- 是否启动时就要打开(always_on);
machin使用struct regulator_init_data,静态的描述regulator在板级的硬件连接情况,这些限制通过驱动或dts配置,涉及到系统供电安全,因此必须小心,这些配置主要包括:
1)用于描述regulator在板级的级联关系:前级regulator(即该regulator的输出是另一个regulator的输入,简称supply regulator)和后级regulator(即该regulator的输入是其它regulator的输出,简称consumer regulator)。
2)利用struct regulation_constraints 描述regulator的物理限制,比如:
- 输出电压的最大值和最小值(voltage regulator);
- 输出电流的最大值和最小值(current regulator);
- 允许的操作(修改电压值、修改电流限制、enable、disable等);
- 输入电压是多少(当输入是另一个regulator时);
- 是否不允许关闭(always_on);
- 是否启动时就要打开(always_on);
regulator driver
regulator driver指的是regulator设备的驱动,主要包含如下结构:
1)使用struct regulator_desc,描述regulator的静态信息,包括:名字、supply regulator的名字、中断号、操作函数集(struct regulator_ops)、使用regmap时相应的寄存器即bitmap等。
2)使用struct regulator_config,描述regulator的动态信息(所谓的动态信息,体现在struct regulator_config变量都是局部变量,因此不会永久保存),包括struct regulator_init_data指针、设备指针、enable gpio等。
3)提供regulator的注册接口(regulator_register/devm_regulator_register),该接口接受描述该regulator的两个变量的指针:struct regulator_desc和struct regulator_config,并分配一个新的数据结构(struct regulator_dev,从设备的角度描述regulator),并把静态指针(struct regulator_desc)和动态指针(struct regulator_config)提供的信息保存在其中。
4)regulator driver以struct regulator_dev(代表设备)指针为对象,对regulator进行后续的操作。
PMIC
电源管理芯片,一个电源管理芯片可包含多个regulator;
Consumer
表示一个regulator使用者,regulator是电源的提供者,而consumer则是电源的消费者(比如LCD),一个regulator可供多个consumer使用;
regulator consumer抽象出regulator设备(struct regulator),并提供regulator操作相关的接口。包括:regulator_get/regulator_put/regulator_enable/regulator_disable/ regulator_set_voltage/regulator_get_voltage等。
regulator core
regulator core负责上述regulator driver/consumer/machine逻辑的具体实现,对底层的硬件进行封装,并提供接口给内核中其他的consumer(使用当前regulator设备的驱动)提供操作接口,并以sysfs的形式,向用户空间提供接口。
Power Domain
电源域,regulator可以级联,不同的regulator的使用者属于相同的电源域。如下图的regulator,则包含三个电源域:
Domain1: switch-1、consumer D、Consumer E;
Domain2:switch-2、consumer B、consumer C;
Domain3:consumer A;
这三个电源域的关系如下:
Domain-1 --> Domain-2 --> Domain-3,在使用时domain-3下consumer相关的电压、电流设置需要满足这三级电源域的电压、电流约束。
Constraints
表示regulator的约束,而针对regulator约束也包含三部分:
该regulator自身的约束信息:这属于regulator相关的约束信息,可通过regulator的datasheet中获取该regulator的约束信息;
Power domain级别的约束信息:这属于该regulator下不同电源域的约束信息,这些约束信息时regulator自身约束信息的子集(如regulator的电压输出约束为1v-3.5v;而domain1的约束信息为2v-3v;domain2的约束信息为2.5v等);
consumer级别的约束信息,可动态设置该consumer所需的输入电压或电流约束等。
针对regulator子系统基本上就是这几个主要的概念,而linux regulator子系统的实现也是围绕着这几个概念进行子系统的框架设计的。
regulator的简单使用(参考内核tps6105x-regulator.c)
-
regulator driver’
- 注册一个platform driver, 在它的probe函数里面分配,设置,注册一个regulator
- 设置里面要做的事情: 实现regulator的操作,比如enable, disable, set_voltage’
static struct regulator_ops tps6105x_regulator_ops = { .enable = regulator_enable_regmap, //将对应引脚设置输入/输出电平 .disable = regulator_disable_regmap, //将对应引脚设置输入/输出电平 .is_enabled = regulator_is_enabled_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .list_voltage = regulator_list_voltage_table, }; static const struct regulator_desc tps6105x_regulator_desc = { .name = "tps6105x-boost", .ops = &tps6105x_regulator_ops, //包含regulator的操作函数 .type = REGULATOR_VOLTAGE, .id = 0, .owner = THIS_MODULE, .n_voltages = ARRAY_SIZE(tps6105x_voltages), .volt_table = tps6105x_voltages, .vsel_reg = TPS6105X_REG_0, .vsel_mask = TPS6105X_REG0_VOLTAGE_MASK, .enable_reg = TPS6105X_REG_0, .enable_mask = TPS6105X_REG0_MODE_MASK, .enable_val = TPS6105X_REG0_MODE_VOLTAGE << TPS6105X_REG0_MODE_SHIFT, }; static int tps6105x_regulator_probe(struct platform_device *pdev) { struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev); struct tps6105x_platform_data *pdata = tps6105x->pdata; struct regulator_config config = { }; int ret; config.dev = &tps6105x->client->dev; config.init_data = pdata->regulator_data; // 这个init_data就是单板中regulator的对应关系,约束条件==, 这个从pdev中的私有数据获取 config.driver_data = tps6105x; config.regmap = tps6105x->regmap; /* Register regulator with framework */ tps6105x->regulator = devm_regulator_register(&pdev->dev, &tps6105x_regulator_desc, &config); } static struct platform_driver tps6105x_regulator_driver = { .driver = { .name = "tps6105x-regulator", }, .probe = tps6105x_regulator_probe, };
-
machine: 构造一个regulator的platform_device, 然后填充对应关系和约束条件
- 注册一个platfrom_device, 在他的私有数据里指定regulator和consumer的对应关系(这个电源芯片给哪一些设备供电)
- 指定约束条件, 比如电压范围
- 填充regulator_init_data(在内核搜索)
-
consumer
- 使用regulator core提供的接口使用,比如regulator_get获取一个regulator, regulator_enable使能regulator
regulator注册流程
static const struct regulator_desc dummy_desc = {
.name = "regulator-dummy",
.id = -1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.ops = &dummy_ops,
};
static struct regulator_init_data dummy_initdata = {
.constraints = {
.always_on = 1,
},
};
config.dev = &pdev->dev;
config.init_data = &dummy_initdata;
regulator_register(&dummy_desc, &config);
kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); //申请一个regulator dev
config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL); //把config取出来
regulator_of_get_init_data(dev, regulator_desc, config,
&rdev->dev.of_node); //从中取出config配置
INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work);初始化一个工作队列
rdev->dev.class = ®ulator_class; register with sysfs
set_machine_constraints(rdev, constraints); //设置对应关系
//add consumers devices
在regulator_map_list链表里生成一项regulator map它里面有dev->name(consumer的名字,还有supply(consumer的电源引脚名字)
device_register(&rdev->dev);
struct regulator *regulator_get(struct device *dev, const char *id) // 第一个参数就是consumer的名字,第二个参数就是supply电源引脚的名字