通过函数omap_dm_timer_set_source()来为定时器指定时钟源。由代码可以知道,source的取值只能在1~3之间。
arch/arm/plat-omap/dmtimer.c
int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
{
int ret = -EINVAL;
if (source < 0 || source >= 3)
return -EINVAL;
clk_disable(timer->fclk);
ret = clk_set_parent(timer->fclk, dm_source_clocks[source]);
clk_enable(timer->fclk);
/*
* When the functional clock disappears, too quick writes seem
* to cause an abort. XXX Is this still necessary?
*/
__delay(300000);
return ret;
}
即在头文件arch/arm/plat-omap/include/plat/dmtimer.h中所定义的宏:
arch/arm/plat-omap/include/plat/dmtimer.h
/* clock sources */
#define OMAP_TIMER_SRC_SYS_CLK 0x00
#define OMAP_TIMER_SRC_32_KHZ 0x01
#define OMAP_TIMER_SRC_EXT_CLK 0x02
通过函数omap_dm_timer_init来设置dm_source_clocks的值。具体的代码如下所示:
static struct clk **dm_source_clocks。
/* FIXME: Currently set_source only allows selecting form first three sources */
static const char *ti814x_dm_source_names[] __initdata = {
"osc0_clkin_ck",
"sysclk18_ck",
"tclkin_ck",
"osc1_clkin_ck",
"xref0_ck",
"xref1_ck",
"xref2_ck",
NULL
};
。。。
dm_source_names = ti814x_dm_source_names;
。。。
int __init omap_dm_timer_init(void)
{
。。。
if (cpu_class_is_omap2())
for (i = 0; dm_source_names[i] != NULL; i++)
dm_source_clocks[i] = clk_get(NULL, dm_source_names[i]);
。。。
}
clk_get 的原型为如下,用来查找并获取一个时钟源struct clk *clk,注意:不可在中断上下文调用,同时时钟也是禁止的。其中dev可以为NULL。由如下代码可知,现在已经将参数struct device *转换为了char *。分析到这一步已经足以说明定时器的时钟源是如何来的。clk_get_sys就不向下展开了,否则就没完没了了。
arch/arm/common/clkdev.c
struct clk *clk_get(struct device *dev, const char *con_id)
{
const char *dev_id = dev ? dev_name(dev) : NULL;
return clk_get_sys(dev_id, con_id);
}
include/linux/device.h
static inline const char *dev_name(const struct device *dev)
{
/* Use the init name until the kobject becomes available */
if (dev->init_name)
return dev->init_name;
return kobject_name(&dev->kobj);
}
static inline const char *kobject_name(const struct kobject *kobj)
{
return kobj->name;
}
可以参考arch/arm/mach-omap2/clock814x_data.c来查看一个时钟设备的dev_name是NULL或是合法的字符串。查看数组ti814x_clks[],在数组中,第一个域为dev_id,第三个域为struct clk结构。可以通过源代码查看:
arch/arm/mach-omap2/clock814x_data.c
#define CLK(dev, con, ck, cp) \
{ \
.cpu = cp, \
.lk = { \
.dev_id = dev, \
.con_id = con, \
.clk = ck, \
}, \
}
struct clk_lookup {
struct list_head node;
const char *dev_id;
const char *con_id;
struct clk *clk;
};
struct omap_clk {
u16 cpu;
struct clk_lookup lk;
};
static struct omap_clk ti814x_clks[] = {
。。。
CLK(NULL,"tclkin_ck",&tclkin_ck,CK_TI814X | CK_DM385),
CLK(NULL,"osc0_clkin_ck",&osc0_clkin_ck,CK_TI814X | CK_DM385),
。。。
CLK(NULL,"sysclk18_ck",&sysclk18_ck,CK_TI814X | CK_DM385),
。。。
CLK(NULL,"rcosc_32k_ck", &rcosc_32k_ck,CK_TI814X),
CLK(NULL,"sys_32k_clkin_ck",&sys_32k_clkin_ck, CK_TI814X),
...
CLK("cpsw.0",NULL,&cpsw_ick, CK_TI814X),
...
};
例子
struct clk *clkp;
clkp=clk_get(NULL,”gem_fck”);
if(!clkp)
pr_err(“clk_get failed for gem_fck!\n”);
然后将ti814x_clks[]结构添加到一个链表中,方便clk_get_sys()时根据具体的dev和con_id来获取时钟。下面则是struct clk结构的具体定义。
arch/arm/mach-omap2/clock814x_data.c
static struct clk osc0_clkin_ck = {
.name = "osc0_clkin_ck",
.ops = &clkops_null,
.rate = 20000000,
.flags = RATE_IN_TI814X,
};
static struct clk tclkin_ck = {
.name = "tclkin_ck",
.ops = &clkops_null,
.rate = 32768, /* TODO: Check */
.flags = RATE_IN_TI814X,
};
static struct clk sysclk18_ck = {
.name = "sysclk18_ck",
.init = &omap2_init_clksel_parent,
.clksel = sysclk18_mux_sel,
.clksel_reg = TI81XX_CM_DPLL_SYSCLK18_CLKSEL,
.clksel_mask = TI81XX_CLKSEL_0_0_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
};
总结:
1、为DM814x指定时钟源时,可以有三个选择,分别是:OMAP_TIMER_SRC_SYS_CLK对应于struct clk结构的osc0_clkin_ck,OMAP_TIMER_SRC_32_KHZ对应于struct clk结构的sysclk18_ck,OMAP_TIMER_SRC_EXT_CLK 对应于struct clk结构的tclkin_ck。
2、通过struct clk *clk_get(struct device *dev, const char *con_id)来获取时钟时,可以结合dev的kboj->name和con_id在arch/arm/mach-omap2/clock814x_data.c中查找即可。