ARM 设备树

ARM 设备树的起源

  • 设备树是一种描述硬件的数据结构,它起源于OpenFirmware。在linux2.6中,ARM架构的板级硬件细节过多的被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx中,采用设备树后,很多硬件信息可以直接通过设备树传递给linux,而不需要在内核中进行大量的冗余编码。
  • 设备树由一系列被命名的节点(Node)属性(Property)组成,而节点本身可以包含子节点。所谓属性,其实就是成对出现名称和值。在设备树中,可描述的信息包括:
    • CPU的数量和类别
    • 内存基地址和大小
    • 总线和桥
    • 外设连接
    • 中断控制器和中断使用情况
    • GPIO控制器和GPIO使用情况
    • 时钟控制器和时钟使用情况
  • 设备树基本上就是画一棵电路板上的CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核识别这棵树,并根据设备树展开linux内科中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

一、设备树的组成和结构

  • 整个设备树牵涉面比较广,增加了新的用于描述设备硬件信息的文本格式,又增加了编译这个文本的工具,同时Bootloader也需要支持将编译后的设备树传递给linux内核。

1.1 DTS

  • 文件.dts是一种ASCII文件格式的设备树描述,此文本格式非常人性化,适合人类的阅读习惯。基本上,在ARM liunx中,一个.dts对应一个ARM的设备,一般放置在内核的arch/arm/boot/dts目录中,DTS绝不是ARM的专利,在其他的目录下也存在大量的.dts文件。

  • 一个SOC可能对应多个设备(一个SOC可以对应对个产品和电路板),这些.dts文件势必会包含许多共同的部分,linux内核为了简化,把SOC公用的部分一般提炼为.dtsi,类似于C语言的头文件。其他的设备对应的.dts就包括这个.dtsi

    • 例如vexpress-v2m.dtsi就被vexpress-v2p.dts所引用,在vexpress-v2p.dts中有:
      • /include/ "vexpress-v2m.dtsi"
  • 和C语言的头文件类似,.dtsi也可以包含其他的.dtsi

  • 文件.dts的基本元素为节点属性,如下是一个设备树结构的模板。

/{
	node1 {
		a-string-property = "A string";
		a-string-list-property = "first string","second string";
		a-byte-data-property = {0x01 0x23 0x34 0x56};
		child-node1{
			first-child-property;
			second-child-property = <1>;
			a-string-property = "Hello,World";
		};
		child-node2{
		};
	};
	node2 {
		an-empty-property;
		a-cell-property = <1 2 3 4>;
		child-node1{
		};
	};
};
  • 上述的.dts文件没有任何其他用途,但是它基本的表征了一个设备树源文件的结构。
    • 1个root节点"/";
    • root节点下面含一系列子节点,本例中为node1和node2;
    • 节点node1下又含有一系列的子节点,本例中为child-node1和child-node2;
    • 各节点又有一系列的属性,这些属性
      • 可能为空:an-empty-property;
      • 可能为字符串:a-string-property;
      • 可能为字符串数组:a-string-list-property;
      • 可能为Cells(由u32整数组成):second-child-property;
      • 可能为二进制数:a-byte-data-property

1.2 DTC

  • DTC是将.dts文件编译为.dtb文件的工具。DTC的源代码位于内核的scripts/dtc目录中,在linux内核使能了设备树的情况下,编译内核的时候主机工具DTC会被编译出来,对应于scripts/dtc/Makefile"hostprogs-y := dtc"这一hostprogs的编译目标。
  • DTC也可以在ubuntu中单独安装。
  • 在linux内核arch/arm/boot/dts/Makefile中,描述了当某种SOC被选中后,哪些.dtb文件会被编译出来。
  • DTC除了可以编译.dts文件以外,还可以反汇编.dtb文件为.dts文件
    • 命令格式:./scripts/dtc/dtc -I dtb -O dts -o xxx.dts arch/arm/boot/dts/xxx.dtb

1.3 DTB

  • 文件.dtb.dts被DTC编译后的二进制格式的设备树描述,可由linux内核解析,当然U-Boot这样的bootloader也可以识别.dtb的。
  • 在为电路板制作NAND、SD启动映像时,会为.dtb文件留下单独的区域存放,之后bootloader在引导内核时,会先读取该.dtb到内存。

1.4 绑定(Binding)

  • 对于设备树中的节点和属性具体是如何来描述设备的硬件细节的,一般需要文档来进行讲解,文档的后缀名为.txt,在这个文件中,需要描述对应节点的兼容性、必需的属性和可选的属性。
  • 这些.txt文件位于内核的Document/devicetree/bindings目录下,其下又分为很多子目录。
    • 设备树绑定文档主要内容包括:
      • 关于该模块的最基本的描述
      • 必需属性(Required Properties)
      • 可选属性(Optional Property)
      • 一个实例(Example)
  • linux内核下的scripts/checkpatch.pl会运行一个检查,如果在设备树中添加了新的compatible字符串,而没有添加对应的文档进行解释,checkpatch程序会报出警告:
    • UNDOCUMENTED_DT_STRINGDT compatible string xxx appears un-documented

1.5 Bootloader

  • Uboot设备从v1.1.3开始支持设备树
  • 为了使能设备树,需要在编译Uboot的时候在config文件中加入:
    #define CONFIG_OF_LIBFDT
    
  • 在Uboot中,可以从NAND、SD或者TFTP等介质中将.dtb读入内存。
    • 假设.dtb放入的地址为0x71000000,之后可以在Uboot运行fdt addr命令设置.dtb的地址,如:
    	UBoot> fdt addr 0x71000000
    
    • fdt其他命令就变得可以使用,如:fdt resizefdt print

二、根节点兼容性

  • .dts文件中,兼容属性:
    • compatible = "acme,coyotes-revenge";
    • 定义了整个系统的名称(设备级别),它的组织形式是:<manufacturer>,<model>
  • linux内核通过根节点兼容属性可以判断它启动的是什么设备。在真实的项目中,这个顶层设备的兼容属性,一般包括两个或者两个以上的兼容性字符串,首个兼容性字符串是板子级别的名字,后一个兼容性是芯片级别(或者芯片系列的名字)的名字。
  • 譬如板子arch/arm/boot/dts/vexpress-v2p-ca9.dts兼容于arm,vexpress,v2p-ca9arm,vexpress
    • compatible = "arm,vexpress,v2p-ca9","arm,vexpress";
  • 譬如板子arch/arm/boot/dts/vexpress-v2p-ca5s.dts的兼容性为:
    • compatible = "arm,vexpress,v2p-ca5s","arm,vexpress";
  • 譬如板子arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts的兼容性为:
    • compatible = "arm,vexpress,v2p-ca15_a7","arm,vexpress";
  • 可以看出,上述各个电路板的共性是兼容arm,vexpress,而特性是分别兼容与"arm,vexpress,v2p-ca9""arm,vexpress,v2p-ca5s""arm,vexpress,v2p-ca15_a7"
  • linux2.6内核中,ARM linux针对不同的电路板会建立由MACHINE_STARTMACHINE_END包围起来的针对这个设备的一系列回调函数。这些不同的设备,会有不同的MACHINE ID,Uboot在启动内核时,会将MAHCINE ID存放在r1寄存器,linux启动时会匹配Bootloader传递的MACHINE ID和MACHINE_START申明的MACHINE ID,然后再执行一系列的初始化函数
  • linux 3.x引入设备树之后,MACHINE_START变更为DT_MACHINE_START,其中含有一个.dt_compat成员,用于表明相关的设备与.dts中根节点的兼容属性兼容关系。如果Bootloader传递给内核的设备树根节点中的兼容属性出现在某设备的.dt_compat表中,相关的设备就对应匹配,从而引发这一设备的一系列的初始化函数。
    • 示例见书《linux设备驱动开发详解:基于最新的linux 4.0内核》P469

三、设备节点兼容性

  • .dts文件的每个设备节点中,都有一个兼容属性,兼容属性用于驱动和设备的绑定
  • 兼容属性是一个字符串的列表,列表的第一个字符串表征了节点代表的确切设备,形式为"<manufacturer>,<model>",其后的字符串表征可兼容的其他设备。可以说前面是特指,后面是涵盖更广的范围。
  • 使用设备树后,驱动需要与.dts中描述的设备节点进行匹配,从而使驱动的probe()函数执行。对于platform_driver而言,需要添加一个OF(operation_file)匹配表。

四、设备节点及Label的命名

  • node-name@unit-address
    • unit-address一般都是外设寄存器的起始地址,有时候是I2C的设备地址,或者是其他含义,具体节点具体分析。
  • 设备树里面经常遇到如下节点名字:
    • intc:interrupt-controller@00a01000
      • ':'前面是标签label,后面才是名字
    • 可以通过&label的形式进行访问这个label,这种引用是通过phandle(pointer handle)进行的。

五、地址编码

  • 可寻址的设备使用如下信息在设备树中编码地址信息:
    reg
    	#address-cells
    	#size-cells
    
    • 其中reg的组织形式为reg=<address1 length1 [address2 length2] [address3 length3]...>,其中的每一组address length表明了设备使用的地址范围
    • address一个或者多个32位的整型(cell),而length意味着从address到address+length-1的地址范围都属于该节点。若#size-cells = 0,则length字段为
    • addresslength字段可变长,父节点的#address-cells和#size-cells分别决定了子节点的reg属性address和length字段长度。
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值