调节器Regulator是为其他设备供电的设备。由regulator供电的设备称为消费者。提供调节器的芯片称为电源集成电路PMIC。调节器可以启用/禁用其输出,也可以控制其输出电压和电流。
一、PMIC/生产者驱动程序
生产者是产生调节电压电流的设备,这个设备的名称是PMIC,可用于控制加电时序、电池管理、DC-DC转换或简单的电源开关。
1. 驱动程序数据结构
i). 调节器描述结构regulator_desc
内核通过struct regulator_desc{}描述PMIC提供的每个调节器。所谓的调节器是一个可以独立调节的输出,例如Intersil的ISL6217A是一款具有3个独立调节输出的PMIC,因此其驱动程序应该有3个regulator_desc{}实例。
struct regulator_desc {
const char* name;
const char *of_match;
int id; // 调节器的数字标识
unsigned n_voltages; // 调节器可输出的数值数量,如果是固定输出电压,则为1.
const struct regulator_ops *ops;
int irq; // 调节器的中断号
enum regulator_type type; // 是电压调节器还是电流调节器,REGULATOR_VOLTAGE, REGULATOR_CURRENT
struct module *owner;
uint min_uV; // 调节器可以输出的最小电压值
uint uV_step; // 每个选择器的电压增量
};
ii). 调节器限制结构regulator_reconstraints{}
当PMIC向消费者公开调节器时,它必须借助于struct regulator_restraints{}结构为调节器加一些名义上的限制。这个结构收集调节器的安全限制,定义消费者不能跨越的边界。这是调节器驱动程序和消费者驱动程序之间的一种约定:
struct regulator_constraints {
const char *name;
//电压输出范围
int min_uV;
int max_uV;
int uV_offset; // 应用于消费者的电压偏移量,以补偿电压下降
// 电流输出范围
int min_uA;
int max_uA;
uint valid_modes_mask; // 消费者可能配置的模式的掩码
uint valid_ops_mask; // 消费者可能执行的操作的掩码
// 系统位于磁盘模式、内存模式、待机模式时regulator的状态;
struct regulator_state state_disk;
struct regulator_state state_mem;
struct regulator_state state_standby;
suspend_state_t initial_state; // 在init处设置挂起状态
uint initial_mode; // 启动时要设置的模式
unsigned always_on:1;
unsigned boot_on:1;
unsigned apply_uV:1; //
};
iii). regulator初始化结构regulator_init_data{}
初始化数据结构struct regulator_init_data{},可以通过SoC文件或DT树把初始化数据结构传递给驱动程序,在DT树模式下可以用of_get_regulator_init_data()获取数据结构:
struct regulator_init_data {
struct regulation_constraints constraints;
int (*regulator_init)(void *driver_data);
void *driver_data;
};
// 将初始化数据放入SoC的开发板文件中,示例intersil的ISL6271A
static struct regulator_init_data isl_init_data[] = {
[0] = { .constraints = { .name = "Core Buck",
.min_uV = 850000,
.max_uV = 1600000,
.valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
.valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS,
},
},
[1] = { .constraints = { .name = "LDO1",
.min_uV = 1100000,
.max_uV = 1100000,
.always_on = true,
.valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
.valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS,
},
},
[0] = { .constraints = { .name = "LDO2",
.min_uV = 1300000,
.max_uV = 1300000,
.always_on = true,
.valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
.valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS,
},
},
};
iv). 在DT文件中绑定regulator
DT文件中绑定,每个PMIC设备的节点都需要有一个名为regulators的子节点,在子节点regulators中声明PMIC的每个调节器regulator。各个调节器具体DT节点属性和具体示例如下:
// 和regulator相关的标准属性
/*
regulator-name:字符串,作为调节器输出的描述性名称;
regulator-min-microvolt:消费者可以设置的最低电压
regulator-max-microvolt:消费者可以设置的最高电压
regulator-microvolt-offset:应用于电压的偏移量,以补偿消费者的电压下降
regulator-min-microamp:消费者可以设置的最小电流
regulator-max-microamp:消费者可以设置的最大电流
regulator-always-on:bool值,说明调节器永不禁用
regulator-boot-on:由引导加载程序/固件启用的调节器
<name>-handler : 指向父节点/调节器节点的phandle
regulator-ramp-delay:调节器的斜坡延迟(单位uV/uS)
这些属性和regulator_init_data{}.regulator_constraints{}中的字段基本相同
*/
// 示例,ISL6271A的驱动程序的DT如下:
isl6271a@3c {
compatible = "isl6271a";
reg = <0x3c>;
interrupts = <0 86 0x4>;
in-v1-supply = <&some_reg>; // 假设该PMIC由另一个调节器供电
[...]
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="LDO1";
regulator-min-microvolt = <1300000>;
regulator-max-microvolt = <1300000>;
regulator-always-on;
};
};
};
使用DT文件的方式把初始化数据引入到regulator驱动程序中:
// 为了使用DT,首先需要引入数据结构struct struct_regulator_match{}
struct struct_regulator_match{
const char *name;
void *driver_data;
struct regulator_init_data *init_data;
struct device_node *of_node;
const struct regulator_desc *desc;
};
在PMIC驱动程序的probe函数中,使用内核辅助函数of_regulator_match()时,将regulators的子节点作为参数传递给它,该函数将遍历每个调节器的节点,并为其建立一个struct regulator_init_data{}结构;
v). regulator配置结构regulator_config{}
调节器设备通过struct regulator_config{}结构进行配置。在向内核注册调节器时,该结构被传递给regulator框架:
struct regulator_config {
struct device *dev; // 调节器设备所属的struct device
const struct regulator_init_data *init_data;
void *driver_data; // 保存调节器的私有数据
struct device_node *of_node;
};
vi). regulator设备操作结构
调节器设备通过struct regulator_config{}是表示调节器可执行的所有操作的回调列表。
struct regulator_ops {
// 枚举支持的电压值
int (*list_voltage)(struct regulator_dev *reg, unsigned selector);
// 获取或设置电压调节器
int (*set_voltage)(struct regulator_dev *reg, int min_uV,
int max_uV, uint *selector);
int (*map_voltage)(struct regulator_dev *reg, int min_uV,
int max_uV);
int (*set_voltage_sel)(struct regulator_dev *reg, uint selector);
int (*get_voltage)(struct regulator_dev *reg);
int (*get_voltage_sel)(struct regulator_dev *reg);
// 获取或设置电流调节器
int (*set_current_limit)(struct regulator_dev *reg, int min_uA, int max_uA);
int (*get_current_limit)(struct regulator_dev *reg);
int (*set_input_current_limit)(struct regulator_dev *reg, int lim_uA);
int (*set_owner_current_protection)(struct regulator_dev *reg);
int (*set_active_discharge)(struct regulator_dev *reg, bool enable);
int (*enable)(struct regulator_dev *reg);
int (*disable)(struct regulator_dev *reg);
int (*is_enabled)(struct regulator_dev *reg);
int (*set_mode)(struct regulator_dev *reg, uint mode);
uint (*get_mode)(struct regulator_dev *reg);
};
// 在这些回调函数中参数为regulator_dev{},调用rdev_get_id()可获得对应调节器的id,
int rdev_get_id(struct regulator_dev *rdev);
2. 驱动程序方法
1. probe函数的实现
PMIC驱动程序的实现可分为几个步骤:
- 为该PMIC提供的所有调节器定义为regulator_desc{}对象数组。并确保已经定义了有效的struct regulator_ops{}实例。假设所有的调节器都支持相同的操作,则它们都有相同的regulator_ops{}示例。
- 在probe函数中,从平台数据中获取合适的struct regulator_init_data{},且平台中已经包含了有效的struct regulator_constraints{}。或者从DT中构建struct regulator_constraints{}实例,以构建合适的struct regulator_init_data{}实例。
- 接着使用前面的struct regulator_init_data{}对象来设置struct regulator_config{}结构;如果驱动程序支持DT,则可以将regulator_config.of_node指向用于获取调节器属性的节点。
- 调用regulator_register()(或者devres版本的devm_regulator_register)将调节器注册到内核,并用regulator_desc和regulator_config作为参数。
其中regulator_register()函数的原型:
struct regulator_dev *regulator_register(const struct regulator_desc *desc,
const struct regulator_config *cfg);
函数regulator_register()返回struct regulator_dev{},这个结构体代表regulator生产者一端的调节器设备实例。
2. remove函数的实现
驱动程序的remove回调函数回滚probe函数期间的每个操作。其中从内核中删除调节器的函数是:
void regulator_unregister(struct regulator_dev *rdev);
3. 示例:Inter ISL6271A驱动的实现
该PMIC提供3个调节器,其中一个可以改变输出值,两个提供固定电压:
i). PMIC驱动的内部结构
struct isl_pmic {
struct i2c_client *client;
struct regulator_dev *rdev[3];
struct mutex mtx;
};
ii). 实现regulator_ops的回调函数
static int isl6271a_get_voltage_sel(struct regulator_dev *rdev) {
struct isl_pmic *pmic = rdev_get_drvdata(rdev);
int idx = rdev_get_id(rdev);
int ret = i2c_smbu_read_byte(pmic->client);
if (ret<0) [...]; // 错误处理
return ret;
}
static int isl6271a_set_voltage_sel(struct regulator_dev *rdev, uint selector) {
struct isl_pmic *pmic = rdev_get_drvdata(rdev);
int ret = i2c_smbu_write_byte(pmic->client, selector);
if (ret<0) [...]; // 错误处理
return ret;
}
iii). 完成回调定义后,可以构建struct regulator_ops{}
static struct regulator_ops isl_core_ops = {
.get_voltage_sel = isl6271a_get_voltage_sel,
.set_voltage_sel = isl6271a_set_voltage_sel,
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
};
static struct regulator_ops isl_fixed_ops = {
.list_voltage = regulator_list_voltage_linear,
};
// 为所有的调节器构建struct regulator_desc{}数组
static struct regulator_desc isl_rd[] = {
{
.name = "Core Buck",
.id = 0,
.n_voltages = 16,
.ops = &isl_core_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.min_uV = ISL6217A_VOLTAGE_MIN,
.uV_step = ISL6217A_VOLTAGE_STEP,
},
{
.name = "LDO1",
.id = 1,
.n_voltages = 1,
.ops = &isl_fixed_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.min_uV = 1100000,
},
{
.name = "LDO2",
.id = 1,
.n_voltages = 1,
.ops = &isl_fixed_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.min_uV = 1300000,
},
};
LDO1和LDO2具有固定输出电压,其n_voltages属性为1。
iv). 接着在probe函数中构建struct regulator_init_data结构,这将使用前面接收的struct of_regulator_match{},下面是声明的类型:
static struct of_regulator_match isl6217a_matches[] = {
{.name = "Core_buck",}, {.name="ldo1",},{.name="ldo2",},
};
v). 下面是probe函数的实现,
PMIC由3个调节器,在probe()中需要调用3次regulator_register()。
static int isl_6271a_probe(struct i2c_client *i2c,
const struct i2c_device_id *id) {
struct regulator_config config = {};
struct regulator_init_data *init_data =
dev_get_platdata(&i2c->dev);
struct isl_pmic *pmic;
int i, ret;
struct device *dev = &i2c->dev;
struct device_node *np, *parent;
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
pmic = devm_kzalloc(&i2c->dev, sizeof(*pmic), GFP_KERNEL);
if (!pmic) return -ENOMEM;
np = of_node_get(dev->of_node);
parent = of_get_child_by_name(np, "regulators");
// 1. 此处会给isl6217a_matches赋值
ret = of_regulator_match(dev, parent, isl6217a_matches,
ARRAY_SIZE(isl6217a_matches));
pmic->client = i2c;
mutex_init(&pmic->mtx);
// 2. 循环注册3个调节器
for (i=0;i<3;i++) {
struct regulator_init_data *init_data;
struct regulator_desc *desc;
int val;
// 2.1 此处要区分是否使用DT, 获取regulator_init_data{}
if (pdata) config.init_data = pdata->init_data[i];
else config.init_data = isl6217a_matches[i].init_data;
config.dev = &i2c->dev;
config.of_node = isl6217a_matches[i].of_node;
config.ena_gpio = -EINVAL;
// 2.2 注册regulator
pmic->rdev[i] = devm_regulator_register(&i2c->dev, &isl_rd[i], &config);
}
i2c_set_clientdata(i2c, pmic);
return 0;
}
二、调节器消费者接口
调节器消费者可以是静态和动态的,静态消费者只需要固定电源,动态消费者则需要在运行时对调节器进行主动管理。从消费者角度来看,调节器在内核中表示为struct regulator{}文章来源:https://www.toymoban.com/news/detail-803342.html
struct regulator {
struct device *dev;
struct list_head list;
uint always_on:1;
uint bypass:1;
int uA_load;
int min _uV;
int max_uV;
char *supply_name;
struct device_attribute dev_attr;
struct regulator_dev *dev;
struct dentry *debugfs;
};
1 消费者调节器设备请求
调用regulator_get()获取regulator消费者实例;文章来源地址https://www.toymoban.com/news/detail-803342.html
// 其中id是regulator在DT中的名称
struct regulator *regulator_get(struct device *dev, const char *id);
//释放regultaor,在释放之前必须确保所有的regulator必须被调用了regulator_disable();
void regulator_put(struct regulator *reg);
//如下示例,一个消费者有可能有多个电源
digital = regulator_get(dev, "vcc");
analog = regulator_get(dev, "Avdd");
2. 控制调节器设备
// 使能或禁止regulator
int regulator_enable(struct regulator *reg);
int regulator_disable(struct regulator *reg);
int regulator_is_enabled(struct regulator *reg);
// 对应共享的调节器,只有其引用计数为0时,才真正会禁用reg
// 设置电压
int regulato_set_voltage(struct regulator *reg, int min_uV, int max_uV);
int regulato_get_voltage(struct regulator *reg);
// 设置电流
int regulato_set_current_limit(struct regulator *reg, int min_uA, int max_uA);
int regulato_get_current_limit(struct regulator *reg);
// 设置regulator的状态
int regulator_set_optimum_mode(struct regulator *reg, int load_uA); // 间接模式,根据电流确定需要的模式
int regulator_set_mode(struct regulator *reg, uint mode);
uint regulator_get_mode(struct regulator *reg);
三、调节器绑定
// 消费者可以使用以下方式绑定若干个电源
<name>-supply: phandle to regulator mode;
// 示例
twl_reg1: {};
twl_reg2: {};
mmc: mmc@0x0 {
vmmc-supply = <&twl_reg1>;
vmmcaux-supply = <&twl_reg2>;
};
// 在probe函数中获取regulator
struct regulator *main_regulator = regulator_get(dev, "vmmc");
struct regulator *aux_regulator = regulator_get(dev, "vmmcaux");
到了这里,关于linux Regulator电源设备驱动框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!