




已阅读5页,还剩12页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Linux Kernel DT(Device Tree) 之前在使用的3.0.8版本内核还没有使用上DT,而最近在研发使用的3.10.37版本内核已使用上了DT,瞬间感觉自己的知识体系更新慢了,查了资料发现3.x版本的内核已经支持DT了,为何ARM也要使用上DT呢? 在旧版本的ARM Linux内核里,我们习惯上会去arch/arm/mach-XXX/目录下进行一些板载级设备配置,尤其在board-YYY.c文件里使用platform_add_devices()等函数去注册一堆硬件设备以及板级初始化操作,还有如下宏:MACHINE_START(project name, board name) .boot_params = PLAT_PHYS_OFFSET + 0x800, .fixup = XXX_fixup, .reserve = &XXX_reserve, .map_io = XXX_map_io, .init_irq = XXX_init_irq, .timer = &XXX_timer, .init_machine = XXX_init,MACHINE_END 其中的XXX_init函数里就会调用platform_add_devices()。 以及arch/arm/plat-XXX目录下也有一堆平台级的操作,一般在进行移植工作的时候,就是修改了上面的board-YYY.c文件,调试好了各种Clock之后,剩下的就是设备驱动程序了。这些处理往往在所有ARM平台里有很多的相同操作和共同定义,而这些往往存在了大量的重复编码工作,故而Linux内核的开发人员和ARM的相关人员引入了DT来改善该问题,相关的历史有如下引用内容:Linus Torvalds 在2011 年3 月17 日的ARM Linux 邮件列表宣称“this whole ARM thing is a f*cking pain in the ass”,引发ARM Linux 社区的地震,随后ARM 社区进行了一系列的重大修正。在过去的ARM Linux 中,arch/arm/plat-xxx 和arch/arm/mach-xxx 中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform 设备、resource、i2c_board_info、spi_board_info 以及各种硬件的platform_data。读者有兴趣可以统计下常见的s3c2410、s3c6410 等板级目录,代码量在数万行。社区必须改变这种局面,于是PowerPC 等其他体系架构下已经使用的Flattened Device Tree(FDT)进入ARM 社区的视野。Device Tree 是一种描述硬件的数据结构,它起源于OpenFirmware (OF)。在Linux 2.6 中,ARM 架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx 和arch/arm/mach-xxx,采用Device Tree 后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel 中进行大量的冗余编码。Device Tree 由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的name 和value。在Device Tree 中,可描述的信息包括(原先这些信息大多被hard code 到kernel 中): CPU 的数量和类别 内存基地址和大小 总线和桥 外设连接 中断控制器和中断使用情况 GPIO 控制器和GPIO 使用情况 Clock 控制器和Clock 使用情况它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader 会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux 内核中的platform_device、i2c_client、spi_device 等设备,而这些设备用到的内存、IRQ 等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。 了解完相关历史后,我们接下来分如下几个方面了解ARM Linux中的DT:1 使用和不使用DT对bootloader和kernel的影响2 关于DT3 kernel中的DT 一.使用和不使用DT对bootloader和kernel的影响1.不使用DT 不使用DT时,kernel包含了硬件的完整描述信息,bootloader加载单独的一个二进制文件(kernel镜像文件uImage或zImage)并执行它,bootloader通过寄存器r2传递ATAGS(为一些附加信息,如RAM大小和地址、cmdline等)给kernel,通过寄存器r1传递一个机器类型(machine type,用于告诉内核将启动哪一款板卡)整数给kernel。有如下映射: 这时候,在U-boot命令行里执行bootm 命令可以启动kernel。2.使用DT 使用DT时,kernel包含的硬件完整打桩信息被提取为一个二进制文件DTB(device tree blob)文件,bootloader则需要加载kernel镜像(uImage或zImage)以及DTB(arch/arm/boot/dts/目录下的DTS文件通过DTC编译成DTB文件),bootloader通过寄存器r2传递DTB文件(该文件也包含了RAM信息、cmdline等信息)所在地址给kernel,而原先传递板卡类型整数的r1则不需要再关注了,相应的映射如下: 这时候,在U-boot里使用命令bootm - 来启动kernel。3.bootloader对DT的支持 Uboot的主线代码从v1.1.3开始就支持DT了,其对ARM的支持和kernel对Device Tree的支持是同期完成的,在Uboot中需要在config文件中加入#define CONFIG_OF_LIBFDT配置项即可,当我们将DTB文件在Uboot里加载到内存中后,通过fdt addr 0xnnnnnnnn命令来设置DTB文件对应地址,这样就可以使用fdt resize、fdt print等命令对DTB文件进行操作了。对于ARM,使用bootz kernel_addr initrd_addr dtb_addr命令来启动kernel,dtb_addr作为bootz或bootm最后一个参数,第一个参数为内核镜像的地址,第二个参数为initrd的地址,如不存在,使用-代替(看完这句话,一的2中的命令就能理解了)。4.kernel的DT兼容引导模式 在实际情况下,存在部分平台过旧,方案厂无法提供新版的bootloader,而原有的bootloader不支持DT时,还好kernel有兼容机制: 当设置CONFIG_ARM_APPENDED_DTB为y时,它表示我们使用kernel时,需要在kernel镜像后面查找DTB信息(即kernel镜像后紧挨着DTB),而Makefile也没有相应的规则去生成相应格式的kernel镜像,此时,我们需要执行如下命令手动制作相应的镜像: cat arch/arm/boot/zImage arch/arm/boot/dts/myboard.dtb my-zImage mkimage . d my-zImage my-uImage 而当设置CONFIG_ARM_ATAG_DTB_COMPAT为y时,它表示kernel将从bootloader获取到ATAGS信息,并更新DT文件使用这些信息。二.关于DT 首先,先看下一个基本的DT语法格式图示: 上图中所保存成的一个.dts文件并不会有实质的功能,仅仅是一个Device Tree源文件结构的呈现,由图可见,一个.dts文件包含一个root结点/,root结点下面有一系列子结点,上图中有node0和node1,其中node0下面还有两个子结点child-node0和child-node1,node1下面有child-node0子结点,而结点中又有一系列的属性,如属性来空:an-empty-property,为字符串:a-string-property,为字符串数组:a-string-list-property,为二进制:a-byte-data-property,为Cells(由u32整数组成):a-cell-property、second-child-property,为引用:a-reference-to-something。还有别名node1,实际上是node1结点,如果引用时,则需要完整路径/node1,而使用别名可以省掉这绝对路径的一长串字符。 接下来,拿一个实例来说明下一个平台于DTS的配置: 假如我们有如下配置的一台机器: 1个双核ARM Cortex-A9 32位处理器 ARM的local bus上的内在映射区域分布如下控制器: 2个串口(分别位于0x101F1000和0x101F2000) GPIO控制器(0x101F3000) SPI控制器(0x10170000) 中断控制器(0x10160000) 一个external bus桥,桥上连接如下设备: SMC SMC91111 Ethernet(0x10100000) I2C控制器(0x10160000),上面接了如下设备: Maxim DS1338 RTC(I2C地址0x58) 64MB Nor Flash(0x30000000) 上述配置对应的.dts文件内容如下: / compatible = acme,coyotes-revenge; #address-cells = ; #size-cells = ; interrupt-parent = ; cpus #address-cells = ; #size-cells = ; cpu0 compatible = arm,cortex-a9; reg = ; ; cpu1 compatible = arm,cortex-a9; reg = ; ; ; serial101f0000 compatible = arm,pl011; reg = ; interrupts = ; ; serial101f2000 compatible = arm,pl011; reg = ; interrupts = ; ; gpio101f3000 compatible = arm,pl061; reg = ; interrupts = ; ; intc: interrupt-controller10140000 compatible = arm,pl190; reg = ; interrupt-controller; #interrupt-cells = ; ; spi10115000 compatible = arm,pl022; reg = ; interrupts = ; ; external-bus #address-cells = #size-cells = ; ranges = ; / Chipselect 3, NOR Flash ethernet0,0 compatible = smc,smc91c111; reg = ; interrupts = ; ; i2c1,0 compatible = acme,a1234-i2c-bus; #address-cells = ; #size-cells = ; reg = ; interrupts = ; rtc58 compatible = maxim,ds1338; reg = ; interrupts = ; ; ; flash2,0 compatible = samsung,k8f1315ebm, cfi-flash; reg = ; ; ; ; 上面的.dts文件中,root结点/的compatible属性(compatible = acme,coyotes-revenge;)定义了系统的名字,其组织形式为:,。Linux kernel通过root结点的该属性就可以判断需要启动的是什么machine。 在.dts文件中的每个设备,都有一个compatible属性,用于驱动与设备间的绑定。compatible属性是一个字符串列表,该列表第一个字符串表示了结点所代表的确切设备,其后的字符串代表可兼容的设备,在上面的.dts文件中有如下内容: flash2,0 compatible = samsung,k8f1315ebm, cfi-flash; reg = ; ; 其中,compatible属性的第一个字符串samsung,k8f1315ebm表示了该配置项是对samsung的k8f1315ebm这款Nor Flash的支持,第二个字符串cfi-flash表示可被兼容的设备型号(省略了厂商信息,表示可用的范围更广)。 接下来root结点/的cpus子结点下包含了2个cpu子结点,描述了该machine上的双核CPU,并且这两个子结点的compatible属性都为arm,cortex-a9。注意这两个子结点的命名都遵循的组织形式为:,其中中的内容是必选项,而中的则为可选项。name是一个ASCII字符串,用于描述结点对应的设备类型,如3com Ethernet PHY对应的结点name为ethernet,而不是3com509。如果一结点描述的设备有地址,则应给出相应的unit-address。多个相同类型的设备结点的name可以一样,只需相应的unit-address不同即可,如上述.dts文件中所示。设备的unit-address地址通常也在其对应结点的reg属性中给出。 可寻址的设备使用reg、#address-cells、#size-cells三属性来确定其在DT中的编址信息,其中reg的组织形式为:reg=,这里每一组address length表明了该设备使用的一个地址范围。address和length字段是可变长的,address为1个或多个cell(32位的整形),而length则为cell列表或空,父结点的#address-cells和#size-cells决定了子结点的reg属性的address和length字段的长度。在上面的.dts中,root结点的#address-cells=;和#size-cells=;决定了serial、gpio、spi等结点的address和length字段的长度都为1。cpus结点的#address-cells=;和#size-cells=;决定了2个cpu子结点的address长度为1,而length为空,即2个cpu的reg分别为reg=;和reg=;。external-bus结点的#address-cells=;和#size-cells=;决定了其子结点ethernet、i2c、flash的reg字段为reg=;、reg=;和reg=;,这三个reg的第一个值分别为0、1、2,是对应的片选,第二个值都为0,表示相应片选的基地址,第三个值分别为0x1000、0x1000、0x4000000为length。特别要注意的是i2c结点中定义的#address-cells=;和#size-cells=;作用到相应总结上的RTC设备的address字段则为0x58,是该设备的i2c地址。 对于root结点的子结点,其address区域直接位于CPU的memory区域,均为CPU的视图范围,但是,经过总线桥后的address一般需要进行转换才能被CPU的memory区域映射到,从上面的.dts文件中可看到,external-bus的ranges属性定义了经过external-bus桥后的地址范围如何映射到CPU的memory区域,如下所示: ranges = ; / Chipselect 3, NOR Flash ranges是地址转换表,其中第个项目是一个子地址、父地址及其空间大小的映射。映射表中的子地址、父地址分别采用子地址空间的#address-cells和父地址空间的#address-cells大小。对于这儿的ranges,子地址空间的#address-cells为2,父地址空间的#address-cells为1,故而0 0 0x10100000 0x10000的前两项(cell)为external-bus片选0上偏移0,第3项为external-bus片选0上偏移0的地址空间被映射到CPU的0x10100000位置上,第4项为映射的大小为0x10000。后面的两组片选同样理解。 对于中断控制器,DT提供了如下属性: interrupt-controller这个属性为空,中断控制器使用该属性表明身份; #interrupt-cells和#address-cells和#size-cells相似,表明使用该中断控制器的设备的interrupts属性的cell大小; interrupt-parent设备结点通过它来指定其所依附的中断控制器的phandle,当结点没指定interrupt-parent时,则从父结点继续。上面的.dts中,root结点指定了interrupt-parent=;其对应于intc:interrupt-controller10140000,而root结点的子结点没有指定interrupt-parent,故而都继续了intc,即位于0x10140000的中断控制器。 interrupts用到了中断的设备结点通过它来指定中断号、触发方式等,具体该属性含有多少个cell,由其所依附的中断控制器结点的#interrupt-cells属性决定,而具体每个cell的含义,一般由驱动的实现决定,其会在DT的binding文档中说明,如ARM GIC中断控制器,#interrupt-cells为3,3个cell的具体含义可查阅Documentation/devicetree/bindings/arm/gic.txt文件,有如下说明: The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI interrupts. The 2nd cell contains the interrupt number for the interrupt type. SPI interrupts are in the range 0-987. PPI interrupts are in the range 0-15. The 3rd cell is the flags, encoded as follows: bits3:0 trigger type and level flags. 1 = low-to-high edge triggered 2 = high-to-low edge triggered 4 = active high level-sensitive 8 = active low level-sensitive bits15:8 PPI interrupt cpu mask. Each bit corresponds to each of the 8 possible cpus attached to the GIC. A bit set to 1 indicated the interrupt is wired to that CPU. Only valid for PPI interrupts. 还需注意的是,一个设备可能使用多个中断号。对于ARM GIC来说,若某设备使用SPI的168、169两个中断,并且都是高电平触发,那么该设备结点的interrupts属性可定义为:interrupts=,;。除了中断外,ARM Linux中clock、GPIO、pinmux都可通过.dts来描述,而pinmux/pinctl与对应的平台关系较大。三.kernel中的DT1.DTB、DTS和DTC 在ARM Linux Kernel中,所有的DTS(Device Tree Source)文件存放在arch/arm/boot/dts目录下,该目录下后缀为.dts的文件为板级定义,而.dtsi为SOC级定义,是被包含的文件,在.dts文件中会使用/include/ XXX.dtsi或#include XXX.dtsi这样的语句放在文件最开始位置,将相关文件包含进来。既然.dts可以包含.dtsi,如果碰到两文件中有定义了同一结点,那么会合并两文件中的所有属性,而当为同一属性时,则以.dts文件中的为准,有如下例图所示: 对于DTC(Device Tree Compiler)会将.dts文件编译为.dtb文件,该工具的源码在scripts/dtc目录下,在该目录下的Makefile文件中有hostprogs-y := dtc一句,确保在编译时会将该dtc编译为主机工具。 对于DTB(Device Tree Blob)文件由DTC编译生成,是在bootloader中被加载和在kernel引导时被解析的二进制文件。2.kernel对DT支持做的改变 使用DT后,以往所使用的大量板级信息都不再需要的,例如在arch/arm/plat-xxx和arch/arm/mach-xxx曾经经常实施的操作: a.注册platform_device、绑定resource,即内存和IRQ等板级信息 使用DT后,形如 static struct resource xxx_resources = 0 = .start = , .end = , .flags = IORESOURCE_MEM, , 1 = .start = , .end = , .flags = IORESOURCE_IRQ, , ; static struct platform_device xxx_device = .name = xxx, .id = -1, .dev = .platform_data = &xxx_data, , .resource = xxx_resources, .num_resources = ARRAY_SIZE(xxx_resources), ; 之类的platform_device代码都不再需要了,其中platform_device 会由kernel自动展开,其中这些resource来自.dts的设备结点reg、interruptes属性。 比较典型的,大多数总结与“simple_bus”都兼容,而在SoC对应machine的.init_machine成员函数中,调用of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL);来展开所有的platform_device。例如,有XXX SoC,在arch/arm/mach-xxx/的板级文件中有如下的展开.dts中设备结点对就的platform_device: static struct of_device_id xxx_of_bus_ids _initdata = .compatible = simple-bus, , , ; void _init xxx_mach_init(void) of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL); #ifdef CONFIG_ARCH_XXX DT_MACHINE_START(XXX_DT, Generic XXX (Flattened Device Tree) .init_machine = xxx_mach_init, MACHINE_END #endif b.注册i2c_board_info,指定IRQ等板级信息 形如: static struct i2c_board_info _initdata afeb9260_i2c_devices = I2C_BOARD_INFO(tlv320aic23, 0x1a), , I2C_BOARD_INFO(fm3130, 0x68), , I2C_BOARD_INFO(24c64, 0x50), , ; 之类的代码,现在也不需要了,只需要将tlv320aic23、fm3130和24c64之类的设备结点填充到相应的I2C Controller结点的子结点中,如上面的.dts中所示。DT的I2C Client会通过I2C Host驱动的probe()函数中调用of_i2c_register_devices(&i2c_dev-adapter);被自动展开使用。 c.注册spi_board_info,指定IRQ等板级信息 形如: static struct spi_board_info afeb9260_spi_devices = /* DataFlash chip */ .modalias = mtd_dataflash, .chip_select = 1, .max_speed_hz = 15 * 1000 * 1000, .bus_num = 0, , ; 之类的代码也不再需要了,只需将mtd_dataflash之类结点作为SPI控制器的子结点即可,SPI Host驱动的probe函数通过spi_register_master()注册master的时候,会自动展开其slave。 d.指定电路板的machine及相关callback 以前,ARM Linux 针对不同的电路板会建立由MACHINE_START 和MACHINE_END 包围起来的针对这个machine 的一系列callback,如: MACHINE_START(VEXPRESS, ARM-Versatile Express) .atag_offset = 0x100, .smp = smp_ops(vexpress_smp_ops), .map_io = v2m_map_io, .init_early = v2m_init_early, .init_irq = v2m_init_irq, .timer = &v2m_timer, .handle_irq = gic_handle_irq, .init_machine = v2m_init, .restart = vexpress_restart, MACHINE_END Uboot在启动kernel时会将MACHINE_ID放在r1中,Linux Kernel启动时会匹配传过来的MACHINE_ID,将该值与MACHINE_START处声明的MACHINE_ID比对,如匹配则会执行相应machine的一系列初始化函数。 使用DT后,MACHINE_START变为DT_MACHINE_START,其中多了一个.dt_compat成员,用于指定该machine与.dts中root结点compatible属性兼容关系。一旦bootloader传递给kernel的DTB中root结点compatible属性出现在某machine的.dt_compat表中,则匹配上了,从而执行相应的初始化函数。如: static const char * const v2m_dt_match _initconst = arm,vexpress, xen,xenvm, NULL, ; DT_MACHINE_START(VEXPRESS_DT, ARM-Versatile Express) .dt_compat = v2m_dt_match, .smp = smp_ops(vexpress_smp_ops), .map_io = v2m_dt_map_io, .init_early = v2m_dt_init_early, .init_irq = v2m_dt_init_irq, .timer = &v2m_dt_timer, .init_machine = v2m_dt_init, .handle_irq = gic_handle_irq, .restart = vexpress_restart, MACHINE_END Linux中倡导对多个SoC、多个电路板的通用DT machine,即一个DT machine的.dt_compat表含有多个电路板.dts文件的root结点compatible属性字符串,之后,如果不同的电路板的初始化序列不一样时,可通过of_machine_is_compatible() API来判断。例如arch/arm/mach-exynos/mach-exynos5-dt.c 的EXYNOS5_DT machine 同时兼容samsung,exynos5250和samsung,exynos5440: static char const *exynos5_dt_compat _initdata = samsung,exynos5250, samsung,exynos5440, NULL ; DT_MACHINE_START(EXYNOS5_DT, SAMSUNG EXYNOS5 (Flattened Device Tree) /* Maintainer: Kukjin Kim */ .init_irq = exynos5_init_irq, .smp = smp_ops(exynos_smp_ops), .map_io = exynos5_dt_map_io, .handle_irq = gic_handle_irq, .init_machine = exynos5_dt_machine_init, .init_late = exynos_init_late, .timer = &exynos4_timer, .dt_compat = exynos5_dt_compat, .restart = exynos5_restart, .reserve = exynos5_reserve, MACHINE_END 其.init_machine成员函数中有针对不同machine进行分支处理: static void _init exynos5_dt_machine_init(void) if (of_machine_is_compatible(samsung,exynos5250) of_platform_populate(NULL, of_default_bus_match_table, exynos5250_auxdata_lookup, NULL); else if (of_machine_is_compatible(samsung,exynos5440) of_platform_populate(NULL, of_default_bus_match_table, exynos5440_auxdata_lookup, NULL); 使用DT后,驱动需要与.dts中描述的设备结点匹配,才会引发驱动的probe()函数执行。对于platform_driver,需要添加一个OF匹配表,如上面的.dts文件的acme,a1234-i2c-bus兼容I2C 控制器结点的OF 匹配表为: static const struct of_device_id a1234_i2c_of_match = .compatible = acme,a1234-i2c-bus , , , ; MODULE_DEVICE_TABLE(of, a1234_i
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 网络社区建设与管理会议协议
- 苗木市场合作协议
- OOEM供应合同范例
- 农村赠与合同标准文本
- 动物标本制作合同样本
- 交接合同标准文本
- 动迁出售合同标准文本
- 公司换股东合同标准文本
- 分销芯采购合同样本
- 劳务合同样本 豆丁
- 七年级语文下册《登幽州台歌》课件
- 兼职劳务协议合同模板
- 2025年河南机电职业学院高职单招语文2019-2024历年真题考点试卷含答案解析
- 2025年浙江长征职业技术学院单招综合素质考试题库及答案1套
- 湖南新高考教学教研联盟暨长郡二十校联盟2025届高三年级第二次联考历史试题及答案
- (二模)东北三省三校2025年高三第二次联合模拟考试 英语试卷(含答案解析)
- 2025年甘肃西北永新集团招聘11人笔试参考题库附带答案详解
- 雨水泵站机电设备安装工程施工方案52719
- 2025年吉林交通职业技术学院单招职业技能考试题库新版
- 2025 保健品行业专题报告:保健品蓝帽子九问九答
- 基于树枝振动特性的香榧采摘机设计
评论
0/150
提交评论