LINUX kernel clock系统,基于内核4.4
转载需声明出处:https://blog.csdn.net/chenjiebing2016/article/details/83058816内核把驱动分为三部分,分别为为框架实现层(与具体soc平台相关)、框架层(内核自身)、驱动层(调用框架提供的api)今天,我们将建立在这个框架思想上来分析时钟系统使用过程以rk3399上的rk808 pmic为例为什么要选用这个?rk808...
转载需声明出处:https://blog.csdn.net/chenjiebing2016/article/details/83058816
内核把驱动分为三部分,分别为为框架实现层(与具体soc平台相关)、框架层(内核自身)、驱动层(调用框架提供的api)
今天,我们将建立在这个框架思想上来分析时钟系统使用过程
以rk3399上的rk808 pmic为例
为什么要选用这个?
rk808 pmic上有两个clock输出,其中一路提供给蓝牙模块,由于没有具体的soc datasheet,只能以这个来分析。具体流程框架都是一样的,只是涉及的具体平台的操作实现内容不一样。
由于是内核4.4,插讲device tree
这里只是再分析框架的同时需要我们要理解设备树的简单知识,设备树并不是很难,我们只是学习怎么使用设备树,不必深入分析
wireless-bluetooth {
compatible = "bluetooth-platdata";
clocks = <&rk808 1>;
clock-names = "ext_clock";
//wifi-bt-power-toggle;
uart_rts_gpios = <&gpio2 19 GPIO_ACTIVE_LOW>; /* GPIO2_C3 */
pinctrl-names = "default", "rts_gpio";
pinctrl-0 = <&uart0_rts>;
pinctrl-1 = <&uart0_gpios>;
//BT,power_gpio = <&gpio3 19 GPIO_ACTIVE_HIGH>; /* GPIOx_xx */
BT,reset_gpio = <&gpio0 9 GPIO_ACTIVE_HIGH>; /* GPIO0_B1 */
BT,wake_gpio = <&gpio2 26 GPIO_ACTIVE_HIGH>; /* GPIO2_D2 */
BT,wake_host_irq = <&gpio0 4 GPIO_ACTIVE_HIGH>; /* GPIO0_A4 */
status = "okay";
};
我们只分析红色部分,其他部分大家可以上谷歌百度查阅资料,或者发邮件到我的个人邮箱咨询 jiebing2016@163.com
关于红色部分怎么在内核驱动里面使用
内核有一套完整的of开头api来解析,建议多看源码,加深对device tree的理解。
以下分析使用:
我们在设备驱动代码中仅使用以下两个api即打开了对应的时钟
data->ext_clk = devm_clk_get(dev, "ext_clock");
if (IS_ERR(data->ext_clk)) {
LOG("%s: clk_get failed!!!.\n", __func__);
} else {
clk_prepare_enable(data->ext_clk);
}
我们解析来分析它的流程
"ext_clock"为设备树中 clock-names = "ext_clock";
继续追源码
dev_id为设备的名称,对应设备树节点的 wireless-bluetooth
con_id为“ext_clock”
我们继续分析__of_clk_get_by_name
of_property_match_string查找ext_clock是否在clock-names的属性中,我们从设备树中看出 clock-names的属性值为“ext_clock”
结果返回0,即index为0
index是什么意思?
它代表的是属性值的编号。
比如clock-names=“ext_clock”,"xxxx";
其中index 0为“ext_clock, index 1 为“xxxx”
继续分析__of_clk_get
这个api很重要,为什么说很重要,很多人在学习设备设备树的时候不知道
clocks = <&rk808 1>;
这个尖括号里面代表的意思
我们一起分析,第一个参数是device_node,第二个参数是设备树中的clocks ,那#clock-cells是啥?
这个是另一节点里面的属性
为什么会扯到这个节点。这就是这个api的重要性,我简单解释下这个api的作用:
clocks = <&rk808 1>;
&rk808引用的是rk808这个节点,如上图
其中#clock-cells是用来表示 clocks 这个属性参数的个数
#clock-cells=<1>;表示参数只有一个
参数值为1
那么解析出来的参数又存在哪里?
struct of_phandle_args clkspec;
struct of_phandle_args {
struct device_node *np; //引用到的节点
int args_count; //参数数量
uint32_t args[MAX_PHANDLE_ARGS];参数
};
这个参数很重要,我们暂且记住,后面时钟用到了再说
__of_clk_get_from_provider(&clkspec, dev_id, con_id, true);
继续分析
遍历of_clk_providers链表
获得struct of_clk_provider *provider;
这结构体是用来干嘛的
重点,函数指针,获得clk结构体的函数,分析到现在,终于知道了clk是怎么来的,那么,这个函数指针又是在哪被赋值的呢?
既然有遍历,就有add链表。
我们追到了这个api,这个非常关键,它将由具体平台的实现框架进行调用。
这个api即为刚才那个函数指针
我们注意到了idx,这个就是我们提到的参数
struct clk_onecell_data {
struct clk **clks;
unsigned int clk_num;
};
最后返回一个clk_data->clks[idx];,这代表是什么意思?
从函数看出它应该是一个存放clk指针的数组,取参数对应的clk
函数指针通过一下api传入
注意:回调函数由内核框架提供,固定。所以,这个of_clk_add_provider的最后一个参数void*data只能为struct clk_onecell_data 类型
clk设备注册流程:
devm_clk_register
clk_register
core->ops = hw->init->ops; //时钟操作函数赋值
hw->core = core;
clk_hw_create_clk
简单说下:
创建clk,并对其中成员进行赋值
其中的core里面的 const struct clk_ops *ops;即为时钟操作函数
讲了这里,基本结束
总结框架:
devm_clk_register返回一个时钟指针
struct clk_onecell_data {
struct clk **clks;
unsigned int clk_num;
};
返回的clk指针存放在这个结构体,设备树的参数代表取得是哪一个clk
devm_clk_get获得对应的clk,clk_prepare_enable调用对应的硬件操作函数,
具体的代码,大家可以结合自己的平台分析,框架不变。
接下来将发布:linux audio驱动框架,详细分析。敬请关注!
更多推荐
所有评论(0)