设备树详解

1. 设备树概念

设备树常用概念有:dtc(用来编译设备树的工具)、dts(设备树描述文件)、dtsi(设备树头文件)、dtb(编译后的二进制文件)。

dtc设备树编译工具:dtc是用来编译设备树的工具,就像gcc可以用来编译C语言一样,设备树源文件也需要编译器来对它进行编译,而这个编译器就是dtc。dtc编译工具的源代码在内核根目录下的/scripts/dtc文件夹中,在对应的Makefile中可以看到内核将跟dtc有关的文件都进行编译,最后生成一个可执行程序。"make dtbs"可以对设备树文件进行编译生成设备文件后缀为xxx.dtb。

# 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

# Source files need to get at the userspace version of libfdt_env.h to compile

HOSTCFLAGS_DTC := -I$(src) -I$(src)/libfdt

HOSTCFLAGS_checks.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_data.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_dtc.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_flattree.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_fstree.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_livetree.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_srcpos.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_treesource.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_util.o := $(HOSTCFLAGS_DTC)

HOSTCFLAGS_dtc-lexer.lex.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_dtc-parser.tab.o := $(HOSTCFLAGS_DTC)

# dependencies on generated files need to be listed explicitly
$(obj)/dtc-lexer.lex.o: $(obj)/dtc-parser.tab.h

# generated files need to be cleaned explicitly
clean-files	:= dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h

dts/dtsi设备树源文件:dts是设备树的源文件,驱动开发者需要在dts文件中构造描述板级的设备树,其位置一般位于内核根目录下的/arch/arm/boot/dts(以arm架构为例)。dts编写好之后会用dtc来进行编译,最后会生成dtb二进制文件。

dtsi设备树头文件:设备树就像C语言,同样可以进行头文件包含。开发者通常会把同一个品牌芯片的共同点提取出来,将相同的代码写成一个头文件供其他文件包含。包含dtsi文件跟C语言的头文件包含用法一样,也是include"xxx.dtsi"。

dtb设备树二进制文件:dtb是由dts/dtsi被编译后生成的二进制文件,也是最终真正的设备树,dtb文件是可以被内核解析的文件,所以dtb文件会被烧到系统中,由uboot指定dtb的地址供内核去读取。

 2. 设备树的结构

以imx6dl-hummingboard.dts以及imx6dl.dtsi文件为例,来理解设备树结构。

  • imx6dl-hummingboard.dts 文件节选
    /dts-v1/;
    #include "imx6dl.dtsi"
    #include "imx6qdl-microsom.dtsi"
    #include "imx6qdl-microsom-ar8035.dtsi"
     
    / {
        model = "SolidRun HummingBoard DL/Solo";
        compatible = "solidrun,hummingboard", "fsl,imx6dl";
     
        ir_recv: ir-receiver {
            compatible = "gpio-ir-receiver";
            gpios = <&gpio1 2 1>;
            pinctrl-names = "default";
            pinctrl-0 = <&pinctrl_hummingboard_gpio1_2>;
        };
     
        regulators {
            compatible = "simple-bus";
     
            reg_3p3v: 3p3v {
                compatible = "regulator-fixed";
                regulator-name = "3P3V";
                regulator-min-microvolt = <3300000>;
                regulator-max-microvolt = <3300000>;
                regulator-always-on;
            };
        }
     
    &i2c1 {
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_hummingboard_i2c1>;
     
        rtc: pcf8523@68 {
            compatible = "nxp,pcf8523";
            reg = <0x68>;
        };
    };
  • imx6dl.dtsi文件节选
    / {
        aliases {
     
        /*省略无关代码*/
        }
        soc {
            #address-cells = <1>;
            #size-cells = <1>;
            compatible = "simple-bus";
            interrupt-parent = <&intc>;
            ranges;
     
            /*省略无关代码*/
     
            timer@00a00600 {
                compatible = "arm,cortex-a9-twd-timer";
                reg = <0x00a00600 0x20>;
                interrupts = <1 13 0xf01>;
                clocks = <&clks IMX6QDL_CLK_TWD>;
            };
     
            aips-bus@02000000 { /* AIPS1 */
                compatible = "fsl,aips-bus", "simple-bus";
                #address-cells = <1>;
                #size-cells = <1>;
                reg = <0x02000000 0x100000>;
                ranges;
     
                /*省略无关代码*/      
     
                gpio1: gpio@0209c000 {
                    compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
                    reg = <0x0209c000 0x4000>;
                    interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH>,
                             <0 67 IRQ_TYPE_LEVEL_HIGH>;
                    gpio-controller;
                    #gpio-cells = <2>;
                    interrupt-controller;
                    #interrupt-cells = <2>;
                };
     
                /*省略无关代码*/  
     
                i2c1: i2c@021a0000 {
                    #address-cells = <1>;
                    #size-cells = <0>;
                    compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
                    reg = <0x021a0000 0x4000>;
                    interrupts = <0 36 IRQ_TYPE_LEVEL_HIGH>;
                    clocks = <&clks IMX6QDL_CLK_I2C1>;
                    status = "disabled";
                };
     
            };
                /*省略无关代码*/  
        };  
    };

2.1 基本构造

{}包围起来的结构称之为节点,dts中最开头的/ {},称为根节点。节点的标准结构是xxx@yyy{…},xxx是节点的名字,yyy则不是必须的,其值为节点的地址(寄存器地址或其他地址),比如i2c1: i2c@021a0000中的021a0000就是一个i2c控制器的寄存器基地址。

2.2 属性:地址

有关节点的地址,比如i2c@021a0000,虽然它在名字后面跟了地址,但是正式的设置是在reg属性中设置的比如:reg = <0x021a0000 0x4000>; reg的格式为 <address length>,0x021a0000是寄存器基地址,0x4000是长度。address 和length的个数是可变的,由父节点的属性#address-cells 和#size-cells 决定,比如节点i2c@021a0000的父节点是aips-bus@02000000,其#address-cells 和#size-cells均为1,所以下面的i2c节点的reg属性就有一个address 和length,而i2c节点本身#address-cells 和#size-cells 分别为1和0,所以其下的rtc: pcf8523@68 的reg属性就只有一个0x68(i2c地址)了。

2.3 属性:兼容性

在.dts文件的每个设备节点中,都有一个兼容属性兼容属性用于驱动和设备的绑定。兼容属性是一个字符串的列表,列表中的第一个字符串表征了节点代表的确切设备,形式为",",其后的字符串表征可兼容的其他设备。可以说前面的是特指,后面的则涵盖更广的范围。
如下所示:

flash@0,00000000 {
compatible = "arm,vexpress-flash", "cfi-flash";
reg = <0 0x00000000 0x04000000>,
<1 0x00000000 0x04000000>;
bank-width = <4>;
 };

兼容属性的第2个字符串"cfi-flash"明显比第1个字符串"arm,vexpress-flash"涵盖的范围更广。
再如,Freescale MPC8349SoC含一个串口设备,它实现了国家半导体(National Sem-iconductor )的NS16550寄存器接口。则MPC8349串口设备的兼容属性为compatible=“fsl,mpc8349-uart”,“ns16550”。其中,fsl,mpc8349-uart指代了确切的设备,ns16550代表该设备与NS16550UART保持了寄存器兼容。因此,设备节点的兼容性和根节点的兼容性是类似的,都是“从具体到抽象”。

使用设备树后,驱动需要与.dts中描述的设备节点进行匹配,从而使驱动的probe()函数执行。对于platform_driver而言,需要添加一个OF匹配表,如前文的.dts文件的"acme,a1234-i2c-bus"兼容I2C控制器节点的OF匹配表,具体代码如下所示:

static const struct of_device_id a1234_i2c_of_match[] = {
 2          { .compatible = "acme,a1234-i2c-bus", },
 3          {},
 4 };
 5 MODULE_DEVICE_TABLE(of, a1234_i2c_of_match);
 6
 7 static struct platform_driver i2c_a1234_driver = {
 8          .driver = {
 9                  .name = "a1234-i2c-bus",
10                  .owner = THIS_MODULE,
11                  .of_match_table = a1234_i2c_of_match,
12          },
13          .probe = i2c_a1234_probe,
14          .remove = i2c_a1234_remove,
15 };
16 module_platform_driver(i2c_a1234_driver);

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值