关于修改clock freq, 在uboot和kernel中都是可以修改的,但是当在uboot中修改后,由于kernel也有修改clock freq的能力,这时就要注意kernel可能会覆盖uboot中已经设置好的频率。
所以最好的办法还是在kernel中进行clock的配置。
而在kernel中配置时钟的方法据我现在所知又有两种,一种是在DTS里设置,另一种则是用代码设置,了解到这些以后会发现修改时钟频率其实是一种比较简单的事情,前提是分清楚这些方法,避免重复设置。
1,DTS方式
这种方式我在这里就直接放上官方文档和其翻译,具体位置在
Documentation/devicetree/bindings/clock/clock-bindings.txt
译:
该绑定是一个进行中的工作,它基于benh [1]的一些实验工作。
时钟信号源可以由设备树中的任何节点表示。 这些节点被指定为时钟提供者。 时钟使用者节点使用虚拟机和时钟说明符对连接时钟提供者输出到时钟输入。 与gpio说明符相似,时钟说明符是一个零数组,一个或多个单元标识一个设备上的时钟输出。 时钟说明符的长度由时钟提供程序节点中的#clock-cells属性的值定义。
译:
== 时钟提供者 ==
必需的属性:
#clock-cells:时钟说明符中的单元数;通常,对于具有单个时钟输出的节点为0,对于具有多个时钟输出的节点通常为1。
可选属性:
#clock-output-names:建议是由时钟说明符中从第一个节点开始索引的时钟输出信号名称的字符串列表。
但是,clock-output-names的含义是特定于时钟提供者的域,并且仅用于鼓励大多数时钟提供者使用相同的含义。此格式可能不适用于使用复杂时钟说明符格式的时钟提供程序。在这种情况下,建议忽略此属性并创建绑定特定名称的属性。
时钟使用者节点绝对不能直接引用提供者的clock-output-names属性。
例如:
oscillator{
#clock-cells = <1>;
clock-output-names =“ ckil”,“ ckih”;
};
-该节点定义了一个具有两个时钟输出的设备,第一个名为“ ckil”,第二个名为“ ckih”。 消费者节点始终按索引引用时钟。 名称应反映设备的时钟输出信号名称。
clock-indices:如果节点中时钟的标识号不是从零线性开始的,则这允许将标识符映射到clock-output-names数组中。
例如,如果我们有两个时钟<&oscillator 1>和<&oscillator 3>:
oscillator {
compatible = “myclocktype”;
#clock-cells = <1>;
clock-indices = <1>, <3>;
clock-output-names = “clka”, “clkb”;
}
这样可以确保我们在clock-output-names中没有任何空字符串
译:
== 时钟使用者 ==
必需的属性:
clocks:一对phandle和时钟说明符对(即&clk),每个输入到设备的时钟一对。 注意:如果时钟提供者将#clock-cells指定为“ 0”,则仅会出现该对中的phandle部分。
可选属性:
clock-names:以与clocks属性相同的顺序排序的时钟输入名称字符串的列表。 使用者驱动程序将使用时钟名称来将时钟输入名称与时钟说明符进行匹配。
clock-ranges:空属性表示子节点可以从该节点继承命名的时钟。 对于总线节点向其子节点提供时钟非常有用。
例如:
device {
clocks = <&osc 1>, <&ref 0>;
clock-names = "baud", "register";
};
这表示具有两个时钟输入的设备,分别称为"baud"和"register"。
波特时钟连接到&osc1时钟,寄存器时钟连接到&ref0时钟。
例如:
/* external oscillator */
osc: oscillator {
compatible = "fixed-clock";
#clock-cells = <1>;
clock-frequency = <32678>;
clock-output-names = "osc";
};
/* phase-locked-loop device, generates a higher frequency clock
* from the external oscillator reference */
pll: pll@4c000 {
compatible = "vendor,some-pll-interface"
#clock-cells = <1>;
clocks = <&osc 0>;
clock-names = "ref";
reg = <0x4c000 0x1000>;
clock-output-names = "pll", "pll-switched";
};
/* UART, using the low frequency oscillator for the baud clock,
* and the high frequency switched PLL output for register
* clocking */
uart@a000 {
compatible = "fsl,imx-uart";
reg = <0xa000 0x1000>;
interrupts = <33>;
clocks = <&osc 0>, <&pll 1>;
clock-names = "baud", "register";
};
该DT片段定义了三个设备:一个提供低频参考时钟的外部振荡器,一个产生更高频率时钟信号的PLL设备以及一个UART。
**振荡器为固定频率,并提供一个时钟输出,称为“ osc”。
**PLL既是时钟提供者又是时钟消耗者。 它使用外部振荡器产生的时钟信号,并提供两个输出信号(“ pll”和“ pll-switched”)。
**UART的波特率时钟连接到外部振荡器,寄存器时钟连接到PLL时钟(“ PLL切换”信号)
译:
== 分配的父时钟和速率 ==
某些平台可能需要初始配置默认的父时钟和时钟频率。 可以在设备树节点中通过已分配的时钟,已分配的时钟父对象和已分配的时钟速率属性指定这种配置。 Assigned-clock-parents属性应包含一对以phandle和clock标识符对形式的父时钟,而assigned-clock-rates属性应包含以Hz为单位的频率列表。 这两个属性都应对应于assigned-clocks属性中列出的时钟。
要跳过设置时钟的父级或时钟速率,应将其相应的条目设置为0,如果后面没有任何非零条目,则可以将其省略。
uart@a000 {
compatible = "fsl,imx-uart";
reg = <0xa000 0x1000>;
...
clocks = <&osc 0>, <&pll 1>;
clock-names = "baud", "register";
assigned-clocks = <&clkcon 0>, <&pll 2>;
assigned-clock-parents = <&pll 2>;
assigned-clock-rates = <0>, <460800>;
};
在此示例中,<&pll 2>时钟被设置为时钟<&clkcon 0>的父级,并且<&pll 2>时钟被分配了460800 Hz的频率值。
通过使用该时钟的设备节点配置时钟的父级和速率只能对具有单个用户的时钟进行。 禁止在多个使用方节点中为共享时钟指定冲突的父级或速率配置。
可以在时钟提供程序节点中类似地指定影响多个用户设备的公共时钟配置。
2,在clock驱动中代码修改
在代码中就比较简单如下:
imx_clk_set_parent(clk[IMX6QDL_CLK_VPU_AXI_SEL], clk[IMX6QDL_CLK_PLL2_PFD2_396M]);
imx_clk_set_rate(clk[IMX6QDL_CLK_VPU_AXI_PODF], 396000000);
但是这种方法不太灵活,相比较而言还是推荐修改DTS的方法。
既然你已经看到这里了,再说几个我的发现:
1, PLL是在uboot里设好的,也就是说在kernel boot之前这些pll源要准备好。
2, 可变频率的pll其实在uboot中已经固定了,也就是说,可变只是kernel启动之前可变
3, source clock的频率虽说可以是待设频率的整数倍,但是也要注意,source clock不是随便设的,要和clock parent table 对应起来。