Linux加载DTS设备节点的流程(高通一)

ARM :
一. 启动条件
通常从系统上电执行的boot loader的代码, 而要从boot loader跳转到linux kernel的第一条指令处执行需要一些特定的条件。
这里讨论下进入到linux kernel时必须具备的一些条件,这一般是boot loader在跳转到kernel之前要完成的:
1. CPU必须处于SVC(supervisor)模式,并且IRQ和FIQ中断都是禁止的;
2. MMU(内存管理单元)必须是关闭的, 此时虚拟地址就是物理地址;
3. 数据cache(Data cache)必须是关闭的
4. 指令cache(Instruction cache)可以是打开的,也可以是关闭的,这个没有强制要求;
5. CPU 通用寄存器0 (r0)必须是 0;
6. CPU 通用寄存器1 (r1)必须是 ARM Linux machine type (关于machine type, 我们后面会有讲解)
7. CPU 通用寄存器2 (r2) 必须是 kernel parameter list 的物理地址(parameter list 是由boot loader传递给kernel,用来描述设备信息属性的列表)。
二. starting kernel
KERNEL_RAM_ADDR arch/arm/kernel/head.S 0xc0008000 kernel在RAM中的虚拟地址
PAGE_OFFSET include/asm-arm/memeory.h 0xc0000000 内核空间的起始虚拟地址
TEXT_OFFSET arch/arm/Makefile 0x00008000 内核在RAM中起始位置相对于RAM起始地址的偏移

在 arch/arm/kernel/head-common.S 中:
00026: /*
00027:  * The following fragment of code is executed with the MMU on in MMU mode,

00028:  * and uses absolute addresses; this is not position independent.

00029:  *

00030:  *  r0  = cp#15 control register

00031:  *  r1  = machine ID

00032:  *  r9  = processor ID

00033:  */

00034:  .type  __mmap_switched, %function

00035: __mmap_switched:

00036:      adr r3, __switch_data + 4

00037:

00038:      ldmia   r3!, {r4, r5, r6, r7}

00039:      cmp r4, r5             @ Copy data segment if needed

00040: 1:   cmpne   r5, r6

00041:      ldrne   fp, [r4], #4

00042:      strne   fp, [r5], #4

00043:      bne 1b

00044:

00045:      mov fp, #0             @ Clear BSS (and zero fp)

00046: 1:   cmp r6, r7

00047:      strcc   fp, [r6],#4

00048:      bcc 1b

00049:

00050:      ldmia   r3, {r4, r5, r6, sp}

00051:      str r9, [r4]           @ Save processor ID

00052:      str r1, [r5]           @ Save machine type

00053:      bic r4, r0, #CR_A          @ Clear 'A' bit

00054:      stmia   r6, {r0, r4}           @ Save control register values

00055:      b   start_kernel

asmlinkage void __init start_kernel(void)->
void __init setup_arch(char **cmdline_p)->
mdesc = setup_machine_fdt(__atags_pointer); //通过atags_pointer获取(devtree = phys_to_virt(dt_phys))
void __init unflatten_device_tree(void);

void __init unflatten_device_tree(void)
{
    __unflatten_device_tree(initial_boot_params, &allnodes,
                early_init_dt_alloc_memory_arch);

    /* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
    of_alias_scan(early_init_dt_alloc_memory_arch);
}

这些工作完成解析DTS文件。保存到全局链表allnodes中。
随后,当系统启动到board文件时,会调用.init_machine,高通平台对应的是msm_init()。接着调用of_platform_populate(….)接口,加载平台总线和平台设备。至此,系统平台上的所有已配置的总线和设备将被注册到系统中。注意:不是dtsi文件中所有的节点都会被注册,在注册总线和设备时,会对dts节点的状态作一个判断,如果节点里面的status属性没有被定义,或者status属性被定义了并且值被设为“ok”或者“okay”,其他情况则不被注册到系统中。

void __init msm8610_init(void)
{
    struct of_dev_auxdata *adata = msm8610_auxdata_lookup;

    if (socinfo_init() < 0)
        pr_err("%s: socinfo_init() failed\n", __func__);

    msm8610_init_gpiomux();
    board_dt_populate(adata);
    msm8610_add_drivers();
}

在注册i2c总线时,会调用到qup_i2c_probe()接口,该接口用于申请总线资源和添加i2c适配器。在成功添加i2c适配器后,会调用of_i2c_register_devices()接口。此接口会解析i2c总线节点的子节点(挂载在该总线上的i2c设备节点),获取i2c设备的地址、中断号等硬件信息。然后调用request_module()加载设备对应的驱动文件,调用i2c_new_device(),生成i2c设备。此时设备和驱动都已加载,于是drvier里面的probe方法将被调用。

你可能感兴趣的:(android,LINUX,arm,高通,DTS)