Linux核心regulator建筑和准备
电源引入的物种
(百度百科)LDO这是low dropout regulator,这意味着低压差线性稳压器。它相比于传统的线性调节器。传统的线性稳压器。例如78xx系列芯片需要输入电压比输出电压高2v~3V以上,否则就不能正常工作。
可是在一些情况下。这种条件显然是太苛刻了。如5v转3.3v,输入与输出的压差仅仅有1.7v,显然是不满足条件的。针对这种情况,才有了LDO类的电源转换芯片。Ldo适合电压要求比較稳,可是功率不是非常大的设备。
BUCK电路,降压式变换电路。就是一种DC-DC转换器,简单的讲就是通过震荡电路将一直流电压转变为一高频电源。然后通过脉冲变压器、整流滤波回路输出须要的直流电压,类似于开关电源
数据结构
structregulator_dev {
structregulator_desc *desc;
structlist_head list; // regulator通过此结构挂到regulator_list链表中
structlist_head consumer_list; //此regulator负责供电的设备列表
structregulation_constraints *constraints;
structregulator *supply; 父regulator的指针
};
regulator_list全局变量 每注冊一个regulator都会挂到这里
regulator_map_list全局变量 每注冊一个consumer都会挂到这里
编写驱动的步骤
概述
内核里pmu驱动和regulator驱动大多是混合在一起写的,非常不好。比方把regulator_init_data放到platform的driver data里传进去。
一般来讲,一款soc会有配套有限数量的pmu。
regulator作为pmu的抽象层。供电线路分为pmu直接供电和mtcmos供电。
mtcmos也是pmu的一路作为父电源进行供电,可是mtcmos是soc片上的供电线路。比方mtcmos用pmu的一路buck供电,可是usb,sd,pcie分别用了3路mtcmos进行供电。
每路供电节点都是一个regulator。假设不考虑mtcmos情况,通常都是一级供电。
也就是说,从pmu出来一个regulator以下挂接的就是consumer,而不是多个regulator级联。
假设出现级联的regulator。能够在regulator_desc的supply_name字段设置上级regulator。
推荐的写法是。regulator本身用什么实质内容都没有的platform dev和driver注冊,在probe里注冊regulator。
仅仅有regulator ops才会调用真正的pmu驱动。这样实现了适配层和详细驱动分离的原则。
自己定义regulator id格式
regulator_desc有一个成员是id。这个id在区分不同的regulator里没有什么作用,由于regulator都是通过name字符串来查找和区分的。id的作用集中表如今ops函数指针数组里。一个PMU芯片通常有多路供电,可是一类供电(如buck)使用同一组ops函数指针数组一般是同样的。
可是实际不同的regulator设置的寄存器和方法是有差异的。所以通过id进行区分。
通常把id作为offset使用。把寄存器和操作方法放到数组里,这样利用id从数组得到信息,避免了使用大量的if和switch
case语句。
struct regulator_desc {
const char*name;
const char*supply_name;
int id;
…
};
更优化的一些方法能够是。id的有些bit表示pmu或者regulator本身的信息索引。而另外一些bit表示偏移。这样。一个系列的多个PMU芯片可能复用同一套regulator适配层代码。
操作信息能够依照 func[pmu_id][offset]二位数组进行。这些用法都是灵活的。
获得id的函数
int rdev_get_id(struct regulator_dev *rdev)
{
returnrdev->desc->id;
}
step1 准备ops
尽管arm soc pmu通常多达几十路供电,可是ops数量不多。一般一类设备同用一组ops函数,常见的类别有buck ops和ldo ops。ops函数的入參是(struct regulator_dev *)。所以能够依据regulator_dev得到详细的id信息。进行不同的寄存器操作。
int offset = rdev_get_id(rdev);
理论上讲,一个pmu全部的regulator能够用同一组ops,然后用id区分操作。
还有一个极端是每一个regulator用一组不同的ops。
只是为了代码复用和解耦合,一般是一类regulator用一组ops。
样例:
static structregulator_ops max8660_ldo5_ops = {
.list_voltage = max8660_ldo5_list,
.set_voltage = max8660_ldo5_set,
.get_voltage = max8660_ldo5_get,
};
step2 准备consumer
代码样例:
static struct regulator_consumer_supplylp3974_buck3_consumer[] = {
REGULATOR_SUPPLY("vdet", "s5p-sdo"),
REGULATOR_SUPPLY("vdd_reg", "0-003c"),
};
consumer指的是regulator树上的叶子节点。regulator_consumer_supply数组作为为regulator_init_data的成员。
consumer的名字是须要提供给各模块使用的,如上面代码的“vdet”。
所以假设是系列soc的代码,最好这个名字不能带有pmu芯片型号或者soc型号,由于相同的模块,如usb控制器。非常可能系列soc上的都一样,驱动也是复用的。不可能每换个soc就全盘适配regulator。所以这样的情况下最好命名为”myregulator-usb”之类。这样换了pmu或者soc,仅仅要用regulator_get(dev, ”myregulator-usb”)总能适应各种情况,就不须要改动驱动。
step3 准备regulator_desc
注意regulator_register的时候,regulator_desc和regulator_init_data是一一匹配的。所以regulator_desc数组和regulator_init_data数组的顺序要保持一致。
static structregulator_desc regulators[] = {
{
.name = "LDO2",
.id = MAX8998_LDO2,
.ops = &max8998_ldo_ops,
.type =REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
}, {
.name = "LDO3",
.id = MAX8998_LDO3,
.ops = &max8998_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
}, {
另外另一种写法。能够用用定义的枚举作为index,按顺序填写数组。仅仅要regulator_desc数组和regulator_init_data数组都用相同的枚举做index,那肯定是一一匹配的。
static structregulator_desc regulators[] = {
[REG0] = {
.name = "LDO2",
.id = MAX8998_LDO2,
.ops = &max8998_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
},
[REG1] = {
.name = "LDO3",
.id = MAX8998_LDO3,
.ops = &max8998_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
}, {
step4 准备regulator_init_data
有的驱动程序将 regulator_init_data放入platform_data,不推荐。Regulator应该和pmu的驱动程序分开。仅仅须要在ops里调用pmu的操作函数就能够。
regulator_init_data应该也是结构体数组,和regulator_desc结构体数组一一匹配。
注意constraints的name字段在查找regulator的时候,有比regulator_desc更高的优先级。
不推荐在constraints写name。
当中的一段例如以下。
static struct regulator_init_data lp3974_buck3_data ={
.constraints = {
.name = "VCC_1.8V",
.min_uV = 1800000,
.max_uV = 1800000,
.apply_uV = 1,
.always_on = 1,
.state_mem = {
.enabled = 1,
},
},
.num_consumer_supplies = ARRAY_SIZE(lp3974_buck3_consumer),
.consumer_supplies = lp3974_buck3_consumer,
};
step5 准备注冊regulator
最简单的platform注冊就能够。仅仅须要在probe里把regulator都注冊上。
static int my_regulator_probe (struct platform_device*pdev)
{
int i;
for(i = 0;i < MAX ; i ++)
{
regulator_register(®ulator_desc[i], &pdev->dev,regulator_init_data[i], NULL, NULL);
}
}
static struct platform_device my_regulator_dev = {
.name =“my_regulator”,
};
static struct platform_driver my_regulator_driver = {
.driver ={
.name= " my_regulator ",
.owner= THIS_MODULE,
},
.probe =my_regulator_probe,
.remove =__devexit_p(my_regulator__remove),
};
在module_init里注冊device和driver就能够。
platform_device_register(&my_regulator_dev)
platform_driver_register(&my_regulator_driver);
regulator使用
整体来说就是先调用regulator_get供电节点的名字。得到regulator指针。然后调用ops函数集。
举比例如以下:
regulator = regulator_get(dev, “my_usb”)。
就会返回usb名字供电的regulator。
打开和关闭校准器(regulator)API例如以下。
int regulator_enable(regulator);
int regulator_disable(regulator);
还有其它一些函数,參见struct regulator_ops。常见的有
struct regulator_ops {
/* get/setregulator voltage */
int(*set_voltage) (struct regulator_dev *, int min_uV, int max_uV,
unsigned *selector);
int(*get_voltage) (struct regulator_dev *);
/* get/setregulator current */
int(*set_current_limit) (struct regulator_dev *,
int min_uA, int max_uA);
int(*get_current_limit) (struct regulator_dev *);
/*enable/disable regulator */
int(*enable) (struct regulator_dev *);
int(*disable) (struct regulator_dev *);
int(*is_enabled) (struct regulator_dev *);
};
内核代码分析
注冊regulator,包含设置supply和consumer
/**
*regulator_register - register regulator
*@regulator_desc: 就是regulator_desc
* @dev:
* @init_data: 就是regulator_init_data
* @driver_data:用户私有信息,不推荐。设置为NULL就能够
* @of_node:device tree能够设置regulator的树状结构。先不考虑).
*/
struct regulator_dev *regulator_register(structregulator_desc *regulator_desc,
structdevice *dev, const struct regulator_init_data *init_data,
void*driver_data, struct device_node *of_node)
{
// 编写驱动仅仅要提供regulator_desc和regulator_init_data就能够,分配regulator_dev结构在这里
rdev =kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
//初始化regulator_dev结构
// 设置constraints
ret =set_machine_constraints(rdev, constraints);
// 假设此regulator有父regulator。设置父regulator. 优先选init_data
if(init_data && init_data->supply_regulator)
supply= init_data->supply_regulator;
else if(regulator_desc->supply_name)
supply= regulator_desc->supply_name;
if(supply) {
//用name字符串在regulator_list查找。 假设找到,就把本regulator增加到上级的consumer_list
r =regulator_dev_lookup(dev, supply);
ret= set_supply(rdev, r);
...
}
/*
首先检查regulator_map_list全局变量里有没有冲突或者反复的。假设没有为consumer申请struct regulator_map,
然后设置regulator_map的regulator设置为本regulator。同一时候用regulator_map记录父子信息。
而且把regulator_map增加到regulator_map_list全局变量里。
*/
if(init_data) {
for(i = 0; i < init_data->num_consumer_supplies; i++) {
ret= set_consumer_device_supply(rdev,
init_data->consumer_supplies[i].dev_name,
init_data->consumer_supplies[i].supply);
}
}
//将本regulator增加到全局regulator_list
list_add(&rdev->list,®ulator_list);
}
regulator_desc和regulator_init_data的name反复字段
Regulator的name在struct regulator_init_data的constraints->name字段能够定义,也能够在regulator_desc的name字段定义。可是constraints->name优先级更高。
regulator_register-》regulator_dev_lookup-》rdev_get_name里代码例如以下:
static const char *rdev_get_name(struct regulator_dev*rdev)
{
if(rdev->constraints && rdev->constraints->name)
returnrdev->constraints->name;
else if(rdev->desc->name)
returnrdev->desc->name;
else
return"";
}
static LIST_HEAD(regulator_list); //全部regulator都注冊在这个链表里
上级电源supply反复字段
除了name,regulator_desc->supply_name和regulator_init_data->supply_regulator也是反复的,都是指向上级regulator的指针,regulator_init_data优先级更高,能够在regulator_register里看到代码例如以下
if (init_data &&init_data->supply_regulator)
supply= init_data->supply_regulator;
else if(regulator_desc->supply_name)
supply= regulator_desc->supply_name;
版权声明:本文博客原创文章。博客,未经同意,不得转载。
Linux核心regulator建筑和准备的更多相关文章
- 举例说,Linux核心名单(两)
使用列表 我认为最好的方式,成为熟悉的核心列表功能是看一些简单的例子,素材去更好的理解链表. 以下是一个样例.包括创建.加入.删除和遍历链表. <span style="font-si ...
- Linux核心命令
Linux核心命令 strace(查看系统调用的一个过程) 例:strace cat /test.txt netstat perf top pidstat mpstat dstat vmstat sl ...
- linux核心版本号的说明
日志不会很长,因为每天都在学习,我认为的重点,我自己做的记录,我很高兴能分享给大家: Linux的核心版本编号有点类似如下癿样子: 2.6.18-92.el5 主版本.次版本.释出版本-修改版本 因为 ...
- 动态替换Linux核心函数的原理和实现
转载:https://www.ibm.com/developerworks/cn/linux/l-knldebug/ 动态替换Linux核心函数的原理和实现 在调试Linux核心模块时,有时需要能够实 ...
- bootparam - 介绍Linux核心的启动参数
描叙 Linux 核心在启动的时候可以接受指定的"命令行参数"或"启动参数".在通常情况下,由于核心有可能无法识别某些硬件,或可能将某些硬件识别为不正确的配置, ...
- Cgroup maintainer丽泽范:解剖Linux核心容器技术
摘要:Cgroup和namespace等内核特性如何出现,在社区处于如何的开发状况?Docker如火如荼.内核社区是否会因此加紧完好容器技术的隔离性安全性?华为Linux内核高级project师李泽帆 ...
- Linux核心调度器之周期性调度器scheduler_tick--Linux进程的管理与调度(十八)
我们前面提到linux有两种方法激活调度器:核心调度器和 周期调度器 一种是直接的, 比如进程打算睡眠或出于其他原因放弃CPU 另一种是通过周期性的机制, 以固定的频率运行, 不时的检测是否有必要 因 ...
- (转)关于Linux核心转储文件 core dump
所谓核心转储文件是内含进程终止时内存映像的一个文件.产生条件:特定的信号会引发进程创建一个核心转储文件并终止运行. 包括哪些特定信号,请参见http://man7.org/linux/man-page ...
- Linux核心参数Shmmax,shmall,shmni
Linux 下核心参数调整 kernel.shmmax shmmax是核心参数中最重要的参数之一,用于定义单个共享内存段的最大值,shmmax设置应足够大,能在一个共享内存段下容纳下整个的SGA,设置 ...
随机推荐
- Java调用摄像头截图
使用webcam-capture替换JMF调用摄像头 最近有个需要通过java调用摄像头,并截图的需求,在网上找了下资料,大部分是用一个叫jmf的库,但是jmf已经几百年没有更新,用起来各种问题.后来 ...
- Source not found for StandardEngine(ContainerBase).initInternal() line: 1078
总是这样 在复制完一个项目,并重新起了个名字后. 再打开网页就怎么也打开不了. 第一反应是tomcat出问题了. 于是有了这样的问题: Source not found for StandardE ...
- Unix/Linux周边环境C编程新手教程(1) Solaris 11 64bit环境结构
Unix/Linux许多的版本号.我们推荐Unix/Linux刚開始学习的人选用几款典型的Unix/Linux操作系统进行学习. 本文就带大家来安装Solaris 11 64位而且配置好C/C++开发 ...
- Visual Studio Code中配置GO开发环境
在Visual Studio Code中配置GO开发环境 一.GO语言安装 详情查看:GO语言下载.安装.配置 二.GoLang插件介绍 对于Visual Studio Code开发工具,有一款优秀的 ...
- Very Deep Convolutional Networks for Large-Scale Image Recognition
Very Deep Convolutional Networks for Large-Scale Image Recognition 转载请注明:http://blog.csdn.net/stdcou ...
- 认为C/C++很难理解、找工作面试笔试,快看看这本书!
假设你是C/C++谁刚开始学习,看这本书.因为也许你读其他的书还不如不看.一定要选择一本好书. 假设你正在准备工作,请认真看这本书,由于这本书会教会你工作中必备的知识,相信你即将面临的语法类题目不会超 ...
- 面向对象的方式进行数据交换网络之间的差异--无缝切换的发展到单机游戏C/S模式
上一页本文描述描述有关数据的发展过程之间的差异支撑点,这里展示的另一个特点:无缝切换的发展,以独立C/S模式 一般C/S模式都面临一个问题: 就是开发过程中的调试难题,由于涉及到client和服务端相 ...
- 【夸QT十一】外来物品:通用脚本帮助Web运行基础Linux命令
需求分析: 需要注意的是在这里第一次,这个人是不是QT系列文章,它是关于Web的,之所以写这篇文章.这是因为碍着Web相关开发时间,而且往往涉及linux与底层指令处理.例如,创建一个文件夹,删除一个 ...
- iOS UIWebView 载入https 网站出现NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL,
今天在载入https网站的时候遇到例如以下的错误问题.所以对自己之前写的iOS内嵌webview做了一些改动,能够让它载入http网站也能够让它载入https网站. 以下是我载入https网站的时候出 ...
- Java常见问题3:周期之谜
谜24 byte是有符号的.范围是-128 - 127. 而0x90是int类型. 比較的时候.不相等. 假设想让其相等,须要进行类型转换:(byte & 0xff) 或者 (byte)0x9 ...