在上一节迅为IMX6ULL开发板已经对DTS的语法做了比较详细的介绍,在本节中根据前面讲解的语法,从头到尾编写一个小型的设备树文件。我们会以一个虚拟的设备作为参考,提前假设一些外部设备和功能。当然这个虚拟的设备没有任何的意思,只是为了复习掌握前面学习的设备树语法。在实际产品的开发过程中,我们不需要从头编写一个dts设备树文件,一般都是使用soc厂商提供的dts文件,我们只需要根据自己的实际情况修改添加自己的内容即可。 下面这个假设的设备,制造商为“Acme”,并命名为“Coyote's Revenge”,具体功能如下: l 一个32位ARM CPU l 处理器本地总线连接到内存映射的串行口、spi 总线控制器、i2c 控制器、中断控制器和外部总线桥 l 256MB SDRAM起始地址为0 l 两个串口起始地址:0x101F1000和0x101F2000 l GPIO控制器起始地址:0x101F3000 l 带有一下设备的SPI控制器起始地址:0x10170000 n MMC插槽的SS管脚连接至GPIO #1 l 外部总线桥挂载一下设备 n SMC SMC91111 以太网,起始地址:0x10100000 l i2c控制器起始地址:0x10160000,并挂载一下设备 n Maxim DS1338实时时钟,响应至从地址11010000(0x58) n 64MB NOR闪存起始地址:0x30000000 1、初始结构 第一步就是要给这个虚拟的设备构建一个基本结构,这是一个有效的设备树的最基本的结构,在这个阶段需要唯一的标识该设备: / { compatible = "acme,coyotes-revenge"; }; 2、添加CPU处理器 接着就是描述每个CPU了,先添加一个名为“cpus”的容器节点,然后为每个CPU分别添加子节点,具体到我们的情况就是一个ARM的双核Cortex A9系统。 / { compatible = "acme,coyotes-revenge"; cpus { compatible = "arm,cortex-a9"; }; compatible = "arm,cortex-a9"; }; }; }; 1、添加设备 系统中的每个设备都表示为一个设备树节点,所以接下来就应该为这个设备树填充设备节点,我们先简单的形成一个框架,设备节点中的某些属性在后面不断添加。 / { 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 视角的系统视图。 2、CPU编址 现在这个树还是无效的,因为他缺少关于设备之间的关联信息,接下来我们添加这些属性信息。 cpus { #address-cells = <1>; #size-cells = <0>; cpu@0 { compatible = "arm,cortex-a9"; reg = <0>; }; cpu@1 { compatible = "arm,cortex-a9"; reg = <1>; }; }; 在 cpu 节点中,#address-cells 设置为 1,#size-cells 设置为 0。这意味着子节点的 reg 值是一个单一的 uint32,这是一个不包含大小字段的地址,为这两个 cpu 分配的地址是 0 和 1。cpu 节点的 #size-cells 为 0 是因为只为每个 cpu 分配一个单独的地址。 3、内存映射设备 与 cpu 节点里单一地址值不同,应该分配给内存映射设备一个地址范围。#size-cells 声明每个子节点的 reg 元组中长度字段的大小。在接下来的例子中,每个地址值是 1 cell(32 位),每个长度值也是 1 cell,这是典型的 32 位系统。 / { #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 >; }; ... }; 一些挂在总线上的设备有不同的编址方案。例如一个带独立片选线的设备也可以连接至外部总线。由于父节点会为其子节点定义地址域,所以可以选择不同的地址映射来最恰当的描述该系统。 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>; }; }; 外部总线的地址值使用了两个 cell,一个用于片选号;另一个则用于片选基址的偏移量。而长度字段则还是单个 cell,这是因为只有地址的偏移部分才需要一个范围量。所以,在这个例子中,每个 reg 项都有三个 cell:片选号、偏移量和长度。 4、非内存映射设备 其他的设备没有被映射到处理机总线上。虽然这些设备可以有一个地址范围,但他们并不是由 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>; }; }; 请看下一篇
|