struct clk 宣告在:include/linux/clk-private.h
裡面除了 struck clk 外,還定義了:
DEFINE_CLK
DEFINE_CLK_FIXED_RATE
DEFINE_CLK_GATE
DEFINE_CLK_DIVIDER
DEFINE_CLK_DIVIDER_TABLE
DEFINE_CLK_MUX
DEFINE_CLK_FIXED_FACTOR
但是這些imx 都沒用...
是在arch/arm/mach-imx/clk-imx6q.c 中宣告,生成。
CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init);
device tree 宣告在: imx6qdl.dtsi:
clks: ccm@020c4000 {
compatible = "fsl,imx6q-ccm";
reg = <0x020c4000 0x4000>;
interrupts = <0 87 0x04 0 88 0x04>;
#clock-cells = <1>;
};
在 Documentation/devicetree/bindings/clock/imx6q-clock.txt 有清楚的說明:
每個使用clk 的裝置(consumer) 都要在 device tree 中指名使用到的 clk id
imx bsp 把所有的 clk 排列在一起,並且給每個clk 一個index
Clock ID
---------------------------
dummy 0
ckil 1
ckih 2
osc 3
pll2_pfd0_352m 4
pll2_pfd1_594m 5
pll2_pfd2_396m 6
pll3_pfd0_720m 7
pll3_pfd1_540m 8
pll3_pfd2_508m 9
pll3_pfd3_454m 10
pll2_198m 11
pll3_120m 12
pll3_80m 13
pll3_60m 14
twd 15
step 16
pll1_sw 17
periph_pre 18
periph2_pre 19
periph_clk2_sel 20
periph2_clk2_sel 21
axi_sel 22
esai_sel 23
spdif1_sel 24
spdif_sel 25
...
這個表其實宣告在 clk-imx6q.c 的 enum mx6q_clks {}
系統clock 的 device node:
Examples:
clks: ccm@020c4000 {
compatible = "fsl,imx6q-ccm";
reg = <0x020c4000 0x4000>;
interrupts = <0 87 0x04 0 88 0x04>;
#clock-cells = <1>;
};
以 serial port (UART) 為例,他使用兩種clk, ipg, serial, 所以 device node 是:
uart1: serial@02020000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x02020000 0x4000>;
interrupts = <0 26 0x04>;
clocks = <&clks 160>, <&clks 161>;
clock-names = "ipg", "per";
status = "disabled";
};
idex 分別是 160, 161
clock 的 hierachy 是寫在 clk-imx6q.c:
imx6q_clocks_init( ):
約略是由上而下...
clk[dummy] = imx_clk_fixed("dummy", 0);
clk[ckil] = imx_obtain_fixed_clock("ckil", 0);
clk[ckih] = imx_obtain_fixed_clock("ckih1", 0);
clk[osc] = imx_obtain_fixed_clock("osc", 0);
/* Clock source from external clock via ANACLK1/2 PADs */
clk[anaclk1] = imx_obtain_fixed_clock("anaclk1", 0);
clk[anaclk2] = imx_obtain_fixed_clock("anaclk2", 0);
ckil : external low freq
ckih : external high freq clock and internal oscillator
osc : the 24MHz
anaclk1, anaclk2 : another external osc in
這幾個 最源頭的 clk 都定義在 dts 中。
最後系統的 clk hierachy, 在 debugfs 中可以看到:
wondboard:
root@wandboard:/sys/kernel/debug/clk # busybox find . -name 'esai*'
./osc/pll2_bus/pll2_pfd2_396m/periph_pre/periph/ahb/esai_ipg
./osc/pll2_bus/pll2_pfd2_396m/periph_pre/periph/ahb/esai_mem
./osc/pll4_sel/pll4_audio/pll4_post_div/pll4_audio_div/esai_sel
./osc/pll4_sel/pll4_audio/pll4_post_div/pll4_audio_div/esai_sel/esai_pred
./osc/pll4_sel/pll4_audio/pll4_post_div/pll4_audio_div/esai_sel/esai_pred/esai_podf
./osc/pll4_sel/pll4_audio/pll4_post_div/pll4_audio_div/esai_sel/esai_pred/esai_podf/esai_extal
sabreauto:
root@sabreauto_6q:/sys/kernel/debug/clk # busybox find . -name 'esai*'
./osc/pll2_bus/pll2_pfd2_396m/periph_pre/periph/ahb/esai_ipg
./osc/pll2_bus/pll2_pfd2_396m/periph_pre/periph/ahb/esai_mem
./anaclk2/lvds2_in/pll4_sel/pll4_audio/pll4_post_div/pll4_audio_div/esai_sel
./anaclk2/lvds2_in/pll4_sel/pll4_audio/pll4_post_div/pll4_audio_div/esai_sel/esai_pred
./anaclk2/lvds2_in/pll4_sel/pll4_audio/pll4_post_div/pll4_audio_div/esai_sel/esai_pred/esai_podf
./anaclk2/lvds2_in/pll4_sel/pll4_audio/pll4_post_div/pll4_audio_div/esai_sel/esai_pred/esai_podf/esai_extal
因為 mach-imx6q.c 的 ..
static void __init imx6q_audio_lvds2_init(void)
{
struct clk *pll4_sel, *lvds2_in, *pll4_audio_div, *esai_extal;
printk("%s\n",__func__);
pll4_audio_div = clk_get_sys(NULL, "pll4_audio_div");
pll4_sel = clk_get_sys(NULL, "pll4_sel");
lvds2_in = clk_get_sys(NULL, "lvds2_in");
esai_extal = clk_get_sys(NULL, "esai_extal");
if (IS_ERR(pll4_audio_div) || IS_ERR(pll4_sel) ||
IS_ERR(lvds2_in) || IS_ERR(esai_extal))
return;
if (clk_get_rate(lvds2_in) != ESAI_AUDIO_MCLK)
return;
clk_set_parent(pll4_sel, lvds2_in);
clk_set_rate(pll4_audio_div, 786432000);
clk_set_rate(esai_extal, ESAI_AUDIO_MCLK);
}
把 pll4_sel 的 parent 改到 lvds2_in
pll4_sel 的 parent 可以是:
{ "osc", "lvds1_in", "lvds2_in", "dummy", }