内核移植之设备树

作者:曹老师,华清远见教育科技集团讲师。

拿到板子虽然板子后决定移植一个比较新的内核,于是从www.kernel.org上下了个内核源码linux-3.14.tar.xz,可是解压后却发现内核变了,变得和以前不一样。arch/arm/还在,arch/arm/mach-exynos也在,可是我的arch/arm/mach-exynox/mach-smdk4412.c哪里去了,多出一个mach-exynos4-dt.c难道是这个文件,可是打开后发现短短几十行代码,而且

DT_MACHINE_START(EXYNOS4210_DT, "Samsung Exynos4 (Flattened Device Tree)")
        …..
        MACHINE_END

这是什么东西,不应该是

MACHINE_START(SMDK4412, "SMDK4X12")
        ……
        MACHINE_END

看了半天发现到处都是DT这个字,看到了Device Tree的字眼,对于这个字眼做PowerPc的不会陌生,对于我这种一直在做ARM的人就只是闻其名二不见其人了,这个东西什么时候出现的呢?查了资料才发现这还有个小故事的:

Device Tree在Linux内核驱动中的使用源于2011年3月17日Linus Torvalds在ARM Linux邮件列表中的一封邮件,他宣称“this whole ARM thing is a fucking pain in the ass”,并提倡学习PowerPC等其他架构已经成熟使用的Device Tree技术。自此,Device Tree正式进入ARM社区的视野中。

原因是在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。读者有兴趣可以统计下常见的s3c2410、s3c6410等板级目录,代码量在数万行。于是ARM Linux也采用了Device Tree这种技术。

Device Tree是一种用来描述硬件的数据结构,类似板级描述语言,起源于OpenFirmware(OF)。在目前广泛使用的Linux kernel 2.6.x版本中,对于不同平台、不同硬件,往往存在着大量的不同的、移植性差的板级描述代码,以达到对这些不同平台和不同硬件特殊适配的需求。但是过多的平台、过的的不同硬件导致了这样的代码越来越多,比如arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码, platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。在一些常见的芯片如s3c2410、s3c6410等板级目录,代码量在数万行。终引发了Linux创始人Linus的不满,以及强烈呼吁改变。Device Tree的引入给驱动适配带来了很大的方便,一套完整的Device Tree可以将一个PCB摆在你眼前。Device Tree可以描述CPU,可以描述时钟、中断控制器、IO控制器、SPI总线控制器、I2C控制器、存储设备等任何现有驱动单位。对具体器件能够描述到使用哪个中断,内存映射空间是多少等等。

1.1. Devicetree基本数据格式

Devicetree是一个树状结构,由节点(node)和属性(properties)构成,而属性就是成对出现的名字(key)和值(value),每个节点除了属性还可能有若干子节点如:

/ {
                node1 {
                        a-string-property = "A string";
                        a-string-list-property = "first string", "second string";
                        a-byte-data-property = [0x01 0x23 0x34 0x56];
                        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 {
                        };
                };
        };

DTS(device tree source)

现在在内核里的arch/arm/boot/dts目录下存在着大量的.dts和.dtsi文件,dts文件是一种ASCII文本格式的文件,用来描述一个DeviceTree,由于一个SOC可能有多个machine(使用同一个SOC的不同产品),且同一系列的SOC也有很多相同的地方,所以这些dts也就有很多相同的部分,Linux内核把这些相同的东西提炼出来就有了dtsi文件的存在,dtsi名字中的”I”就是”include”的以上,所以作用类似C语言中的头文件。

这个结构中有:

一个根节点”/”

根节点有两个子节点”node1”和”node2”

node1又有两子节点”child-node1”和“child-node2”

属性是成对的key和value,可以是一个字符串,可以是一个整数也可以是一个列表

● 字符串信息

a-string-property = "A string";

也可以是一个字符串列表

a-string-list-property = "first string", "second string";

● Cells信息(u32组成)

a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */

● 二进制数

a-byte-data-property = [0x01 0x23 0x34 0x56];

● 混合属性

mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;

比如我们的板子FS4412它的属性:

1、 ARM Cortex-9 CPU 四核处理器Exynos4412

/{
                compatible = “Samsung, smdk4412”;
                cpus {
                        cpu@0 {
                                compatible = “arm,cortex-a9”;
                        };
                        cpu@1{
                                compatible = “arm,cortex-a9”;
                        };
                        cpu@2 {
                                compatible = “arm,cortex-a9”;
                        };
                        cpu@3{
                                compatible = “arm,cortex-a9”;
                        }
                };
        }

根节点有个compatible属性compatible = “Samsung,smdk4412” ,它定义了一个系统的名称,它的结构是”,”,Linux内核根据它确定启动什么样的machine。除了根有这个属性之外,每个设备都有这个属性,设备驱动通过这个属性找到设备树上某个特定的结点从中获取相应的信息。这个属性也可能是一个列表,也就是说可能是compatible = “Samsung, smdk4412”,”Samsung,Exynos4412”,列表中第一个字符串表示主要支持的设备,后续的字符串表示兼容的其他设备。

2、 Exynos4412上有串口

serial@13800000 {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = “samsung,exynos4412-uart”;
        reg = <0x13800000 0x100>;
        interruprs = <0 52 0>;
        }

结点的名字Serial@13800000,serial表示设备的名字,13800000是控制器的地址,主要是防止重名。

reg为驱动的意思,格式为;

reg = <addresslen [address1 len1] [address2 len2]>

数据与数据之间用空格间隔

后续内容在实例中继续添加