2016/3/2

imx6 CLK.

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", }


沒有留言:

張貼留言