前言
1.首先介绍下 linux初始化时,时钟的初始化的脉络。 2.介绍各个种类时钟的初始化流程。 3.此篇基于 全志的t7进行讲述:sun8iw17p1
linux启动时,时钟的初期化
在介绍之前,先要理清楚,machine_desc,即 设备描述符。
machine_desc的获取
这个描述符比较重要,里面有个init_time函数,被内核调用,所以想要搞清楚他的由来。 首先看下 连接脚本文件 。
cat linux-3.10/arch/arm/kernel/vmlinux.lds.S .init.arch.info : { __arch_info_begin = .; *(.arch.info.init) __arch_info_end = .; }
即,连接脚本定义了个 .arch.info.init的段,软件通过 __arch_info_begin 来获取首地址。 接下来,平台驱动文件定义了machine_desc
#define DT_MACHINE_START(_name, _namestr) \
static const struct machine_desc __mach_desc_##_name \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = ~0, \
.name = _namestr,
DT_MA
CHINE_START(SUNXI_DT, CONFIG_SUNXI_SOC_NAME)
#ifdef CONFIG_SMP
.smp_init = smp_init_ops(sun8i_smp_init_ops),
#ifndef CONFIG_MCPM
.smp = smp_ops(sunxi_smp_ops),
#endif
#endif
.map_io = sunxi_map_io,
.init_time = sunxi_timer_init,
.dt_compat = sunxi_board_dt_compat,
MACHINE_END
即,在 “.arch.info.init” section中定义了,__mach_desc_SUNXI_DT 结构体。
系统的初始化
start_kernel
setup_arch
mdesc = setup_machine_fdt
time_init
machine_desc->init_time();
正式进入 init_time
static void __init sunxi_timer_init(void)
{
of_clk_init(NULL);
#ifdef CONFIG_COMMON_CLK_ENABLE_SYNCBOOT_EARLY
clk_syncboot();
#endif
clocksource_of_init();
}
void __init of_clk_init(const struct of_device_id *matches)
{
struct device_node *np;
if (!matches)
matches = __clk_of_table;
pr_debug("[%s][%d]match-name:%s\n", __func__, __LINE__, matches->name);
for_each_matching_node(np, matches) {
const struct of_device_id *match = of_match_node(matches, np);
of_clk_init_cb_t clk_init_cb = match->data;
pr_debug("[%s][%d]match-name:%s\n", __func__, __LINE__, matches->name);
clk_init_cb(np);
}
}
#define CLK_OF_DECLARE(name, compat, fn) \
static const struct of_device_id __clk_of_table_##name \
__used __section(__clk_of_table) \
= { .compatible = compat, .data = fn };
CLK_OF_DECLARE(sunxi_clocks_init, "allwinner,sunxi-clk-init",
of_sunxi_clocks_init);
CLK_OF_DECLARE(sunxi_fixed_clk, "allwinner,fixed-clock",
of_sunxi_fixed_clk_setup);
CLK_OF_DECLARE(pll_clk, "allwinner,sunxi-pll-clock",
of_sunxi_pll_clk_setup);
CLK_OF_DECLARE(sunxi_fixed_factor_clk, "allwinner,fixed-factor-clock",
of_sunxi_fixed_factor_clk_setup);
CLK_OF_DECLARE(periph_clk, "allwinner,sunxi-periph-clock",
of_sunxi_periph_clk_setup);
CLK_OF_DECLARE(periph_cpus_clk, "allwinner,sunxi-periph-cpus-clock",
of_sunxi_periph_cpus_clk_setup);
所以从上面来看 init_time执行后,按照以下的顺序执行
of_sunxi_clocks_init of_sunxi_fixed_clk_setup of_sunxi_pll_clk_setup of_sunxi_periph_clk_setup
之后我们开始一个一个分析这几个函数如何构筑了庞大的时钟帝国。
of_sunxi_clocks_init
通过定义:
CLK_OF_DECLARE(sunxi_clocks_init, "allwinner,sunxi-clk-init",
of_sunxi_clocks_init);
我们可以知道,此函数的设备树参数,对应下面的设备树
clocks {
compatible = "allwinner,sunxi-clk-init";
device_type = "clocks";
#address-cells = <0x2>;
#size-cells = <0x2>;
ranges;
reg = <0x0 0x3001000 0x0 0x1000 0x0 0x7010000 0x0 0x400 0x0 0x7000060 0x0 0x4>;
losc {
#clock-cells = <0x0>;
compatible = "allwinner,fixed-clock";
clock-frequency = <0x8000>;
clock-output-names = "losc";
linux,phandle = <0xf>;
phandle = <0xf>;
};
我们看下函数内容:
void of_sunxi_clocks_init(struct device_node *node)
{
sunxi_clk_base = of_iomap(node, 0);
sunxi_clk_cpus_base = of_iomap(node, 1);
sunxi_clk_periph_losc_out.gate.bus = of_iomap(node, 2);
sunxi_clk_factor_initlimits();
sunxi_clk_get_factors_ops(&pll_mipi_ops);
pll_mipi_ops.get_parent = get_parent_pll_mipi;
pll_mipi_ops.set_parent = set_parent_pll_mipi;
pll_mipi_ops.enable = clk_enable_pll_mipi;
pll_mipi_ops.disable = clk_disable_pll_mipi;
sunxi_set_clk_priv_ops("mipi_rx", set_mipi_rx_priv_ops);
}
static const struct clk_ops clk_factors_ops = {
.enable = sunxi_clk_fators_enable,
.disable = sunxi_clk_fators_disable,
.is_enabled = sunxi_clk_fators_is_enabled,
.recalc_rate = sunxi_clk_factors_recalc_rate,
.round_rate = sunxi_clk_factors_round_rate,
.set_rate = sunxi_clk_factors_set_rate,
};
void sunxi_clk_get_factors_ops(struct clk_ops* ops)
{
memcpy(ops,&clk_factors_ops,sizeof(clk_factors_ops));
}
sunxi_clk_get_factors_ops(&pll_mipi_ops);
of_sunxi_fixed_clk_setup
通过定义:
CLK_OF_DECLARE(sunxi_fixed_clk, "allwinner,fixed-clock",
of_sunxi_fixed_clk_setup);
我们知道匹配的设备树是:
hosc {
#clock-cells = <0x0>;
compatible = "allwinner,fixed-clock";
clock-frequency = <0x16e3600>;
clock-output-names = "hosc";
linux,phandle = <0x8>;
phandle = <0x8>;
};
osc48m {
#clock-cells = <0x0>;
compatible = "allwinner,fixed-clock";
clock-frequency = <0x2dc6c00>;
clock-output-names = "osc48m";
linux,phandle = <0x7>;
phandle = <0x7>;
};
这里 osc48m 和 hosc 都是晶振,都属于 clk root。
|