4.3.2的ZYNQ的时钟驱动架构和原理解析 - FPGA/ASIC技术 -
在查看zynq的clk时钟驱动时,在源码文件clkc.c中我们看到匹配属性字段”xlnx,ps7-clkc”,该字段匹配zynq-7000.dtsi的时钟子节点的compaTIble关键字属性相匹配,时钟的setup函数为zynq_clk_setup,查看整个源码包没有发现有调用该函数的痕迹,但是发现该函数被宏CLK_OF_DECLARE引用了。
首先看一下CLK_OF_DECLARE宏,它的定义位于“include/linux/clk-provider.h”中,负责在指定的secTIon中(以__clk_of_table开始的位置),定义structof_device_id类型的变量,并由of_clk_init(在函数 zynq_TImer_init (mach-zynq/common.c)中被调用)接口解析、匹配,如果匹配成功,则执行相应的回调函数(这里为of_fixed_clk_setup);初始化的时候,device tree负责读取DTS,并和这些变量的名字(这里为" xlnx,ps7-clkc ")匹配,如果匹配成功,则执行相应的回调函数(这里为of_fixed_clk_setup);of_fixed_clk_setup会解析两个DTS字段"clock-frequency"和"clock-output-names",然后调用clk_register_fixed_rate,注册clock。注意,注册时的flags为CLK_IS_ROOT,说明目前只支持ROOT类型的clock通过DTS注册;最后,调用of_clk_add_provider接口,将该clock添加到provider_list中,方便后续的查找使用。该接口会在后面再详细介绍。
在of_clk_init被调用之前,zynq_TImer_init 先调用了zynq_clock_init函数,该函数实现功能如下:
1. 根据字段“compatible”匹配“xlnx,ps7-clkc”判断节点np是否存在
2. 判断np是否有地址内存定义
3. 判断np是否存在父节点slcr
4. 将节点np的物理地址赋值给全局变量zynq_clkc_base,该变量是void *指针,并通过__iomem修饰,强制定义链接区域
5. 通过of_node_put函数将np和slcr的refcount减1,我们查到of_node_put函数的说明,但我发现它调用了kobject_put,该函数简要说明如下:当一个kobject对象的引用ref被减少到0时,程序就会释放这个kobject相关的资源,所以在减少引用的函数中就应该有调用释放资源的相关代码,在下面内核代码中也可以看到。
下面我们正式来看下函数of_clk_init,该函数被调用时传递进来的参数matches为NULL,该函数具体实现了以下功能:
1. 初始化全局链表clk_provider_list,
2. 如果matches为空,就把__clk_of_table的地址赋值给matches,对应我们上面谈到的宏CLK_OF_DECLARE
3. 通过宏for_each_matching_node_and_match来捕获matches指向的of_device_id型指针数组中所有成员,当捕获数组成员时,执行操作如下:
3.1. 创造一个clock_provider对象
3.2. 把捕获道德数组成员的data和np指针分别赋值给clock_provider对象的clk_init_cb和np成员
3.3. 最后把clock_provider对象添加到全局链表clk_provider_list中
4. 判断如果clk_provider_list不为空,执行如下操作:
4.1. 遍历并去除该链表中的所有成员,并通过宏定义获取包含该成员的对象的指针,对象为clock_provider
4.2. 判断是否是否强制处理,一般我们都处理所有节点,然后判断clock_provider中np的父节点是否能使用,如果以上判断成立,执行以下操作:
4.2.1 通过调用clk_provider->clk_init_cb(clk_provider->np),初始化clock_provider中的时钟节点,具体函数为zynq_clk_setup,此处不做具体讨论
4.2.2 接着对父节点和子节点做时钟匹配(暂时不理解)
4.2.3 摧毁该节点和对象clock_provider
我在此处有一个疑问,为什么大费周章的去创造和销毁对象clock_provider,为什么不直接处理?希望有读者来解答一下。
接下来看到函数zynq_clk_setup,由上面看到我们把节点指针np传递了进去,具体实现功能如下:
1. 取出np中所有对象clock-output-names的数字中的字符串的指针,这些都是时钟的名字,在设备树文件zynq-7000.dtsi中被定义
2. 构建系统时钟树,具体如下,先看图:
由图中不难看出,ps_clk进来以后直接连接了3个时钟锁相环,分别是:ARM PLL、I/O PLL、 DDR PLL,其他所有的时钟如CPU时钟和外设时钟,都是从这几个模块中输出的,也就是为什么,会有代码cpu_parents[0] = clk_output_name[armpll]等的原因了,至于为什么有些时钟作为时钟源使用了2次,比如ARM PLL,这个时钟除了给CPU提供时钟以外,还给内部互联接口提供时钟,所以引用了2次;而I/O PLL不仅负责PS端的I/O设备,还负责PL部分I/O设备,所以也使用了2次。
3. 接着取出fclk-enable和ps-clk-frequency的32位整型值,其中ps_clk的频率为33.333333MHz,也可以从原理图来验证这一点
查看评论 回复