前言

在之前的文章中已经对设备树的基本概念作了讲解, 操作系统(例如在 Android 中使用的 Linux 内核)会使用 DT 来支持 Android 设备使用的各种硬件配置。硬件供应商(ODM)会提供自己的 DT 源文件,接下来 Linux 会将这些文件编译到引导加载程序使用的设备树 Blob (DTB) 文件中。

Android在原有的DT基础上增加了设备树叠加层的处理方式。进一步的对于芯片产品的DT和开发者(ODM/OEM/产品开发者)的DT做了解耦。

设备树叠加层 (DTO) 可让主要的(ODM)设备树 Blob (DTB) 叠加在Soc设备树上。使用 DTO 的引导加载程序可以维护系统芯片 (SoC) DT,并动态叠加针对特定设备(ODM)的 DT,从而向树中添加节点并对现有树中的属性进行更改。
本篇文章主要讲述如下内容:

  • DT的专有名词
  • Bootloader 加载DT的基本流程
  • Android 下DTO的实现以及原理
  • 实际案例-MT8167 平台DTS的加载完整流程
DT相关的专有名词

DT

Device Tree

DTB

Device Tree Blob

DTBO

Device Tree Blob for Overlay

DTC

Device Tree Compiler

DTO

Device Tree Overlay

DTS

Device Tree Source

FDT

Flattened Device Tree, a binary format contained in a .dtb blob file

Bootloader 加载DT的基本流程

android获取固件版本 从android固件提取dtb_DTO


如上图所示,系统加载DT主要包含: DTS源文件编译, .dtb分区以及对应镜像文件生成, bootloader运行将.dtb 分区的文件加载到内存中, 将对应的内存地址通过寄存器传递到kernel。

在支持DTO的Android下, DT 是由以下两个部分组成:

  • Main DT, 主要是Soc-only的部分以及默认的系统配置,例如cpu配置/内存相关配置等,soc的供应商提供, 本文所用的MT8167 那么这个Main DT就是由MTK提供的.
  • Overlay DT, 该Soc所对应的产品需要的特定配置, 主要是由ODM/OEM提供,这里可以理解为开发者自己定义(当然目前在MT8167上MTK也提供了基本的Oerlay DT基本模板)
  1. 编译阶段
  • 通过dtc(device tree compiler)将Main DT dts源文件编译为.dtb文件.
  • 通过dtc(device tree compiler)将Overlay DT的dts源文件编译为后缀名为.dtbo的文件.
    这里需要注意的是, .dtb和.dtbo 的文件格式是相同的都是FDT. 后缀名不同只是为了区分.
  1. dtb分区
  2. android获取固件版本 从android固件提取dtb_android_02

  3. 在MT8167这一平台上是将dtbo划分为了独立的分区, 具体的分析在实际案例章节会详细说明.
  4. 运行

android获取固件版本 从android固件提取dtb_DTS加载流程_03

  • 在bootloader中将.dtb文件读取到内存中
  • 在bootloader中将.dtbo文件从dtbo分区(emmc指定分区)读取到内存中.
  • 将.dtb 和 .dtbo 合并
  • 在bootloader跳转启动kernel时,将该内存地址通过寄存器传递给到kernel
    关于这个环节的详细流程在实际案例章节会详细说明.
实际案例-MT8167 平台DTS的加载完整流程
1. MT8167 平台的Main DT和Overlay DT一览

截取了MT8167平台Main DT的部分内容, 关于Main DT 和 Oerlay DT 在上一章节"Bootloader 加载DT的基本流程"已描述.

# kernel-4.4/arch/arm/boot/dts/mt8167.dtsi

/ {
        model = "MT8167";
        compatible = "mediatek,mt8167";
        interrupt-parent = <&sysirq>;
        #address-cells = <2>;
        #size-cells = <2>;
        
        cpus {
                #address-cells = <1>;
                #size-cells = <0>;

                cpu0: cpu@0 {
                        device_type = "cpu";
                        compatible = "arm,cortex-a53";
                        reg = <0x0>;
                        enable-method = "psci";
                        cpu-idle-states = <&CLUSTER_SLEEP_0 &CLUSTER_SLEEP_0>,
                                          <&CPU_SLEEP_0_0 &CPU_SLEEP_0_0 &CPU_SLEEP_0_0>;
                        clocks = <&infracfg CLK_IFR_MUX1_SEL>,
                                 <&topckgen CLK_TOP_MAINPLL_D2>,
                                 <&apmixedsys CLK_APMIXED_ARMPLL>;
                        clock-names = "cpu", "intermediate", "armpll";
                        operating-points-v2 = <&cluster0_opp>;
                        clock-frequency = <1300000000>;
                };
         ...
# kernel-4.4/arch/arm/boot/dts/mt8167_dtbo.dts

/dts-v1/;

#include "mt8167.dtsi"
#include "mt6392.dtsi"

/ {
        /* chosen */
        chosen {
                bootargs = "console=ttyS0,921600n1 root=/dev/ram initrd=0x44000200,0x200000";
        };

        firmware: firmware {
                android: android {
                        compatible = "android,firmware";
                        fstab: fstab {
                                compatible = "android,fstab";
                        };
                };
        };
        odm: odm {
                compatible = "simple-bus";
                /* reserved for overlay by odm */
        };
};

基于上述截取的部分内容可以看到,针对MT8167平台+Android O Main DT由以下两个文件构成,

kernel-4.4/arch/arm/boot/dts/mt8167.dtsi
# 而mt8167.dtsi 是以include的方式包含在mt8167_dtbo.dts, 类似C语言中的头文件包含
kernel-4.4/arch/arm/boot/dts/mt8167_dtbo.dts

继续看Overlay DT,

# kernel-4.4/arch/arm/boot/dts/apollo.dts

#include <generated/autoconf.h>
/dts-v1/;

#ifdef CONFIG_MTK_DTBO_FEATURE
/plugin/;

#define ROOT_NODE &odm

ROOT_NODE {

        mtcpufreq {
                compatible = "mediatek,mt8167-cpufreq";
        };

        mt8167_audio_codec: mt8167_audio_codec {
                compatible = "mediatek,mt8167-codec";
                clocks = <&topckgen CLK_TOP_AUDIO>;
                clock-names = "bus";
                mediatek,afe-regmap = <&afe>;
                mediatek,apmixedsys-regmap = <&apmixedsys>;
                mediatek,dmic-wire-mode = <1>; /* 0(ONE_WIRE) 1(TWO_WIRE) */
                mediatek,headphone-cap-sel = <1>; /* 0(10UF) 1(22UF) 2(33UF) 3(47UF) */
        };
        
        ...

这里需要注意的是,在apollo.dts 中ROOT_NODE 实际的定义是&odm, &符号在dts语法中是引用的意思,这里暂时可以理解为:

# mt8167_dtbo.dts 中odm 最终在merge apollo.dts时会将apollo.dts下的odm相关定义覆盖mt8167_dtbo.dts中的odm相关内容

# 最终合并mt8167_dtbo.dts apollo.dts,部分内容如下.
# 当然这里所指的合并 具体是如何处理的接下来的文章中会详细说明

/dts-v1/;

#include "mt8167.dtsi"
#include "mt6392.dtsi"

/ {
        /* chosen */
        chosen {
                bootargs = "console=ttyS0,921600n1 root=/dev/ram initrd=0x44000200,0x200000";
        };

        firmware: firmware {
                android: android {
                        compatible = "android,firmware";
                        fstab: fstab {
                                compatible = "android,fstab";
                        };
                };
        };
        odm: odm {
                    compatible = "simple-bus";
                    mtcpufreq {
                    compatible = "mediatek,mt8167-cpufreq";
                    };

                    mt8167_audio_codec: mt8167_audio_codec {
                        compatible = "mediatek,mt8167-codec";
                        clocks = <&topckgen CLK_TOP_AUDIO>;
                        clock-names = "bus";
                        mediatek,afe-regmap = <&afe>;
                        mediatek,apmixedsys-regmap = <&apmixedsys>;
                        mediatek,dmic-wire-mode = <1>; /* 0(ONE_WIRE) 1(TWO_WIRE) */
                        mediatek,headphone-cap-sel = <1>; /* 0(10UF) 1(22UF) 2(33UF) 3(47UF) */
                    }; 
                /* reserved for overlay by odm */
                };
};
2. Main DT 和 Overlay DT的构建

在分析之前提前揭晓下答案:

Main DT
    -> mt8167_dtbo.dts & mt8167_dtbo.dts
       -> DTC - mt8167_dtbo.dtb  
          -> CAT mt8167_dtbo.dtb && zImage > zImage-dtb中
也就是Main DT最终会包含在zImage-dtb.img中


Overlay DT
    -> apollo.dts
        -> DTC - apollo.dtb
            -> mkimage apollo.dtb -> odmdtbo.img
也就是Overlay DT最终会包含在odmdtbo.img,作为独立的分区文件写入设备的指定分区

下面来看各自的构建流程.
Main DT,
相关文件以及路径:

# 源文件 
kernel-4.4/arch/arm/boot/dts/mt8167.dtsi
kernel-4.4/arch/arm/boot/dts/mt8167_dtbo.dts

# 相关Makefile
arm/boot/Makefile
scripts/Makefile.lib

下面来看下具体的构建过程,
在arm/boot/Makefile中有如下相关代码,

# arm/boot/Makefile

# 在kernel的配置文件apollo_defconfig中将CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES 定义为了mt8167_dtbo
# 也就是这里DTB_NAMES := mt8167_dtbo
DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES))
ifneq ($(DTB_NAMES),)
# DTB_LIST := mt8167_dtbo.dtb
DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES))
else
DTB_LIST := $(dtb-y)
endif
DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST))
targets += $(addprefix dts/,$(DTB_LIST))

$(obj)/zImage:  $(obj)/compressed/vmlinux FORCE
    $(call if_changed,objcopy)
    @$(kecho) '  Kernel: $@ is ready'
# Documentation/kbuild/makefiles.txt -> --- 6.7 Commands useful for building a boot image if_changed
$(obj)/zImage-dtb:  $(obj)/zImage $(DTB_OBJS) FORCE
    $(call if_changed,cat)
    @echo '  Kernel: $@ is ready'

如上图所截取的部分Makfile, 在构建zImage-dtb时, 首先会构建zImage 和 $(DTB_OBJS)-mt8167_dtbo.dtb。
在构建构建生成zImage和mt8167_dtbo.dtb会调自定义的cat指令将mt8167_dtbo.dtb追加到zImage尾部.

# Kernel源码目录: Documentation/kbuild/makefiles.txt -> --- 6.7 Commands useful for building a boot image if_change
$(obj)/zImage-dtb:  $(obj)/zImage $(DTB_OBJS) FORCE
    # 而cat是在scripts/Makefile.lib中定义的.
    $(call if_changed,cat)
    @echo '  Kernel: $@ is ready'
   
# scripts/Makefile.lib   
# cat
# ---------------------------------------------------------------------------
# Concatentate multiple files together
quiet_cmd_cat = CAT     $@
# 这里可以看出,dtb是直接加载在kernel image 后
# $(filter-out FORCE,$^) 获取到的是 zImage 和 mt8167_dtbo.dtb,$@为zImage-dtb
cmd_cat = (cat $(filter-out FORCE,$^) > $@) || (rm -f $@; false)

以上描述了Main DT的构建以及如何追加到zImage.

Overlay DT
相关文件以及路径:

# 源文件 
arch/arm/boot/dts/apollo.dts

# Makefile
arch/arm/boot/dts/Makefile
scripts/drvgen/drvgen.mk
arch/arm/boot/Makefile

下面来看下具体的构建过程,
在arch/arm/Makefile有如下代码:

boot := arch/arm/boot

# 当make 目标zImage-dtb时,会先构建以赖的dtbs,然后是$(DTB_OVERLAY_IMAGE_TAGERT)
zImage-dtb: vmlinux scripts dtbs $(DTB_OVERLAY_IMAGE_TAGERT)
  $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
  
# 构建dtbs
dtbs: prepare scripts
  # 通过debug以及上下文分析得知如下编译指令是:
  # make arch/arm/boot/dts
  # 也就是会执行arch/arm/boot/dts/下的Makefile
  $(Q)$(MAKE) $(build)=$(boot)/dts

继续执行kernel-4.4/arch/arm/boot/dts/下的Makefile

# kernel-4.4/arch/arm/boot/dts/Makefile
# CONFIG_BUILD_ARM_DTB_OVERLAY_IMAGE是在arch/arm/configs/apollo_defconfig中定义的,在我这个案例中为CONFIG_BUILD_ARM_DTB_OVERLAY_IMAGE_NAMES="apollo"
ifeq ($(strip $(CONFIG_BUILD_ARM_DTB_OVERLAY_IMAGE)), y)
DTB_LIST += $(addsuffix .dtb, $(subst $\",,$(CONFIG_BUILD_ARM_DTB_OVERLAY_IMAGE_NAMES)))
endif
# 将构建Overlay dts的目标添加到targets中进行apollo.dts的构建, 输出目标为apollo.dtb
targets += $(DTB_LIST)

看到这里你可能会有疑问? 为什么赋值给targets 关于这一点kernel官方文档有做了初步的说明,

# 对于dtc构建dts的规则做了基本的说明
     dtc
        Create flattened device tree blob object suitable for linking
        into vmlinux. Device tree blobs linked into vmlinux are placed
        in an init section in the image. Platform code *must* copy the
        blob to non-init memory prior to calling unflatten_device_tree().

        To use this command, simply add *.dtb into obj-y or targets, or make
        some other target depend on %.dtb

        A central rule exists to create $(obj)/%.dtb from $(src)/%.dts;
        architecture Makefiles do no need to explicitly write out that rule.

        Example:
                targets += $(dtb-y)
                clean-files += *.dtb
                DTC_FLAGS ?= -p 1024
                
      # 而dtc自定义是在scripts/Makefile.lib中实现
      
quiet_cmd_dtc = DTC     $@

cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
        $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
        $(srctree)/scripts/dtc/dtc_overlay -@ -O dtb -o $@ -b 0 \
                -i $(dir $<) $(DTC_FLAGS) \
                -d $(depfile).dtc.tmp $(dtc-tmp) ; \
        cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile)

$(obj)/%.dtb: $(src)/%.dts FORCE
        $(call if_changed_dep,dtc)

dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)

以上的构建只是将apollo.dts编译生成了apollo.dtb还是并不是最终烧录到dtb分区的Overlay DT.
下面来继续看下中的dtb image是如何生成的.

# arch/arm/Makefile
# 前面的内容已经讲述了dtbs的构建生成了apollo.dtb,下面继续看下$(DTB_OVERLAY_IMAGE_TAGERT)的构建
zImage-dtb: vmlinux scripts dtbs $(DTB_OVERLAY_IMAGE_TAGERT)
  $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
  
  
# DTB_OVERLAY_IMAGE_TAGERT是在scripts/drvgen/drvgen.mk中定义
ifeq ($(strip $(CONFIG_MTK_DTBO_FEATURE)), y)

DTB_OVERLAY_IMAGE_TAGERT := $(DRVGEN_OUT)/odmdtbo.img
$(DTB_OVERLAY_IMAGE_TAGERT) : PRIVATE_DTB_OVERLAY_OBJ:=$(DTB_FILES)
$(DTB_OVERLAY_IMAGE_TAGERT) : PRIVATE_MULTIPLE_DTB_OVERLAY_OBJ:=$(DRVGEN_OUT)/$(MTK_PROJECT).mdtb
$(DTB_OVERLAY_IMAGE_TAGERT) : PRIVATE_MULTIPLE_DTB_OVERLAY_IMG:=$(DRVGEN_OUT)/$(MTK_PROJECT).mimg
$(DTB_OVERLAY_IMAGE_TAGERT) : PRIVATE_MULTIPLE_DTB_OVERLAY_HDR:=$(srctree)/scripts/multiple_dtbo.py
$(DTB_OVERLAY_IMAGE_TAGERT) : PRIVATE_MKIMAGE_TOOL:=$(srctree)/scripts/mkimage
$(DTB_OVERLAY_IMAGE_TAGERT) : PRIVATE_MKIMAGE_CFG:=$(srctree)/scripts/odmdtbo.cfg
$(DTB_OVERLAY_IMAGE_TAGERT) : $(PRIVATE_MULTIPLE_DTB_OVERLAY_OBJ) dtbs $(PRIVATE_MKIMAGE_TOOL) $(PRIVATE_MKIMAGE_CFG) $(PRIVATE_MULTIPLE_DTB_OVERLAY_HDR)
    @echo Singing the generated overlay dtbo.
    # Singing the generated overlay dtbo.
    #  cat ./arch/arm/boot/dts/apollo.dtb > ./arch/arm/boot/dts/apollo.mdtb || (rm -f ./arch/arm/boot/dts/apollo.mdtb; false)
    cat $(PRIVATE_DTB_OVERLAY_OBJ) > $(PRIVATE_MULTIPLE_DTB_OVERLAY_OBJ) || (rm -f $(PRIVATE_MULTIPLE_DTB_OVERLAY_OBJ); false)
    # python kernel-4.4/scripts/multiple_dtbo.py ./arch/arm/boot/dts/apollo.mdtb ./arch/arm/boot/dts/apollo.mimg
    python $(PRIVATE_MULTIPLE_DTB_OVERLAY_HDR) $(PRIVATE_MULTIPLE_DTB_OVERLAY_OBJ) $(PRIVATE_MULTIPLE_DTB_OVERLAY_IMG)
    # mkimage  apollo.mimg odmdtbo.cfg > odmdtbo.img
    $(PRIVATE_MKIMAGE_TOOL) $(PRIVATE_MULTIPLE_DTB_OVERLAY_IMG) $(PRIVATE_MKIMAGE_CFG)  > $@
.PHONY: odmdtboimage
odmdtboimage : $(DTB_OVERLAY_IMAGE_TAGERT) dtbs
endif

上述截取的部分构建代码只是展示了下在MT8167+Kernel 4.4中是如何构建Overlay DT的, 对于具体的构建过程不同平台会有所差异,但核心逻辑是类似的.
但可以看下scripts/multiple_dtbo.py中实现,

# scripts/multiple_dtbo.py
# dtbo的构建规则可以查看google官方给出的规则
# https://source.android.com/devices/architecture/dto/partitions DTB/DTBO Partitions Format
def parse_dtb(input):
    dtb_list = []
    with open(input, 'rb') as f:
        img_data = f.read()
        img_size = f.tell()
        dtb_offset = 0
        while dtb_offset <= img_size - 8:
            dtb_magic = struct.unpack('>I', img_data[dtb_offset : dtb_offset+4])[0]
            if dtb_magic == 0xD00DFEED:
                dtb_size = struct.unpack('>I', img_data[dtb_offset+4 : dtb_offset+8])[0]
                dtb_list.append(dtb_offset)
                dtb_offset = dtb_offset + dtb_size
            else:
                dtb_offset = dtb_offset + 1
        print('{}.'.format(dtb_list))
        f.closed
    return dtb_list

def write_header(output_file, input_file, dtb_list):
    head[0] = struct.pack('I',0xdeaddead) #Magic number
    head[1] = struct.pack('I', os.path.getsize(input_file))#dtbo size without header
    head[2] = struct.pack('I', 512) #Header Size
    head[3] = struct.pack('I', 2) #Header version
    head[4] = struct.pack('I', len(dtb_list)) #number of dtbo
    head[5] = struct.pack('I', 0xffffffff) #Reserved
    head[6] = struct.pack('I', 0xffffffff) #Reserved
    head[7] = struct.pack('I', 0xffffffff) #Reserved

    i = 0
    for offset in dtb_list:
        head[8 + i] = struct.pack('I', offset)
        i = i + 1

    with open(output_file, 'w') as fo:
        for item in head:
            fo.write("%s" % item)
        with open(input_file, 'r') as fi:
            for line in fi.readlines():
                fo.write(line)
            fi.close
        fo.close

def main(argv):
    if len(argv) < 2:
        print("Usage: python post_process_dtbs.py odmdtbs.dtb odmdtbo.img")
        sys.exit(1)
    input_img = argv[1]
    output_img = argv[2]
    dtb_list = parse_dtb(input_img)

    if len(dtb_list) > 111 :
        print("ERROR: Append too much DTBO")
        sys.exit(1)

    write_header(output_img, input_img, dtb_list)

if __name__ == '__main__':
    main(sys.argv)

通过以上分析可以得出Overlay DT的构建过程如下:

1. 通过dtc编译器将apollo.dts编译为apollo.dtb 
2. 调用python脚本multiple_dtbo.py添加dtbo的规定格式.
3. 调用mkimage生成最终的烧录到dtb分区的odmdtbo.img
3. bootloader中如何加载dtb和dtbo

针对MT8167+Android O平台, bootloader 是由preloader和lk两部分构成,在这里就不做过多的描述。
我们直接来看下在lk中是如何加载相关DTB内容的.
在lk中是如何加载Main DT的,

// vendor/mediatek/proprietary/bootable/bootloader/lk/app/mt_boot/mt_boot.c
// 在跳转到kernel前需要先加载相关的dtb到内存中
int boot_linux_fdt(void *kernel, unsigned *tags,
           unsigned machtype,
           void *ramdisk, unsigned ramdisk_sz)
{
    void *fdt = tags;
    int ret = 0;
    int offset;
    char tmpbuf[TMPBUF_SIZE];
    
    //当前使用的kernel是32bit
    if (g_is_64bit_kernel) {
        ...
    }
    else {
        dprintf(INFO, "32 bits kernel\n");
        zimage_size = *(unsigned int *)((unsigned int)kernel + 0x2c) - *
                  (unsigned int *)((unsigned int)kernel + 0x28);
        //在“Main DT和Overlay DT的构建”这一章节中已经做了说明,在这一平台Main DT是直接跟在zImage后面.
        // 获取到dtb_addr(这里的前提是lk已经把zImage-dtb分区的内容全部加载到内存了)      
        dtb_addr = (unsigned int)kernel + zimage_size;
        wake_up_iothread();
        wait_for_iothread();
    }
    //在获取到dtb_addr后,需要校验下dtb的FD_Magic,FD_Magic=0xd00dfeed
    if (fdt32_to_cpu(*(unsigned int *)dtb_addr) == FDT_MAGIC) {
#if CFG_DTB_EARLY_LOADER_SUPPORT
                dtb_size = fdt32_to_cpu(*(unsigned int *)(fdt + 0x4));
#else
                dtb_size = fdt32_to_cpu(*(unsigned int *)(dtb_addr + 0x4));
#endif
        } else {
                dprintf(CRITICAL, "Can't find device tree. Please check your kernel image\n");
                while (1)
                        ;
        }
    //将加载到内存的Main DT memcpy 到fdt(内存指针)
    memcpy(fdt, (void *)dtb_addr, dtb_size);
    ...
    // 将Overlay DT(odmdtbo.img)从dtbo分区加载到内存中并与Main DT合并,合并后的dtb存储在指针:g_fdt
    /*The memory pointed to by "g_fdt" is the location that the Linux kernel expects to find the device tree, and it is at least a few mega-bytes free. The merged device tree is therefore copied to that space.
    */
    // dtb_overlay函数的具体实现这里就不在展开了,相关代码比较好理解.
    bool rtn = dtb_overlay(fdt, dtb_size);
    
    ...
    if (platform_atag_append) {
        //将fdt追加到atag中,最终通过r2寄存器传递给kernel
        ret = platform_atag_append(fdt);
        if (ret) {
            assert(0);
            return FALSE;
        }
    }       
}
4. kernel中是如何使用获取到合并后的DT

在kernel-4.4/arch/arm/kernel/setup.c中,

// arch/arm/kernel/setup.c
void __init setup_arch(char **cmdline_p)
{
    const struct machine_desc *mdesc;

    setup_processor();
    /* arch/arm/kernel/head-common.S中赋值的.
    .long   __atags_pointer                 @ r6
    str     r2, [r6]                        @ Save atags pointer
    而r2是lk(bootloader)用于与内存通信的媒介,传递了atags的内存地址.
    r2包含了dtb pointer.
    */
    mdesc = setup_machine_fdt(__atags_pointer);   
}
参考链接

Google官方文档-Device Tree Overlays 【Device Tree】Device Tree 基础概念【Device Tree】Kernel中的gpio driver在DTS下是如何初始化的