linux msm dts加载分析

本文介绍了Linux kernel在MSM8974平台上的DeviceTree加载流程。设备描述文件在编译时被转换为.dtb,由bootloader加载到内存。在kernel启动时,bootloader选择合适的devicetree,kernel通过compatible属性匹配machine description,创建设备。以i2c总线为例,讲解设备节点如何被解析并注册到系统中。
摘要由CSDN通过智能技术生成

MSM8974 上DeviceTree

设备描述源文件放在”kernel/arch/arm/boot/dts/”下后缀是”.dts”或”.dtsi”,一般”.dtsi”是被其它文件包含的,只用dtc编译”.dts”文件。Build时会用”dtc”命令把需要的设备描述文件编译成”.dtb”文件,并放到到bootimage的某个地方。对设备描述的解析处理实现主要在”kernel/drivers/of/”目录中,需要配置”CONFIG_OF“。
启动过程中,bootloader(默认是bootable/bootloader/lk)会根据机器硬件信息选择合适的devicetree装入内存,把地址等相关信息传给kernel。kernel中,会根据传入的信息创建设备。

版本声明和包含其它文件
一般”.dts”文件会先进行版本申明,如下面的第一行。”.dts”或”.dtsi”文件也可能包含其它”.dtsi”文件,如下面的3/4行。

/dts-v1/;

/include/ "msm8974-v2.2.dtsi"
/include/ "msm8974-mtp.dtsi" 

关于取地址符号的使用
如经常碰到类似下面的写法。没找到相关文档说明(看源码来理解也很费劲)。作用应该是对之前定义过的设备(例子中是”soc”)进行补充描述。

&soc {
android_usb@fe8050c8 {
    compatible = "qcom,android-usb";
    reg = <0xfe8050c8 0xc8>;
    qcom,android-usb-swfi-latency = <1>;
};

......
};

哪些文件被编入二进制映像
有两种方式使用DT。第一种可包含多个dtb,编入dt.img,放入boot.img。第二种只包含一个dtb,直接追加到kernelimage后面,放入boot.img。
dtc编译在kernel/AndroidKernel.mk中定义。先用定义”DTS_NAMES“变量,它的每个entry(记为”DTS_NAME“变量,下面的$$arch)中可能有arch和rev两部分,和.config中相关配置有关,用下面方法找出。

while (<>) {
$$a = $$1 if /CONFIG_ARCH_((?:MSM|QSD|MPQ)[a-zA-Z0-9]+)=y/;
$$r = $$1 if /CONFIG_MSM_SOC_REV_(?!NONE)(\w+)=y/;
$$arch = $$arch.lc("$$a$$r ") if /CONFIG_ARCH_((?:MSM|QSD|MPQ)[a-zA-Z0-9]+)=y/
} print $$arch;

得到上述”DTS_NAMES“变量,用”$(DTS_NAME)*.dts“方式去”kernel/arch/arm/boot/dts/”下匹配。见下面的定义,其中”cat”命令就是生成带DT的kernelimage。

define append-dtb
mkdir -p $(KERNEL_OUT)/arch/arm/boot;\
$(foreach DTS_NAME, $(DTS_NAMES), \
   $(foreach d, $(DTS_FILES), \
  $(DTC) -p 1024 -O dtb -o $(call DTB_FILE,$(d)) $(d); \
  cat $(KERNEL_ZIMG) $(call DTB_FILE,$(d)) > $(call ZIMG_FILE,$(d));))
endef

如,msm8974的MR2的ES1版中,”DTS_NAMES“变量的值为”msm8974 msmsamarium”,会编入的文件有。

msm8974pro-ab-cdp.dts      
msm8974pro-ac-mtp.dts   
msm8974-v1-mtp.dts       
msm8974-v2.0-1-fluid.dts    
msm8974-v2.2-fluid.dts    
msmsamarium-sim.dts
msm8974pro-ab-fluid.dts    
msm8974-v1-cdp.dts      
msm8974-v1-rumi.dts      
msm8974-v2.0-1-liquid.dts   
msm8974-v2.2-liquid.dts
msm8974pro-ab-liquid.dts   
msm8974-v1-fluid.dts    
msm8974-v1-sim.dts       
msm8974-v2.0-1-mtp.dts      
msm8974-v2.2-mtp.dts
msm8974pro-ab-mtp.dts      
msm8974-v1-liquid.dts   
msm8974-v2.0-1-cdp.dts   
msm8974-v2.2-cdp.dts        
msmsamarium-rumi.dts 

第二种方式没看到后续如何放入 boot.img。对于第一种方式,会用”device/qcom/common/generate_extra_images.mk“中定义的下面规则编出”dt.img“,

$(INSTALLED_DTIMAGE_TARGET): $(DTBTOOL) $(INSTALLED_KERNEL_TARGET)
$(build-dtimage-target)

在”build/core/Makefile“中用下面语句使它被编入boot.img。

ifeq ($(strip $(BOARD_KERNEL_SEPARATED_DT)),true)
  INTERNAL_BOOTIMAGE_ARGS += --dt $(INSTALLED_DTIMAGE_TARGET)
  BOOTIMAGE_EXTRA_DEPS    := $(INSTALLED_DTIMAGE_TARGET)
endif  

dts加载流程

启动过程中,bootloader(默认是bootable/bootloader/lk)会根据机器硬件信息选择合适的devicetree装入内存,把地址等相关信息传给kernel。kernel中,会根据传入的信息创建设备。

这里写图片描述

msm8974目前实际用的应该是方式1。在下面boot_linux_from_mmc()中,调用dev_tree_get_entry_info(),里面会根据硬件(chipset和platform的id,系统实际跑时的信息在系统boot的更早阶段由N侧设置并传来,而DT中的信息由根节点的”qcom,msm-id“属性定义)来选择合适的DT,后面会把该DT装入内存,把地址等信息传给kernel(据说是通过CPU寄存器)。

    qcom,msm-id = <126 8 0x20002>,
              <185 8 0x20002>,
              <186 8 0x20002>; 

从lk中的处理
Lk/arch/arm/crt0.S文件中语句:bl kmain
调用的是lk/kernel/main.c文件中的函数:kmain()

kmain()
  |bootstrap2()
 |arch_init()
 |platform_init()
 |target_init()
 |apps_init()//call init() of APPs defined using APP_START macro
    |aboot_init()
       |boot_linux_from_mmc()
          |//for device tree approach 1
             |dev_tree_get_entry_info()
                |__dev_tree_get_entry_info()
             |memmove((void *)hdr->tags_addr, (char *)dt_table_offset + dt_entry.offset, dt_entry.size);
          |//for device tree approach 2
             |dev_tree_appended()
          |boot_linux()
             |update_device_tree()
             |entry(0, machtype, (unsigned*)tags_phys);//pass control to kernel  

详细过程
Aboot.c (bootable\bootloader\lk\app\aboot)

APP_START(aboot)
.init = aboot_init,
APP_END

在下面aboot_init()—> boot_linux_from_mmc()中,调用dev_tree_get_entry_info(),里面会根据硬件(chipset和platform的id,系统实际跑时的信息在系统boot的更早阶段由N侧设置并传来,而DT中的信息由根节点的”qcom,msm-id“属性定义)来选择合适的DT,后面会把该DT装入内存,把地址等信息传给kernel(通过CPU寄存器)。

void boot_linux(void *kernel, unsigned *tags,
                const char *cmdline, unsigned machtype,
void *ramdisk, unsigned ramdisk_size)
{
#if DEVICE_TREE
//更新Device Tree
    ret = update_device_tree((void *)tags, final_cmdline, ramdisk, ramdisk_size);
}

/* Top level function that updates the device tree. */
int update_device_tree(void *fdt, const char *cmdline,
  void *ramdisk, uint32_t ramdisk_size)
{
    int ret = 0;
    uint32_t offset;

    /* Check the device tree header */
//核查其magic数是否正确:version和size
    ret = fdt_check_header(fdt);

    /* Add padding to make space for new nodes and properties. */
//Move or resize dtb buffer
    ret = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + DTB_PAD_SIZE);

    /* Get offset of the memory node */
    ret = fdt_path_offset(fdt, "/memory");
    offset = ret;

    ret = target_dev_tree_mem(fdt, offset);
    /* Get offset of the chosen node */
    ret = fdt_path_offset(fdt, "/chosen");
    offset = ret;
    /* Adding the cmdline to the chosen node */
    ret = fdt_setprop_string(fdt, offset, (const char*)"bootargs", (const void*)cmdline);

    /* Adding the initrd-start to the chosen node */
    ret = fdt_setprop_u32(fdt, offset, "linux,initrd-start", (uint32_t)ramdisk);
    if (ret)
    /* Adding the initrd-end to the chosen node */
    ret = fdt_setprop_u32(fdt, offset, "linux,initrd-end", ((uint32_t)ramdisk + ramdisk_size));
    fdt_pack(fdt);
    return ret;
}  

Kernel中的处理 <

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值