博客:fireaxe.blog.chinaunix.net

根据蜗窝科技相关文档整理

1. clock硬件结构

如今,可运行Linux的主流处理器平台,都有非常复杂的clock tree,我们随便拿一个处理器的spec,查看clock相关的章节,一定会有一个非常庞大和复杂的树状图,这个图由clock相关的器件,以及这些器件输出的clock组成。下图是一个示例:

204e718feeb95bc0956fb578f42a06fd.png

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

structclk *clk_register(structdevice *dev,structclk_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;

u8num_parents;

unsigned longflags;

};

其中的name、parent_names、num_parents都来自于DTS文件,文件格式如下:

hw3_clk: hw3_clk@01c2005c {

#clock-cells = <1>;

compatible = "allwinner,sun4i-axi-gates-clk";

reg = <0x01c2005c 0x4="">;

clocks = ;

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 = , ;

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

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐