博客:fireaxe.blog.chinaunix.net
根据蜗窝科技相关文档整理
1. clock硬件结构
如今,可运行Linux的主流处理器平台,都有非常复杂的clock tree,我们随便拿一个处理器的spec,查看clock相关的章节,一定会有一个非常庞大和复杂的树状图,这个图由clock相关的器件,以及这些器件输出的clock组成。下图是一个示例:
clock相关的器件包括:用于产生clock的Oscillator(有源振荡器,也称作谐振荡器)或者Crystal(无源振荡器,也称晶振);用于倍频的PLL(锁相环,Phase Locked Loop);用于分频的divider;用于多路选择的Mux;用于clock enable控制的与门;使用clock的硬件模块(可称作consumer);等等。
common clock framework的管理对象,就是上图蓝色字体描述的clock(在软件中用struct clk抽象,以后就简称clk),主要内容包括(不需要所有clk都支持):
1)enable/disable clk。
2)设置clk的频率。
3)选择clk的parent,例如hw3_clk可以选择osc_clk、pll2_clk或者pll3_clk作为输入源。
2. 代码实现
管理clock的最终目的,是让device driver可以方便的使用,这些是通过include/linux/clk.h中的通用API实现的,如下:
1)struct clk结构
一个系统的clock tree是固定的,因此clock的数目和用途也是固定的。假设上面图片所描述的是一个系统,它的clock包括osc_clk、pll1_clk、pll2_clk、pll3_clk、hw1_clk、hw2_clk和hw3_clk。我们完全可以通过名字,抽象这7个clock,进行开/关、rate调整等操作。但这样做有一个缺点:不能很好的处理clock之间的级联关系,如hw2_clk和hw3_clk都关闭后,pll2_clk才能关闭。因此就引入struct clk结构,以链表的形式维护这种关系。
同样的道理,系统的struct clk,也是固定的,由clock driver在系统启动时初始化完毕,需要访问某个clock时,只要获取它对应的struct clk结构即可。怎么获取呢?可以通过名字索引啊!很长一段时间内,kernel及driver就是使用这种方式管理和使用clock的。
最后,设备(由struct device表示)对应的clock(由struct clk表示)也是固定的啊,可不可以找到设备就能找到clock?可以,不过需要借助device tree。
2) 注册driver
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
用于clock driver向ccf(clock common framework)注册,驱动需要实现的是hw结构体中的init结构体(clk_init_data):
struct clk_init_data {
const char *name;
const struct clk_ops *ops;
const char * const *parent_names;
u8 num_parents;
unsigned long flags;
};
其中的name、parent_names、num_parents都来自于DTS文件,文件格式如下:
hw3_clk: hw3_clk@01c2005c {
#clock-cells = <1>;
compatible = "allwinner,sun4i-axi-gates-clk";
reg = <0x01c2005c 0x4="">;
clocks = <&pll3_clk>;
clock-output-names = "hw3_clk";
};
clocks 对应着父节点,以图中为例,hw3_clk的父节点是pll3_clk。clock-output-names就是init中的name。
ops对应着驱动对实际硬件的各种操作,也是驱动编写时的主要工作。对于很多系统中,时钟没那么麻烦,可能外设只能使用一个固定频率的时钟(叫做fixed clock),驱动编写时就只需要实现ops里的get_rate了。
2) 设备驱动使用时钟
首先是要获得clk结构体的指针,这个是根据DTS中的信息设置的:
1: /* DTS */
2: device {
3: clocks = <&hw1_clk>, <&osc_clk 0>;
4: clock-names = "baud", "register";
5: };
图中的clocks表示使用的是哪个时钟,clock-name是dts给他们起的名字。驱动中利用名字找到对应的时钟,如使用devm_get_clock("baud"),会得到执行hw1_clk的指针,后面就可以利用该指针进行相应的操作了。一般只需要获得对应时钟的频率就可以了,如串口需要通过时钟的频率计算如何设置baud率。
本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。
作者:fireaxe.hq@outlook.com
博客:fireaxe.blog.chinaunix.net