DTS 语法

    .dtsi 头文件

             设备树支持头文件,设备树的头文件扩展名为.dtsi

//linux-5.5.4\linux-5.5.4\arch\arm\boot\dts\s5pv210-smdkv210.dts

// SPDX-License-Identifier: GPL-2.0
/*
 * Samsung's S5PV210 SoC device tree source
 *
 * Copyright (c) 2013-2014 Samsung Electronics, Co. Ltd.
 *
 * Mateusz Krawczuk <m.krawczuk@partner.samsung.com>
 * Tomasz Figa <t.figa@samsung.com>
 *
 * Board device tree source for YIC System SMDV210 board.
 *
 * NOTE: This file is completely based on original board file for mach-smdkv210
 * available in Linux 3.15 and intends to provide equivalent level of hardware
 * support. Due to lack of hardware, _no_ testing has been performed.
 */

/dts-v1/;

// #include 来引用.h .dtsi  .dts 文件

#include <dt-bindings/input/input.h>
#include "s5pv210.dtsi"
//...

          .dtsi 文件 描述 SOC 的内部外设信息 (CPU架构  主频  外设寄存器地址范围)

// linux-5.5.4\linux-5.5.4\arch\arm\boot\dts\s5pv210.dtsi

// SPDX-License-Identifier: GPL-2.0
/*
 * Samsung's S5PV210 SoC device tree source
 *
 * Copyright (c) 2013-2014 Samsung Electronics, Co. Ltd.
 * 版权所有(c)2013-2014三星电子有限公司
 *
 * Mateusz Krawczuk <m.krawczuk@partner.samsung.com>
 * Tomasz Figa <t.figa@samsung.com>
 *
 * Samsung's S5PV210 SoC device nodes are listed in this file. S5PV210
 * based board files can include this file and provide values for board specfic
 * bindings.
 * 三星的S5PV210 SoC设备节点列在这个文件中
 * 基于S5PV210的板文件可以包含此文件并为板特定绑定提供值
 *
 * Note: This file does not include device nodes for all the controllers in
 * S5PV210 SoC. As device tree coverage for S5PV210 increases, additional
 * nodes can be added to this file.
 * 注意:此文件不包括S5PV210 SoC中所有控制器的设备节点
 * 随着S5PV210的设备树覆盖率的增加,可以向该文件添加额外的节点
 */

#include <dt-bindings/clock/s5pv210.h>
#include <dt-bindings/clock/s5pv210-audss.h>

/ {                        // 根节点 /
	#address-cells = <1>;
	#size-cells = <1>;

	aliases {                //子节点
   	    csis0 = &csis0;
	    //...
	};

	cpus {                  //子节点
  	    #address-cells = <1>;
	    #size-cells = <0>;

	    cpu@0 {                    // cpus 的子节点
		 device_type = "cpu";
		 compatible = "arm,cortex-a8";    //架构信息

                 // reg 属性为 0,数据形式:32 位无符号整数
		 reg = <0>;
	    };
	};
        soc {

            // : 前为 节点标签(label)
            // :后为 节点名 是 ASCII 字符串
            // @ 后为 设备的地址 或 寄存器首地址
            // 可 &spi0 访问该节点
            spi0: spi@e1300000 { 
   
                // compatible 属性为 "samsung,s5pv210-spi",数据形式:字符串
		compatible = "samsung,s5pv210-spi";

                // reg属性为 一组值
		reg = <0xe1300000 0x1000>;
		interrupt-parent = <&vic1>;
		interrupts = <15>;
		dmas = <&pdma0 7>, <&pdma0 6>;
		dma-names = "tx", "rx";
		clocks = <&clocks SCLK_SPI0>, <&clocks CLK_SPI0>;
		clock-names = "spi", "spi_busclk0";
		pinctrl-names = "default";
		pinctrl-0 = <&spi0_bus>;
		#address-cells = <1>;
		#size-cells = <0>;
		status = "disabled";
	    };
        };

    //...
};

#include "s5pv210-pinctrl.dtsi"


     标准属性

                   节点是很多属性组成,节点是具体的设备

                  不同的设备有属性不同,用户可自定义属性

         compatible 属性

static const struct of_device_id imx_wm8960_dt_ids[] =    //匹配表
{
    {
        // compatible 属性 也叫 “兼容性”属性
        // compatible 属性将 设备 和 驱动 绑定起来
        // 厂商:fsl 
        // 驱动名字:imx-dudio-wm8960
        .compatible = "fsl, imx-dudio-wm8960",            //配置值
    },
    {
        /*sentinel*/
    }
};
MODULE_DEVICE_TABLE(of, imx_wm8960_dt_ids);

static struct platform_driver imx_wm8960_driver =
{
    .driver =
    {
        .name = "imx-wm8960",
        .pm = &snd_soc_pm_ops,
        .of_match_table = imx_wm8960_dt_ids,    //platform_driver驱动模式,使用OF匹配表
    },
    .probe = imx_wm8960_probe,
    .remore = imx_wm8960_remore,
};

        model属性         

model = "wm8960-audio";    //设备模块信息

       status 属性

               表示 设备的状态信息

描述
“okay” 设备可操作
"disabled" 设备不可操作,具体看设备绑定文件
"fail" 设备不可操作,检测一系列的错误
"fail-sss" sss:检测错误内容

     #address-cells #size-cells 属性

           #address-cells 属性值 决定 子节点reg属性的 地址信息 占用字长(32位)

           #size-cells 属性值 决定 子节点reg属性的 长度信息 占用字长(32位)

// address length组合表示一个地址的范围
// address 表示 起始地址
// length 表示 地址长度
// #address-cells 表明 address 数据占用的字长
// #size-cells 表明 length 数据占用的字长
reg = <address1 length1 address2 length2 ...>
spi4
{
    compatible = "spi-gpio";
    #address-cells = <1>;    //起始地址为1 字节
    #size-cells = <0>;       //地址长度为 0 字节
    gpio_spi:gpio_spi@0
    {
        compatible = "fairchild, 74hc595";
        reg = <0>;            // reg属性为 0
    };
};

aips3:aips-bus@02200000
{
    comptible = "fsl, aips-bus", "simple-bus";
    #address-cells = <1>;     // 起始地址长度占用 为 1 字节
    #size-cells = <1>;        // 地址长度占用 为 1字节
    dcp:dcp@02280000
    {
        compatible = "fsl, imx6sl-dcp";

        //起始地址:0x02280000
        //地址长度:0x40000
        reg = <0x02280000 0x4000>;   // 描述设备地址空间资源信息
    };
};

          ranges 属性

                ranges属性值 可为 空 或者 按照(child-bus-address , parent-bus-address , length)格式编写的数字矩阵

                ranges :地址映射 / 转换表

                ranges 属性每个项目 由 子地址、父地址和 地址空间长度 三部分组成:

                      child-bus-address:子总线地址空间的物理地址,由父节点 #address-cells 确定 物理地址 的占用字长

                      parent-bus-address:父总线地址空间的物理地址,由父节点 #address-cells 确定 物理地址 的占用字长

                      length:子地址空间的长度,由 父节点#size-cells确定 地址长度 的占用字长

              ranges 属性值为空,说明 子地址空间 和 父地址空间 一样

soc
{
    compatible = "simple-bus";
    #address-cells = <1>;
    #size-cells = <1>;

    // 子地址空间的物理起始地址为 0x0
    // 父地址空间的物理起始地址为 0xe0000000
    // 地址范围 0x00100000(1024KB)
    ranges = <0x0 0xe0000000 0x00100000>;   
 
    serial                        // 串口设备节点
    {
        device_type = "serial";
        compatible = "ns16550";

        // 起始地址 0x4600
        // 寄存器长度 0x100
        // serial设备从 0xe0004600(0x4600 + 0xe0000000)开始读写
        reg = <0x4600 0x100>;
        clock-frequency = <0>;
        interrupts = <0xA 0x8>;
        interrupt-parent = <&ipic>;
    };
};

          device_type 属性

                   device_type 描述设备的 FCode

                   IEEE 1275 会用到 device_type

                   device_type属性只用于 cpu 节点 或者 memory 节点

cpu0:cpu@0
{
    compatible = "arm, cortex-a7";
    device_type = "cpu";
    reg = <0>;
    //...
}


     根节点 compatible 属性

              

/
{
    model = "Freescale i.MX6_ULL_14x14_EVK_Board";
    compatible = "fsl,imx6ull-14x14-evk","fsl, imx6ull";    //匹配Linux内核中的驱动程序
    //...
}

          没用设备树时设备匹配方法

// arch/arm/mach-imx/machmx35_3ds.c

MACHINE_START(MX35_3DS, "Freescale MX35PDK")
    /* Maintainer: Freescale Semiconductor, Inc */
    /* 维护者:飞思卡尔半导体公司 */

    .atag_offset = 0x100,
    .map_io = mx35_map_io,
    .init_early = imx35_init_early,
    .init_irq = mx35_init_irq,
    .init_time = mx35pdk_timer_init,
    .init_machine = mx35_3ds_init,
    .init_late = mx35_3ds_late_init,
    .restart = mxc_restart,
MACHINE_END

     MACHINE_START  和  MACHINE_END 定义

// arch/arm/include/asm/mach/arch.h

/*
 * 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,    \    // machine id 设备ID
    .name    = _name,

#define    MACHINE_END    };

      设备树的引入,已经不使用这个方法了

         用设备树的设备匹配方法

// arch/arm/include/asm/mach/arch.h

#define    DT_MACHINE_START(_name, _namestr)    \
static const struct machine_desc __mach_desc_##_name    \
__usend                    \
__attribute__((__section__(".arch.info.init"))) = {    \
    .nr    = ~0,            //设备树不靠machine id 检查Linux内核是否支持某设备
    .name    = _namestr,
// linux-5.5.4/arch/arm/mach-s5pv210/s5pv210.c

static char const *const s5pv210_dt_compat[] __initconst=
{
    "samsung, s5pc110",
    "samsung, s5pv210",
    NULL
};

DT_MACHINE_START(SSPV210_DT, "Samsung SSPC110/SSPV210-based board")
    // 保存着本设备兼容属性
    // 判断根节点compatible属性值与它是否相等
    // 相等,Linux内核支持该设备
    .dt_compat = s5pv210_dt_compat,        
    .map_io = s5pv210_dt_map_io,
    .restart = s5pv210_dt_restart,
    .init_late = s5pv210_dt_init_late,
MACHINE_END

  Linux 内核调用 start_kernel 函数启动内核

// linux-5.5.4/init/main.c

asmlinkage __visible void __init start_kernel(void)
{
    char *command_line;
    char *after_dashes;

    /*
	 * Interrupts are still disabled. Do necessary setups, then
	 * enable them.
         * 中断禁用 设置启动函数
	 */
    setup_arch(&command_line);
    // ...
}

start_kernel 函数会调用setup_arch 函数来匹配 machine_desc

// arch/arm/kernel/setup.c

void __init setup_arch(char **cmdline_p)
{
    const struct machine_desc *mdesc;

    setup_processor();

    // 获取匹配的machine_desc
    // atags的首地址(Linux 内核的 dtb 文件首地址):__atags_pointer
    mdesc = setup_machine_fdt(__atags_pointer);    
    if(!mdesc)
    {
        mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
    }
    if(!mdesc)
    {
        early_printf(" \nError:invalid dtb and unrecognized/unsupported machine ID\n");
        early_printf(" r1 = 0x%08x, r2 = 0x%08x\n",__machine_arch_type, __atags_pointer);
        if(__atags_pointer)
        {
            early_print("")
        }
        dump_machine_table();
    }
    
    machine_desc = mdesc;
    machine_name = madesc->name;
    dump_stack_set_arch_desc("%s",mdesc->name);
}

 

// arch/arm/kernel/devtree.c

/**
 * setup_machine_fdt - Machine setup when an dtb was passed to the kernel
 * setup_machine_fdt-将dtb传递到内核的 machine 设置
 *
 * @dt_phys: physical address of dt blob
 * @dt_phys: dt blob的物理地址
 *
 * If a dtb was passed to the kernel in r2, then use it to choose the
 * correct machine_desc and to setup the system.
 * 如果在r2中将dtb传递给内核,则使用它来选择正确的machine_desc并设置系统
 */
const struct machine_desc *__init setup_machine_fdt(unsigned int dt_phys)
{
    const struct machine_desc *__init setup_best = NULL;
    
    if(!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))
    {
        return NULL;
    }
    mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);//获取匹配的machine_desc
    if(!mdesc)
    {
        //...
    }
    //...
    /* Change machine number to match the mdesc we're using */
    __machine_arch_type = mdesc->nr;
}

// linux-5.5.4/arch/arm/kernel/devtree.c
// 获取 Linux 内核中下一个 machine_desc 结构体
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;
}

//  linux-5.5.4/drivers/of/fdt.c
/**
 * of_flat_dt_match_machine - Iterate match tables to find matching machine.
 *                            迭代匹配表以找到匹配的 machine
 *
 * @default_match: A machine specific ptr to return in case of no match.
 *                在不匹配的情况下返回的 machine 具体 ptr
 * @get_next_compat: callback function to return next compatible match table.
 *                   返回下一个兼容匹配表的回调函数
 *
 * Iterate through machine match tables to find the best match for the machine
 * compatible string in the FDT.
 * 遍历machine 匹配表以在FDT中找到与 machine 兼容的字符串的最佳匹配
 */
const void * __init of_flat_dt_match_machine(const void *default_match,
                                            const void *(*get_next_compat)(const char *const**))
{
    const void *data = NULl;
    const void *best_data = default_match;
    const char *const *compat;
    unsigned long dt_root;
    unsigned int best_score = ~1,score = 0;

    dt_root =of_get_flat_dt_root();    // 获取设备树根节点
    while(( data = get_next_compat(&compet) ))    // 查找匹配的 machine_desc 
    {
        score = of_flag_dt_match(dt_root,compat); //根节点 compatible 与每个dt_compat 比较
        if(score > 0 && score < best_score)
        {
            best_data = data;
            best_score = score;
        }
    }
    if(!best_data)
    {
        const char *prop;
        int size;
        if(prop)
        {
            //...
        }
        return NULL;
    }
    pr_info("Machine model: %s\n", of_flat_dt_get_machine_name() );
    return best_data;
}

      Linux 内核通过根节点 compatible 属性找到对应的设备的函数调用过程
  


     向节点追加或修改内容

            设备树是描述板子硬件信息的文件

           追加节点不能在  xxx.dtsi

            xxx.dtsi 是设备树头文件

 

// xxx.dts

&i2cl    //访问 i2c1
{
    clock-frequency = <100000>;    // 时钟为 100KHz
    pinctrl-names = "defaulft";
    pinctrl-0 = <&pinctrl_i2cl>;
    status = "okay";

    mag31100@0e        // 子节点
    {
        compatible = "fsl, mag3110";
        reg = <0x0e>;
        position = <2>;
    };
    
    fxls8471@1e    //子节点
    {
        compatible = "fsl,fxls8471";
        reg = <0x1e>;
        position = <0>;
        interrupt-parent = <&gpio5>;
        interrupts = <0 8>;
    };
};