dts入门.docx

上传人:b****1 文档编号:22810582 上传时间:2023-04-28 格式:DOCX 页数:21 大小:25.78KB
下载 相关 举报
dts入门.docx_第1页
第1页 / 共21页
dts入门.docx_第2页
第2页 / 共21页
dts入门.docx_第3页
第3页 / 共21页
dts入门.docx_第4页
第4页 / 共21页
dts入门.docx_第5页
第5页 / 共21页
点击查看更多>>
下载资源
资源描述

dts入门.docx

《dts入门.docx》由会员分享,可在线阅读,更多相关《dts入门.docx(21页珍藏版)》请在冰豆网上搜索。

dts入门.docx

dts入门

dts入门

本文目标是了解dts基本语法,可以尝试去看内核dts中的文件,follow去配置。

一、dts产生原因

DTS即DeviceTreeSource设备树源码,DeviceTree是一种描述硬件的数据结构,它起源于OpenFirmware(OF)。

它替代arch/arm/plat-xxx和arch/arm/mach-xxx中的板级spec代码,便于code管理。

ARM平台的相关code相关规范调整:

1、ARM的核心代码仍然保存在arch/arm目录下

2、ARMSOCcorearchitecturecode保存在arch/arm目录下

3、ARMSOC的周边外设模块的驱动保存在drivers目录下

4、ARMSOC的特定代码在arch/arm/mach-xxx目录下

5、ARMSOCboardspecific的代码被移除,由DeviceTree机制来负责传递硬件拓扑和硬件资源信息。

本质上,DeviceTree改变了原来用hardcode方式将HW配置信息嵌入到内核代码的方法,改用bootloader传递一些参数。

如果我们认为kernel是一个blackbox,那么其输入参数应该包括:

a.识别platform的信息b.runtime的配置参数c.设备的拓扑结构以及特性

对于嵌入式系统,在系统启动阶段,bootloader会加载内核并将控制权转交给内核,此外,还需要把上述的三个参数信息传递给kernel,以便kernel可以有较大的灵活性。

在linuxkernel中,DeviceTree的设计目标就

是如此。

二、dts基本知识

2.1dts加载过程

如果要使用DeviceTree,首先用户要了解自己的硬件配置和系统运行参数,并把这些信息组织成DeviceTreesourcefile。

通过DTC(DeviceTreeCompiler),可以将这些适合人类阅读的DeviceTreesourcefile变成适合机器处理的DeviceTreebinaryfile(有一个更好听的名字,DTB,devicetreeblob)。

在系统启动的时候,bootprogram(例如:

firmware、bootloader)可以将保存在flash中的DTBcopy到内存(当然也可以通过其他方式,例如可以通过bootloader的交互式命令加载DTB,或者firmware可以探测到device的信息,组织成DTB保存在内存中),并把DTB的起始地址传递给clientprogram(例如OSkernel,bootloader或者其他特殊功能的程序)。

对于计算机系统(computersystem),一般是firmware->bootloader->OS,对于嵌入式系统,一般是bootloader->OS。

在高通msm8992平台:

sbl将烧写在emmc中的cdt.bin进行读取和处理,获得nPlatform和nSubtype,如果没有CDT分区,那么就使用代码里配置的cdt,存入共享内存;

lk将从共享内存中获得nPlatform和nSubtype进行相应处理,确定dtbentry,然后选择一个对应的dtb(所有dtb保存在boot.img中)从emmc中读进内存地址hdr->tags_addr,这个参数会传递给kernel;

kernel通过传入的dtb地址进行设备的创建。

2.2dts描述信息

DeviceTree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。

所谓属性,其实就是成对出现的name和value。

在DeviceTree中,可描述的信息包括(原先这些信息大多被hardcode到kernel中):

CPU的数量和类别

内存基地址和大小

总线和桥

外设连接

中断控制器和中断使用情况

GPIO控制器和GPIO使用情况

Clock控制器和Clock使用情况

它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

是否DeviceTree要描述系统中的所有硬件信息?

答案是否定的。

基本上,那些可以动态探测到的设备是不需要描述的,例如USBdevice。

不过对于SOC上的usbhostcontroller,它是无法动态识别的,需要在devicetree中描述。

同样的道理,在computersystem中,PCIdevice可以被动态探测到,不需要在devicetree中描述,但是PCIbridge如果不能被探测,那么就需要描述之。

.dts文件是一种ASCII文本格式的DeviceTree描述,此文本格式非常人性化,适合人类的阅读习惯。

基本上,在ARMLinux在,一个.dts文件对应一个ARM的machine,一般放置在内核的arch/arm/boot/dts/目录。

由于一个SoC可能对应多个machine(一个SoC可以对应多个产品和电路板),势必这些.dts文件需包含许多共同的部分,Linux内核为了简化,把SoC公用的部分或者多个machine共同的部分一般提炼为.dtsi,类似于C语言的头文件。

其他的machine对应的.dts就include这个.dtsi。

譬如在arch/arm/boot/dts/qcom/目录下,高通的很多.dtsi都include了skeleton.dtsi或者skeleton64.dtsi。

正常情况下所有的dts文件以及dtsi文件都含有一个根节点”/”,这样include之后就会造成有很多个“根节点”。

按理说devicetree既然是一个树,那么其只能有一个根节点,所有其他的节点都是派生于根节点的childnode。

其实DeviceTreeCompiler会对DTS的node进行合并,最终生成的DTB中只有一个rootnode.

devicetree的基本单元是node。

这些node被组织成树状结构,除了rootnode,每个node都只有一个parent。

一个devicetree文件中只能有一个rootnode。

每个node中包含了若干的property/value来描述该node的一些特性。

每个node用节点名字(nodename)标识,节点名字的格式是node-name@unit-address。

如果该node没有reg属性(后面会描述这个property),那么该节点名字中必须不能包括@和unit-address。

unit-address的具体格式是和设备挂在那个bus上相关。

例如对于cpu,其unit-address就是从0开始编址,依次加一。

而具体的设备,例如以太网控制器,其unit-address就是寄存器地址。

rootnode的nodename是确定的,必须是“/”。

在一个树状结构的devicetree中,如何引用一个node呢?

要想唯一指定一个node必须使用fullpath,例如/node-name-1/node-name-2/node-name-N

2.3dts组成结构示例

/{"/"表示root结点,该结点下有两个子结点node1和node2

node1{结点"node1"下又含有子结点,本例中为"child-node1"和"child-node2",各结点都有一系列属性

a-string-property="Astring";属性是字符串

a-string-list-property="firststring","secondstring";字符串数组

a-byte-data-property=[0x010x230x340x56];二进制数组

child-node1{

first-child-property;

second-child-property=<1>;Cells(由u32整数组成)

a-string-property="Hello,world";

};

child-node2{

};

};

node2{

an-empty-property;属性为空

a-cell-property=<1234>;/*eachnumber(cell)isauint32*/

child-node1{

};

};

};

上述.dts文件并没有什么真实的用途,但它基本表征了一个DeviceTree源文件的结构。

2.4dts语法

完整的DeviceTree可以将一个PCB摆在你眼前,下面我们一起来看一下:

下面以一个最简单的machine为例来看如何写一个.dts文件。

假设此machine的配置如下:

1个双核ARMCortex-A932位处理器;

ARM的localbus上的内存映射区域分布了2个串口(分别位于0x101F1000和0x101F2000)、GPIO控制器(位于0x101F3000)、SPI控制器(位于0x10115000)、中断控制器(位于0x10140000)和一个externalbus桥;

Externalbus桥上又连接了SMCSMC91111Ethernet(位于0x10100000)、I2C控制器(位于0x10160000)、64MBNORFlash(位于0x30000000);

Externalbus桥上连接的I2C控制器所对应的I2C总线上又连接了MaximDS1338实时钟(I2C地址为0x58)。

其对应的.dts文件为:

/ {  

    compatible = "acme,coyotes-revenge";  

    #address-cells = <1>;  子结点需要一个cell描述地址

    #size-cells = <1>;  子结点需要一个cell描述长度

    interrupt-parent = <&intc>;  

  

    cpus {  

        #address-cells = <1>;  

        #size-cells = <0>;  

        cpu@0 {  

            compatible = "arm,cortex-a9";  

            reg = <0>;  

        };  

        cpu@1 {  

            compatible = "arm,cortex-a9";  

            reg = <1>;  

        };  

    };  

  

    serial@101f1000 {  串口

        compatible = "arm,pl011";  

        reg = <0x101f1000 0x1000 >;  

        interrupts = < 1 0 >;  

    };  

  

    serial@101f2000 {  串口

        compatible = "arm,pl011";  

        reg = <0x101f2000 0x1000 >;  

        interrupts = < 2 0 >;  

    };  

gpio@101f3000 {  GPIO控制器

        compatible = "arm,pl061";  

        reg = <0x101f3000 0x1000  

               0x101f4000 0x0010>;  

        interrupts = < 3 0 >;  

    };  

  

    intc:

 interrupt-controller@10140000 {  中断控制器

        compatible = "arm,pl190";  

        reg = <0x10140000 0x1000 >;  

        interrupt-controller;  

        #interrupt-cells = <2>;  

    };  

  

    spi@10115000 {  spi控制器

        compatible = "arm,pl022";  

        reg = <0x10115000 0x1000 >;  起始地址为0x10115000,长度为0x1000

        interrupts = < 4 0 >;  

};  

external-bus {  externalbus桥

        #address-cells = <2>  子结点需要两个cell描述地址,片选

        #size-cells = <1>;  子结点需要一个cell描述长度

        ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet  片选00,地址0x10100000 ,长度0x10000

                  1 0  0x10160000   0x10000     // Chipselect 2, i2c controller  

2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash  

  

        ethernet@0,0 {  

            compatible = "smc,smc91c111";  

            reg = <0 0 0x1000>;  

            interrupts = < 5 2 >;  

        };  

         i2c@1,0 {  

            compatible = "acme,a1234-i2c-bus";  

            #address-cells = <1>;  rtc需要一个cell描述地址

            #size-cells = <0>;  rtc不需要0描述长度

            reg = <1 0 0x1000>;  

            rtc@58 {  

                compatible = "maxim,ds1338";  

                reg = <58>;  

                interrupts = < 7 3 >;  

            };  

        };  

        flash@2,0 {  

            compatible = "samsung,k8f1315ebm", "cfi-flash";  

            reg = <2 0 0x4000000>;  

        };  

    };  

};  

注释不是太多,下面来详细分类解释:

2.4.1compatible 

上述.dts文件中,root结点"/"的compatible属性compatible="acme,coyotes-revenge";定义了系统的名称,它的组织形式为:

,

Linux内核透过root结点"/"的compatible属性即可判断它启动的是什么machine。

在.dts文件的每个设备,都有一个compatible属性,compatible属性用户驱动和设备的绑定。

compatible属性是一个字符串的列表,列表中的第一个字符串表征了结点代表的确切设备,形式为",",其后的字符串表征可兼容的其他设备。

可以说前面的是特指,后面的则涵盖更广的范围。

如在arch/arm/boot/dts/vexpress-v2m.dtsi中的Flash结点:

flash@0,00000000 {

     compatible = "arm,vexpress-flash", "cfi-flash";  

     reg = <0 0x00000000 0x04000000>,  

     <1 0x00000000 0x04000000>;  

     bank-width = <4>;  

 }; 

compatible属性的第2个字符串"cfi-flash"明显比第1个字符串"arm,vexpress-flash"涵盖的范围更广。

2.4.2name@unit-address

接下来root结点"/"的cpus子结点下面又包含2个cpu子结点,描述了此machine上的2个CPU,并且二者的compatible属性为"arm,cortex-a9"。

注意cpus和cpus的2个cpu子结点的命名,它们遵循的组织形式为:

[@],<>中的内容是必选项,[]中的则为可选项。

name是一个ASCII字符串,用于描述结点对应的设备类型,如网卡适配器对应的结点name宜为ethernet,表示这个是网卡。

如果一个结点描述的设备有地址,则应该给出@unit-address。

多个相同类型设备结点的name可以一样,只要unit-address不同即可,如本例中含有cpu@0、cpu@1以及serial@101f0000与serial@101f2000这样的同名结点。

设备的unit-address地址也经常在其对应结点的reg属性中给出。

 

2.4.3regaddress-cellssize-cells

设备的地址特性根据一下几个属性来控制:

●reg

●#address-cells

●#size-cells

reg意为region,区域。

格式为:

reg=;

父类的address-cells和size-cells决定了子类的相关属性要包含多少个cell,如果子节点有特殊需求的话,可以自己再定义,这样就可以摆脱父节点的控制。

address-cells决定了address1/2/3包含几个cell,size-cells决定了length1/2/3包含了几个cell,用上面的dts文件内容举例子说明:

例子2.4.3.1

root结点的#address-cells=<1>;和#size-cells=<1>;决定了serial、gpio、spi等结点的address和length字段的长度分别为1

serial@101f1000 { 

        compatible = "arm,pl011";  

        reg = <0x101f1000 0x1000 >;地址0x101f1000,长度 0x1000

        interrupts = < 1 0 >;  

 };  

例子2.4.3.2

i2c控制器模块下的rtc模块。

因为I2C设备只是被分配在一个地址上,不需要其他任何空间,所以只需要一个address的cell就可以描述完整,不需要size-cells

/ {  

    compatible = "acme,coyotes-revenge";  

    #address-cells = <1>;  子结点需要一个cell描述地址

    #size-cells = <1>;  子结点需要一个cell描述长度

    interrupt-parent = <&intc>; 

...

external-bus{

#address-cells=<2>

#size-cells=<1>;

...

i2c@1,0{

compatible="acme,a1234-i2c-bus";

#address-cells=<1>;重新写address-cells

#size-cells=<0>;重新写address-cells

reg=<100x1000>;

rtc@58{

compatible="maxim,ds1338";

reg=<58>;只需要一个addresscell,不需要cell再描述长度

};

};

...

};

例子2.4.3.3

当需要描述的设备不是本地设备时,就需要描述一个从设备地址空间到CPU地址空间的映射关系,这里就需要用到ranges属性。

还是以上边的external-bus举例

ranges属性为一个地址转换表。

表中的每一行都包含了子地址、父地址、在自地址空间内的区域大小。

他们的大小(包含的cell)分别由子节点的address-cells的值、父节点的address-cells的值和子节点的size-cells来决定。

/ {  

    compatible = "acme,coyotes-revenge";  

    #address-cells = <1>;  子结点需要一个cell描述地址

    #size-cells = <1>;  子结点需要一个cell描述长度

    interrupt-parent = <&intc>; 

...

00两个cell,由子节点external-bus的address-cells=<2>决定;

0x10100000一个cell,由父节点(/)的address-cells=<1>决定;

0x10000一个cell,由子节点external-bus的size-cells=<1>决定。

最终第一行说明的意思就是:

片选0,偏移0(选中了网卡),被映射到CPU地址空间的0x10100000~0x10110000中,地址长度为0x10000。

external-bus{

#address-cells=<2>

#size-cells=<1>;

ranges=<000x101000000x10000//Chipselect1,Ethernet

100x101600000x10000//Chipselect2,i2ccontroller

200x300000000x1000000>;//Chipselect3,NORFlash

};

进阶例子2.4.3.4

pci@0x10180000{

compatible="arm,versatile-pci-hostbridge","pci";

reg=<0x101800000x1000>;

interrupts=<80>;

bus-ranges=<00>;

#address-cells=<3>

#size-cells=<2>;

ran

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 成人教育 > 远程网络教育

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1