1. 概念:
Regulator : 电源芯片, 比如电压转换芯片
Consumer : 消费者,使用电源的部件, Regulator是给Consumer供电的
machine : 单板,上面焊接有Regulator和Consumer
Constraints : 约束, 比如某个电源管理芯片输出的电压范围
Supply : 提供电源的部件, Regulator就是一个Supply; Regulator A可以给Regulator B供电, 那么Regulator B的Supply就是A

2. 写驱动程序:

(1). regulator驱动:
注册一个platform_driver: 在它的probe函数里分配、设置、注册一个regulator
"设置"里要做的事情: 实现regulator的操作, 比如enable, disable, set_voltage
(2). machine(单板)驱动:
注册一个platform_device: 在它的私有数据里指定regulator和consume的对应关系(这个电源芯片给哪一个部件供电)
指定约束条件(比如电压范围)
(3). consumer使用此电源的设备驱动:
使用即可: regulator_get, regulator_enable, regulator_disable, regulator_set_voltage....
注: 这只是没有使用设备树的情况下是这样的,其实就是以平台设备模型注册一个regulator,然后consumer去使用。

3. 不使用设备树的regulator_register流程分析:

    // 分配regulator_dev
rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); /* set regulator constraints */
set_machine_constraints
add_regulator_attributes /* add consumers devices */
set_consumer_device_supply
在regulator_map_list链表里生成一项regulator_map: 它里面有dev_name(consumer的名字),supply(cosumer的电源引脚名字) // 把regulator_dev放入regulator_list
list_add(&rdev->list, &regulator_list);

4. 使用设备树的regulator_register()和regulator_get()流程分析:

regulator_register(const struct regulator_desc *, const struct regulator_config *) //drivers/regulator/core.c
先自行各种检查,然后构造一个struct regulator_dev,从设备树中获取它的各种属性
regulator_of_get_init_data //drivers/regulator/of_regulator.c
of_get_regulation_constraints //drivers/regulator/of_regulator.c 通过设备树构造init_data,和约束
若设备树中指定了“regulator-initial-mode”且regulator_desc.of_map_mode()存在,则调用它
初始化rdev->consumer_list,rdev->list,rdev->notifier,指定rdev->disable_work=regulator_disable_work
如果init_data->regulator_init()存在则调用
判断是不是gpio控制的regulator,如果是就调用regulator_ena_gpio_request()
使rdev->dev.class = &regulator_class,此时sysfs下会有"regulator"文件夹,其下有regulator_dev_attrs中列出的文件。
自动递增设置&rdev->dev的name为"regulator.%lu" "regulator"下的"regulator.%lu"下才会有regulator_dev_attrs中列出的文件了。
set_machine_constraints
device_register(&rdev->dev) //注册到设备模型
dev_set_drvdata(&rdev->dev, rdev);
rdev_init_debugfs(rdev); 使用设备树的regulator_get()流程分析:
struct regulator *regulator_get(struct device *dev, const char *id) //drivers/regulator/core.c, arg2为consumer的电源引脚
regulator_dev_lookup //drivers/regulator/core.c
使用devname与全局链表regulator_map_list中的每一项struct regulator_map->dev_name进行匹配,并返回匹配的struct regulator_dev结构体。
regulator_resolve_supply 还要和rdev->supply_name的名字逐个比较
create_regulator 根据struct regulator_dev创建一个struct regulator并返回

5.驱动示例代码

/* 文件名:machine.c 作为一个regulator平台设备模型的设备端。
* 参考: arch\arm\mach-omap2\board-2430sdp.c
* 这里只是注册一个单板的所有regulator的平台设备端,目前这类文件应该被设备树给替代了
*/ #include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/mfd/core.h> /* 分配/设置/注册regulator_init_data */ #if 0
regulator_consumer_supply:
const char *dev_name; /* consumer的名字 */
const char *supply; /* consumer的电源引脚名称 */ #endif static struct regulator_consumer_supply myregulator_supplies[] = {
REGULATOR_SUPPLY("VCC", "mylcd"), /*此regulator是对设备"mylcd"上的"VCC"引脚供电的*/
}; static struct regulator_init_data myregulator_init_data = {
.constraints = {
.min_uV = ,
.max_uV = ,
.valid_modes_mask = REGULATOR_MODE_NORMAL,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(myregulator_supplies),
.consumer_supplies = myregulator_supplies,
}; static void myregulator_release(struct device * dev)
{
} static struct platform_device myregulator_dev = {
.name = "myregulator", /* 平台设备设备端名字*/
.id = -,
.dev = {
.release = myregulator_release,
.platform_data = &myregulator_init_data,
},
}; /*使用设备树后就不再需要注册平台设备端了*/
static int myregulator_machine_init(void)
{
platform_device_register(&myregulator_dev);
return ;
} static void myregulator_machine_exit(void)
{
platform_device_unregister(&myregulator_dev);
} module_init(myregulator_machine_init);
module_exit(myregulator_machine_exit); MODULE_LICENSE("GPL v2");
/* 文件名:regulator.c 作为一个regulator平台设备模型的驱动端。
* 参考: drivers/regulator/tps6105x-regulator.c
* 其实machine.c充当的是平台设备的设备端,将单板上的所有regulator写在了一起,目前已被设备树替代。
* regulator.c充当的是平台设备的驱动端,每个regulator都可能有一个平台设备驱动端。
*/ #include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/mfd/core.h> static volatile unsigned long *gpbcon;
static volatile unsigned long *gpbdat; static int myregulator_enable(struct regulator_dev *rdev)
{
*gpbdat |= ; /* 输出高电平 */
return ;
} static int myregulator_disable(struct regulator_dev *rdev)
{
*gpbdat &= ~; /* 输出低电平 */
return ;
} static int myregulator_is_enabled(struct regulator_dev *rdev)
{
if (*gpbdat & )
return ;
else
return ;
} static struct regulator_ops myregulator_ops = {
.enable = myregulator_enable,
.disable = myregulator_disable,
.is_enabled = myregulator_is_enabled,
}; static struct regulator_desc myregulator_desc = {
.name = "myregulator",
.ops = &myregulator_ops,
.type = REGULATOR_VOLTAGE,
.id = ,
.owner = THIS_MODULE,
.n_voltages = , /*只提供一种电压值得*/
}; static struct regulator_dev *myregulator_dev;
static int myregulator_probe(struct platform_device *pdev)
{
struct regulator_init_data *init_data = dev_get_platdata(&pdev->dev); /*可以在platform_driver初始化的时候对dev赋值*//* 分配/设置/注册 regulator */
myregulator_dev = regulator_register(&myregulator_desc,
&pdev->dev,
init_data, NULL,
NULL); if (IS_ERR(myregulator_dev)) {
printk("regulator_register error!\n");
return -EIO;
} return ;
} static int myregulator_remove(struct platform_device *pdev)
{
regulator_unregister(myregulator_dev);
return ;
} struct platform_driver myregulator_drv = {
.probe = myregulator_probe,
.remove = myregulator_remove,
.driver = {
.name = "myregulator", /* 驱动上面regulator平台设备的设备端 */
}
}; static int myregulator_init(void)
{
platform_driver_register(&myregulator_drv);
return ;
} static void myregulator_exit(void)
{
platform_driver_unregister(&myregulator_drv);
} module_init(myregulator_init);
module_exit(myregulator_exit); MODULE_LICENSE("GPL v2");
/* 文件名:consumer_lcd.c 作为一个consumer驱动
*
*/
static int mylcd_open(struct fb_info *info, int user)
{
pm_runtime_get_sync(&lcd_dev.dev);
return ;
}
static int mylcd_release(struct fb_info *info, int user)
{
pm_runtime_mark_last_busy(&lcd_dev.dev);
pm_runtime_put_sync_autosuspend(&lcd_dev.dev);
return ;
} static int lcd_suspend_notifier(struct notifier_block *nb,
unsigned long event,
void *dummy)
{ switch (event) {
case PM_SUSPEND_PREPARE:
printk("lcd suspend notifiler test: PM_SUSPEND_PREPARE\n");
return NOTIFY_OK;
case PM_POST_SUSPEND:
printk("lcd suspend notifiler test: PM_POST_SUSPEND\n");
return NOTIFY_OK; default:
return NOTIFY_DONE;
}
} static struct notifier_block lcd_pm_notif_block = {
.notifier_call = lcd_suspend_notifier,
}; static void lcd_release(struct device * dev)
{
} static struct platform_device lcd_dev = { /* regulator匹配1 */
.name = "mylcd",
.id = -,
.dev = {
.release = lcd_release,
},
}; static struct regulator *myregulator; static int lcd_probe(struct platform_device *pdev)
{
myregulator = regulator_get(pdev->dev, "VCC"); /* regulator 匹配2 */
if (IS_ERR(myregulator)) {
printk("regulator_get error!\n");
return -EIO;
}
regulator_enable(myregulator); /*没有它会报unblance regulator disable of myregulator*/ pm_runtime_set_active(&pdev->dev);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return ;
}
static int lcd_remove(struct platform_device *pdev)
{
regulator_put(myregulator);
pm_runtime_disable(&pdev->dev);
return ;
}
static int lcd_suspend(struct device *dev)
{
int i;
unsigned long *dest = &lcd_regs_backup;
unsigned long *src = lcd_regs; for (i = ; i < sizeof(lcd_regs_backup)/sizeof(unsigned long); i++)
{
dest[i] = src[i];
} lcd_regs->lcdcon1 &= ~(<<); /* 关闭LCD本身 */
//*gpbdat &= ~1; /* 关闭背光 */
regulator_disable(myregulator);
return ;
} static int lcd_resume(struct device *dev)
{
int i;
unsigned long *dest = lcd_regs;
unsigned long *src = &lcd_regs_backup; struct clk *clk = clk_get(NULL, "lcd");
clk_enable(clk);
clk_put(clk); for (i = ; i < sizeof(lcd_regs_backup)/sizeof(unsigned long); i++)
{
dest[i] = src[i];
} regulator_enable(myregulator);
return ;
} static struct dev_pm_ops lcd_pm = {
.suspend = lcd_suspend,
.resume = lcd_resume,
.runtime_suspend = lcd_suspend,
.runtime_resume = lcd_resume,
}; struct platform_driver lcd_drv = {
.probe = lcd_probe,
.remove = lcd_remove,
.driver = {
.name = "mylcd",
.pm = &lcd_pm,
}
}; static int lcd_init(void)
{
/* 电源管理 */
register_pm_notifier(&lcd_pm_notif_block); platform_device_register(&lcd_dev);
platform_driver_register(&lcd_drv); return ;
} static void lcd_exit(void)
{
unregister_pm_notifier(&lcd_pm_notif_block);
platform_device_unregister(&lcd_dev);
platform_driver_unregister(&lcd_drv);
} module_init(lcd_init);
module_exit(lcd_exit); MODULE_LICENSE("GPL");

Linux regulator系统的更多相关文章

  1. Linux Regulator Framework(2)_regulator driver

    转自蜗窝科技:http://www.wowotech.net/pm_subsystem/regulator_driver.html 说实话,这篇好难懂啊... 1. 前言 本文从regulator d ...

  2. Linux regulator framework(1) - 概述【转】

    转自蜗窝科技:http://www.wowotech.net/pm_subsystem/regulator_framework_overview.html 1. 前言 Regulator,中文名翻译为 ...

  3. Linux电源管理-Linux regulator framework概述

    前言 1.  什么是regulator?      regulator翻译为"调节器",分为voltage regulator(电压调节器)和current(电流调节器).一般电源 ...

  4. Linux查看系统状态命令

    Linux查看系统状态命令       iostat iostat 命令详细地显示了存储子系统方面的情况.你通常用iostat来监控存储子系统总体上运行状况如何,并且在用户注意到服务器运行缓慢之前提早 ...

  5. 82 fsck-检查与修复 Linux 档案系统

    Linux fsck命令用于 检查与修复 Linux 档案系统,可以同时检查一个或多个 Linux 档案系统. 语法 fsck [-sACVRP] [-t fstype] [--] [fsck-opt ...

  6. 解决:WPS for Linux提示“系统缺失字体symbol、wingdings、wingdings 2、wingdings 3、webding”

    WPS for Linux提示“系统缺失字体symbol.wingdings.wingdings 2.wingdings 3.webding” 出现提示的原因是因为WPS for Linux没有自带以 ...

  7. 如何查看、修改Linux的系统时间

    如题: 如何修改Linux的系统时间? date -s 05/27/2011 date -s 10:24:00 clock -w 就这三条命令就OK了! 查看/修改Linux时区和时间 一.时区 1. ...

  8. 【解决方法】安装Win7和linux双系统后,linux报错“无法分配所提交的分区 not enough free space on disks”问题,以及win7无法启动“BootMGR image is corrupt....”问题

    近日,在笔记本上重装了Win7 企业版(64位)后,想装个linux双系统,于是开始安装 centOS 6.2(光盘安装) 硬盘分了一个主分区(c盘),一个扩展分区(3个逻辑分区:d,e,f盘),然后 ...

  9. Win7下硬盘安装Linux双系统

    Win7下硬盘安装CentOS6.2 一.准备工作:划出磁盘空闲空间和准备安装文件  参考文献: [Win7下硬盘安装Linux总结(CentOS)]来源:Linux社区  作者:lixianlin ...

随机推荐

  1. WPF PasswordBox.Password 的数据绑定

    WPF的PasswordBox控件的Password属性不是依赖属性,无法直接进行数据绑定,为使其在MVVM模式中正常使用,可以为PasswordBox增加一个助手类,代码如下: 注:代码摘自:htt ...

  2. 20170711xlVBA自定义分类汇总一例

    Public Sub CustomSubTotal() AppSettings On Error GoTo ErrHandler Dim StartTime, UsedTime As Variant ...

  3. n转m进制标准写法(必须记忆)

    #include <bits/stdc++.h> using namespace std; int main() { int n,m; cin >> n >> m; ...

  4. spring PROPAGATION

    PROPAGATION_REQUIRED Support a current transaction; create a new one if none exists.  支持一个当前事务;如果不存在 ...

  5. nodejs初探一二

    概念 简单来说,node.js 是一个让 JavaScript 运行在服务端的开发平台,让开发者通过JS编写服务端程序. 安装 从nodeJS官网下载对应平台的安装程序,安装完成后,打开命令行工具,然 ...

  6. BZOJ1652 [Usaco2006 Feb]Treats for the Cows

    蒟蒻许久没做题了,然后连动规方程都写不出了. 参照iwtwiioi大神,这样表示区间貌似更方便. 令f[i, j]表示i到j还没卖出去,则 f[i, j] = max(f[i + 1, j] + v[ ...

  7. C#中使用Spire.docx操作Word文档

    使用docx一段时间之后,一些地方还是不方便,然后就尝试寻找一种更加简便的方法. 之前有尝试过使用Npoi操作word表格,但是太烦人了,随后放弃,然后发现免费版本的spire不错,并且在莫种程度上比 ...

  8. Log4j在Java工程中使用方法

    Eclipse新建Java工程,工程目录如下 1.下载log4j的Jar包,在Java工程下新建lib文件夹,将jar包拷贝到此文件夹,并将其加入到路径中,即:Jar包上右键——Build Path— ...

  9. CDMA学习

    1.关于RC:http://www.mscbsc.com/askpro/question74915 2.CDMA知识要点:http://wenku.baidu.com/view/d4511442a89 ...

  10. windowsphone开发页面跳转到另一个dll中的页面

    WP的页面跳转一般是只能跳转到本DLL的页面, 如果要跳转到其他DLL的页面则需要这样写 (Application.Current.RootVisual as PhoneApplicationFram ...