总结
内核通过start_kernel从汇编进入c世界,在用serup_arch设置体系架构的时候,会返回当前机器的machine_desc;然后init_IRQ和time_init来初始化中断。时钟子系统
start_kernel
setup_arch(&command_line);
mdesc = setup_machine_fdt(atags_vaddr) //返回成功匹配的machine_desc
machine_desc = mdesc; //赋值给全局变量machine_desc,以供后续初始化等操作使用,比如给下面的中断和时钟子系统使用
......
init_IRQ
machine_desc->init_irq()
irqchip_init
of_irq_init(__irqchip_of_table) //IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init)
......
time_init
if (machine_desc->init_time)
machine_desc->init_time
of_clk_init(NULL);
timer_probe();
else
of_clk_init(NULL);
for_each_matching_node_and_match(np, matches, &match) // CLK_OF_DECLARE(asr1803_clk, "asr,asr1803-clock", asr1803_clk_init);由晶振+pll给cpu核心和外设产生时钟
timer_probe();
for_each_matching_node_and_match(np, __timer_of_table, &match) //TIMER_OF_DECLARE(mmp_timer, "mrvl,mmp-timer", mmp_dt_init_timer);定时器外设
setup_arch
setup_machine_fdt
of_flat_dt_match_machine去匹配设备树,和DT_MACHINE_START定义的machine_desc中的兼容属性,并返回这个machine_desc
const struct machine_desc * __init setup_machine_fdt(void *dt_virt)
{
const struct machine_desc *mdesc, *mdesc_best = NULL;
#if defined(CONFIG_ARCH_MULTIPLATFORM) || defined(CONFIG_ARM_SINGLE_ARMV7M)
DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
.l2c_aux_val = 0x0,
.l2c_aux_mask = ~0x0,
MACHINE_END
mdesc_best = &__mach_desc_GENERIC_DT;
#endif
if (!dt_virt || !early_init_dt_verify(dt_virt))
return NULL;
mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
if (!mdesc) {
const char *prop;
int size;
unsigned long dt_root;
early_print("\nError: unrecognized/unsupported "
"device tree compatible list:\n[ ");
dt_root = of_get_flat_dt_root();
prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
while (size > 0) {
early_print("'%s' ", prop);
size -= strlen(prop) + 1;
prop += strlen(prop) + 1;
}
early_print("]\n\n");
dump_machine_table(); /* does not return */
}
/* We really don't want to do this, but sometimes firmware provides buggy data */
if (mdesc->dt_fixup)
mdesc->dt_fixup();
early_init_dt_scan_nodes();
/* Change machine number to match the mdesc we're using */
__machine_arch_type = mdesc->nr;
return mdesc;
}
arch_get_next_mach
__arch_info_begin就是在链接脚本vmlinux.lds.S定义,在这个里面找打一个个machine_desc,并返回当前的machine_desc的dt_compat成员,来匹配设备树
static const void * __init arch_get_next_mach(const char *const **match)
{
static const struct machine_desc *mdesc = __arch_info_begin;
const struct machine_desc *m = mdesc;
if (m >= __arch_info_end)
return NULL;
mdesc++;
*match = m->dt_compat;
return m;
}
//arch/arm/kernel/vmlinux.lds.S
.init.arch.info : {
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
}
......
ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")
ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined")
DT_MACHINE_START
arch/arm/include/asm/mach/arch.h的DT_MACHINE_START将machine_desc放进__arch_info_begin -- __arch_info_end的段空间中
extern const struct machine_desc __arch_info_begin[], __arch_info_end[];
#define for_each_machine_desc(p) \
for (p = __arch_info_begin; p < __arch_info_end; p++)
/*
* Set of macros to define architecture features. This is built into
* a table by the linker.
*/
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
#define DT_MACHINE_START(_name, _namestr) \
static const struct machine_desc __mach_desc_##_name \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = ~0, \
.name = _namestr,
#endif
dt_compat
ARM Linux 3.x在引入设备树之后,MACHINE_START变更为DT_MACHINE_START,其中含有一个.dt_compat成员,用于表明相关的设备与.dts中根节点的兼容属性兼容关系
/ {
model = "ASR 1828(C260SA) CPE";
compatible = "asr,1803-evb", "asr,1803";
.......
}
static const char *asr1803_dt_board_compat[] __initdata = {
"asr,1803-evb",
NULL,
};
DT_MACHINE_START(ASR1803_DT, "ASR ASR1803 (Device Tree Support)")
.map_io = mmp_map_io,
.init_irq = irqchip_init,
.init_time = mmp_init_time,
.reserve = asr1803_reserve,
.init_machine = asr1803_dt_init_machine,
.dt_compat = asr1803_dt_board_compat,
.restart = mmp_arch_restart,
MACHINE_END
init_IRQ
init_IRQ会调用machine_desc->init_irq,根据上面DT_MACHINE_START的定义,也即irqchip_init;中断控制器驱动会定义IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gicv3_of_init);经过_OF_DECLARE展开,就会产生__irqchip_of_table;因为还没初始化deivce-bus-driver这套去走match/probe自动回调驱动的probe,又要兼容设备树,只能前期去主动匹配后,调用初始化函数
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__used __section(__##table##_of_table) \
__aligned(__alignof__(struct of_device_id)) \
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
void __init irqchip_init(void)
{
of_irq_init(__irqchip_of_table);
acpi_probe_device_table(irqchip);
}
然后通过for_each_matching_node_and_match 来解析设备树节点,并在后续主动调用.data,也即gicv3_of_init。主要代码为desc->irq_init_cb = match->data; desc->irq_init_cb(desc->dev,desc->interrupt_parent);
void __init of_irq_init(const struct of_device_id *matches)
{
const struct of_device_id *match;
struct device_node *np, *parent = NULL;
struct of_intc_desc *desc, *temp_desc;
struct list_head intc_desc_list, intc_parent_list;
INIT_LIST_HEAD(&intc_desc_list);
INIT_LIST_HEAD(&intc_parent_list);
for_each_matching_node_and_match(np, matches, &match) {
if (!of_property_read_bool(np, "interrupt-controller") ||
!of_device_is_available(np))
continue;
if (WARN(!match->data, "of_irq_init: no init function for %s\n",
match->compatible))
continue;
/*
* Here, we allocate and populate an of_intc_desc with the node
* pointer, interrupt-parent device_node etc.
*/
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (!desc) {
of_node_put(np);
goto err;
}
desc->irq_init_cb = match->data;
desc->dev = of_node_get(np);
desc->interrupt_parent = of_irq_find_parent(np);
if (desc->interrupt_parent == np)
desc->interrupt_parent = NULL;
list_add_tail(&desc->list, &intc_desc_list);
}
/*
* The root irq controller is the one without an interrupt-parent.
* That one goes first, followed by the controllers that reference it,
* followed by the ones that reference the 2nd level controllers, etc.
*/
while (!list_empty(&intc_desc_list)) {
/*
* Process all controllers with the current 'parent'.
* First pass will be looking for NULL as the parent.
* The assumption is that NULL parent means a root controller.
*/
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
int ret;
if (desc->interrupt_parent != parent)
continue;
list_del(&desc->list);
of_node_set_flag(desc->dev, OF_POPULATED);
pr_debug("of_irq_init: init %pOF (%p), parent %p\n",
desc->dev,
desc->dev, desc->interrupt_parent);
ret = desc->irq_init_cb(desc->dev,
desc->interrupt_parent);
if (ret) {
of_node_clear_flag(desc->dev, OF_POPULATED);
kfree(desc);
continue;
}
/*
* This one is now set up; add it to the parent list so
* its children can get processed in a subsequent pass.
*/
list_add_tail(&desc->list, &intc_parent_list);
}
/* Get the next pending parent that might have children */
desc = list_first_entry_or_null(&intc_parent_list,
typeof(*desc), list);
if (!desc) {
pr_err("of_irq_init: children remain, but no parents\n");
break;
}
list_del(&desc->list);
parent = desc->dev;
kfree(desc);
}
list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
list_del(&desc->list);
kfree(desc);
}
err:
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
list_del(&desc->list);
of_node_put(desc->dev);
kfree(desc);
}
}
time_init
time_init会调用machine_desc->init_time,根据上面DT_MACHINE_START的定义,也即mmp_init_time;主要使用of_clk_init和timer_probe初始化时钟
static void __init mmp_init_time(void)
{
enable_pxawdt_clock();
/* Reset and Enable the Timer set0, Select the timer set's fast clock source as 3.25MHz */
/* The actual clock value for each timer in the timer set are decided by the TMR_CCR */
/* The MIPSRAM actually uses the 2nd timer of timer set0 to count LPM periods*/
__raw_writel(APBC_APBCLK | APBC_RST, APBC_MMPX_TIMER0);
__raw_writel(APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3),
APBC_MMPX_TIMER0);
#if 0
/* Reset and Enable the Timer set0, Select the timer set's fast clock source as 3.25MHz */
/* MIPSRAM uses this timer to time LPM periods */
__raw_writel(APBC_APBCLK | APBC_RST, APBC_MMPX_TIMER1);
__raw_writel(APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3),
APBC_MMPX_TIMER1);
#else
/*
* the CP timer(used as cp watchdog timer) is not routed to AP, AP uses
* cp timer as timer tick, cp uses APB Timer1(d4016000)
* cp timer is fixed at 13MHZ, here to enable and reset the core timer
*/
__raw_writel(APBC_APBCLK | APBC_FNCLK | APBC_RST, APBCP_TICER);
__raw_writel(APBC_APBCLK | APBC_FNCLK, APBCP_TICER);
#endif
of_clk_init(NULL);
timer_probe();
coresight_debug_init();
}
of_clk_init
CLK_OF_DECLARE(asr1803_clk, "asr,asr1803-clock", asr1803_clk_init);经过_OF_DECLARE展开,就会产生__clk_of_table;然后通过for_each_matching_node_and_match 来解析设备树节点,并在后续主动调用.data,也即asr1803_clk_init;它由晶振+pll给cpu核心和外设产生时钟;主要代码为:
matches = &__clk_of_table; (匹配)
parent->clk_init_cb = match->data; (初始化回调函数)
clk_provider->clk_init_cb(clk_provider->np); (执行回调)
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);
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);
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;
}
}
timer_probe
TIMER_OF_DECLARE(mmp_timer, "mrvl,mmp-timer", mmp_dt_init_timer); 经过_OF_DECLARE展开,就会产生__timer_of_table;然后通过for_each_matching_node_and_match 来解析设备树节点,并在后续主动调用.data,也即mmp_dt_init_timer;主要代码为:
for_each_matching_node_and_match(np, __timer_of_table, &match); (匹配)
init_func_ret = match->data; (初始化回调函数)
init_func_ret(np); (执行回调)
extern struct of_device_id __timer_of_table[];
static const struct of_device_id __timer_of_table_sentinel
__used __section(__timer_of_table_end);
void __init timer_probe(void)
{
struct device_node *np;
const struct of_device_id *match;
of_init_fn_1_ret init_func_ret;
unsigned timers = 0;
int ret;
for_each_matching_node_and_match(np, __timer_of_table, &match) {
if (!of_device_is_available(np))
continue;
init_func_ret = match->data;
ret = init_func_ret(np);
if (ret) {
if (ret != -EPROBE_DEFER)
pr_err("Failed to initialize '%pOF': %d\n", np,
ret);
continue;
}
timers++;
}
timers += acpi_probe_device_table(timer);
if (!timers)
pr_crit("%s: no matching timers found\n", __func__);
}