下文转载自:
点击打开链接
现在
android/linux为模块设备供电有两种,一种GPIO供电,另一个就是电源管理芯片。
电源管理芯片可以为多设备供电,且这些设备电压电流有所同。为这些设备提供的稳压器代码模型即为
regulator。
下面通过下面三个过程分析
regulartor供电机制:
1.分析
regulator结构体
2.regulator 注册过程
3.设备使用
regulator过程
一
.分析
regulator结构体
Regulator模块用于控制系统中某些设备的电压
/电流供应。在嵌入式系统(尤其是手机)中,控制耗电量很重要,直接影响到电池的续航时间。所以,如果系统中某一个模块暂时不需要使用,就可以通过
regulator关闭其电源供应;或者降低提供给该模块的电压、电流大小。
Regulator的文档在
KERNEL/Documentation/Power/Regulator中。
Regulator与模块之间是树状关系。父
regulator可以给设备供电,也可以给子
regulator供电:
父
Regulator
--> 子
Regulator
--> [supply]
--> 设备
[Consumer]
具体细节可参考内核文档
machine.txt。
regulator_dev
regulator_dev代表一个
regulator设备。
struct regulator_dev {
struct regulator_desc *desc; // 描述符,包括
regulator的名称、
ID、
regulator_ops等
int use_count; // 使用计数
struct list_head list; // regulator通过此结构挂到
regulator_list链表中
struct list_head slist; // 如果有父
regulator,通过此域挂到父
regulator的链表
struct list_head consumer_list; // 此
regulator负责供电的设备列表
struct list_head supply_list; //此
regulator负责供电的子
regulator
struct blocking_notifier_head notifier; // notifier,具体的值在
consumer.h中,比如
REGULATOR_EVENT_FAIL
struct mutex mutex;
struct module *owner;
struct device dev; // device结构,属于
class
regulator_class
struct regulation_constraints *constraints; // 限制,比如最大电压
/电流、最小电压
/电流
struct regulator_dev *supply; // 父
regulator的指针,即由此regulator
供电
void *reg_data;
};
regulator_init_data
regulator_init_data在初始化时使用,用来建立父子
regulator、受电模块之间的树状结构,以及一些
regulator的基本参数。
struct regulator_init_data {
struct device *supply_regulator_dev; // 父
regulator的指针
struct regulation_constraints constraints;
int num_consumer_supplies;
struct regulator_consumer_supply *consumer_supplies; // 负责供电的设备数组
int (*regulator_init)(void *driver_data); // 初始化函数,在
regulator_register被调用
void *driver_data;
};
其它结构体自己可以看看~如
struct regulator -------> 设备驱动直接操作的结构体
struct regulation_constraints ----->regulator限制范围,其它信息,在于
struct regulator_init_data,用于初始化
struct regulator_consumer_supply ----->consumer信息
struct regulator_desc ----->这个多关注些,内有正真操作设备函数结构体~
struct regulator_map ----->这个为consumers与regulator对应表
........................................
............................................
二
. 注册
regulator过程
先说明下两具在
regulator的
core中有两个关键的全局变量链表:
regulator_list 每注册一个
regulator都会挂到这里
regulator_map_list 每一个
regulator都会为多个
consumer供电,此表为挂
consumer
regulator注册过程是通过
platform平台注册,当然一个电源管理芯片可以供几十个设备供电,所以不可能每个
regulator一个驱动文件,它们是通过,在电源管理芯片驱动中一块注册到
regulato的
core中。
对于电源管理芯片驱动的注册则通过I2C注册的。接下来以中星微方案过下,
首先,在平台设备文件中,有关
struct regulator_init_data XX定义~
如:
…
.........................
static struct regulator_init_data va7882_ldo13_data = {
.constraints = {
.name = "LDO13-HDMI1V2", //Default: 1.5V , Powered By DCDC5, C-class
.min_uV = 1200000,
.max_uV = 1800000,
.apply_uV = 1,
// TEMP_ON
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE,
.initial_mode = REGULATOR_MODE_NORMAL,
.valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
},
.supply_regulator = supply_regulator_name_arrary[ID_VA7882_LDO13],
.num_consumer_supplies = 2,
.consumer_supplies = (struct regulator_consumer_supply []) {
{ .supply = regulator_name_arrary[ID_VA7882_LDO13][0] },
{ .supply = regulator_name_arrary[ID_VA7882_LDO13][1] },
{ .supply = regulator_name_arrary[ID_VA7882_LDO13][2] },
},
};
…
........................................
其实这些结构体又会被同一文件中的自定义文件初始化函数 v8_va7882_init调用,其实就是
va7882_register_regulator---->为每个
regulator分配对应的
struct
platform_device---->再
platform_device_add
那
platform_driver在哪?
static struct platform_driver va7882_regulator_driver = {
.driver = {
.name = "va7882-regulator",
.owner = THIS_MODULE,
},
.probe = va7882_regulator_probe,
.remove = __exit_p(va7882_regulator_remove),
.suspend = va7882_regulator_suspend, //可见休眠唤醒是使用同一个,
.resume = va7882_regulator_resume, //再次说明它们是一统管理,它们也是为了节省.
.shutdown = va7882_regulator_shutdown,
};
这个
platform_driver是每个
regulator共用的的,因为
name都是"
va7882-regulator"。
在这个
platform_driver中
probe中就干了一件事
regulator_register,从而获得
regulator_dev。
接下来分析下
regulator_register注册
Regulator的注册由
regulator_register完成。
一般来说,为了添加
regulator_dev,需要实现一个设备驱动程序,以及在板子的设备列表中增加一个该驱动对应的设备(比如
platform_device)。在这个设备的
struct
device->platform_data域,需要设置
regulator_init结构,填写该
regulator的相关信息。另外,还需要定义一个
regulator_desc结构。这样,在这个物理设备的驱动程序中,就可以通过
regulator_register函数登记生成一个
regulator_dev。
struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, struct device *dev, void *driver_data)
struct regulator_init_data *init_data = dev->platform_data;// 得到
init_data
// 完整性检查
…
// 分配
regulator_dev结构
struct regulator_dev *rdev = kzalloc (sizeof(struct regulator_dev), GFP_KERNEL);
// 初始化
regulator_dev结构
…
// 执行
regulator_init,该函数中实现
regulator代表的硬件设备的初始化
if (init_data->regulator_init)
ret = init_data->regulator_init(rdev->reg_data);
rdev->dev.class = ®ulator_class; // 指定
class为
regulator_class
rdev->dev.parent = dev;
device_register(&rdev->dev); // 注册设备
// 设置
constraints,其中可能会包括供电状态的初始化(设置初始电压,
enable/disable等等)
set_machine_constraints(rdev, &init_data->constraints);
add_regulator_attributes (rdev);
// 如果此
regulator有父
regulator,设置父
regulator
if (init_data->supply_regulator_dev) {
ret = set_supply(rdev,
dev_get_drvdata(init_data->supply_regulator_dev));
if (ret < 0)
goto scrub;
}
// 设置此
regulator与其负责供电的设备之间的联系
for (i = 0; i < init_data->num_consumer_supplies; i++)
ret = set_consumer_device_supply(rdev, init_data->consumer_supplies[i].dev,
init_data->consumer_supplies[i].supply);
// 将
regulator加入一个链表,该链表包含所有
regulator
list_add(&rdev->list, ®ulator_list);
…
.............................
那个
regulator 根据在
regulator_list中用
init_data->supply_regulator来匹配
匹配成功用
set_supply()来设置注册的
regulator是由那个
regulator供电,
rdev->supply
= supply_rdev; list_add(&rdev->slist, &supply_rdev->supply_list);
多个
consumer用
set_consumer_device_supply(),先检查
list_for_each_entry(node, ®ulator_map_list, list) 后添加
list_add(&node->list, ®ulator_map_list);当然
node已经在
node->regulator = rdev;
node->supply = supply;
形成 对于每一个
regulator_dev—comsumer_dev的配对
最后在把
regulator通过
list_add(&rdev->list,
®ulator_list);加到
regulator_list链表中。
三
.设备使用
regulator过程
在设备驱动使用
regulator对其驱动的设备供电时,首先要确保设备与对应
regulator之间的匹配关系已经被登记到
regulator框架中。
之后,设备驱动通过
regulator_get函数得到
regulator结构,此函数通过前文所述
regulator_map_list找到对应
regulator_dev,再生成
regulator结构给用户使用。
通过
regulator_enable / regulator_disable打开、关闭
regulator,这两个函数最终都是调用
struct
regulator_ops里的对应成员。
除此之外,还有
regualtor_set_voltage / regulator_get_voltage等等。
Regulator能够支持的所有功能列表都在
struct
regulator_ops中定义,具体可参考代码中的注释。
struct regulator_ops {
int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV);
int (*get_voltage) (struct regulator_dev *);
int (*set_current_limit) (struct regulator_dev *,
int min_uA, int max_uA);
int (*get_current_limit) (struct regulator_dev *);
int (*enable) (struct regulator_dev *);
int (*disable) (struct regulator_dev *);
int (*is_enabled) (struct regulator_dev *);
int (*set_mode) (struct regulator_dev *, unsigned int mode);
unsigned int (*get_mode) (struct regulator_dev *);
unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV,
int output_uV, int load_uA);
int (*set_suspend_voltage) (struct regulator_dev *, int uV);
int (*set_suspend_enable) (struct regulator_dev *);
int (*set_suspend_disable) (struct regulator_dev *);
int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode);
};
接下来我就以中星型的HDMI驱动使用
regulator过一遍。
vc088x_hdmi.c文件:
HDMI驱动也是通过
platform平台注册上去,所以在
platform_driver的
probe中有这个句
ret = v8hdmi_pwr_get(&pdev->dev);
regulator_get(dev,id);
_regulator_get(dev, id, 0);
{
…........
…...........
if (dev)
devname = dev_name(dev);
list_for_each_entry(map, ®ulator_map_list, list) {
if (map->dev_name &&(!devname || strcmp(map->dev_name, devname)))
continue;
if (strcmp(map->supply, id) == 0) {//查找配对
rdev = map->regulator;
goto found;
}
}
….....................
…..........................
//这个用于在
sys目录下建立对应的
regulator文件,用于用户空间操作
regulator = create_regulator(rdev, dev, id); //regulator -->struct regulator
….............................
return regulator;
}
返回的
regulator会赋给全局变量,如
hdmi_core_consumer
= regulator,//这只是例子,不同方案处理不一样。
在恰当的时候使能它,如
ret = v8hdmi_pwr_enable();
regulator_enable(hdmi_io_consumer);
struct regulator_dev *rdev = regulator->rdev;
ret = _regulator_enable(rdev);
….................
if (rdev->use_count == 0) {
if (rdev->supply) {
mutex_lock(&rdev->supply->mutex);
ret = _regulator_enable(rdev->supply);//使能父regulator
mutex_unlock(&rdev->supply->mutex);
If (ret < 0) {
rdev_err(rdev, "failed to enable: %d
", ret);
return ret;
}
}
}
…...............................
ret = rdev->desc->ops->enable(rdev);//调用真正使能操作.
….......................
使能函数到此结束
.
总的来看,使用也是通过
regulator_get()-->regulator_enable()就可以了
想关时,
regulator_disable()--->regulator_put(),反操作~
其实我疑惑是真正操作电源管理芯片那些操作,存放在
struct regulator_ops 结构体内
而这个结构体包含在于
struct regulator_desc内,这个结构体,在执行注册
regulator时,使用到,被赋到
regulator_dev-->desc中~
对于
struct regulator_ops中的操作方法,就涉及到电源芯片驱动,下面是
va7882的操作方法
static struct regulator_ops va7882_dcdc_ops = {
.set_voltage = va7882_dcdc_set_voltage,
.get_voltage = va7882_dcdc_get_voltage,
.list_voltage = va7882_dcdc_list_voltage,
.enable = va7882_dcdc_enable,
.disable = va7882_dcdc_disable,
.is_enabled = va7882_dcdc_is_enabled,
.get_mode = va7882_dcdc_get_mode,
.set_mode = va7882_dcdc_set_mode,
.get_optimum_mode = va7882_dcdc_get_optimum_mode,
.set_suspend_voltage = va7882_dcdc_set_suspend_voltage,
.set_suspend_enable = va7882_dcdc_set_suspend_enable,
.set_suspend_disable = va7882_dcdc_set_suspend_disable,
.set_suspend_mode = va7882_dcdc_set_suspend_mode,
.enable_time = va7882_enable_time,
};
接后面再讨论电源管理芯片驱动,还有像v8_va7882_init函数来初始化及注册各种regulator,那谁去调用这个初始函数呢?怎么的调用流程呢?这些都在电源管理芯片驱动会讲到!