Linux clock tree 的实现
1. Linux 里已经预定义了几种 clock
- clk-fixed-rate: basic fixed-rate clock that cannot gate, 比如固定频率的 oscillator
- clk-fixed-factor: basic fixed multiplier and divider clock that cannot gate,比如从上一级按固定比例分频
- clk-divider: basic adjustable divider clock that cannot gate,用于分频
- clk-composite,
- clk-gate: basic gatable clock which can gate and ungate it's ouput
- clk-mux: basic adjustable multiplexer clock that cannot gate,用于多路选择
2. 实现一个 clock 的驱动
对于厂商自己的 clock,上面这些预定义的就没法直接用了,需要添加驱动来支持。实现过程大概是这样的:
比如,添加 drivers/clk/clk-<vendor>.c
实现:
// 定义自己的 clock 结构,比如
struct my_clk {
struct clk_hw hw;
// 寄存器以及其他实现相关的内容
...
}
<vendor>_clk_init()
{
// 初始化 clk_init_data,包括 name、parent_names、clk_ops 等等
struct clk_init_data init;
...
// 注册 clock
clk_hw_register(..., &my_clk->hw);
}
其中,clock 的 name 以及 parent_names,决定了在 clock tree 里的具体位置。
而在 clk_ops 回调里,需要实现:
// 查询硬件寄存器的信息,计算 clock 的频率
unsigned long (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate);
// 返回能支持的最接近的频率
long (*round_rate)(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate);
// 把频率设置到硬件寄存器
int (*set_rate)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate);
这样就完成了 clock 的封装。对于依赖这个 clock 的设备的驱动来说,只需要通过 Linux 里标准的 clk_xxx API 来访问,而不需要关心 clock 的实现细节,比如 clk_get_rate() 获取时钟频率,等等。
3. 使用 clock
在使用的时候,有多种方式,比如:
(1) 在 dts 里定义,比如:
osc: oscillator {
compatible = "fixed-clock";
clock-frequency = <24000000>;
...
};
mmc_clk: mmc_clk@12340000 {
compatible = "vendor,mmc-clock";
reg = <0x12340000 0x100>;
clocks = <&osc>;
...
};
这里定义了两个 clock。一个是 24MHz 固定频率的 osc,另一个是 mmc_clk,由刚才实现的 clock 代码来驱动,并且是以 osc 为 parent input。
这样,在具体设备的驱动里,就可以引用这个 clock 了,比如:
mmc0: mmc@56780000 {
clocks = <&mmc_clk>;
...
};
(2) 定义在驱动实现里 :
对应的 drivers/clk/clk-<vendor>.c
文件里,除了实现 clock 的驱动,另外也要定义 clock tree:
<vendor>_clk_init()
{
clk_hw *hws;
hws[xx] = clk_hw_register_fixed_rate(NULL, "osc", NULL, 0, 24000000);
hws[yy] = vendor_clk_hw_register("mmc_clk", ..., "osc");
...
// 可选,如果导出,可以从 clk_get_sys(..., "mmc_clk") 查询到
clk_hw_register_clkdev(..., "mmc_clk", ...);
}
其中, xx、yy 定义了对应 clock 的 dt-bindings,通常在 include/dt-bindings/clock/
目录下。
这时,对应的 dts 是这样引用:
clks: clock-controller {
#clock-cells = <1>;
compatible = "vender,xxx";
...
};
mmc0: mmc@56780000 {
clocks = <&clks yy>;
...
};
4. 查看 clock
可以通过 debugfs,查看系统里的 clock tree,比如:
mount -t debugfs debugfs /proc/sys/debug
cd /proc/sys/debug/clk
cat clk_summary
可以看到每个 clock 的名字,当前频率,以及相互之间的关系等等信息:
enable prepare protect duty hardware
clock count count count rate accuracy phase cycle enable
---------------------------------------------------------------------------------------
osc 2 2 0 24000000 0 0 50000 Y
mmc_clk 1 1 0 30000000 0 0 50000 Y
...
参考资料
- linux/drivers/clk/*.c
- Documentation/devicetree/bindings/clock/clock-bindings.txt
- http://www.wowotech.net/pm_subsystem/clk_overview.html