我眼中的Linux设备树(一 概述)
一 概述
设备树(Device tree)是一套用来描述硬件属相的规则。ARM Linux采用设备树机制源于2011年3月份Linux创始人Linus Torvalds发的一封邮件,在这封邮件中他提倡ARM平台应该参考其他平台如PowerPC的设备树机制描述硬件。因为在此之前,ARM平台还是采用旧的机制,在kernel/arch/arm/plat-xxx目录和kernel/arch/arm/mach-xxx目录下用代码描述硬件,如注册platform设备,声明设备的resource等。因为这些代码都是用来描述芯片平台及板级差异的,所以对于内核来讲都是垃圾代码。因为嵌入式平台中很多公司的芯片采用的都是ARM架构,随着Android的成功,这些代码越来越多。据说常见的平台如s3c2410板级目录下边的代码有数万行,难怪Linux Torvalds会说“this whole ARM thing is a fucking pain in the ass”。
内核中关于设备树的文档位于kernel/Documentation/devicetree/目录。设备树是Power.org组织定义的一套规范,规范文档可以在官网上找到,目前最新的版本是https://www.power.org/documentation/epapr-version-1-1/。内核中设备树相关的函数都是以of开头的,我推测原因是设备树机制是源于IEEE 1275 Open Firmware standard规范的,相关的代码都是继承下来的。如果想快速了解下设备树怎么用,可以参考http://devicetree.org/Device_Tree_Usage。
设备树是从软件使用的角度描述硬件的,不是从硬件设计的角度描述的。我们在写设备树时没有必要按照硬件逻辑生搬硬套,也不要指望通过阅读设备树弄清楚硬件是如何设计的。对于软件可以自动识别的硬件,如USB设备,PCI设备,也是没有必要通过设备树描述的。
我个人觉得规范内容是可以分为两个层次的。第一层是关于设备树组织形式的,如设备树结构,节点名字的构成等,第一个层次是基础,是理解第二个层次的前提。第二层是关于设备树内容的,如多核CPU怎样描述,一个具体的设备如何描述。第二层可以看成是第一层的具体应用。相对来说第二层内容更多,更具体,根据描述的内容不同,定义规范的方式也有差别,比如关于CPU,内存,中断这些基础的内容,是在epapr中说明的,而关于外设的规范是在专门的地方说明的。
DTS(Device tree syntax,另一种说法是Device tree source)是设备树源文件,为了方便阅读及修改,采用文本格式。DTC(Device tree compiler)是一个小工具,负责将DTS转换成DTB(Device tree blob)。DTB是DTS的二进制形式,供机器使用。使用中,我们首先根据硬件修改DTS文件,然后在编译的时候通过DTC工具将DTS文件转换成DTB文件,然后将DTB文件烧写到机器上(如emmc,磁盘等存储介质)。系统启动时,fastboot(或者类似的启动程序,如Uboot)在启动内核前将DTB文件读到内存中,跳转到内核执行的同时将DTB起始地址传给内核。内核通过起始地址就可以根据DTB的结构解析整个设备树。说设备树的规范可以分成两个层次,是针对DTS的,关于DTB的结构不在此范围内。DTB仅仅是为了方便机器使用而对DTS的转换而已(也可以说DTS仅是为了方便人类使用而对DTB的一种描述)。
设备树首先是一个树形结构,并且是一棵树。除了根节点外其他子节点都有唯一的父节点,节点下可以有子节点和属性(子节点可以看成是树枝,属性可以看成是叶子)。属性由名字和值组成(名字是必须的,但是值不是必须的,如果只要根据是否存在这个属性就可以表示我们想要的功能,那么可以不需要有值)。
下边是我们从内核代码中截取的一个DTS片段。“/”表示根节点。“model = "Newflow AM335x NanoBone"”是根节点下边的属性。“cpus”是根节点的一个子节点。“cpu0-supply = <&dcdc2_reg>”是“cpu@0”子节点下的属性。节点下的属性用来表示节点的特性,子节点和父节点具有一定的从属关系。真实的硬件不可能是这样规则的树形结构,所以设备树仅是软件开发人员为了描述硬件而做的一个近似表示而已,连抽象都算不上。
/ {
model = "Newflow AM335x NanoBone";
compatible = "ti,am33xx";
cpus {
cpu@0 {
cpu0-supply = <&dcdc2_reg>;
};
};
memory {
device_type = "memory";
reg = <0x80000000 0x10000000>; /* 256 MB */
};
leds {
compatible = "gpio-leds";
led@0 {
label = "nanobone:green:usr1";
gpios = <&gpio1 5 0>;
default-state = "off";
};
};
};