这里写自定义目录标题
1. max20087 regulator注册
目前在调试美信的max9286_max96705,转接板上需要调试gpio扩展芯片tca6408和电源保护芯片max20087(会生成4路regulator)
max20087 driver
/* * max20087-regulator.c - Regulator device (Power Protector) * driver for MAX20087 * * Copyright (C) 2018 Avnet, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * This driver is based on da9211-regulator.c */#include <linux/err.h> #include <linux/gpio.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regmap.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/of_gpio.h> #include <linux/regulator/of_regulator.h> /* Register Offset */ #define MAX20087_REG_MASK 0x00 #define MAX20087_REG_CONFIG 0x01 #define MAX20087_REG_ID 0x02 #define MAX20087_REG_STAT1 0x03 #define MAX20087_REG_STAT2_L 0x04 #define MAX20087_REG_STAT2_H 0x05 #define MAX20087_REG_ADC1 0x06 #define MAX20087_REG_ADC2 0x07 #define MAX20087_REG_ADC3 0x08 #define MAX20087_REG_ADC4 0x09 /* DEVICE IDs */ #define MAX20087_DEVICE_ID 0x20 #define DEVICE_ID_MASK 0xf0 /* MAX20087 REGULATOR IDs */ #define MAX20087_ID_OUT1 0 #define MAX20087_ID_OUT2 1 #define MAX20087_ID_OUT3 2 #define MAX20087_ID_OUT4 3 /* Register bits */ #define MAX20087_EN_MASK 0x0f #define MAX20087_EN_ALL 0x0f #define MAX20087_EN_OUT1 0x01 #define MAX20087_EN_OUT2 0x02 #define MAX20087_EN_OUT3 0x04 #define MAX20087_EN_OUT4 0x08 #define MAX20087_INT_DISABLE_ALL 0x3f /* Default limits measured in millivolts and milliamps */ #define MAX20087_MIN_MV 1200 // dummy #define MAX20087_MAX_MV 1200 // dummy #define MAX20087_STEP_MV 10 // dummy #define MAX20087_MAX_REGULATORS 4 enum max20087_chip_id { MAX20087_ID, }; struct max20087 { struct device *dev; struct regmap *regmap; int num_output; struct regulator_dev *rdev[MAX20087_MAX_REGULATORS]; struct device_node *reg_node[MAX20087_MAX_REGULATORS]; struct regulator_init_data *init_data[MAX20087_MAX_REGULATORS]; struct gpio_desc *gpio_en; int chip_irq; // reserved int chip_id; }; static unsigned int max20087_get_mode(struct regulator_dev *rdev) { return 0; // Dummy } static int max20087_set_mode(struct regulator_dev *rdev, unsigned int mode) { return 0; // Dummy } static int max20087_set_current_limit(struct regulator_dev *rdev, int min, int max) { return 0; // Dummy } static int max20087_get_current_limit(struct regulator_dev *rdev) { return 0; // Dummy } static const struct regulator_ops max20087_buck_ops = { .get_mode = max20087_get_mode, .set_mode = max20087_set_mode, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .list_voltage = regulator_list_voltage_linear, .set_current_limit = max20087_set_current_limit, .get_current_limit = max20087_get_current_limit, }; #define MAX20087_OUT1 \ {\ .name = "OUT1",\ .ops = &max20087_buck_ops,\ .type = REGULATOR_VOLTAGE,\ .id = MAX20087_ID_OUT1,\ .n_voltages = \ (MAX20087_MAX_MV - MAX20087_MIN_MV) / MAX20087_STEP_MV + 1,\ .min_uV = (MAX20087_MIN_MV * 1000),\ .uV_step = (MAX20087_STEP_MV * 1000),\ .enable_reg = MAX20087_REG_CONFIG,\ .enable_mask = MAX20087_EN_OUT1,\ .enable_val = MAX20087_EN_OUT1,\ .disable_val = 0,\ .owner = THIS_MODULE,\ } #define MAX20087_OUT2 \ {\ .name = "OUT2",\ .ops = &max20087_buck_ops,\ .type = REGULATOR_VOLTAGE,\ .id = MAX20087_ID_OUT2,\ .n_voltages = \ (MAX20087_MAX_MV - MAX20087_MIN_MV) / MAX20087_STEP_MV + 1,\ .min_uV = (MAX20087_MIN_MV * 1000),\ .uV_step = (MAX20087_STEP_MV * 1000),\ .enable_reg = MAX20087_REG_CONFIG,\ .enable_mask = MAX20087_EN_OUT2,\ .enable_val = MAX20087_EN_OUT2,\ .disable_val = 0,\ .owner = THIS_MODULE,\ } #define MAX20087_OUT3 \ {\ .name = "OUT3",\ .ops = &max20087_buck_ops,\ .type = REGULATOR_VOLTAGE,\ .id = MAX20087_ID_OUT3,\ .n_voltages = \ (MAX20087_MAX_MV - MAX20087_MIN_MV) / MAX20087_STEP_MV + 1,\ .min_uV = (MAX20087_MIN_MV * 1000),\ .uV_step = (MAX20087_STEP_MV * 1000),\ .enable_reg = MAX20087_REG_CONFIG,\ .enable_mask = MAX20087_EN_OUT3,\ .enable_val = MAX20087_EN_OUT3,\ .disable_val = 0,\ .owner = THIS_MODULE,\ } #define MAX20087_OUT4 \ {\ .name = "OUT4",\ .ops = &max20087_buck_ops,\ .type = REGULATOR_VOLTAGE,\ .id = MAX20087_ID_OUT4,\ .n_voltages = \ (MAX20087_MAX_MV - MAX20087_MIN_MV) / MAX20087_STEP_MV + 1,\ .min_uV = (MAX20087_MIN_MV * 1000),\ .uV_step = (MAX20087_STEP_MV * 1000),\ .enable_reg = MAX20087_REG_CONFIG,\ .enable_mask = MAX20087_EN_OUT4,\ .enable_val = MAX20087_EN_OUT4,\ .disable_val = 0,\ .owner = THIS_MODULE,\ } static struct regulator_desc max20087_regulators[] = { MAX20087_OUT1, MAX20087_OUT2, MAX20087_OUT3, MAX20087_OUT4, }; static struct of_regulator_match max20087_matches[] = { [MAX20087_ID_OUT1] = { .name = "OUT1" }, [MAX20087_ID_OUT2] = { .name = "OUT2" }, [MAX20087_ID_OUT3] = { .name = "OUT3" }, [MAX20087_ID_OUT4] = { .name = "OUT4" }, }; static int max20087_parse_regulators_dt(struct max20087 *chip) { struct device_node *node; int i, num, n; /* Get the mux out of reset if a reset GPIO is specified. */ chip->gpio_en = devm_gpiod_get(chip->dev, "enable", GPIOD_OUT_HIGH); if (IS_ERR(chip->gpio_en)) return PTR_ERR(chip->gpio_en); node = of_get_child_by_name(chip->dev->of_node, "regulators"); if (!node) { dev_err(chip->dev, "regulators node not found\n"); //return ERR_PTR(-ENODEV); return PTR_ERR(node); } num = of_regulator_match(chip->dev, node, max20087_matches, ARRAY_SIZE(max20087_matches)); of_node_put(node); if (num < 0) { dev_err(chip->dev, "Failed to match regulators\n"); //return ERR_PTR(-EINVAL); return -EINVAL; } chip->num_output = num; n = 0; for (i = 0; i < ARRAY_SIZE(max20087_matches); i++) { if (!max20087_matches[i].init_data) continue; chip->init_data[n] = max20087_matches[i].init_data; chip->reg_node[n] = max20087_matches[i].of_node; dev_dbg(chip->dev, "name %s\n", max20087_matches[i].name); printk("%s: name %s\n", __func__, max20087_matches[i].name); // testing n++; } return 0; } static irqreturn_t max20087_irq_handler(int irq, void *data) { // Not support interrupt yet // TODO: Implement interrupt handler code return IRQ_NONE; } static int max20087_regulator_init(struct max20087 *chip) { struct regulator_config config = { }; int i, ret; for (i = 0; i < chip->num_output; i++) { config.init_data = chip->init_data[i]; config.dev = chip->dev; config.driver_data = chip; config.regmap = chip->regmap; config.of_node = chip->reg_node[i]; config.ena_gpiod = chip->gpio_en; //config.ena_gpio_initialized = false; chip->rdev[i] = devm_regulator_register(chip->dev, &max20087_regulators[i], &config); if (IS_ERR(chip->rdev[i])) { dev_err(chip->dev, "Failed to register MAX20087 regulator\n"); return PTR_ERR(chip->rdev[i]); } } // TODO: Enable interrupt support ret = regmap_update_bits(chip->regmap, MAX20087_REG_MASK, MAX20087_INT_DISABLE_ALL, MAX20087_INT_DISABLE_ALL); if (ret < 0) { dev_err(chip->dev, "Failed to update mask reg: %d\n", ret); return ret; } return 0; } static bool max20087_gen_is_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { case MAX20087_REG_MASK: case MAX20087_REG_CONFIG: return true; default: return false; } } static const struct regmap_config max20087_regmap_config = { .reg_bits = 8, .val_bits = 8, .writeable_reg = max20087_gen_is_writeable_reg, .max_register = 0x9, .cache_type = REGCACHE_NONE, }; /* * I2C driver interface functions */ static int max20087_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct max20087 *chip; int error, ret; unsigned int data; printk("%s: enter\n", __func__); // testing chip = devm_kzalloc(&i2c->dev, sizeof(struct max20087), GFP_KERNEL); if (!chip) return -ENOMEM; chip->dev = &i2c->dev; chip->regmap = devm_regmap_init_i2c(i2c, &max20087_regmap_config); if (IS_ERR(chip->regmap)) { error = PTR_ERR(chip->regmap); dev_err(chip->dev, "Failed to allocate register map: %d\n", error); return error; } i2c_set_clientdata(i2c, chip); ret = max20087_parse_regulators_dt(chip); if (ret < 0) { dev_err(chip->dev, "No regulators defined for the platform\n"); return ret; } ret = regmap_read(chip->regmap, MAX20087_REG_ID, &data); if (ret < 0) { dev_err(chip->dev, "Failed to read DEVICE_ID reg: %d\n", ret); return ret; } data &= DEVICE_ID_MASK; switch (data) { case MAX20087_DEVICE_ID: /* Turn off all outputs */ chip->chip_id = MAX20087_ID; ret = regmap_update_bits(chip->regmap, MAX20087_REG_CONFIG, MAX20087_EN_MASK, 0); if (ret < 0) { dev_err(chip->dev, "Failed to write "); dev_err(chip->dev, "MAX20087_REG_CONFIG reg: %d\n", ret); } break; default: dev_err(chip->dev, "Unsupported device id = 0x%x.\n", data); return -ENODEV; } chip->chip_irq = i2c->irq; if (chip->chip_irq != 0) { ret = devm_request_threaded_irq(chip->dev, chip->chip_irq, NULL, max20087_irq_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "max20087", chip); if (ret != 0) { dev_err(chip->dev, "Failed to request IRQ: %d\n", chip->chip_irq); return ret; } } else { dev_warn(chip->dev, "No IRQ configured\n"); } ret = max20087_regulator_init(chip); if (ret < 0) dev_err(chip->dev, "Failed to initialize regulator: %d\n", ret); ret = regmap_read(chip->regmap, MAX20087_REG_CONFIG, &data); if (ret < 0) { dev_err(chip->dev, "Failed to read MAX20087_REG_CONFIG reg: %d\n", ret); return ret; } dev_dbg(chip->dev, "reg CONFIG 0x%x\n", data); printk("%s: reg CONFIG 0x%x\n", __func__, data); return ret; } static const struct i2c_device_id max20087_i2c_id[] = { {"max20087", MAX20087_ID}, {}, }; MODULE_DEVICE_TABLE(i2c, max20087_i2c_id); static const struct of_device_id max20087_dt_ids[] = { { .compatible = "max20087", .data = &max20087_i2c_id[0] }, {}, }; MODULE_DEVICE_TABLE(of, max20087_dt_ids); static struct i2c_driver max20087_regulator_driver = { .driver = { .name = "max20087", .of_match_table = of_match_ptr(max20087_dt_ids), }, .probe = max20087_i2c_probe, .id_table = max20087_i2c_id, }; module_i2c_driver(max20087_regulator_driver); MODULE_AUTHOR("Watson Chow <watson.chow@avnet.com>"); MODULE_DESCRIPTION("MAX20087 Power Protector driver"); MODULE_LICENSE("GPL");
dts配置
max20087-regulator@28 { compatible = "max20087"; reg = <0x28>; enable-gpios = <&tca_gpio 2 GPIO_ACTIVE_HIGH>; regulators { camvcc_0: OUT1 { regulator-name = "VOUT1"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; regulator-min-microamp = <600000>; regulator-max-microamp = <600000>; }; camvcc_1: OUT2 { regulator-name = "VOUT2"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; regulator-min-microamp = <600000>; regulator-max-microamp = <600000>; }; camvcc_2: OUT3 { regulator-name = "VOUT3"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; regulator-min-microamp = <600000>; regulator-max-microamp = <600000>; }; camvcc_3: OUT4 { regulator-name = "VOUT4"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; regulator-min-microamp = <600000>; regulator-max-microamp = <600000>; }; }; };
2. regulator 框架
1.regulator概念及作用
Regulator,中文名翻译为“稳定器”,在电子工程中,是voltage regulator(稳压器)或者current regulator(稳流器)的简称,指可以自动维持恒定电压(或电流)的装置。一般电源管理芯片(Power Management IC)中会包含一个甚至多个regulator。
通常的作用是给电子设备供电。大多数regulator可以启用(enable)和禁用(disable)其输出,同时也可以控制其输出电压(voltage)和电流(current)。
从上图可以看出,input power会经过 regulator 转化为output power,regulator会做如下的约束:
- Voltage control: 输入5V输出1.8V
- Current limiting: 电流的输出最大为20MA
- Power switch: 可以控制电压enable/disable
2.Linux Regulator Framework
- Linux Regulator Framework设计出主要是提供一个标准的内核接口来控制电压和电流调节器。目的是允许系统动态控制regulator power输出以节省能源延长电池寿命。这适用于voltage regulator和current regulator(其中电压和电流都是可控的)。
- Linux Regulator Framework分为四个部分,分别是machine,regulator,consumer,sys-class-regulator。
machine
machine可以理解为regulator在板级的硬件配置,使用regulator_init_data结构体代表regulator板级的配置。
machine的主要功能,是使用软件语言(struct regulator_init_data),静态的描述regulator在板级的物理现状,包括:
1)前级regulator(即该regulator的输出是另一个regulator的输入,简称supply regulator)和后级regulator(即该regulator的输入是其它regulator的输出,简称consumer regulator)。
2)该regulator的物理限制(struct regulation_constraints)
struct regulator_init_data {
const char *supply_regulator; /* or NULL for system supply */
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 */
};
.supply_regulator: regulator的parent。用于级联regulator使用。
.constraints: 此regulator的约束,比如输出电压范围,输出电流范围等。
.num_consumer_supplies: 此regulator提供的consumer的个数,也就是控制外设的个数。
.consumer_supplies: 使用此结构确定regulator和consumer之间的联系。
.regulator_init: regulator注册时候的回调函数。
.driver_data: regulator_init回调函数的参数。
struct regulation_constraints {
const char *name;
/* voltage output range (inclusive) - for voltage control */
int min_uV;
int max_uV;
int uV_offset;
/* current output range (inclusive) - for current control */
int min_uA;
int max_uA;
/* valid regulator operating modes for this machine */
unsigned int valid_modes_mask;
/* valid operations for regulator on this machine */
unsigned int valid_ops_mask;
/* regulator input voltage - only if supply is another regulator */
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 */
/* mode to set on startup */
unsigned int initial_mode;
unsigned int ramp_delay;
unsigned int enable_time;
/* constraint flags */
unsigned always_on:1; /* regulator never off when system is on */
unsigned boot_on:1; /* bootloader/firmware enabled regulator */
unsigned apply_uV:1; /* apply uV constraint if min == max */
unsigned ramp_disable:1; /* disable ramp delay */
};
.name: 描述该约束的名字。
.min_uV/max_uV: 最小/最大的输出电压。
.uV_offset: consumer看到的电源和实际电源之间的偏移值,用于电源补偿。
.min_uA/max_uA: 最小/最大的输出电流。
.valid_modes_mask: 该regulator支持的操作模式。
#define REGULATOR_MODE_FAST 0x1 //快速改变模式
#define REGULATOR_MODE_NORMAL 0x2 //正常模式,大多数驱动都使用这种模式
#define REGULATOR_MODE_IDLE 0x4 //设备在idle状态,regulator给设备提供服务
#define REGULATOR_MODE_STANDBY 0x8 //设备在standby状态,regulator给设备提供服务
.valid_ops_mask: 该regulator支持的操作。
#define REGULATOR_CHANGE_VOLTAGE 0x1 //该regulator可以改变电压
#define REGULATOR_CHANGE_CURRENT 0x2 //该regulator可以改变电流
#define REGULATOR_CHANGE_MODE 0x4 //该regulator可以改变mode
#define REGULATOR_CHANGE_STATUS 0x8 //该regulator可以改变状态,也就是enable/disable power
#define REGULATOR_CHANGE_DRMS 0x10 //该regulator可以动态该变mode
#define REGULATOR_CHANGE_BYPASS 0x20 //该regulator支持bypass mode
.input_uV: 表示该regulator的input是另一个regulator。
.state_disk/state_mem/state_standby: 代表该regulator的各种suspend状态。
.always_on: 是否在系统启动后一直使能。
.boot_on: 是否在boot阶段使能。
.apply_uV: 当min_uV=max_uV的时候时使用。
.ramp_delay: 改变电压到电源稳定后时间。因为硬件原因,改变电源后不能立刻就成功,其中需要有一定的延迟。
.enable_time: regulator的使能时间。
aldo8: regulator-aldo8 {
regulator-compatible = "lc,lc1160-aldo8";
regulator-min-microvolt = <1750000>;
regulator-max-microvolt = <3300000>;
regulator-always-on = <1>;
regulator-boot-on = <1>;
};
egulator-name对应struct regulation_constraints中的name
regulator-min-microvolt对应struct regulation_constraints中的min_uV
regulator-max-microvolt对应struct regulation_constraints中的max_uV
regulator-always-on对应struct regulation_constraints中的always_on
regulator-boot-on对应struct regulation_constraints中的boot_on
1)在regulator注册前,调用of_get_regulator_init_data接口自行解析,该接口的实现如下
struct regulator_init_data *of_get_regulator_init_data(struct device *dev,
struct device_node *node)
{
struct regulator_init_data *init_data;
if (!node)
return NULL;
init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL);
if (!init_data)
return NULL; /* Out of memory? */
of_get_regulation_constraints(node, &init_data);
return init_data;
}
该接口有两个输入参数:设备指针,以及包含了DTS信息的node指针,它会分配一个struct regulator_init_data变量,并调用of_get_regulation_constraints解析DTS,把结果保存在该变量中。最后返回struct regulator_init_data变量的地址。
2)在regulator注册前, 多个电源可以由of_regulator_match调用of_get_regulator_init_data帮忙解析,该接口的实现如下
int of_regulator_match(struct device *dev, struct device_node *node,
struct of_regulator_match *matches,
unsigned int num_matches){
for (i = 0; i < num_matches; i++) {
struct of_regulator_match *match = &matches[i];
if (match->of_node)
continue;
if (strcmp(match->name, name))
continue;
match->init_data =
of_get_regulator_init_data(dev, child);
if (!match->init_data) {
dev_err(dev,
"failed to parse DT for regulator %s\n",
child->name);
return -EINVAL;
}
match->of_node = child;
count++;
break;
}
regulator
regulator可以理解为regulator driver。主要作用有注册自己的regulator服务到regulator core framework中,给consumer提供服务。regulator driver通过regulator_register函数注册regulator operation到regulator core。其中第一个参数struct regulator_desc代表静态的regulator配置,所谓静态就是不再会改变的配置。第二个参数代表regulator的动态配置信息。
使用struct regulator_desc描述regulator的静态信息,包括:名字、supply regulator的名字、中断号、操作函数集(struct regulator_ops)、使用regmap时相应的寄存器即bitmap等等。
结构体如下:
struct regulator_desc {
const char *name; //该regulator的名字
const char *supply_name; // 该regulator parent的name,在级联时使用。
const char *of_match; // 在dt中指定的name。
const char *regulators_node; //定义在dt中的regulator name.
int id; //用于标识该regulator,也可以标识系统有多少个regulator.
bool continuous_voltage_range;
//表示regulator是否可以在电压约束范围内连续输出电压。
unsigned n_voltages; //通过ops.list_voltage函数获取可用的电压数量。
const struct regulator_ops *ops; //regulator的操作函数集合。
int irq; //该regulator的中断号。
enum regulator_type type; //代表当前regulator的类型。
struct module *owner;
unsigned int min_uV; //如果是线性mapp的话,使用最低的selector获取的电压。
unsigned int uV_step; //每个selector下电压增加step。
unsigned int linear_min_sel; //线性mapp下最小的selector。
int fixed_uV; // 固定电压。
unsigned int ramp_delay; //电压改变之后需要多久时间稳定下来。
const struct regulator_linear_range *linear_ranges; // 电压的可能范围的常数表
int n_linear_ranges; // 电压范围常数表的个数。
const unsigned int *volt_table;// 电压的mapp表
unsigned int vsel_reg;
unsigned int vsel_mask;
unsigned int apply_reg;
unsigned int apply_bit;
unsigned int enable_reg;
unsigned int enable_mask;
unsigned int enable_val;
unsigned int disable_val;
bool enable_is_inverted;
unsigned int bypass_reg;
unsigned int bypass_mask;
unsigned int bypass_val_on;
unsigned int bypass_val_off;
unsigned int enable_time;// regulator初始化所需要的时间。
unsigned int off_on_delay;// 重新使能regulator的保护时间。
};
使用struct regulator_config,描述regulator的动态信息(所谓的动态信息,体现在struct regulator_config变量都是局部变量,因此不会永久保存),包括struct regulator_init_data指针、设备指针、enable gpio等等。此结构体如下:
struct regulator_config {
struct device *dev;
const struct regulator_init_data *init_data;
void *driver_data;
struct device_node *of_node;
struct regmap *regmap;
int ena_gpio;
unsigned int ena_gpio_invert:1;
unsigned int ena_gpio_flags;
};
.dev: struct device指针。
.init_data: 板级的相关初始化信息,通过解析DT,保存在此。
.dirver_data: 私有数据。
.of_node: dt相关的node。
.regmap: regulator register map。
当调用regulator_register函数之后,传入静态和动态参数之后,就会返回一个regulator_dev结构。此结构可以认为是regulator设备的一个抽象描述。
struct regulator_dev {
const struct regulator_desc *desc;
int exclusive;
u32 use_count;
u32 open_count;
u32 bypass_count;
/* lists we belong to */
struct list_head list; /* list of all regulators */
/* lists we own */
struct list_head consumer_list; /* consumers we supply */
struct blocking_notifier_head notifier;
struct mutex mutex; /* consumer lock */
struct module *owner;
struct device dev;
struct regulation_constraints *constraints;
struct regulator *supply; /* for tree */
struct regmap *regmap;
struct delayed_work disable_work;
int deferred_disables;
void *reg_data; /* regulator_dev data */
struct dentry *debugfs;
struct regulator_enable_gpio *ena_pin;
unsigned int ena_gpio_state:1;
/* time when this regulator was disabled last time */
unsigned long last_off_jiffy;
};
.list: 所有的regulator链表。
.consumer: 此regulator下所有的consumer。
.notifier: 此regulator的通知链,用于给consumer通知event。
.supply: 该regulator的supply,级联时候使用。
.disable_work: 该regulator的延迟工作,用于延迟disable regulator。
.use_count/open_count:代表该regulator的使用计数。
.exclusive: 该regulator是否是唯一的标志。
consumer
consumer可以理解为regulator提供服务的对象。比如LCD使用regulator管理自己,就必须使用regulator core提供的regulator相关接口函数。regulator_get()/regulator_put()函数。
详细可以看Documentation/power/regulator/consumer.txt文档。
sysfs-class-regulator
regulator core framework通过sysfs文件系统导出了一些"voltage/current/opmode"相关的信息,此将很有帮忙监控设备的功耗使用情况。
详细信息可以看Documentation/ABI/testing/sysfs-class-regulator文档。
Regulator注册
Lc1860上使用的PMIC为lc1160芯片,包含了电源管理以及audio codec,除了电源管理功能外,还内置了多个regulator,例如dcdc、aldo、dldo等
具体的regulator注册在kernel\linux-3.10.y\drivers\comip\pmic\lc1160目录中,dcdc、ldo分别在lc1160-dcdc.c和lc1160-ldo.c中注册。
|lc1160_ldo_probe
|–lc1160_ldo_parse_dt解析dts得到pdata
|–|–of_regulator_match计算出找到定义在ldo_reg_matches中的name的个数
|–|–|–of_get_regulator_init_data 调用of_get_regulation_constraints解析dts得到init data
|–regulator_register该接口接受描述该regulator的两个变量的指针:struct regulator_desc和struct regulator_config,并分配一个新的数据结构(struct regulator_dev,从设备的角度描述regulator),并把静态指针(struct regulator_desc)和动态指针(struct regulator_config)提供的信息保存在其中。注册regulator返回regulator_dev,后续所有的regulator操作,都将以该变量为对象
regulator consumer interface
-
获取regulator/释放regulator
regulator = regulator_get(dev, “Vcc”);
regulator_put(regulator); -
Enable and disable
int regulator_enable(regulator);
int regulator_disable(regulator);
int regulator_force_disable(regulator); -
设置regulator的电流,获得regulator的电流状态
int regulator_set_current_limit(regulator, min_uA, max_uA);
int regulator_get_current_limit(regulator); -
regulator的模式设置,间接(通过负载),直接设置
int regulator_set_optimum_mode(struct regulator *regulator, int load_uA);
int regulator_set_mode(struct regulator *regulator, unsigned int mode);
unsigned int regulator_get_mode(struct regulator *regulator);
core模块向用户空间提供的sysfs接口
regulator设备在内核中是以regulator class的形式存在的,regulator core通过class->dev_groups的方式,提供了一些默认的attribute,包括:
name,读取可以获取该regulator的名字;
num_users,读取可获取regulator的使用者数目;
type,读取可以获取该regulator的类型(voltage或者current)。
另外,如果regulator driver需要提供更多的attribute(如状态、最大/最小电压等等),可以调用add_regulator_attributes接口,主动添加。
Question
始终没有找到consumer_supplies和num_consumer_supplies在哪里提供的,//regulator_map_list
linux3.x下采用dts的方式建立设备树,自然给驱动带来很多改变。regulator也有很多变化
没有对regulator_consumer_supply进行初始化,那如何获得regulator指针,并对regulator进行电压设定呢?
通过regulator.txt我们发现,在regulator的optional properties中有
-supply: phandle to the parent supply /regulator node
看起来这个是用来标记父节点的。那么这个值又是如何使用的呢?才让设备能正确的获得regulator的呢?
在drivers/regulator/core.c中我们发现。
regulator_get->_regulator_get->regulaotr_dev_lookup->list_for_each_entry->of_get_regulator:
snprintf(prop_name, 32, “%s-supply”, supply);
regnode = of_parse_phandle(dev->of_node, prop_name, 0);
将我们传入的name,通过组合称name-supply的形式,再读取dts里的值,以此来获得我们需要的regulator的指针。
因此我们需要在dts里组织称类似
***-supply = <$dcdc1>
样子的属性,来表示所有设备的供电关系。
参考:
http://www.360doc.com/content/15/0708/16/10366845_483590831.shtml#
https://blog.csdn.net/bingqingsuimeng/article/details/8216782
http://www.wowotech.net/pm_subsystem/regulator_framework_overview.html
https://blog.csdn.net/longwang155069/article/details/53129378
http://blog.sina.com.cn/s/blog_a336aee70102v91l.html