clock子系統的總結

工作中接觸到的clock代碼的學習和總結。clock子系統。閱讀了工作中的代碼,參考了http://www.wowotech.net/tag/clock 裏的一系列文章。

clock設備,包括root clock,如外部晶振;PLL,倍頻; divider, 分頻; gate , 只有開和關兩種,還有mux,從多個上級clock輸入中選擇其中一種。

工作代碼,clock設備沒有放在dts中,只是把兩個clock的基地址放在了dts裏,clock的配置信息以結構體數組的table形式,放在了源代碼中。

在項目自己的clk.c文件中,
有一個static struct clk **clk_table; 
clock系統初始化時
clk_data.clks = clk_table;
clk_data.clk_num = nr_clks;
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
會把這個clk_table註冊爲clock provider
每初始化一個clock,會把這個clock加入到clk_table中:
void XXX_clk_add_lookup(struct clk *clk, unsigned int id)
{
if (clk_table && id)
clk_table[id] = clk;
}


clock初始化,包括了 fixed root clock, pll, mux, divider, gate 的初始化,每種clock基本上是一張表,表中的每一項就是一個clock的配置信息。比如fixed root clock的表中:
FRATE(CLK_XTAL, "xtal", NULL, CLK_IS_ROOT, 24000000),
FRATE(0, "32Khz", NULL, CLK_IS_ROOT, 32000),
FRATE(CLK_81, "clk81", NULL, CLK_IS_ROOT, 166666666),
FRATE(CLK_FIXED_PLL, "fixed_pll", NULL, CLK_IS_ROOT, 2000000000),
FRATE(CLK_FPLL_DIV2, "fclk_div2", NULL, CLK_IS_ROOT, 1000000000),
FRATE(CLK_FPLL_DIV3, "fclk_div3", NULL, CLK_IS_ROOT,  666666666),
FRATE(CLK_FPLL_DIV4, "fclk_div4", NULL, CLK_IS_ROOT,  500000000),
FRATE(CLK_FPLL_DIV5, "fclk_div5", NULL, CLK_IS_ROOT,  400000000),
FRATE(CLK_FPLL_DIV7, "fclk_div7", NULL, CLK_IS_ROOT,  285714285),
通過for循環,分別讀取這些配置信息,每讀取一項,就調用一次clk_register.
register 過程:
實現每個clock的ops回調函數,如recalc_rate, enable, disable,把回調函數賦值到ops域,填充初始化結構體,比如前面說的ops域, parent_names, num_parents, 然後調用clk_register,參數是struct clk_hw, 初始化需要用到的信息都放在這個結構體參數重。在clk_register中,又調用了int __clk_init(struct device *dev, struct clk *clk)
, 在__clk_init中,首先檢查clk結構體中的各個域是否合法,如果已註冊的clock中有待註冊的clock的parent, 則建立parent child關聯,如果沒有parent,並且是root,就加入到clk_root_list鏈表中,如果既沒有parent,也不是root, 就加入到clk_orphan_list中。如果clock實現了clk->ops->recalc_rate這個回調函數,則調用這個函數計算clock rate,並且把rate保存在clk->rate中,如果沒有實現clk->ops->recalc_rate,就以該clock的parent clock->rate作爲該clock的clock->rate. 遍歷clk_orphan_list,調用list中的clk的get_parent回調,確認待註冊的clock是否爲orphan clock的parent, 如果是,則調用__clk_parent,建立關係。完成clk_register之後。clk_table[id] = clk;把這個clock保存到項目的靜態的數據表格中。調用ret = clk_register_clkdev(clk, list->name, NULL);用於之後獲取clock。clk_register_clkdev已經過時了,我們的項目中還沒有使用dts來配置clock,是使用的靜態數組,所以還需要調用這個函數。

fix rate clock, pll, mux, gate, 等類型的clock,都會調用到clk_register. 不同的是,每種類型的clock的ops不一樣,像mux,主要是選擇parent, 有get parent, set parent的ops, gate類型的,有enable, disable. 還有的clock有計算clock rate和set clock rate的回調函數。

recalc_rate回調函數:看了一個給cpu提供clock的ops, 主要是讀取相關的寄存器,根據寄存器數值,進行倍頻,分頻,計算得到clock頻率。
set_rate: 對於給cpu提供clock的clock,
irq_flags = arch_local_irq_save();
preempt_disable();
/*CPU switch to xtal*/
cpu clock rate有一個clock rate表格,如果目標rate小於最小rate,就按最小rate那一項來設置,如果目標rate大於表格中的最大rate,就按最大rate來設置。
查找到待設置的rate屬於表格中的哪一項,按照表格中的配置項,配置pll,配置寄存器。
  /*cpu switch to sys pll*/
preempt_enable();
arch_local_irq_restore(irq_flags);
切換cpu頻率之後,需要對LPJ loop_per_jiffiy 校準,用於udelay延時。





















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