新到了一個硬件,需要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,因爲還要維護引用計數呢。
關於各個實現代碼,就不貼了,內核裏都有,各個平臺應該略有差異。