首页 > 其他分享 >【IMX6ULL学习笔记】十三、DTS语法

【IMX6ULL学习笔记】十三、DTS语法

时间:2023-02-18 17:12:32浏览次数:52  
标签:compatible imx6ull fsl 语法 machine DTS IMX6ULL 节点 属性

一、设备树

设备树是一种描述硬件的数据结构,它起源于OpenFirmware(OF)。
在 Linux 2.6 中, ARM 架构的板极硬件细节过多地被硬编码在 arch/arm/plat-xxx 和 arch/arm/mach-xxx 中,采用设备树后,许多硬件的细节可以直接通过它传递给Linux,而不再需要在内核中进行大量的冗余编码。
DTS 是设备树源码文件,DTB 是将DTS 编译以后得到的二进制文件,将.dts 编译为.dtb 需要用到 DTC 工具!DTC 工具源码在 Linux 内核的 scripts/dtc 目录下,scripts/dtc/Makefile 文件内容如下:

hostprogs-y := dtc
always := $(hostprogs-y)

dtc-objs:= dtc.o flattree.o fstree.o data.o livetree.o treesource.o \
srcpos.o checks.o util.o
dtc-objs += dtc-lexer.lex.o dtc-parser.tab.o
......

DTC 工具依赖于 dtc.c、flattree.c、fstree.c 等文件,最终编译并链接出 DTC 这个主机文件。
编译 DTS 文件的话只需要进入到 Linux 源码根目录下,然后执行如下命令:

make all
或者:
make dtbs

二、DTS 语法

1、.dtsi 头文件

和 C 语言一样,设备树也支持头文件,设备树的头文件扩展名为.dtsi。在 imx6ull-alientek-emmc.dts 中有如下所示内容:

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

第 12 行,使用“#include”来引用“input.h”这个.h 头文件。
第 13 行,使用“#include”来引用“imx6ull.dtsi”这个.dtsi 头文件。

.dts 文件引用 C 语言中的.h 文件,甚至也可以引用.dts 文件,打开 imx6ull-14x14-evk-gpmi-weim.dts 这个文件,此文件中有如下内容:

#include "imx6ull-14x14-evk.dts"

因此在.dts 设备树文件中,可以通过“#include”来引用.h、.dtsi 和.dts 文件。

2、设备节点

设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设备节点,每个节点都通过一些属性信息来描述节点信息,属性就是键—值对。
以下是从imx6ull.dtsi 文件中缩减出来的设备树文件内容:

{
    aliases {
        can0 = &flexcan1;
    };
    
    cpus {
        #address-cells = <1>;
        #size-cells = <0>;

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

    intc: interrupt-controller@00a01000 {
        compatible = "arm,cortex-a7-gic";
        #interrupt-cells = <3>;
        interrupt-controller;
        reg = <0x00a01000 0x1000>,
        <0x00a02000 0x100>;
    };
}

第 1 行:“/”是根节点,每个设备树文件只有一个根节点。imx6ull.dtsi 和 imx6ull-alientek-emmc.dts 这两个文件都有一个“/”根节点,这两个“/”根节点的内容会合并成一个根节点。

第 2、6 和 17 行:aliases、cpus 和 intc 是三个子节点,节点命名格式如下:
node-name@unit-address

其中“node-name”是节点名字,为 ASCII 字符串,节点名字应该能够清晰的描述出节点的功能,比如“uart1”就表示这个节点是 UART1 外设。“unit-address”一般表示设备的地址或寄存器首地址,如果节点没有地址或者寄存器的话“unit-address”可以不要,比如“cpu@0”、“interrupt-controller@00a01000”。
示例代码中我们看到的节点命名却如下所示:

cpu0:cpu@0

上述命令并不是 “node-name@unit-address” 这样的格式,而是用“:”隔开成了两部分,“:”前面的是节点标签(label),“:”后面的才是节点名字,格式如下所示:

label: node-name@unit-address

引入 label 的目的就是为了方便访问节点,可以直接通过 &label 来访问这个节点,比如通过 &cpu0 就可以访问 “cpu@0” 这个节点,而不需要输入完整节点名字。
第 10 行:cpu0 也是一个节点,只是 cpu0 是 cpus 的子节点。

每个节点都有不同属性,不同的属性又有不同的内容,属性都是键值对,值可以为空或任意的字节流。设备树源码中常用的几种数据形式如下所示:
①、字符串

compatible = "arm,cortex-a7";

上述代码设置 compatible 属性的值为字符串“arm,cortex-a7”。
②、32 位无符号整数

reg = <0>;

上述代码设置 reg 属性的值为 0,reg 的值也可以设置为一组值,比如:

reg = <0 0x123456 100>;

③、字符串列表
属性值也可以为字符串列表,字符串和字符串之间采用“,”隔开,如下所示:
compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";

上述代码设置属性 compatible 的值为“fsl,imx6ull-gpmi-nand”和“fsl, imx6ul-gpmi-nand”。

3、标准属性

节点是由一堆的属性组成,节点都是具体的设备,不同的设备需要的属性不同,用户可以自定义属性。除了用户自定义属性,有很多属性是标准属性。

3.1、compatible 属性

compatible 属性也叫做“兼容性”属性,这是非常重要的一个属性!compatible 属性的值是一个字符串列表,compatible 属性用于将设备和驱动绑定起来。字符串列表用于选择设备所要使用的驱动程序,compatible 属性的值格式如下所示:

"manufacturer,model"

其中 manufacturer 表示厂商,model 一般是模块对应的驱动名字。
比如 imx6ull-alientek-emmc.dts 中 sound 节点是 I.MX6U-ALPHA 开发板的音频设备节点,I.MX6U-ALPHA 开发板上的音频芯片采用的欧胜(WOLFSON)出品的 WM8960,sound 节点的 compatible 属性值如下:

compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";

属性值有两个,分为“fsl,imx6ul-evk-wm8960”和“fsl,imx-audio-wm8960”,其中“fsl”表示厂商是飞思卡尔,“imx6ul-evk-wm8960”和“imx-audio-wm8960” 表示驱动模块名字。sound这个设备首先使用第一个兼容值在 Linux 内核里面查找,看看能不能找到与之匹配的驱动文件,如果没有找到的话就使用第二个兼容值查。
一般驱动程序文件都会有一个 OF 匹配表,OF 匹配表保存着一些 compatible 值,如果设备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动。比如在文件 imx-wm8960.c 中有如下内容:

static const struct of_device_id imx_wm8960_dt_ids[] = {
    { .compatible = "fsl,imx-audio-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,
    },
    .probe = imx_wm8960_probe,
    .remove = imx_wm8960_remove,
};

第 1~4 行:数组 imx_wm8960_dt_ids 就是 imx-wm8960.c 这个驱动文件的匹配表,此匹配表只有一个匹配值“fsl,imx-audio-wm8960”。如果在设备树中有哪个节点的 compatible 属性值与此相等,那么这个节点就会使用此驱动文件。
第 11 行:wm8960 采用了 platform_driver 驱动模式。设置.of_match_table 为 imx_wm8960_dt_ids,就是设置这个 platform_driver 所使用的OF匹配表。

3.2、model 属性

model 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的,比如:

model = "wm8960-audio";

3.3、status 属性

status 属性看名字就知道是和设备状态有关的,status 属性值也是字符串,字符串是设备的状态信息,可选的状态如表所示:
image

3.4、#address-cells 和#size-cells 属性

这两个属性的值都是无符号 32 位整形,#address-cells 和#size-cells 这两个属性可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。

address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位),#size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)。

address-cells 和 #size-cells 表明了子节点应该如何编写 reg 属性值,一般 reg 属性都是和地址有关的内容,和地址相关的信息有两种:起始地址和地址长度,reg 属性的格式一为:

reg = <address1 length1 address2 length2 address3 length3……>

每个“address length”组合表示一个地址范围,其中 address 是起始地址,length 是地址长度,#address-cells 表明 address 这个数据所占用的字长,#size-cells 表明 length 这个数据所占用的字长,如:

spi4 {
    compatible = "spi-gpio";
    #address-cells = <1>;
    #size-cells = <0>;

    gpio_spi: gpio_spi@0 {
        compatible = "fairchild,74hc595";
        reg = <0>;
    };
};

aips3: aips-bus@02200000 {
    compatible = "fsl,aips-bus", "simple-bus";
    #address-cells = <1>;
    #size-cells = <1>;

    dcp: dcp@02280000 {
        compatible = "fsl,imx6sl-dcp";
        reg = <0x02280000 0x4000>;
    };
};

第 3,4 行:节点 spi4 的 #address-cells = <1>,#size-cells = <0>,说明 spi4 的子节点 reg 属性中起始地址所占的字长为 1,地址长度所占的字长为0。
第 8 行:子节点 gpio_spi: gpio_spi@0 的 reg 属性值为 <0>,因为父节点设置了#address-cells = <1>,#size-cells = <0>,因此 addres=0,没有 length 的值,相当于设置了起始地址,而没有设置地址长度。
第 14,15 行:设置 aips3: aips-bus@02200000 节点 #address-cells = <1>,#size-cells = <1>,说明 aips3: aips-bus@02200000 节点起始地址长度所占用的字长为 1,地址长度所占用的字长也为 1。
第 19 行:子节点 dcp: dcp@02280000 的 reg 属性为<0x02280000 0x4000> ,因为父节点设置了#address-cells = <1>,#size-cells = <1>,address= 0x02280000,length= 0x4000,相当于设置了起始地址为 0x02280000,地址长度为 0x40000。

3.5、reg 属性

reg 属性前面已经提到过了,reg 属性的值一般是(address,length)对。reg 属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息,比如在 imx6ull.dtsi 中有如下内容:

uart1: serial@02020000 {
    compatible = "fsl,imx6ul-uart",
            "fsl,imx6q-uart", "fsl,imx21-uart";
    reg = <0x02020000 0x4000>;
    interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_UART1_IPG>,
            <&clks IMX6UL_CLK_UART1_SERIAL>;
    clock-names = "ipg", "per";
    status = "disabled";
};

上述代码是节点 uart1,uart1 节点描述了 I.MX6ULL 的 UART1 相关信息,重点是第 4 行的 reg 属性。
其中 uart1 的父节点 aips1: aips-bus@02000000 设置了#address-cells = <1>、#size-cells = <1>,因此 reg 属性 address=0x02020000,length=0x4000。查阅《I.MX6ULL 参考手册》可知,I.MX6ULL 的 UART1 寄存器首地址为 0x02020000,但是 UART1 的地址长度(范围)并没有 0x4000 这么多,这里我们重点是获取 UART1 寄存器首地址。

3.6、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 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换,对于我们所使用的 I.MX6ULL 来说,子地址空间和父地址空间完全相同,因此会在 imx6ull.dtsi 中找到大量的值为空的 ranges 属性,如下所示:

soc {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "simple-bus";
    interrupt-parent = <&gpc>;
    ranges;
......
}

ranges 属性不为空的示例代码如下所示:

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

    #size-cells = <1>;
    ranges = <0x0 0xe0000000 0x00100000>;
    serial {
        device_type = "serial";
        compatible = "ns16550";
        reg = <0x4600 0x100>;
        clock-frequency = <0>;
        interrupts = <0xA 0x8>;
        interrupt-parent = <&ipic>;
    };
};

第 5 行:节点 soc 定义的 ranges 属性,为<0x0 0xe0000000 0x00100000>,此属性值指定了一个 1024KB(0x00100000) 的地址范围,子地址空间的物理起始地址为 0x0,父地址空间的物理起始地址为 0xe0000000。
第 10 行:serial 是串口设备节点,reg 属性定义了 serial 设备寄存器起始地址为 0x4600,寄存器长度为 0x100。经地址转换,serial 设备从 0xe0004600 开始进行读写操作,0xe0004600=0x4600+0xe0000000。

3.7、name 属性

name 属性值为字符串,name 属性用于记录节点名字,name 属性已经被弃用,不推荐使用 name 属性,一些老的设备树文件可能会使用此属性。

3.8、device_type 属性

device_type 属性值为字符串,IEEE 1275 会用到此属性,用于描述设备的 FCode,但是设备树没有 FCode,所以此属性也被抛弃了。此属性只能用于 cpu 节点或者 memory 节点。
imx6ull.dtsi 的 cpu0 节点用到了此属性,内容如下所示:

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

关于标准属性就讲解这么多,其他的比如中断、IIC、SPI 等使用的标准属性等到具体的例程再讲解。

4、根节点 compatible 属性

每个节点都有 compatible 属性,根节点“/”也不例外,imx6ull-alientek-emmc.dts 文件中根节点的 compatible 属性内容如下所示:

/ {
    model = "Freescale i.MX6 ULL 14x14 EVK Board";
    compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
......
}

compatible 有两个值:“fsl,imx6ull-14x14-evk”和“fsl,imx6ull”。前面说了,设备节点的 compatible 属性值是为了匹配 Linux 内核中的驱动程序,那么根节点中的 compatible 属性是为了做什么工作的?通过根节点的 compatible 属性可以知道我们所使用的设备,一般第一个值描述了所使用的硬件设备名字,比如这里使用的是“imx6ull-14x14-evk”这个设备,第二个值描述了设备所使用的 SOC,比如这里使用的是“imx6ull”这颗 SOC。Linux 内核会通过根节点的 compoatible 属性查看是否支持此设备,如果支持的话设备就会启动 Linux 内核。
接下来学习一下 Linux 内核在使用设备树前后是如何判断是否支持某款设备的。

1、使用设备树之前设备匹配方法

在没有使用设备树以前,uboot 会向 Linux 内核传递一个叫做 machine id 的值,machine id 也就是设备 ID,告诉 Linux 内核自己是个什么设备,看看 Linux 内核是否支持。Linux 内核是支持很多设备的,针对每一个设备(板子),Linux内核都用 MACHINE_START 和 MACHINE_END 来定义一个 machine_desc 结构体来描述这个设备,比如在文件 arch/arm/mach-imx/mach-mx35_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,
    .reserve = mx35_3ds_reserve,
    .restart = mxc_restart,
MACHINE_END

上述代码就是定义了“Freescale MX35PDK”这个设备,其中 MACHINE_START 和 MACHINE_END 定义在文件 arch/arm/include/asm/mach/arch.h 中,内容如下:

#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
    .nr = MACH_TYPE_##_type, \
    .name = _name,
#define MACHINE_END \
};

根据 MACHINE_START 和 MACHINE_END 的宏定义,将代码展开后如下所示:

static const struct machine_desc __mach_desc_MX35_3DS \
    __used \
    __attribute__((__section__(".arch.info.init"))) = {
        .nr = MACH_TYPE_MX35_3DS,
        .name = "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,
        .reserve = mx35_3ds_reserve,
        .restart = mxc_restart,
    };

从代码中可以看出,这里定义了一个 machine_desc 类型的结构体变量__mach_desc_MX35_3DS , 这个变量存储在 “ .arch.info.init ” 段中。
第 4 行: MACH_TYPE_MX35_3DS 就是“ Freescale MX35PDK ”这个板子的machine id 。MACH_TYPE_MX35_3DS 定义在文件 include/generated/mach-types.h 中,此文件定义了大量的 machine id,内容如下所示:

#define MACH_TYPE_EBSA110 0
#define MACH_TYPE_RISCPC 1
#define MACH_TYPE_EBSA285 4
#define MACH_TYPE_NETWINDER 5
#define MACH_TYPE_CATS 6
#define MACH_TYPE_SHARK 15
#define MACH_TYPE_BRUTUS 16
#define MACH_TYPE_PERSONAL_SERVER 17
......
#define MACH_TYPE_MX35_3DS 1645
......
#define MACH_TYPE_PFLA03 4575

第 10 行就是 MACH_TYPE_MX35_3DS 的值,为 1645。前面说了,uboot 会给 Linux 内核传递 machine id 这个参数,Linux 内核会检查这个 machine id,其实就是将 machine id 与这些 MACH_TYPE_XXX 宏进行对比,看看有没有相等的,如果相等的话就表示 Linux 内核支持这个设备,如果不支持的话那么这个设
备就没法启动 Linux 内核。

2、使用设备树以后的设备匹配方法

当 Linux 内核引入设备树以后就不再使用 MACHINE_START 了,而是换为了
DT_MACHINE_START。
DT_MACHINE_START 也定义在文件 arch/arm/include/asm/mach/arch.h
里面,定义如下:

#define DT_MACHINE_START(_name, _namestr)            \
static const struct machine_desc __mach_desc_##_name \
__used                                               \
__attribute__((__section__(".arch.info.init"))) = {  \
    .nr = ~0,                                        \
    .name = _namestr,

可以看出,DT_MACHINE_START 和 MACHINE_START 基本相同,只是 .nr 的设置不同,在 DT_MACHINE_START 里面直接将 .nr 设置为~0。说明引入设备树以后不会再根据 machine id 来检查 Linux 内核是否支持某个设备了。
打开文件 arch/arm/mach-imx/mach-imx6ul.c,有如下所示内容:

static const char *imx6ul_dt_compat[] __initconst = {
    "fsl,imx6ul",
    "fsl,imx6ull",
    NULL,
};

DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)")
    .map_io = imx6ul_map_io,
    .init_irq = imx6ul_init_irq,
    .init_machine = imx6ul_init_machine,
    .init_late = imx6ul_init_late,
    .dt_compat = imx6ul_dt_compat,
MACHINE_END

machine_desc 结构体中有个 .dt_compat 成员变量,此成员变量保存着本设备兼容属性,代码中设置.dt_compat = imx6ul_dt_compat, imx6ul_dt_compat 表里面有"fsl,imx6ul"和"fsl,imx6ull"这两个兼容值。只要某个设备(板子)根节点“/”的 compatible 属性值与 imx6ul_dt_compat 表中的任何一个值相等,那么就表示 Linux 内核支持此设备。imx6ull-alientek-emmc.dts 中根节点的 compatible 属性值如下:

compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

其中“fsl,imx6ull”与 imx6ul_dt_compat 中的“fsl,imx6ull”匹配,因此 I.MX6U-ALPHA 开发板可以正常启动 Linux 内核。
如果将 imx6ull-alientek-emmc.dts 根节点的 compatible 属性改为
其他的值,比如:

compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ullll"

重新编译 DTS,并用新的 DTS 启动 Linux 内核,结果如图 43.3.4.1 所示的错误提示:
image
当我们修改了根节点 compatible 属性内容以后,因为 Linux 内核找不到对应的设备,因此 Linux 内核无法启动。在 uboot 输出 Starting kernel…以后就再也没有其他信息输出了。

接下来简单看一下 Linux 内核是如何根据设备树根节点的 compatible 属性来匹配出对应的 machine_desc,Linux 内核调用 start_kernel 函数来启动内核,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();
    mdesc = setup_machine_fdt(__atags_pointer);
    if (!mdesc)
        mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
        
    machine_desc = mdesc;
    machine_name = mdesc->name;
......
}

第 6 行:调用 setup_machine_fdt 函数来获取匹配的 machine_desc,参数就是 atags 的首地址,也就是 uboot 传递给 Linux 内核的 dtb 文件首地址,setup_machine_fdt 函数的返回值就是找到的最匹配的 machine_desc。
函数 setup_machine_fdt 定义在文件 arch/arm/kernel/devtree.c 中,内容如下(有缩减):

const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{
    const struct machine_desc *mdesc, *mdesc_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_arch_type = mdesc->nr;

    return mdesc;
}

第 9 行:调用函数 of_flat_dt_match_machine 获取匹配的 machine_desc,参数 mdesc_best 是默认的 machine_desc ,参数 arch_get_next_mach 是 个函数,此函数定义在定义在 arch/arm/kernel/devtree.c 文件中。找到匹配的 machine_desc 的过程就是用设备树根节点的 compatible 属性值和 Linux 内核中 machine_desc 下 .dt_compat 的值比较,看看哪个相等,如果相等的话就表示找到匹配的 machine_desc,arch_get_next_mach 函数的工作就是获取 Linux 内核中下一个 machine_desc 结构体。

最后再来看一下 of_flat_dt_match_machine 函数,此函数定义在文件 drivers/of/fdt.c 中,内容如下(有缩减):

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(&compat))) {
        score = of_flat_dt_match(dt_root, compat);
        if (score > 0 && score < best_score) {
            best_data = data;
            best_score = score;
        }
    }
......

    pr_info("Machine model: %s\n", of_flat_dt_get_machine_name());

    return best_data;
}

第 9 行:通过函数 of_get_flat_dt_root 获取设备树根节点。
第 10~16 行:此循环就是查找匹配的 machine_desc 过程,第 11 行的 of_flat_dt_match 函数会将根节点 compatible 属性的值和每个 machine_desc 结构体中 .dt_compat 的值进行比较,直至找到匹配的那个 machine_desc。

总结

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

三、向节点追加或修改内容

先看一下 I2C1 接口对应的节点,打开文件 imx6ull.dtsi 文件,找到如下所示内容:

i2c1: i2c@021a0000 {
    #address-cells = <1>;
    #size-cells = <0>;
    compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
    reg = <0x021a0000 0x4000>;
    interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_I2C1>;
    status = "disabled";
};

示例代码是 I.MX6ULL 的 I2C1 节点,现在要在 i2c1 节点下创建一个子节点,这个子节点就是 fxls8471,最简单的方法就是在 i2c1 下直接添加一个名为 fxls8471 的子节点,如下所示:

i2c1: i2c@021a0000 {
    #address-cells = <1>;
    #size-cells = <0>;
    compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
    reg = <0x021a0000 0x4000>;
    interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_I2C1>;
    status = "disabled";

    //fxls8471 子节点
    fxls8471@1e {
        compatible = "fsl,fxls8471";
        reg = <0x1e>;
    };
};

第 11~14 行:添加的 fxls8471 这个芯片对应的子节点。但是这样会有个问题!i2c1 节点是定义在 imx6ull.dtsi 文件中的,而 imx6ull.dtsi 是设备树头文件,其他所有使用到 I.MX6ULL 这颗 SOC 的板子都会引用 imx6ull.dtsi 这个文件。直接在 i2c1 节点中添加 fxls8471 就相当于在其他的所有板子上都添加了 fxls8471 这个设备,但是其他的板子并没有这个设备!
因此,这里就要引入另外一个内容,那就是如何向节点追加数据。I.MX6U-ALPHA 开发板使用的设备树文件为 imx6ull-alientek-emmc.dts,因此我们需要在
imx6ull-alientek-emmc.dts 文件中完成数据追加的内容,方式如下:

&i2c1 {
    /* 要追加或修改的内容 */
};

第 1 行:&i2c1 表示要访问 i2c1 这个 label 所对应的节点,也就是 imx6ull.dtsi 中的“i2c1: i2c@021a0000”。
第 2 行:花括号内就是要向 i2c1 这个节点添加的内容,包括修改某些属性的值。
打开 imx6ull-alientek-emmc.dts,找到如下所示内容:

&i2c1 {
    clock-frequency = <100000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c1>;
    status = "okay";

    mag3110@0e {
        compatible = "fsl,mag3110";
        reg = <0x0e>;
        position = <2>;
    };

    fxls8471@1e {
        compatible = "fsl,fxls8471";
        reg = <0x1e>;
        position = <0>;
        interrupt-parent = <&gpio5>;
        interrupts = <0 8>;
    };
};

示例代码就是向 i2c1 节点添加或修改数据,比如:
第 2 行:属性“clock-frequency”表示 i2c1 时钟为 100KHz。“clock-frequency”就是新添加的属性。
第 5 行:将 status 属性的值由原来的 disabled 改为 okay。
第 7~11 行:i2c1 子节点 mag3110,因为 NXP 官方开发板在 I2C1 上接了一个磁力计芯片 mag3110。
第 13~19 行:i2c1 子节点 fxls8471,同样是因为 NXP 官方开发板在 I2C1 上接了 fxls8471 这颗六轴芯片。

四、绑定信息文档

设备树是用来描述板子上的设备信息的,不同的设备其信息不同,反映到设备树中就是属性不同。Linux 内核源码中有详细的 .txt 文档描述了如何添加节点,这些.txt 文档叫做绑定文档,路径为:/Documentation/devicetree/binding,我们在设备树中添加一个硬件对应的节点的时候可以查阅。

比如我们现在要想在 I.MX6ULL 这颗 SOC 的 I2C 下添加一个节点,那么就可以查看 Documentation/devicetree/bindings/i2c/i2c-imx.txt,此文档详细的描述了 I.MX 系列的 SOC 如何在设备树中添加 I2C 设备节点,文档内容如下所示:

* Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MX

Required properties:
- compatible :
    - "fsl,imx1-i2c" for I2C compatible with the one integrated on i.MX1
SoC
    - "fsl,imx21-i2c" for I2C compatible with the one integrated on i.MX21
SoC
    - "fsl,vf610-i2c" for I2C compatible with the one integrated on Vybrid
vf610 SoC
- reg : Should contain I2C/HS-I2C registers location and length
- interrupts : Should contain I2C/HS-I2C interrupt
- clocks : Should contain the I2C/HS-I2C clock specifier

Optional properties:
- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz.
The absence of the propoerty indicates the default frequency 100 kHz.
- dmas: A list of two dma specifiers, one for each entry in dma-names.
- dma-names: should contain "tx" and "rx".

Examples:
    
i2c@83fc4000 { /* I2C2 on i.MX51 */
    compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";
    reg = <0x83fc4000 0x4000>;
    interrupts = <63>;
};

i2c@70038000 { /* HS-I2C on i.MX51 */
    compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";
    reg = <0x70038000 0x4000>;
    interrupts = <64>;
    clock-frequency = <400000>;
};

i2c0: i2c@40066000 { /* i2c0 on vf610 */
    compatible = "fsl,vf610-i2c";
    reg = <0x40066000 0x1000>;
    interrupts =<0 71 0x04>;
    dmas = <&edma0 0 50>,
        <&edma0 0 51>;
    dma-names = "rx","tx";
};

标签:compatible,imx6ull,fsl,语法,machine,DTS,IMX6ULL,节点,属性
From: https://www.cnblogs.com/KuDianWanJia/p/17130697.html

相关文章

  • 【IMX6ULL学习笔记】十一、LED字符设备
    一、地址映射MMU全称叫做MemoryManageUnit,也就是内存管理单元。MMU主要完成的功能如下:①、完成虚拟空间到物理空间的映射。②、内存保护,设置存储器的访问权限,设置......
  • 【IMX6ULL学习笔记】十二、Linux新字符设备
    一、新字符设备驱动原理1.1分配和释放设备号使用register_chrdev函数注册字符设备的时候只需要给定一个主设备号即可,但是这样会带来两个问题:①、需要我们事先确定好......
  • 【Java-01】java基础-基本语法
    1、基本输出语句/**java*多行注释*///java单行注释publicclass_01_HelloWorld{publicstaticvoidmain(String[]args){//main方法System.......
  • MarkDown语法
    #MarkDown学习 ##标题 ###三级标题   ##字体: **Helloworld!***Helloworld!****Helloworld!***~~Helloworld!~~ ##引用 >重新学习java,转开......
  • Thymeleaf常用语法:模板文件中表达式调用Java类的方法
    在模板文件的表达式中,可以使用“${T(全限定类名).方法名(参数)}”这种格式来调用Java类的静态方法。开发环境:IntelliJIDEA2019.2.2SpringBoot版本:2.1.8新建一个名称......
  • Markdown语法
    #本文章记录了Markdown的一些基本语法,比如标题、字体、图片快捷键等1、标题一级标题:#+空格+标题内容二级标题:##+空格+标题内容三级标题、四级标题、五级标题......以......
  • 强记 分支结构语法 java 230217
    单路分支//格式if(条件){条件成立时要做的事情;}//例子intage=19;if(age>18){System.out.println("天天网吧欢迎你");}二路分支//格式if(条件){......
  • 【IMX6ULL学习笔记】八、Linux启动流程
    一、链接脚本vmlinux.ldsLinux内核的链接脚本文件arch/arm/kernel/vmlinux.lds中有如下代码:ENTRY指明了了Linux内核入口,入口为stext,stext定义在文件arch/arm/k......
  • 【IMX6ULL学习笔记】九、Linux内核移植
    一、在Linux中添加自己的开发板1、添加开发板默认配置文件将arch/arm/configs目录下的imx_v7_mfg_defconfig重新复制一份,命名为imx_kodo_emmc_defconfig,命令如......
  • 【IMX6ULL学习笔记】四、 U-BOOT启动流程
    一、链接脚本u-boot.lds详解要分析uboot的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。......