OMAP4平臺設置時鐘頻率

         新到了一個硬件,需要16.667Mhz的時鐘頻率。目前不想加外置電路,儘量把成本降到最低。採用配置時鐘的方式,可以分頻得到想要的時鐘。

         FREF_CLK1_OUT/GPIO_181/SAFE_MODE 這個引腳是複用的,設爲MODE0可以作爲時鐘源,只需要配置相應的寄存器,就可以完成,這裏不再贅述。

       硬件上決定了該時鐘源受auxclk1控制,現在的工作就是配置auxclk1的頻率了。但是按照目前的配置,並不能得到我們想要的頻率。所以需要設置下父時鐘的頻率,並改變auxclk1的父時鐘。

auxclk1的定義如下

static struct clk auxclk1_ck = {
    .name       = "auxclk1_ck",
    .parent     = &auxclk1_src_ck,//父時鐘
    .clksel     = auxclk1_sel,
    .clksel_reg = OMAP4_SCRM_AUXCLK1,
    .clksel_mask    = OMAP4_CLKDIV_MASK,
    .ops        = &clkops_null,
    .recalc     = &omap2_clksel_recalc,
    .speculate  = &omap2_clksel_speculate,
    .round_rate = &omap2_clksel_round_rate,
    .set_rate   = &omap2_clksel_set_rate,
};


static const struct clksel auxclk1_sel[] = {
    { .parent = &auxclk1_src_ck, .rates = div16_1to16_rates },
    { .parent = NULL },
};

這裏看到auxclk1_ck的父時鐘沒得選了,只能有一個,就是auxclk1_src_ck,既然這樣,就改變auxclk1_src_ck的父時鐘吧。

但是注意哦,auxclk1_ck可以對父時鐘進行最多16分頻得到自己的時鐘,也就是說可以1/1, 1/2, 1/3,... 1/15, 1/16 auxcl1_src_ck的頻率。

static struct clk auxclk1_src_ck = {
    .name       = "auxclk1_src_ck",
    .parent     = &sys_clkin_ck,// 當前parent, 需要改掉
    .init       = &omap2_init_clksel_parent,
    .ops        = &clkops_omap2_dflt,
    .clksel     = auxclk_src_sel,
    .clksel_reg = OMAP4_SCRM_AUXCLK1,
    .clksel_mask    = OMAP4_SRCSELECT_MASK,
    .recalc     = &omap2_clksel_recalc,
    .speculate  = &omap2_clksel_speculate,
    .enable_reg = OMAP4_SCRM_AUXCLK1,
    .enable_bit = OMAP4_ENABLE_SHIFT,
};

static const struct clksel auxclk_src_sel[] = {
    { .parent = &sys_clkin_ck, .rates = div_1_0_rates },
    { .parent = &dpll_core_m3x2_ck, .rates = div_1_1_rates },
    { .parent = &dpll_per_m3x2_ck, .rates = div_1_2_rates },
    { .parent = NULL },
};

這裏就是我們要選擇的父時鐘的選項了,可以從這3個裏面任意選擇一個時鐘作爲auxclk1_src_ck的父時鐘,當前選擇了第一個。我們可以用clk_set_parent修改爲第二個時鐘。這樣auxclk1_src_ck的時鐘就和dpll_core_m3x2_ck的時鐘頻率相同了。

           別忘了我們想要得到的是16.667Mhz,也就是100/6, 或者200/12。dpll_core_m3x2_ck的頻率,從名字上也可以看出來,默認應該是320Mhz,顯然不是我們想要的。還好,我們還有個選擇,可以將頻率設死在200Mhz.

--- a/arch/arm/mach-omap2/clock44xx_data.c
+++ b/arch/arm/mach-omap2/clock44xx_data.c
@@ -3895,7 +3895,8 @@ static int omap4_virt_l3_set_rate(struct clk *clk, unsigned long rate)
        else
                l3_deps = &omap4_virt_l3_clk_deps[L3_OPP_100_INDEX];
 
-       omap4_clksel_set_rate(&dpll_core_m3x2_ck, l3_deps->core_m3_rate);
+       //omap4_clksel_set_rate(&dpll_core_m3x2_ck, l3_deps->core_m3_rate);
+       omap4_clksel_set_rate(&dpll_core_m3x2_ck, DPLL_CORE_M3_OPP50_RATE);

這個補丁可以將頻率設爲200M,目前沒看到其他地方用dpll_core_m3x2_ck作爲時鐘或父時鐘,所以對系統其他部分應該沒有影響。

        最後,看看實現吧。

static int set_clk1_rate(void)
{
    int ret = 0;
    struct clk *free_clk1;
    struct clk *parent_clk;
    struct clk *auxclk1_src;
    unsigned long current_rate = 0;


    free_clk1 = clk_get(NULL, "auxclk1_ck");
    if (IS_ERR(free_clk1))
    {
        pr_err("Cannot request auxclk1\n");
        ret = -EINVAL;
        goto err0;
    }
    parent_clk = clk_get(NULL, "dpll_core_m3x2_ck");
    if (IS_ERR(parent_clk))
    {
        pr_err("Cannot request dpll_core_m3x2\n");
        ret = -EINVAL;
        goto err1;
    }
    auxclk1_src = clk_get(NULL, "auxclk1_src_ck");
    if (IS_ERR(auxclk1_src))
    {
        pr_err("Cannot request auxclk1_src_ck\n");
        ret = -EINVAL;
        goto err2;
    }


    ret = clk_set_parent(auxclk1_src, parent_clk);
    if (ret < 0)
    {
        pr_err("Failed to set auxclk1_src's parent clock to dpll_core_m3x2\n");
        pr_err("\nret: %d\n", ret);
        goto err2;
    }


    unsigned long parent_rate = clk_get_rate(parent_clk);
    printk("################ parent_rate: %ld\n", parent_rate);
    clk_set_rate(parent_clk, 200000000);
    parent_rate = clk_get_rate(parent_clk);
    printk("################ parent_rate: %ld\n", parent_rate);
#if 1
    ret = clk_set_rate(free_clk1, parent_rate / 12);
    clk_enable(free_clk1);
    current_rate = clk_get_rate(free_clk1);
    printk("^^^^^^^^^^^^^^^^ current_rate: %ld\n", current_rate);
#endif
err2:
    clk_put(auxclk1_src);
err1:
    clk_put(parent_clk);
err0:
    clk_put(free_clk1);


    return ret;
}

        Debug代碼比較粗糙。但是意思應該很明確了,最後要注意兩點。一是頻率值:200000000 / 12, 我原來寫成16666667,結果每次都失敗。另外,不要忘了clk_enable(free_clk1);因爲如果其他地方沒有enable它的話,該時鐘是不會啓用的。不要擔心要不要clk_enable()它的父時鐘,因爲在clk_enable(free_clk1)的時候,也會找到其父時鐘,並進行enable,因爲還要維護引用計數呢。

           關於各個實現代碼,就不貼了,內核裏都有,各個平臺應該略有差異。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章