ARM Linux 3x的设备树Device Tree.docx
- 文档编号:9346732
- 上传时间:2023-02-04
- 格式:DOCX
- 页数:26
- 大小:30KB
ARM Linux 3x的设备树Device Tree.docx
《ARM Linux 3x的设备树Device Tree.docx》由会员分享,可在线阅读,更多相关《ARM Linux 3x的设备树Device Tree.docx(26页珍藏版)》请在冰豆网上搜索。
ARMLinux3x的设备树DeviceTree
ARMLinux3.x的设备树(DeviceTree)
1. ARMDeviceTree起源
LinusTorvalds在2011年3月17日的ARMLinux邮件列表宣称“thiswholeARMthingisaf*ckingpainintheass”,引发ARMLinux社区的地震,随后ARM社区进行了一系列的重大修正。
在过去的ARMLinux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。
读者有兴趣可以统计下常见的s3c2410、s3c6410等板级目录,代码量在数万行。
社区必须改变这种局面,于是PowerPC等其他体系架构下已经使用的FlattenedDeviceTree(FDT)进入ARM社区的视野。
DeviceTree是一种描述硬件的数据结构,它起源于OpenFirmware(OF)。
在Linux2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,采用DeviceTree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。
DeviceTree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。
所谓属性,其实就是成对出现的name和value。
在DeviceTree中,可描述的信息包括(原先这些信息大多被hardcode到kernel中):
∙CPU的数量和类别
∙内存基地址和大小
∙总线和桥
∙外设连接
∙中断控制器和中断使用情况
∙GPIO控制器和GPIO使用情况
∙Clock控制器和Clock使用情况
它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。
2. DeviceTree组成和结构
整个DeviceTree牵涉面比较广,即增加了新的用于描述设备硬件信息的文本格式,又增加了编译这一文本的工具,同时Bootloader也需要支持将编译后的DeviceTree传递给Linux内核。
DTS(devicetreesource)
.dts文件是一种ASCII文本格式的DeviceTree描述,此文本格式非常人性化,适合人类的阅读习惯。
基本上,在ARMLinux在,一个.dts文件对应一个ARM的machine,一般放置在内核的arch/arm/boot/dts/目录。
由于一个SoC可能对应多个machine(一个SoC可以对应多个产品和电路板),势必这些.dts文件需包含许多共同的部分,Linux内核为了简化,把SoC公用的部分或者多个machine共同的部分一般提炼为.dtsi,类似于C语言的头文件。
其他的machine对应的.dts就include这个.dtsi。
譬如,对于VEXPRESS而言,vexpress-v2m.dtsi就被vexpress-v2p-ca9.dts所引用,vexpress-v2p-ca9.dts有如下一行:
/include/"vexpress-v2m.dtsi"
当然,和C语言的头文件类似,.dtsi也可以include其他的.dtsi,譬如几乎所有的ARMSoC的.dtsi都引用了skeleton.dtsi。
.dts(或者其include的.dtsi)基本元素即为前文所述的结点和属性:
[plain] viewplaincopy
1./ {
2. node1 {
3. a-string-property = "A string";
4. a-string-list-property = "first string", "second string";
5. a-byte-data-property = [0x01 0x23 0x34 0x56];
6. child-node1 {
7. first-child-property;
8. second-child-property = <1>;
9. a-string-property = "Hello, world";
10. };
11. child-node2 {
12. };
13. };
14. node2 {
15. an-empty-property;
16. a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
17. child-node1 {
18. };
19. };
20.};
上述.dts文件并没有什么真实的用途,但它基本表征了一个DeviceTree源文件的结构:
1个root结点"/";
root结点下面含一系列子结点,本例中为"node1"和"node2";
结点"node1"下又含有一系列子结点,本例中为"child-node1"和"child-node2";
各结点都有一系列属性。
这些属性可能为空,如"an-empty-property";可能为字符串,如"a-string-property";可能为字符串数组,如"a-string-list-property";可能为Cells(由u32整数组成),如"second-child-property",可能为二进制数,如"a-byte-data-property"。
下面以一个最简单的machine为例来看如何写一个.dts文件。
假设此machine的配置如下:
1个双核ARMCortex-A932位处理器;
ARM的localbus上的内存映射区域分布了2个串口(分别位于0x101F1000和0x101F2000)、GPIO控制器(位于0x101F3000)、SPI控制器(位于0x10170000)、中断控制器(位于0x10140000)和一个externalbus桥;
Externalbus桥上又连接了SMCSMC91111Ethernet(位于0x10100000)、I2C控制器(位于0x10160000)、64MBNORFlash(位于0x30000000);
Externalbus桥上连接的I2C控制器所对应的I2C总线上又连接了MaximDS1338实时钟(I2C地址为0x58)。
其对应的.dts文件为:
[plain] viewplaincopy
1./ {
2. compatible = "acme,coyotes-revenge";
3. #address-cells = <1>;
4. #size-cells = <1>;
5. interrupt-parent = <&intc>;
6.
7. cpus {
8. #address-cells = <1>;
9. #size-cells = <0>;
10. cpu@0 {
11. compatible = "arm,cortex-a9";
12. reg = <0>;
13. };
14. cpu@1 {
15. compatible = "arm,cortex-a9";
16. reg = <1>;
17. };
18. };
19.
20. serial@101f0000 {
21. compatible = "arm,pl011";
22. reg = <0x101f0000 0x1000 >;
23. interrupts = < 1 0 >;
24. };
25.
26. serial@101f2000 {
27. compatible = "arm,pl011";
28. reg = <0x101f2000 0x1000 >;
29. interrupts = < 2 0 >;
30. };
31.
32. gpio@101f3000 {
33. compatible = "arm,pl061";
34. reg = <0x101f3000 0x1000
35. 0x101f4000 0x0010>;
36. interrupts = < 3 0 >;
37. };
38.
39. intc:
interrupt-controller@10140000 {
40. compatible = "arm,pl190";
41. reg = <0x10140000 0x1000 >;
42. interrupt-controller;
43. #interrupt-cells = <2>;
44. };
45.
46. spi@10115000 {
47. compatible = "arm,pl022";
48. reg = <0x10115000 0x1000 >;
49. interrupts = < 4 0 >;
50. };
51.
52. external-bus {
53. #address-cells = <2>
54. #size-cells = <1>;
55. ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
56. 1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
57. 2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
58.
59. ethernet@0,0 {
60. compatible = "smc,smc91c111";
61. reg = <0 0 0x1000>;
62. interrupts = < 5 2 >;
63. };
64.
65. i2c@1,0 {
66. compatible = "acme,a1234-i2c-bus";
67. #address-cells = <1>;
68. #size-cells = <0>;
69. reg = <1 0 0x1000>;
70. interrupts = < 6 2 >;
71. rtc@58 {
72. compatible = "maxim,ds1338";
73. reg = <58>;
74. interrupts = < 7 3 >;
75. };
76. };
77.
78. flash@2,0 {
79. compatible = "samsung,k8f1315ebm", "cfi-flash";
80. reg = <2 0 0x4000000>;
81. };
82. };
83.};
上述.dts文件中,root结点"/"的compatible属性compatible="acme,coyotes-revenge";定义了系统的名称,它的组织形式为:
Linux内核透过root结点"/"的compatible属性即可判断它启动的是什么machine。
在.dts文件的每个设备,都有一个compatible属性,compatible属性用户驱动和设备的绑定。
compatible属性是一个字符串的列表,列表中的第一个字符串表征了结点代表的确切设备,形式为"
可以说前面的是特指,后面的则涵盖更广的范围。
如在arch/arm/boot/dts/vexpress-v2m.dtsi中的Flash结点:
[plain] viewplaincopy
1.flash@0,00000000 {
2. compatible = "arm,vexpress-flash", "cfi-flash";
3. reg = <0 0x00000000 0x04000000>,
4. <1 0x00000000 0x04000000>;
5. bank-width = <4>;
6. };
compatible属性的第2个字符串"cfi-flash"明显比第1个字符串"arm,vexpress-flash"涵盖的范围更广。
再比如,FreescaleMPC8349SoC含一个串口设备,它实现了国家半导体(NationalSemiconductor)的ns16550寄存器接口。
则MPC8349串口设备的compatible属性为compatible="fsl,mpc8349-uart","ns16550"。
其中,fsl,mpc8349-uart指代了确切的设备,ns16550代表该设备与NationalSemiconductor的16550UART保持了寄存器兼容。
接下来root结点"/"的cpus子结点下面又包含2个cpu子结点,描述了此machine上的2个CPU,并且二者的compatible属性为"arm,cortex-a9"。
注意cpus和cpus的2个cpu子结点的命名,它们遵循的组织形式为:
name是一个ASCII字符串,用于描述结点对应的设备类型,如3comEthernet适配器对应的结点name宜为ethernet,而不是3com509。
如果一个结点描述的设备有地址,则应该给出@unit-address。
多个相同类型设备结点的name可以一样,只要unit-address不同即可,如本例中含有cpu@0、cpu@1以及serial@101f0000与serial@101f2000这样的同名结点。
设备的unit-address地址也经常在其对应结点的reg属性中给出。
ePAPR标准给出了结点命名的规范。
可寻址的设备使用如下信息来在DeviceTree中编码地址信息:
[plain] viewplaincopy
1.ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
2. 1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
3. 2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
ranges是地址转换表,其中的每个项目是一个子地址、父地址以及在子地址空间的大小的映射。
映射表中的子地址、父地址分别采用子地址空间的#address-cells和父地址空间的#address-cells大小。
对于本例而言,子地址空间的#address-cells为2,父地址空间的#address-cells值为1,因此00 0x10100000 0x10000的前2个cell为external-bus后片选0上偏移0,第3个cell表示external-bus后片选0上偏移0的地址空间被映射到CPU的0x10100000位置,第4个cell表示映射的大小为0x10000。
ranges的后面2个项目的含义可以类推。
DeviceTree中还可以中断连接信息,对于中断控制器而言,它提供如下属性:
interrupt-controller–这个属性为空,中断控制器应该加上此属性表明自己的身份;
#interrupt-cells–与#address-cells和#size-cells相似,它表明连接此中断控制器的设备的interrupts属性的cell大小。
在整个DeviceTree中,与中断相关的属性还包括:
interrupt-parent–设备结点透过它来指定它所依附的中断控制器的phandle,当结点没有指定interrupt-parent时,则从父级结点继承。
对于本例而言,root结点指定了interrupt-parent=<&intc>;其对应于intc:
interrupt-controller@10140000,而root结点的子结点并未指定interrupt-parent,因此它们都继承了intc,即位于0x10140000的中断控制器。
interrupts–用到了中断的设备结点透过它指定中断号、触发方法等,具体这个属性含有多少个cell,由它依附的中断控制器结点的#interrupt-cells属性决定。
而具体每个cell又是什么含义,一般由驱动的实现决定,而且也会在DeviceTree的binding文档中说明。
譬如,对于ARMGIC中断控制器而言,#interrupt-cells为3,它3个cell的具体含义Documentation/devicetree/bindings/arm/gic.txt就有如下文字说明:
另外,值得注意的是,一个设备还可能用到多个中断号。
对于ARMGIC而言,若某设备使用了SPI的168、169号2个中断,而言都是高电平触发,则该设备结点的interrupts属性可定义为:
interrupts=<01684>,<01694>;
除了中断以外,在ARMLinux中clock、GPIO、pinmux都可以透过.dts中的结点和属性进行描述。
DTC(devicetreecompiler)
将.dts编译为.dtb的工具。
DTC的源代码位于内核的scripts/dtc目录,在Linux内核使能了DeviceTree的情况下,编译内核的时候主机工具dtc会被编译出来,对应scripts/dtc/Makefile中的“hostprogs-y:
=dtc”这一hostprogs编译target。
在Linux内核的arch/arm/boot/dts/Makefile中,描述了当某种SoC被选中后,哪些.dtb文件会被编译出来,如与VEXPRESS对应的.dtb包括:
[plain] viewplaincopy
1.dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
2. vexpress-v2p-ca9.dtb \
3. vexpress-v2p-ca15-tc1.dtb \
4. vexpress-v2p-ca15_a7.dtb \
5. xenvm-4.2.dtb
在Linux下,我们可以单独编译DeviceTree文件。
当我们在Linux内核下运行makedtbs时,若我们之前选择了ARCH_VEXPRESS,上述.dtb都会由对应的.dts编译出来。
因为arch/arm/Makefile中含有一个dtbs编译target项目。
DeviceTreeBlob(.dtb)
.dtb是.dts被DTC编译后的二进制格式的DeviceTree描述,可由Linux内核解析。
通常在我们为电路板制作NAND、SD启动image时,会为.dtb文件单独留下一个很小的区域以存放之,之后bootloader在引导kernel的过程中,会先读取该.dtb到内存。
Binding
对于DeviceTree中的结点和属性具体是如何来描述设备的硬件细节的,一般需要文档来进行讲解,文档的后缀名一般为.txt。
这些文档位于内核的Documentation/devicetree/bindings目录,其下又分为很多子目录。
Bootloader
Ubootmainline从v1.1.3开始支持DeviceTree,其对ARM的支持则是和ARM内核支持DeviceTree同期完成。
为了使能DeviceTree,需要编译Uboot的时候在config文件中加入
#defineCONFIG_OF_LIBFDT
在Uboot中,可以从NAND、SD或者TFTP等任意介质将.dtb读入内存,假设.dtb放入的内存地址为0x71000000,之后可在Uboot运行命令fdtaddr命令设置.dtb的地址,如:
U-Boot>fdtaddr0x71000000
fdt的其他命令就变地可以使用,如fdtresize、fdtprint等。
对于ARM来讲,可以透过bootzkernel_addrinitrd_addressdtb_address的命令来启
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ARM Linux 3x的设备树Device Tree 设备 Device