Linux version: 2.6.38
平台: i.mx53 (mxc),以下所有平台相關部分都特指i.mx53
涉及的源文件有:
- include/linux/clk.h
- drivers/clk/clkdev.c
- arch/arm/plat-mxc/clock.c
- arch/arm/mach-mx5/clock.c
內核定義了一套標准的接口(include/linux/clk.h),用於所有的平台之上。每個時鍾源對象使用一個struct clk結構來表示。而struct clk結構的具體內容由各平台自己定義。clk.h頭文件定義了操作一個clk對象的所有接口。內核的其他地方可以也只能使用clk.h中提供的這些接口函數來操作clk。
struct clk *clk_get(struct device *dev, const char *id); int clk_enable(struct clk *clk); void clk_disable(struct clk *clk); unsigned long clk_get_rate(struct clk *clk); void clk_put(struct clk *clk); long clk_round_rate(struct clk *clk, unsigned long rate); int clk_set_rate(struct clk *clk, unsigned long rate); int clk_set_parent(struct clk *clk, struct clk *parent); struct clk *clk_get_parent(struct clk *clk); struct clk *clk_get_sys(const char *dev_id, const char *con_id); int clk_add_alias(const char *alias, const char *alias_dev_name, char *id, struct device *dev);
clk結構體是平台相關的。在arch/arm/mach-mx5/clock.c中會預先描述CPU中所有的clk對象。
- parent - clk是由parent分出來的。那么如果parent關閉了,當前clk也就沒有了。
- secondary - 第二時鍾源,用於enable/disable當前clk。
- usecount - 引用計數。
- get_rate, set_rate, enable, disable, set_parent - 很顯然,這些函數指針指到實際操作的函數。clk.h中的各接口函數最后都會調用到這里的函數指針。函數指針是隔離變化的最好辦法,在這里一下就把層次抽象出來了。
2. clocks鏈表
arch/arm/mach-mx5/clock.c中不僅定義了所有的clk對象,而且每個clk對象還要對應一個struct clk_lookup結構。在初始化時,會將所有的clk_loopup結構添加進入clocks鏈表中。
struct clk_lookup { struct list_head node; const char *dev_id; const char *con_id; struct clk *clk; };
clk_lookup,顧名思義就知道它是用來查找struct clk結構的。有了它,就可以通過設備名或時鍾源的名字來找到相應的struct clk結構。鏈表操作位於drivers/clk/clkdev.c
3. clk平台通用操作
arch/arm/plat-mxc/clock.c源文件中定義了mxc平台clock的通用操作接口。
enable/disable函數中可以看到引用計數usecount的作用。一個clk只有當其usecount為0的時候才會做實際的打開動作,也只有usecount為0時才能確認沒有被任何其他設備使用,可以禁止了。層次關系被遞歸調用和引用計數巧妙的實現。
static void __clk_disable(struct clk *clk) { if (clk == NULL || IS_ERR(clk)) return; WARN_ON(!clk->usecount); if (!(--clk->usecount)) { if (clk->disable) clk->disable(clk); __clk_disable(clk->secondary); __clk_disable(clk->parent); } } static int __clk_enable(struct clk *clk) { if (clk == NULL || IS_ERR(clk)) return -EINVAL; if (clk->usecount++ == 0) { __clk_enable(clk->parent); __clk_enable(clk->secondary); if (clk->enable) clk->enable(clk); } return 0; }
4. clk與pm
為了省電,當不需要clk時將其關閉,上面的clk_enable/clk_disable實現了此功能。除了關閉clk省電,還可以降低clk頻率以達到省電的目的。當系統當前負載較輕,不需要clk跑在那么高的頻率時,就可以對該clk降頻了。從這些關系可以看到,clk與電源管理,cpufreq等都可能有關聯。
mxc為各時鍾定義了幾個屬性標志:
arch/arm/plat-mxc/include/mach/clock.h
/* Clock flags */ #define RATE_PROPAGATES (1 << 0) /* Program children too */ #define ALWAYS_ENABLED (1 << 1) /* Clock cannot be disabled */ #define RATE_FIXED (1 << 2) /* Fixed clock rate */ #define CPU_FREQ_TRIG_UPDATE (1 << 3) /* CPUFREQ trig update */ #define AHB_HIGH_SET_POINT (1 << 4) /* Requires max AHB clock */ #define AHB_MED_SET_POINT (1 << 5) /* Requires med AHB clock */