• android MSM8974 上DeviceTree简介


    简介

    主要功能是不在代码中硬编码设备信息,而是用专门的文件来描述。整个系统的设备节点会形成一个树,设备节点里可以设置属性。官网在http://www.devicetree.org 。入门指南请参考http://www.devicetree.org/Device_Tree_Usage 。Linux上一些状况请参考"kernel/Document/devicetree/",其中"bindings"子目录描述各种设备的devicetree的描述方法,各厂商的各类设备的描述方法都可能不同。

     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
    

    lk中的处理

    8974目前实际用的应该是方式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>;
    
    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
    

    kernel中的处理

    在下面的setup_machine_fdt()中,会通过用各机器描述(machine description)的"dt_compat"属性和传入DT的根节点的"compatible"属性进行匹配来选择适当的machine description。会在下面board_dt_populate()处理中,会根据devicetree信息创建设备。

    start_kernel()
      |setup_arch()
         |setup_machine_fdt()//select machine description according to DT info
    
    customize_machine()//called because it is an arch_initcall
      |msm8974_init()
         |board_dt_populate()
            |of_platform_bus_create()
               |of_platform_device_create_pdata()
                  |of_device_alloc()
                     |dev->dev.of_node = of_node_get(np);//pointer to data of struct device_node, that is device node in DT
               |of_platform_bus_create()//call it recursively to walk through the DT
    

    上面of_device_alloc()时会在dev->dev.of_node中保存对DT中设备节点的引用。在驱动中,一般会增加下面用于设备和驱动匹配的".of_match_table"信息。

    static struct of_device_id msm_otg_dt_match[] = {
    	{	.compatible = "qcom,hsusb-otg",
    	},
    	{}
    };
    
    static struct platform_driver msm_otg_driver = {
    	.remove = __devexit_p(msm_otg_remove),
    	.driver = {
    ......
    		.of_match_table = msm_otg_dt_match,
    	},
    };
    

    在kernel/drivers/base/platform.c的platform_match()中会调用下面处理来进行设备和驱动的匹配。of_driver_match_device()中,一般会根据"compatible"属性来匹配(如果设了name或type,则先根据name/type匹配)。

    	if (of_driver_match_device(dev, drv))
    		return 1;
    

    下面是设备节点操作(一般是读取)的一些API。

    of_property_read_string()
    of_find_property()
    of_get_next_child()
    of_get_parent()
  • 相关阅读:
    Hello, Fedora.
    Android与Linux分道扬镳
    VIM教程V1.5梁昌泰
    强大的NTFS文件系统
    Linux下的cc与gcc
    g++与gcc的区别
    Fedora下解压缩的相关问题
    The GNU C Reference Manual
    Linux Kbuild文档
    实验一:计算机是怎样工作的
  • 原文地址:https://www.cnblogs.com/LoongEmbedded/p/5298229.html
Copyright © 2020-2023  润新知