目录
1. clk的分类
2. 系统调用入口
3. leo clk初始化入口
4. of_leo_clocks_init函数leo的clk系统自定义函数
5. of_clk_init
1. clk的分类
根据clk的特点,clock framework将clock分为fixed rate、gate、devider、mux、fixed factor、composite六类,每一类clock都有相似的功能、相似的控制方式,因而可以使用相同的逻辑,统一处理,这充分体现了面向对象的思想
2. 系统调用入口
start_kernel---> time_init
//arch/arm/kernel/time.c
void __init time_init(void)
{
if (machine_desc->init_time) {
machine_desc->init_time();
} else {
#ifdef CONFIG_COMMON_CLK
of_clk_init(NULL);
#endif
timer_probe();
}
}
3. leo clk初始化入口
在leo的移植的操作系统中,在arch/arm/mach-leo/platform_dt.c中定义machine_desc中初始化了init_time.
//arch/arm/mach-leo/platform_dt.c
static const char *faraday_dt_match[] __initconst = {
"arm,faraday-soc",
NULL,
};
DT_MACHINE_START(FARADAY, "LEO")
.atag_offset = 0x100,
.dt_compat = faraday_dt_match,
.smp = smp_ops(faraday_smp_ops),
.map_io = platform_map_io,
.init_time = platform_sys_timer_init,
.init_early = platform_init_early,
.init_machine = platform_init,
.restart = platform_reset,
MACHINE_END
static void __init platform_sys_timer_init(void)
{
platform_clock_init();
timer_probe();
}
static struct of_device_id faraday_clk_match[] __initconst = {
{ .compatible = "faraday,leoevb-clk", .data = of_leo_clocks_init, },
{}
};
static void __init platform_clock_init(void)
{
struct device_node *np;
const struct of_device_id *match;
void (*clk_init)(struct device_node *);
/* setup clock tree */
/* 查找设备树对应的设备节点 */
np = of_find_matching_node(NULL, faraday_clk_match);
if (!np)
panic("unable to find a matching clock\n");
match = of_match_node(faraday_clk_match, np);
clk_init = match->data;
clk_init(np); //调用of_leo_clocks_init
}
4. of_leo_clocks_init函数leo的clk系统自定义函数
/**
*driver/clk/faraday/clk-leo.c
*/
static const __initconst struct of_device_id leo_clk_match[] = {
{ .compatible = "leo,osc0", .data = of_fixed_clk_setup, },
{ .compatible = "leo,rosc0", .data = of_fixed_clk_setup, },
{ .compatible = "leo,rosc0_div4", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,rosc1", .data = of_fixed_clk_setup, },
{ .compatible = "leo,audio", .data = of_fixed_clk_setup, },
{ .compatible = "leo,pll0", .data = of_leo_faraday_pll_setup, },
{ .compatible = "leo,pll1", .data = of_leo_faraday_pll_setup, },
{ .compatible = "leo,pll1_div4", .data = of_leo_faraday_pll_setup, },
{ .compatible = "leo,pll2", .data = of_leo_faraday_pll_setup, },
{ .compatible = "leo,pll2_div5", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,pll2_div50", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,pll3", .data = of_leo_faraday_pll_setup, },
{ .compatible = "leo,pll4", .data = of_leo_faraday_pll_setup, },
{ .compatible = "leo,pll5", .data = of_leo_faraday_pll_setup, },
{ .compatible = "leo,pll6", .data = of_leo_faraday_pll_setup, },
{ .compatible = "leo,pll7", .data = of_leo_faraday_pll_setup, },
{ .compatible = "leo,fastboot", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,fastboot_div2", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,ahb", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,hclk", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,apb", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,pclk", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,cpu", .data = of_leo_faraday_divider_setup, },
{ .compatible = "leo,ddrmclk", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,spiclk", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,sspclk", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,sspclk_i2s", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,sspclk_i2s_1", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,sdclk", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,lcclk", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,gmacclk", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,uart_uclk_src", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,uart_uclk_30m", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,uart_uclk", .data = of_leo_faraday_mux_setup, },
{ .compatible = "leo,irda", .data = of_fixed_factor_clk_setup, },
};
void __init of_leo_clocks_init(struct device_node *n)
{
struct device_node *node;
struct of_phandle_args clkspec;
struct clk *clk;
unsigned long pll0, pll1, pll2, pll3, pll4, pll5, pll6, pll7;
unsigned long cpuclk, hclk, pclk, mclk;
unsigned long spiclk, sspclk, sspclk_i2s, sdclk;
unsigned long lcclk, irdaclk, gmacclk;
pll0 = pll1 = pll2 = pll3 = pll4 = pll5 = pll6 = pll7 = 0;
cpuclk = hclk = pclk = mclk = 0;
spiclk = sspclk = sspclk_i2s = sdclk = 0;
lcclk = irdaclk = gmacclk = 0;
/* 从自定义的leo_clk_match进行匹配,并执行对应.data字段指定的函数 */
of_clk_init(leo_clk_match);
for (node = of_find_matching_node(NULL, leo_clk_match);
node; node = of_find_matching_node(node, leo_clk_match)) {
clkspec.np = node;
clk = of_clk_get_from_provider(&clkspec);
of_node_put(clkspec.np);
if (!strcmp(__clk_get_name(clk), "pll0"))
pll0 = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "pll1"))
pll1 = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "pll2"))
pll2 = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "pll3"))
pll3 = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "pll4"))
pll4 = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "pll5"))
pll5 = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "pll6"))
pll6 = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "pll7"))
pll7 = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "cpu"))
cpuclk = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "hclk"))
hclk = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "pclk"))
pclk = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "ddrmclk"))
mclk = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "spiclk"))
spiclk = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "sspclk"))
sspclk = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "sspclk_i2s"))
sspclk_i2s = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "sdclk"))
sdclk = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "lcclk"))
lcclk = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "irda"))
irdaclk = clk_get_rate(clk);
else if (!strcmp(__clk_get_name(clk), "gmacclk"))
gmacclk = clk_get_rate(clk);
}
printk(KERN_INFO "PLL0: %4ld MHz, PLL1 MCLK: %4ld MHz, PLL2: %4ld MHz, PLL3: %4ld MHz\n",
pll0/1000/1000, pll1/1000/1000, pll2/1000/1000, pll3/1000/1000);
printk(KERN_INFO "PLL4: %4ld MHz, PLL5 MCLK: %4ld MHz, PLL6: %4ld MHz, PLL7: %4ld MHz\n",
pll4/1000/1000, pll5/1000/1000, pll6/1000/1000, pll7/1000/1000);
printk(KERN_INFO "CPU: %ld MHz, DDR MCLK: %ld MHz, HCLK: %ld MHz, PCLK: %ld MHz\n",
cpuclk/1000/1000, mclk/1000/1000, hclk/1000/1000, pclk/1000/1000);
printk(KERN_INFO "SPI CLK: %ld MHz, SSP CLK: %ldMHz, SSP_I2S CLK: %ldMHz, SD CLK: %ldMHz\n",
spiclk/1000/1000, sspclk/1000/1000, sspclk_i2s/1000/1000, sdclk/1000/1000);
printk(KERN_INFO "LC CLK: %ldMHz, IRDA: %ldMHz , GMAC_REF: %ldMhz\n",
lcclk/1000/1000, irdaclk/1000/1000,gmacclk/1000/1000);
}
5. of_clk_init
/**
* driver/clk/clk.c
*/
/**
* of_clk_init() - Scan and init clock providers from the DT
* @matches: array of compatible values and init functions for providers.
*
* This function scans the device tree for matching clock providers
* and calls their initialization functions. It also does it by trying
* to follow the dependencies.
*/
void __init of_clk_init(const struct of_device_id *matches)
{
const struct of_device_id *match;
struct device_node *np;
struct clock_provider *clk_provider, *next;
bool is_init_done;
bool force = false;
LIST_HEAD(clk_provider_list);
/**
* 如果matches为NULL,则matches指向了.init.data中的__clk_of_table。
* __clk_of_table定义在arch/arm/kernel/vmlinux.lds
*/
if (!matches)
matches = &__clk_of_table;
/* First prepare the list of the clocks providers */
for_each_matching_node_and_match(np, matches, &match) {
struct clock_provider *parent;
if (!of_device_is_available(np))
continue;
parent = kzalloc(sizeof(*parent), GFP_KERNEL);
if (!parent) {
list_for_each_entry_safe(clk_provider, next,
&clk_provider_list, node) {
list_del(&clk_provider->node);
of_node_put(clk_provider->np);
kfree(clk_provider);
}
of_node_put(np);
return;
}
parent->clk_init_cb = match->data;
parent->np = of_node_get(np);
list_add_tail(&parent->node, &clk_provider_list);
}
while (!list_empty(&clk_provider_list)) {
is_init_done = false;
list_for_each_entry_safe(clk_provider, next,
&clk_provider_list, node) {
if (force || parent_ready(clk_provider->np)) {
/* Don't populate platform devices */
of_node_set_flag(clk_provider->np,
OF_POPULATED);
/**
* 依次调用在driver/clk/clk-leo.c中
* leo_clk_match的.data指向的函数
*/
clk_provider->clk_init_cb(clk_provider->np);
of_clk_set_defaults(clk_provider->np, true);
list_del(&clk_provider->node);
of_node_put(clk_provider->np);
kfree(clk_provider);
is_init_done = true;
}
}
/*
* We didn't manage to initialize any of the
* remaining providers during the last loop, so now we
* initialize all the remaining ones unconditionally
* in case the clock parent was not mandatory
*/
if (!is_init_done)
force = true;
}
}