0. 背景介绍
绝大多数的电子器件都是由时钟驱动其工作的。而SoC芯片或电路板中的时钟以树状结构呈现,按时钟域进行划分,按照不同的时钟需求进行管理。
出于功耗和数据传输时序控制等目的,在内核代码中对时钟进行统一注册、统一管理。kernel代码中很早就出现了时钟管理机制,甚至早于git版本管控之前。
1. 时钟定义
ARM处理器平台中基于dts描述时钟树,包括时钟结构、时钟属性等信息。由时钟框架驱动和设备驱动解析dts中时钟树信息,完成时钟系统的初始化、管理、使用。在设备树中定义了时钟的providers
和consumers
。前者代表时钟提供者,通常是一个固定的PLL,后者代表时钟使用者,例如处理器核心以及各种外部设备等。
1.1 Clock providers
1. #clock-cells
时钟提供者输出的时钟路数,当#clock-cells
为0时,代表仅输出1路时钟,若大于等于1,则代表输出多路时钟,Clock consumers
通过编号索引使用。
下面的时钟结点clock-cells
为0,代表仅能够输出1路时钟,这里面只提供了一路24MHz的固定时钟。
xin24m: xin24m {
compatible = "fixed-clock";
clock-frequency = <24000000>;
clock-output-names = "xin24m";
#clock-cells = <0>;
};
下面的时钟结点中clock-cells
为1,代表能够输出多路时钟,这里面我们可以看出它提供了两路时钟分别是PLL_PPLL
和FCLK_CM0S_SRC_PMU
,时钟频率分别是67.6MHz和97MHz。
pmucru: pmu-clock-controller@ff750000 {
...
#clock-cells = <1>;
#reset-cells = <1>;
assigned-clocks = <&pmucru PLL_PPLL>, <&pmucru FCLK_CM0S_SRC_PMU>;
assigned-clock-rates = <676000000>, <97000000>;
};
2. clock-output-names
顾名思义,它定义了输出时钟的名字。当clock consumers
使用这路时钟的时候,我们可以见名知意。从下面的clock-output-names
可以清楚的直到,这是外部晶振提供的24MHz时钟。
xin24m: xin24m {
compatible = "fixed-clock";
clock-frequency = <24000000>;
clock-output-names = "xin24m";
#clock-cells = <0>;
};
3. clock-indices
这并不是一个必选项,当然也不常见。当存在多路输出时钟时,clock consumer
以index
引用对应的时钟,默认不指定clock-indices
且没有使用assigned-clocks
时,index
索引是线性增长的,像下面这样,ckil的index是0,而ckih的index就是1。
oscillator { #clock-cells = <1>;
clock-output-names = "ckil", "ckih"; };
当定义了clock-indices
之后,index
的值由clock-indices
决定,像下面这样:
oscillator { #clock-cells = <1>;
clock-output-names = "ckil", "ckih"; };
4. assigned-clocks
当输出多路时钟时,为没路输出时钟进行编号,以phandle
+specifier
组合进行管理,像下面这样:
pmucru: pmu-clock-controller@ff750000 {
...
assigned-clocks = <&pmucru PLL_PPLL>, <&pmucru FCLK_CM0S_SRC_PMU>;
assigned-clock-rates = <676000000>, <97000000>;
};
PLL_PPLL代表了specifier
,其定义位于头文件include/dt-bindings/clock/rk3399-cru.h
中。需要说明的是,这个头文件很重要,所有的specifier
都可以在这里找到对应的宏。
5. assigned-clock-rates
这个定义是和assigned-clocks
成对使用的。它代表了assigned-clocks
所对应的时钟频率,例如PLL_PPLL
所对应的时钟频率是67.6MHz。
pmucru: pmu-clock-controller@ff750000 {
...
assigned-clocks = <&pmucru PLL_PPLL>, <&pmucru FCLK_CM0S_SRC_PMU>;
assigned-clock-rates = <676000000>, <97000000>;
};
6. clock-frequency
当不使用assigned-clock-rates
为输出时钟指定大小时,可以利用clock-frequency
进行指定。像下面这样,指定了osc的时钟频率为32.678Khz。
osc: oscillator {
compatible = "fixed-clock";
#clock-cells = <1>;
clock-frequency = <32678>;
clock-output-names = "osc"; };
1.2 Clock consumers
Clock consumers
意为时钟使用者,通常是CPU核心部件或者其他外设。
我们以dsi为例说明Clock consumers
设备树的组成。
vopb: vop@ff900000 {
compatible = "rockchip,rk3399-vop-big";
reg = <0x0 0xff900000 0x0 0x600>,
<0x0 0xff901c00 0x0 0x200>,
<0x0 0xff902000 0x0 0x1000>;
reg-names = "regs", "cabc_lut", "gamma_lut";
interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>, <&cru HCLK_VOP0>, <&cru DCLK_VOP0_DIV>;
clock-names = "aclk_vop", "dclk_vop", "hclk_vop", "dclk_source";
...
};
1. clocks
它代表了设备的时钟源,通常以phandle
+specifier
组合进行引用。例如本例中,aclk_vop
时钟使用的是cru
模块提供的ACLK_VOP0
,它的时钟频率在cru结点中定义,大小是400000000
。
2. clock-names
这代表了Clock consumers
中使用的时钟名字,方便设备驱动代码进行相应的时钟解析,例如:
static int vop_bind(struct device *dev, struct device *master, void *data)
{
...
vop->aclk = devm_clk_get(vop->dev, "aclk_vop");
if (IS_ERR(vop->aclk)) {
dev_err(vop->dev, "failed to get aclk source\n");
return PTR_ERR(vop->aclk);
}
...
}
以上是ARM平台RK3399的软件时钟树定义方式,具体可查看rk3399.dtsi
文件。除此之外,可参考如下内核文档做相关了解。
Documentation/evicetree/indings/lock/lock-bindings.txt