本页面介绍了如何为新machine编写设备树文件。它旨在提供设备树概念的概述以及它们如何用于描述machine。
有关设备树数据格式的完整技术说明,请参阅ePAPR v1.1规范。ePAPR规范比本页面介绍的基本主题包含更多详细信息,请参阅此页面以获取本页未涵盖的更高级用法。ePAPR目前正在使用Devicetree规范文档的新名称进行更新。
1.基本数据格式
设备树是由节点和属性组成的树型结构。属性是键-值对的形式,节点可以包含属性和子节点。例如,以下是.dts格式的简单设备树:
/dts-v1/; //设备树的版本号
/ {
node1{ //节点
a-string-property = "A string";
a-string-list-property = "first string", "second string";
// hex is implied in byte arrays. no '0x' prefix is required
a-byte-data-property = [01 23 34 56];
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>; /* each number (cell) is a uint32 */
child-node1{ //子节点
};
};
};
这个设备树文件显然没有实际用处,因为它没有描述任何东西,但它确实显示了节点和属性的结构。包括:
一个根节点:"/";
两个子节点:"node1"和"node2";
node1的子节点:"child-node1"和"child-node2";
各个节点的属性。
属性是简单的键值对形式,其中值可以为空或包含任意字节流。虽然数据类型未编码到数据结构中,但有一些基本数据表示可以在设备树源文件中表示。
文本字符串用双引号表示:
string-property = "a string";
'Cells'是由尖括号分隔的32位无符号整数:
cell-property = <0xbeef 123 0xabcd1234>;
二进制数据用方括号分隔:
binary-property = [0x01 0x23 0x45 0x67];
可以使用逗号将不同表示形式的数据连接在一起:
mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
还可以使用逗号创建字符串列表:
string-list = "red fish", "blue fish";
2.基本概念
要了解设备树的使用方式,我们将从一台简单的机器开始,并构建一个设备树来逐步描述它。
考虑以下设想的机器(基于ARM Versatile),由"Acme"制造并命名为"Coyote's Revenge":
一个32位ARM CPU;
处理器本地总线连接到内存映射的串行端口,spi总线控制器,i2c控制器,中断控制器和外部总线桥;
基于0地址的256MB SDRAM;
2个基于0x101F1000和0x101F2000的串行端口;
GPIO控制器基地址为0x101F3000;
基于0x10170000地址的SPI控制器,具有以下器件;
带有SS引脚的MMC插槽连接到GPIO#1;
外部总线桥接器具有以下设备:
SMC SMC91111以太网设备连接到外部总线,基地址为0x10100000;
i2c控制器基地址为0x10160000,具有以下设备:
Maxim DS1338实时时钟。从地址为1101000(0x58);
基地址为0x30000000的64MB NOR闪存。
初始基本dts框架:
第一步是为machine设置骨架结构。这是有效设备树所需的最小结构。在此阶段,您需要唯一标识机器。
/ DTS-V1 /;
/ {
compatible =“acme,coyotes-revenge”;
};
compatible指定系统的名称。它包含一个"<manufacturer>,<model>"形式的字符串。重要的是指定确切的设备,并包含制造商名称以避免命名空间冲突。由于操作系统将使用该compatible值来决定如何在机器上运行,将正确的数据放入此属性非常重要。
从理论上讲,compatible是操作系统唯一识别机器所需的数据。如果所有机器细节都是硬编码的,那么操作系统可能会专匹配对上层的compatible属性中的"acme,coyotes-revenge"。
CPU的设置:
/ DTS-V1 /;
/ {
compatible =“acme,coyotes-revenge”;
cpus {
cpu @ 0 {
compatible =“arm,cortex-a9”;
};
cpu @ 1 {
compatible =“arm,cortex-a9”;
};
};
};
每个cpu节点中的compatible属性是一个字符串,它指定表单中的确切cpu模型<manufacturer>,<model>,就像顶层的compatible属性一样。稍后将向cpu节点添加更多属性,但我们首先需要讨论更多基本概念。
节点名称:
值得花点时间谈谈命名约定。每个节点必须有一个名称,格式为:<name>[@<unit-address>]。<name>是一个简单的ascii字符串,最多可以包含31个字符。通常,节点根据它代表的设备类型命名。即,3com以太网适配器的节点将使用该名称ethernet,而不是3com509。如果节点描述的是具有地址的设备,则包括地址。通常,地址是用于访问设备的主要地址,并列在节点的reg属性中。我们将在本文档后面介绍reg属性。
同级节点名称必须是唯一的,但只要地址不同(即serial@101f1000和serial@101f2000)即可,多个节点使用相同的通用名称是正常的。有关节点命名的完整详细信息,请参阅ePAPR规范的第2.2.1节。
设备:
系统中的每个设备都由设备树节点表示。下一步是使用每个设备的节点填充设备树文件。现在,新节点将保持为空,直到我们可以讨论如何处理地址范围和irq。
/dts-v1/;
/{
compatible = "acme,coyotes-revenge";
cpus {
cpu@0 {
compatible = "arm,cortex-a9";
};
cpu@1 {
compatible = "arm,cortex-a9";
};
};
serial@101F0000 {
compatible = "arm,pl011";
};
serial@101F2000 {
compatible = "arm,pl011";
};
gpio@101F3000 {
compatible = "arm,pl061";
};
interrupt-controller@10140000 {
compatible = "arm,pl190";
};
spi@10115000 {
compatible = "arm,pl022";
};
external-bus {
ethernet@0,0 {
compatible = "smc,smc91c111";
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
rtc@58 {
compatible = "maxim,ds1338";
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
};
};
};
在此树中,已为系统中的每个设备添加了一个节点,层次结构反映了设备连接到系统的方式。即,外部总线上的设备是外部总线节点的子设备,i2c设备是i2c总线控制器节点的子设备。通常,层次结构从CPU的角度表示系统的视图。
此树在此时无效。它缺少有关设备之间连接的信息。该数据将在稍后添加。在这棵树中要注意的一些事情:
每个设备节点都有一个compatible属性。
flash节点在兼容属性中有2个字符串。请继续阅读下一节以了解原因。
如前所述,节点名称反映了设备的类型,而不是特定的模型。有关应尽可能使用的已定义通用节点名称列表,请参阅ePAPR规范的第2.2.2节。
compatible属性详解:
树中表示设备的每个节点都需要具有该compatible属性。compatible是操作系统用来决定绑定设备和设备驱动程序的关键。
compatible是一个字符串列表。列表中的第一个字符串指定节点表示的确切设备"<manufacturer>,<model>"。以下字符串表示设备兼容的其他设备。
例如,飞思卡尔MPC8349片上系统(SoC)具有串行器件,该器件实现了National Semiconductor ns16550寄存器接口。因此,MPC8349串行设备的兼容属性应为:compatible = "fsl,mpc8349-uart", "ns16550"。在这种情况下,fsl,mpc8349-uart指定确切的器件,并使用ns16550声明它与National Semiconductor 16550 UART寄存器级兼容。
注意:ns16550由于历史原因,没有制造商前缀。所有新的兼容值都应使用制造商前缀。
这种做法允许将现有设备驱动程序绑定到较新的设备,同时仍然唯一地标识确切的硬件。
地址是如何工作的:
可寻址的设备使用以下属性将地址信息编码到设备树中:
reg
#address-cells
#size-cells
每个可寻址的设备获取一个reg表格中的地址reg = <address1 length1 [address2 length2] [address3 length3] ...>。每个cell表示设备使用的地址范围。每个地址值是一个或多个称为单元的32位整数的列表。类似地,长度值可以是单元格列表也可以是空的。
由于地址和长度字段都是可变大小的变量,因此父节点中的#address-cells和#size-cells属性用于表示每个字段中有多少个单元格。或者换句话说,正确解释reg属性需要父节点的#address-cells和#size-cells值。要了解这一切是如何工作的,我们可以从CPU开始,将地址属性添加到示例设备树中。
CPU地址:
在讨论地址时,CPU节点代表了最简单的情况。为每个CPU分配一个唯一的ID,并且没有与CPU ID相关联的大小。
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0{
compatible =“arm,cortex-a9”;
reg = <0>;
};
cpu@1{
compatible =“arm,cortex-a9”;
reg = <1>;
};
};
在cpus节点中,#address-cells设置为1,并#size-cells设置为0。这意味着子reg值是单个uint32,表示没有大小字段。在这种情况下,两个cpus被分配地址0和1;#size-cells对于cpu节点是0,因为每个cpu仅被分配一个地址。
您还会注意到该reg值与节点名称中的值匹配。按照惯例,如果节点具有reg属性,则节点名称必须包含unit-address,这是reg属性中的第一个地址值。
内存映射设备:
不是像在cpu节点中找到的单个地址值,而是为内存映射设备分配一系列将响应的地址。#size-cells用于表示每个子reg元组中长度字段的大小。在以下示例中,每个地址值为1个单元(32位),每个长度值也为1个单元,这在32位系统上是典型的。对于#address-cells和#size-cells,64位机器可以使用值2来获得设备树中的64位寻址。
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
...
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
};
serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
};
gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
};
interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
};
spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
};
...
};
为每个设备分配一个基址,并为其分配区域的大小。本例中的GPIO设备地址分配了两个地址范围; 0x101f3000 ... 0x101f3fff和0x101f4000..0x101f400f。
一些设备存在于具有不同寻址方案的总线上。例如,可以使用分立的芯片选择线将器件连接到外部总线。由于每个父节点为其子节点定义寻址域,因此可以选择地址映射以最好地描述系统。下面的代码显示了连接到外部总线的设备的地址分配,芯片选择号编码到地址中。
external-bus {
#address-cells = <2>;
#size-cells = <1>;
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
reg = <1 0 0x1000>;
rtc@58 {
compatible = "maxim,ds1338";
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
external-bus用2个cell表示地址值;一个用于芯片选择号,一个用于从芯片选择的基极偏移。长度字段保持为单个单元格,因为只有地址的偏移部分需要具有范围。因此,在这个例子中,每个reg条目包含3个单元格;chipselect号码,偏移量和长度。
由于地址域包含在节点及其子节点中,因此父节点可以自由定义对总线有意义的任何寻址方案。直接父节点和子节点之外的节点通常不必关心本地寻址域,并且必须映射地址以从一个域到另一个域。
非内存映射设备:
其他设备未在处理器总线上映射内存地址范围。它们可以具有地址范围,但CPU无法直接访问它们。相反,父设备的驱动程序将代表CPU执行间接访问。以i2c设备为例,每个设备都分配了一个地址,但没有与之关联的长度或范围。这看起来与CPU地址分配大致相同。
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
};
};