简述
Regulator,即LDO(low dropout regulator),低压差线性稳压器,简称稳压器。
作为电源管理中的基础设施,regulator被视作为Linux系统的供电单元;这是一个抽象出来的概念,他的provider可以是PMIC,tpsxx,lpxxx等各种power supply,/kernel/msm-3.18/drivers/regulator目录下保存了各种电源芯片对应的regulator驱动程序,同时他也提供一个dummy regulator驱动作为参考;由于各provider的控制方法不同,所以在向系统注册regulator设备时,需要自备ops,即操作函数,用于对其进行各种控制。
Linux中Regulator的抽象概念,其最大的意义在于为各consumer的使用提供了统一的接口,例如获取跟设置电压输出,控制其使能等;在不同的场景下动态的调整regulator使能,已达到省电的目的,这一点在移动设备上尤为重要。
Regulator framework大致框图如下,下面将针对其源码部分进行简单的解析,以及从一个consumer使用者的角度介绍regulator在实践中的应用。
数据结构及API
1.regulator register
用于各provider向系统注册regulator资源,其中的参数通过provider driver指定,或分析dts获取;
源码位置/kernel/msm-3.18/drivers/regulator/core.c
struct regulator_dev *
regulator_register(const struct regulator_desc *regulator_desc,
const struct regulator_config *config);
struct regulator_dev *
devm_regulator_register(struct device *dev,
const struct regulator_desc *regulator_desc,
const struct regulator_config *config);
void regulator_unregister(struct regulator_dev *rdev);
void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev);
2.regulator ops
provider注册regulator时,需要提前填好该结构体,用于控制provider,该结构体作为成员保存在struct regulator_desc内部(注册regulator时用到的参数),结构体太长不全贴了;
源码位置/kernel/msm-3.18/include/linux/regulator/driver.h
struct regulator_ops {
...
/* 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 *);
/* enable/disable regulator */
int (*enable) (struct regulator_dev *);
int (*disable) (struct regulator_dev *);
int (*is_enabled) (struct regulator_dev *);
/* get/set regulator operating mode (defined in consumer.h) */
int (*set_mode) (struct regulator_dev *, unsigned int mode);
unsigned int (*get_mode) (struct regulator_dev *);
...
};
3.consumer API
用于consumer控制regulator的接口,包括获取/释放regulator handler,获取/设置电压值,控制使能,获取使能状态等
头文件路径/kernel/msm-3.18/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);
void regulator_put(struct regulator *regulator);
void devm_regulator_put(struct regulator *regulator);
/* regulator output control and status */
int __must_check regulator_enable(struct regulator *regulator);
int regulator_disable(struct regulator *regulator);
int regulator_force_disable(struct regulator *regulator);
int regulator_is_enabled(struct regulator *regulator);
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);
int regulator_get_voltage(struct regulator *regulator);
应用实例
1.regulator在dts中的配置
rpm-regulator-ldoa22 {
compatible = "qcom,rpm-smd-regulator-resource";
qcom,resource-name = "ldoa";
qcom,resource-id = <22>;
qcom,regulator-type = <0>;
qcom,hpm-min-load = <10000>;
status = "disabled";
regulator-l22 {
compatible = "qcom,rpm-smd-regulator";
regulator-name = "pm8953_l22";
qcom,set = <3>;
status = "disabled";
};
};
rpm-regulator-ldoa22 {
status = "okay";
pm8953_l22: regulator-l22 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
qcom,init-voltage = <1800000>;
status = "okay";
};
};
补充一点, 上述dts中定义的电压值即为该LDO支持的电压,如需调整该电压值,需提前确认LDO是否支持。以高通配平台为例,pm8953 LDO22的电压默认值就是1v8,如需调整该电压,光改dts是不够的,BP也会有相应改动才能调整该LDO电压输出。目前本人尚未遇到调整LDO电压输出的场景,基本都是支持多少用多少。
2.consumer对regulator的引用
cam_vio-supply = <&pm8953_l22>;
3.consumer driver解析dts获取regulator handler,我们将其命名为“cam-vio”;
此处通过sysfs提供attribute file,用于获取并控制regulator cam-vio,其中全局变量g_dev即为该driver的platform device
static ssize_t pateo_cam_vio_store(struct class *dev,
struct class_attribute *attr, const char *buf,
size_t count)
{
if(!strncmp(buf, "1", strlen("1"))) {
camera_vio = regulator_get(&g_dev->dev, "cam_vio");
printk(KERN_INFO"%s: enable cam_vio device", __func__);
regulator_enable(camera_vio);
} else {
printk(KERN_INFO"%s: disable cam_vio device", __func__);
regulator_disable(camera_vio);
regulator_put(camera_vio);
}
return count;
}
4.通过debugfs中regulator的节点可以检查配置是否成功
msm8953_32:/d/regulator/pm8953_l22 # pwd
/d/regulator/pm8953_l22
msm8953_32:/d/regulator/pm8953_l22 # ls -al
total 0
drwxr-xr-x 4 root root 0 1970-01-01 08:00 .
drwxr-xr-x 49 root root 0 1970-01-01 08:00 ..
-r--r--r-- 1 root root 0 1970-01-01 08:00 bypass_count
-r--r--r-- 1 root root 0 1970-01-01 08:00 consumers
-rw-r--r-- 1 root root 0 1970-01-01 08:00 enable
-rw-r--r-- 1 root root 0 1970-01-01 08:00 force_disable
-rw-r--r-- 1 root root 0 1970-01-01 08:00 mode
-r--r--r-- 1 root root 0 1970-01-01 08:00 open_count
-rw-r--r-- 1 root root 0 1970-01-01 08:00 optimum_mode
drwxr-xr-x 2 root root 0 1970-01-01 08:00 pm8953_l22
drwxr-xr-x 2 root root 0 1970-01-01 08:00 soc:pateo_gpio-cam_vio
-r--r--r-- 1 root root 0 1970-01-01 08:00 use_count
-rw-r--r-- 1 root root 0 1970-01-01 08:00 voltage
msm8953_32:/d/regulator/pm8953_l22 # cat voltage
1800000
msm8953_32:/d/regulator/pm8953_l22 # cat enable
1
msm8953_32:/d/regulator/pm8953_l22 # cat use_count
1
补充一点,通过debugfs可以明确获取当前regulator打开次数,所以如过需要关闭该regulator,需要关闭所有打开过的地方,如有一个未关闭,regulator都将处于打开状态。这个过程可以理解为资源释放,有超过一个driver访问了当前资源,如果想释放该资源,一定是所有driver都释放了,该资源最终才会被释放掉。