Linux设备树使用

本页面介绍了如何为新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>;
	};
};

猜你喜欢

转载自blog.csdn.net/caihaitao2000/article/details/83755133