先看某主板的时钟dts描述,如
1.1固定时钟 fixed-clock
ext_26m: ext-26m {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <26000000>;
clock-output-names = "ext_26m";
};
ext_6m5: ext-6m5 {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <6500000>;
clock-output-names = "ext_6m5";
};
该主板有一个外接的26M晶振,ext_26m即外部的26M晶振,有一个固定的ext_6m5时钟,应该是从ext_26m分频而来。
1.2固定因子时钟 fixed-factor-clock
ext_26m: ext-26m {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <26000000>;
clock-output-names = "ext_26m";
};
clk_twpll: clk@40353004 {
compatible = "sprd,sc9863a-adjustable-pll-clock";
#clock-cells = <0>;
reg = <0x0 0x40353004 0x0 0x4>,
<0x0 0x40353008 0x0 0x4>,
<0x0 0x4035300c 0x0 0x4>;
clocks = <&ext_26m>;
clock-output-names = "clk_twpll";
};
clk_twpll_12m: clk-twpll-12m {
compatible = "fixed-factor-clock";
#clock-cells = <0>;
clock-mult = <1>;
clock-div = <128>;
clocks = <&clk_twpll>;
clock-output-names = "clk_twpll_12m";
};
clk_twpll_768m: clk-twpll-768m {
compatible = "fixed-factor-clock";
#clock-cells = <0>;
clock-mult = <1>;
clock-div = <2>;
clocks = <&clk_twpll>;
clock-output-names = "clk_twpll_768m";
};
由某个时钟固定分频而来,
clock-div = <128>;父时钟分频128而来
clocks = <&clk_twpll>,clk_twpll_12m来源于clk_twpll,而clk_twpll来源ext_26m
1.3muxed-clock混合时钟(可从多个时钟中选一个父时钟)
如clk_pwm0其可选的父时钟是ext_32k,clk_rpll_26m,&ext_26m,clk_twpll_48m
clk_pwm0: clk@402d023c {
compatible = "sprd,muxed-clock";
#clock-cells = <0>;
reg = <0x0 0x402d023c 0x0 0x4>;
sprd,mux-msk = <0x3>;
clocks = <&ext_32k>, <&clk_rpll_26m>, <&ext_26m>,
<&clk_twpll_48m>;
clock-output-names = "clk_pwm0";
};
1.4composite-clock适配时钟(可从多个时钟中选一个父时钟,并对其进行分频)
如clk_aux0其时钟来源可选(clk_set_parent)
clocks = <&ext_32k>, <&clk_rpll_26m>, <&ext_26m>;
sprd,div-msk = <0xf00>;代表其可以进行1到16的分频
clk_aux0: clk@402d022c {
compatible = "sprd,composite-clock";
#clock-cells = <0>;
reg = <0x0 0x402d022c 0x0 0x4>;
sprd,mux-msk = <0x3>;
sprd,div-msk = <0xf00>;
clocks = <&ext_32k>, <&clk_rpll_26m>, <&ext_26m>;
clock-output-names = "clk_aux0";
};
1.5gates-clock门时钟(对时钟进行开关,如aux0_clk的开关时钟就是<&clk_aon_apb_gates1 2)
clk_aon_apb_gates1: clk@402e0004 {
compatible = "sprd,sc1000-gates-clock";
#clock-cells = <1>;
reg = <0x0 0x402e0004 0x0 0x3000>;
sprd,gates-msk = <0xfa7e7fbf>;
clocks = <&clk_aon_apb>;
clock-output-names = "pmu_eb", "thm_eb", "aux0_eb",
"aux1_eb", "aux2_eb", "probe_eb",
"clk_emc_ref_eb", "ca53_wdg_eb",
"ap_tmr1_eb", "ap_tmr2_eb", "disp_emc_eb",
"zip_emc_eb", "gsp_emc_eb", "mm_vsp_eb",
"mdar_eb", "rtc4m0_cal_eb",
"rtc4m1_cal_eb", "djtag_eb", "mbox_eb",
"aon_dma_eb", "aon_apb_def_eb", "orp_jtag_eb",
"dbg_eb", "dbg_emc_eb", "cross_trig_eb",
"serdes_dphy_eb";
};
1.6使用例子
sec-nfc@27 {
compatible = "sec-nfc";
clock-names = "clk_aux0","source","enable";
clocks = <&clk_aux0>,<&ext_26m>,<&clk_aon_apb_gates1 2>;
};
struct clk clk_aux0,clk_parent,clk_enable;
clk_aux0 = devm_clk_get(dev, "clk_aux0");
if (IS_ERR(clk_aux0)){
pr_err("can't get nfc clock dts config: clk_aux0\n");
return -1;
}
clk_parent = devm_clk_get(dev, "source");
if (IS_ERR(clk_parent)) {
pr_err("can't get nfc clock dts config: source\n");
return -1;
}
clk_set_parent(clk_26m, clk_parent);
ret=clk_set_rate(clk_26m,2600000);
clk_enable = devm_clk_get(dev, "enable");
if (IS_ERR(clk_enable)) {
pr_err("can't get nfc clock dts config: enable\n");
return -1;
}
clk_prepare_enable(clk_aux0);
clk_prepare_enable(clk_enable)
1.7调试指南
进入时钟目录,如clk_aux0
/sys/kernel/debug/clk/clk_aux0或/d/clk/clk_aux0/
cat clk_rate可以看设置的频率
cat clk_enable_count看开关状态
如果不正常,继续检索log找原因
比如设置的时钟速率为2M,但实际上设置的速率3M,这是因为源时钟48M,可16分频,能得到的最小时钟为3M,驱动会找一个最进行的时钟频率进行配置。
1.8关键函数解析
clk_set_parent从所有父时钟中选一个(根据名字),计算第几个,然后写相应的寄存器,来选择父时钟, 同时也设置了速率就是父时钟的速率。
clk_set_parent
clk_core_set_parent
__clk_recalc_rates
clk_recalc
clk_divider_ops.recalc_rate
clk_divider_recalc_rate
divider_recalc_rate
DIV_ROUND_UP_ULL
clk_set_rate设置速率代码逻辑如下
clk_set_rate
clk_core_set_rate_nolock
clk_calc_new_rates
clk_calc_subtree
clk_recalc
clk_divider_ops.recalc_rate
clk_divider_recalc_rate
divider_recalc_rate
DIV_ROUND_UP_ULL
#define DIV_ROUND_UP_ULL(ll,d) \
({ unsigned long long _tmp = (ll)+(d)-1; do_div(_tmp, d); _tmp; })