你需要知道的Linux 系统下外设时钟管理
嵌入式系統(tǒng)一般要求低功耗,出于這個(gè)原因,一般只把需要使用到的外設(shè)時(shí)鐘源打開(kāi),其他不需要使用到的模塊,則默認(rèn)關(guān)閉它們。
LCD 模塊,上電時(shí)候默認(rèn)情況是關(guān)閉的,所以,要想使用 LCD 模塊,配置它寄存器必須先開(kāi)啟它時(shí)鐘。
如何知道,哪個(gè)模塊時(shí)鐘源是打開(kāi)的?哪些模塊時(shí)鐘源是關(guān)閉的?不同的芯片時(shí)鐘設(shè)置一定不相同的,所以實(shí)現(xiàn)代碼是編寫在和具體芯片相關(guān)的文件中:
Clock-exynos4.c (arch\arm\mach-exynos)內(nèi)核使用 struct clk 結(jié)構(gòu)描述一個(gè)外設(shè)模塊的時(shí)鐘信息:
struct clk {struct list_head list;
struct module *owner;
struct clk *parent;
const char *name;
const char *devname;//設(shè)備名,用來(lái)查找。
int id;
int usage;
unsigned long rate;
unsigned long ctrlbit;
struct clk_ops *ops;
int (*enable)(struct clk *, int enable);//指向模塊時(shí)鐘使能/禁止時(shí)鐘的函數(shù)
struct clk_lookup lookup;
#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
struct dentry *dent; /* For visible tree hierarchy */
#endif
};
一個(gè)已經(jīng)移植好,可以運(yùn)行的內(nèi)核,它的外設(shè)時(shí)鐘都已經(jīng)在系統(tǒng)初期已經(jīng)完成注冊(cè),實(shí)現(xiàn)文件就在
Clock-exynos4.c arch\arm\Mach-exynos關(guān)于 LCD 控制器(fimd0)模塊的時(shí)鐘定義:
把 exynos4_clk_fimd0 結(jié)構(gòu)放入數(shù)組中:
void __init exynos4_register_clocks(void){
int ptr;
s3c24xx_register_clocks(exynos4_clks, ARRAY_SIZE(exynos4_clks));
for (ptr = 0; ptr < ARRAY_SIZE(exynos4_sysclks); ptr++)
s3c_register_clksrc(exynos4_sysclks[ptr], 1);
for (ptr = 0; ptr < ARRAY_SIZE(exynos4_sclk_tv); ptr++)
s3c_register_clksrc(exynos4_sclk_tv[ptr], 1);
for (ptr = 0; ptr < ARRAY_SIZE(exynos4_clksrc_cdev); ptr++)
s3c_register_clksrc(exynos4_clksrc_cdev[ptr], 1);
//注冊(cè)時(shí)鐘源,其中 sclk_fimd0 就是在這里注冊(cè)的 ,在 exynos4_clksrcs 數(shù)組中定義
s3c_register_clksrc(exynos4_clksrcs, ARRAY_SIZE(exynos4_clksrcs));
//默認(rèn)打開(kāi)時(shí)鐘的模塊
s3c_register_clocks(exynos4_init_clocks_on, ARRAY_SIZE(exynos4_init_clocks_on));
s3c_register_clocks(exynos4_init_audss_clocks, ARRAY_SIZE(exynos4_init_audss_clocks));
s3c_disable_clocks(exynos4_init_audss_clocks, ARRAY_SIZE(exynos4_init_audss_clocks));
s3c24xx_register_clocks(exynos4_gate_clocks, ARRAY_SIZE(exynos4_gate_clocks));
//fyyy:注冊(cè)設(shè)備時(shí)鐘,其中 LCD 時(shí)鐘就在這里注冊(cè),可以通過(guò) clk_get 獲得
s3c24xx_register_clocks(exynos4_clk_cdev, ARRAY_SIZE(exynos4_clk_cdev));
//fyyy:注冊(cè)后禁止它,為了降低功耗
for (ptr = 0; ptr < ARRAY_SIZE(exynos4_clk_cdev); ptr++)
s3c_disable_clocks(exynos4_clk_cdev[ptr], 1);//這里有禁止 lcd 相關(guān)的時(shí)鐘 fimd0
s3c_register_clocks(exynos4_init_clocks_off, ARRAY_SIZE(exynos4_init_clocks_off));
//默認(rèn)關(guān)閉時(shí)鐘的模塊
s3c_disable_clocks(exynos4_init_clocks_off, ARRAY_SIZE(exynos4_init_clocks_off));
//可以查找的時(shí)鐘 ,可以通過(guò) clk_get 獲得
Clkdev_add_table(exynos4_clk_lookup, ARRAY_SIZE(exynos4_clk_lookup));
register_syscore_ops(&exynos4_clock_syscore_ops);
s3c24xx_register_clock(&dummy_apb_pclk);
s3c_pwmclk_init();
}
分析:
s3c24xx_register_clocks(exynos4_clk_cdev, ARRAY_SIZE(exynos4_clk_cdev));是注冊(cè)了 fimd0 模塊的時(shí)鐘信息
//fyyy:注冊(cè)后禁止它,為了降低功耗for (ptr = 0; ptr < ARRAY_SIZE(exynos4_clk_cdev); ptr++) {
s3c_disable_clocks(exynos4_clk_cdev[ptr], 1);//這里有禁止 lcd 相關(guān)的時(shí)鐘 fimd0
}
要使用這個(gè)模塊,必須先開(kāi)這個(gè)模塊的時(shí)鐘。
clkdev_add_table(exynos4_clk_lookup, ARRAY_SIZE(exynos4_clk_lookup));這一行是把可以通過(guò)設(shè)備名查找到的 clk 結(jié)構(gòu)加到可查詢的鏈表上。內(nèi)核 struct clk_lookup 結(jié)構(gòu)來(lái)表示一個(gè)可以被查找到的時(shí)鐘結(jié)構(gòu)。
Clkdev.h linux-3.5\include\Linux//它是用來(lái)查找 struct clk 結(jié)構(gòu)的。
//有了它,就可以通過(guò)設(shè)備名或時(shí)鐘源的名字來(lái)找到相應(yīng)的 struct clk 結(jié)構(gòu)。
struct clk_lookup {
struct list_head node;
const char *dev_id; //設(shè)備名,提供對(duì)外搜索的名字,匹配使用的
const char *con_id; //總線名,也可以用來(lái)搜索,匹配使用
struct clk *clk; //指向模塊時(shí)鐘信息結(jié)構(gòu)
};
實(shí)際的匹配過(guò)程是會(huì)比較 dev_id 和 con_id 兩個(gè)成員的,如果匹配上,則返回 clk 結(jié)構(gòu)。
內(nèi)核提供一個(gè)輔助填充宏:CLKDEV_INIT
定義如下:
#define CLKDEV_INIT(d, n, c) \{ \
.dev_id = d, \
.con_id = n, \
.clk = c, \
}
//可以被查找操作的模塊時(shí)鐘
//它是用來(lái)查找 struct clk 結(jié)構(gòu)的。
//有了它,就可以通過(guò)設(shè)備名或時(shí)鐘源的名字來(lái)找到相應(yīng)的 struct clk 結(jié)構(gòu)。
static struct clk_lookup exynos4_clk_lookup[] = {
……
//通過(guò)設(shè)備名或時(shí)鐘源名查找到 exynos4_clk_fimd0 結(jié)構(gòu)
CLKDEV_INIT("exynos4-fb.0", "lcd", &exynos4_clk_fimd0),
……
};
struct device dev;
struct clk * clk_bus;
dev. init_name = "exynos4-fb.0";
clk_bus = clk_get(&dev, "lcd" );
如何找到模塊的時(shí)鐘結(jié)構(gòu)?內(nèi)核提供了操作時(shí)鐘相關(guān)的 API 函數(shù),這些 API 接口函數(shù)是通用的,聲明在 Clk.h linux-3.5\include\Linux 。時(shí)鐘獲得結(jié)構(gòu)獲取函數(shù):
struct clk *clk_get(struct device *dev, const char *id);功能:通過(guò) dev. init_name 和參數(shù) id 進(jìn)行在 struct clk_lookup 注冊(cè)到內(nèi)核的時(shí)鐘結(jié)構(gòu)鏈表查找。參數(shù) dev. init_name 和 clk_lookup 結(jié)構(gòu)中的 dev_id 成員比較 參數(shù) id 和 clk_lookup 結(jié)構(gòu)中的 con_id 比較 如果兩個(gè)成員都相同就返回 clk_lookup 結(jié)構(gòu)中的中 clk 指針。
返回值:IS_ERR(clk_get 返回值)?
非 0: 獲得失敗,這時(shí)候應(yīng)該返回 –ENODEV 錯(cuò)誤碼 IS_ERR(clk_get 返回值)?
0: 獲得時(shí)鐘成功
示例:
s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");if (IS_ERR(s3c_ac97.ac97_clk)) {
dev_err(&pdev->dev, "ac97 failed to get ac97_clock\n");
ret = -ENODEV;
goto err2;
}
clk_enable(s3c_ac97.ac97_clk); //獲得成功后可以使能模塊時(shí)鐘了
時(shí)鐘使能函數(shù):
int clk_enable(struct clk *clk);功能: 在獲得 clk 結(jié)構(gòu)后,就可以調(diào)用 clk_enable 函數(shù)來(lái)使能模塊的時(shí)鐘
返回: 0:成員;負(fù)數(shù):失敗 時(shí)鐘禁止函數(shù):
void clk_disable(struct clk *clk);功能:當(dāng)不需要使用一個(gè)模塊時(shí)候,要降低功耗,可以關(guān)閉它。獲得模塊的運(yùn)行時(shí)鐘頻率:
unsigned long clk_get_rate(struct clk *clk);功能: 根據(jù)結(jié)構(gòu)獲得模塊的運(yùn)行頻率?
返回:模塊的運(yùn)行頻率,單位是 HZ 減少時(shí)鐘引用計(jì)數(shù),如果你使用
void clk_put(struct clk *clk);當(dāng)使用了 clk_get, clk_enable 后,如果不想使用模塊了,則需要 clk_put 引用計(jì)數(shù)。設(shè)置模塊的運(yùn)行時(shí)鐘:
int clk_set_rate(struct clk *clk, unsigned long rate);參數(shù): rate 要設(shè)置的目標(biāo)運(yùn)行頻率
返回: 0:成員;負(fù)數(shù):失敗
—————END—————
掃碼或長(zhǎng)按關(guān)注
回復(fù)「?加群?」進(jìn)入技術(shù)群聊
總結(jié)
以上是生活随笔為你收集整理的你需要知道的Linux 系统下外设时钟管理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Arm技术与市场
- 下一篇: Linux ALSA 图解