基本介绍
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电源引脚的名字
linux 使用regulator
简要说明一下在Rockchip平台regulator的使用方法
//在pmic这个节点中可以看到一个子节点vcc_sd,表明有一路电源从pmic中引出来连接到sdmmc的外设上,给sdmmc进行供电,那这一路电源如何使用呢?可以看下面的dts sdmmc 这个节点的vqmmc-supply
vcc_sd: LDO_REG4 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
regulator-name = "vcc_sd";
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <3000000>;
};
};
&sdmmc {
clock-frequency = <50000000>;
clock-freq-min-max = <400000 150000000>;
supports-sd;
bus-width = <4>;
cap-mmc-highspeed;
cap-sd-highspeed;
disable-wp;
num-slots = <1>;
//sd-uhs-sdr104;
vqmmc-supply = <&vcc_sd>; //指定vqmmc-supply 使用pmic是使用那一路电源
pinctrl-names = "default";
pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
status = "okay";
};
在代码中使用vqmmc-supply这一路电源代码调用如下:
kernel\drivers\mmc\host\dw_mmc-pltfm.c
//step 1
static const struct of_device_id dw_mci_rockchip_match[] = {
{ .compatible = "rockchip,rk2928-dw-mshc",
.data = &rk2928_drv_data },
{ .compatible = "rockchip,rk3288-dw-mshc",
.data = &rk3288_drv_data },
{},
};
//step 2
static struct platform_driver dw_mci_rockchip_pltfm_driver = {
.probe = dw_mci_rockchip_probe,
.remove = dw_mci_pltfm_remove,
.driver = {
.name = "dwmmc_rockchip",
.of_match_table = dw_mci_rockchip_match,
.pm = &dw_mci_rockchip_pmops,
},
};
static int dw_mci_rockchip_probe(struct platform_device *pdev)
dw_mci_pltfm_register(pdev, drv_data);
dw_mci_probe(host);
ret = dw_mci_init_slot(host, i);
ret = mmc_regulator_get_supply(mmc);
mmc->supply.vmmc = devm_regulator_get_optional(dev, "vmmc");
mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc");
/"代码里通过“vqmmc” 找到 vqmmc-supply = <&vcc_sd>;,从而找到vcc_sd,电源配置*/
参考:
https://blog.csdn.net/qq_39575672/article/details/129729767
https://blog.csdn.net/qq_27809619/article/details/115940174