Linux device tree 简要笔记

第一、DTS简介
     在嵌入式设备上,可能有不同的主板---它们之间差异表现在主板资源不尽相同,比如I2C、SPI、GPIO等接口定义有差别,或者是Timer不同,等等。于是这就产生了BSP的一个说法。所谓BSP,即是是板级支持包,英文全名为:Board Support Package。是介于主板硬件和操纵系统之间的一层。每一个主板,都有自己对应的BSP文件。在kernel/arch/arm/mach-* 目录下,放置着不同主板的BSP文件,比如展讯的某一个项目的BSP文件为:

1 kernel/arch/arm/mach-sc/board-sp7731gea.c 

根据linux设备驱动最抽象的模型(即设备、驱动、总线模型),设备和驱动都会向系统进行注册的。那么,系统正式运行之前,需要登记自己的板载资源,以便后面进行使用。以展讯sc7731-5.1上某个项目为例,这些资源分别包括了:CPU、Memory、UART、TIMER、CLOCK、GPIO、keypad、I2C、FB、SPI等等。这些资源信息,大部分都需要在 board-sp7731gea.c 文件中进行注册。以登记I2C为例:

 1 static struct ft5x0x_ts_platform_data ft5x0x_ts_info = {
 2     .irq_gpio_number    = GPIO_TOUCH_IRQ,
 3     .reset_gpio_number  = GPIO_TOUCH_RESET,
 4     .vdd_name           = "vdd28",
 5 };
 6
 7 static struct i2c_board_info i2c0_boardinfo[] = {
 8     {
 9         I2C_BOARD_INFO(FOCALTECH_TS_NAME, FOCALTECH_TS_ADDR),
10         .platform_data = &ft5x0x_ts_info,
11     },
12 };
13
14 static struct i2c_board_info i2c1_boardinfo[] = {
15     {I2C_BOARD_INFO("sensor_main",0x3C),},
16     {I2C_BOARD_INFO("sensor_sub",0x21),},
17 };
18
19 static int sc8810_add_i2c_devices(void)
20 {
21     i2c_register_board_info(0, i2c1_boardinfo, ARRAY_SIZE(i2c1_boardinfo));
22     i2c_register_board_info(1, i2c0_boardinfo, ARRAY_SIZE(i2c0_boardinfo));
23     return 0;
24 }
25
26 static void __init sc8830_init_machine(void)
27 {
28     //...
29     sc8810_add_i2c_devices();
30     //...
31 }

这上面的I2C设备有这几个:Camera 前后摄、触摸屏(TP)。其中,需要调用 i2c_register_board_info() 这个kernel提供的API对I2C设备进行登记注册。那除开I2C设备以外,比如SPI设备、其他音频设备等等,都可以采用相应的API向系统进行登记。但是这样有两个问题:1.每改动一次板载资源,就得去修改一次BSP文件;2.大量的描述硬件细节的代码,冲进了kernel(Linus似乎对此不能忍受)。
     那么可以把不变的东西和变化的东西分开来做。不变的逻辑,以少量精确的代码搞定;变化的资源,可以形成一个资源配置文件。基于这种思想,Linux device tree(DTS)便应运而生。所谓DTS,它是一个以 ".dts"结尾的文件,该文件会被编译成dtb文件,uboot会把该文件放置到某特定的内存区域,并把相关参数传给kernel;kernel起来之初,便会去解析该文件,以便拿到板载资源配置。DTS文件中内容框架是一棵树的结构,其由一系列的结点(node)和属性(property)键值对组成,此处不进行具体分析。DTS文件一般放在 "kernel/arch/arm/boot/dts/ " 目录下。

二、支持DTS
      linux 3.x kernel已经默认支持了DTS , 可以 make menuconfig -> Boot options -> Flattened Device Tree support 选项;同时,uboot配置文件中也需要定义支持该功能,以展讯某项目为例,在 u-boot64/include/configs/sp7731gea.h 文件中,定义 CONFIG_OF_LIBFDT 宏控:

1 #define CONFIG_OF_LIBFDT

怎么让特定的DTS参加编译呢?在 kernel/arch/arm/boot/dts/Makefile 文件中,比如我们要选择 sprd-scx35_sp7731gea.dts 文件进行编译,则进行如下定义:

1 dtb-$(CONFIG_MACH_SP7731GEA) += sprd-scx35_sp7731gea.dtb

在 kernel/arch/arm/configs/sp7731gea-dt_defconfig 文件中定义 CONFIG_MACH_SP7731GEA 即可。

三、解析DTS简要流程

 1 //在文件 ./kernel/init/main.c 中:
 2 asmlinkage void __init start_kernel(void)
 3 {
 4     //..
 5     setup_arch(&command_line); //选择了什么架构,就去执行该架构下的该函数。比如这里是ARM,则选择进入了 ./kernel/arch/arm/kernel/setup.c 文件
 6     //...
 7 }
 8
 9 //在文件    ./kernel/arch/arm/kernel/setup.c 中:
10 void __init setup_arch(char **cmdline_p)
11 {
12     //...
13     mdesc = setup_machine_fdt(__atags_pointer);  //获取对应机器的dts , __atags_pointer __atags_pointer是uboot传给kernel的一个物理地址。
14     //....
15     unflatten_device_tree(); //全部解析dts树
16     //....
17 }

3.1 setup_machine_fdt

 1 //kernel/arch/arm/kernel/devtree.c
 2 struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
 3 {
 4 //....
 5     devtree = phys_to_virt(dt_phys); //物理地址转虚拟地址
 6
 7     /* check device tree validity */
 8     if (be32_to_cpu(devtree->magic) != OF_DT_HEADER)
 9         return NULL;
10
11     /* Search the mdescs for the ‘best‘ compatible value match */
12     initial_boot_params = devtree;
13     dt_root = of_get_flat_dt_root(); //获取dts的根
14
15     for_each_machine_desc(mdesc) {
16         score = of_flat_dt_match(dt_root, mdesc->dt_compat);
17         if (score > 0 && score < mdesc_score) {
18             mdesc_best = mdesc;
19             mdesc_score = score;
20         }
21     }
22
23     if (!mdesc_best) {
24       //....
25         dump_machine_table(); /* does not return */  //进入了这里是否很麻烦?
26     }
27
28     model = of_get_flat_dt_prop(dt_root, "model", NULL);
29     if (!model)
30         model = of_get_flat_dt_prop(dt_root, "compatible", NULL);
31     if (!model)
32         model = "<unknown>";
33     pr_info("Machine: %s, model: %s\n", mdesc_best->name, model);
34
35     /* Retrieve various information from the /chosen node */
36     of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
37     /* Initialize {size,address}-cells info */
38     of_scan_flat_dt(early_init_dt_scan_root, NULL);
39     /* Setup memory, calling early_init_dt_add_memory_arch */
40     of_scan_flat_dt(early_init_dt_scan_memory, NULL);
41
42     /* Change machine number to match the mdesc we‘re using */
43     __machine_arch_type = mdesc_best->nr;
44
45     return mdesc_best; //返回目标机器的指针
46 }

上面这个函数综合来看的话,就是根据dts来寻找对应的机器了。以展讯某个项目为例,其 sprd-scx35_sp7731gea.dts 文件中定义了字符串 "sprd,sp8835eb" 来标识机器。则它必须要在BSP文件中找到相关的定义,才会继续初始化下去,否则,就认为找不到对应的机器,将跳入 dump_machine_table() 里面,在该函数里面进行死循环。那么,这个BSP文件中是如何提供一个合适的标志来匹配该字符串呢?在对应的BSP文件 board-sp7731gea.c 中这样定义:

1 static const char *sprd_boards_compat[] __initdata = {
2   "sprd,sp8835eb",
3   NULL,
4 };
5
6 MACHINE_START(SCPHONE, "sc8830")
7 //...
8   .dt_compat    = sprd_boards_compat,
9 MACHINE_END 

因为 MACHINE_START ...  MACHINE_END 这一对宏的关系,该文件(board-sp7731gea.c)在编译的时候,会被编译器编译链接到 “.arch.info.init” 段落中去:

1 #define MACHINE_START(_type, _name)            2 static const struct machine_desc __mach_desc_##_type    3 __used                            4 __attribute__((__section__(".arch.info.init"))) = {    5     .name        = _name,
6
7 #define MACHINE_END                8 };

于是,在 setup_machine_fdt() 函数里面,为了匹配特定DTS的机器标志,程序便会去 ".arch.info.init" 段落中将该文件内容读取出来。setup_machine_fdt()中的 for_each_machine_desc(mdesc) 就干了这事:

1 extern struct machine_desc __arch_info_begin[], __arch_info_end[];
2 #define for_each_machine_desc(p)            3     for (p = __arch_info_begin; p < __arch_info_end; p++)

那么,__arch_info_begin 和 __arch_info_end 在哪里定义呢?在文件 kernel/arch/arm/kernel/vmlinux.lds.S (编译器的链接脚本)中:

1 //....
2     .init.arch.info : {
3         __arch_info_begin = .;
4         *(.arch.info.init)
5         __arch_info_end = .;
6     }
7 //....

说简单了:

1 for_each_machine_desc(mdesc) {
2     score = of_flat_dt_match(dt_root, mdesc->dt_compat); //将获取的机器 dt_compat 与DTS中的 机器标识符进行比较
3     if (score > 0 && score < mdesc_score) {
4         mdesc_best = mdesc;
5         mdesc_score = score;
6     }
7 }

这段代码,便是循环去 ".arch.info.init" 区域读取目标机器,然后将该机器定义的 dt_compat 和 DTS根目录下的机器标志进行匹配。一旦正确获取到目标机器后,便返回该机器的指针。

3.2 unflatten_device_tree

这个函数的目的便是通过DTS的内容,创建一个设备节点树。(create tree of device_nodes from flat blob)。在文件 kernel/drivers/of/fdt.c 中:

1 void __init unflatten_device_tree(void)
2 {
3     __unflatten_device_tree(initial_boot_params, &of_allnodes,
4             early_init_dt_alloc_memory_arch);
5
6 /* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
7     of_alias_scan(early_init_dt_alloc_memory_arch);
8 }

其中, of_allnodes 是一个全局变量。解析出来的设备节点将形成一个链表,而 of_allnodes 则是该链表的头节点。

五、机器初始化简要流程

1 start_kernel --> setup_arch --> do_initcalls --> customize_machine

参考资料:
ARM Linux 3.x的设备树(Device Tree) http://blog.csdn.net/21cnbao/article/details/8457546
linux device tree源代码解析 http://www.blog.chinaunix.net/uid-27717694-id-4274992.html
Linux 3.10 ARM Device Tree 的初始化 http://blog.chinaunix.net/uid-20522771-id-3785808.html

时间: 2024-10-08 15:45:36

Linux device tree 简要笔记的相关文章

linux device tree源代码解析--转

//Based on Linux v3.14 source code Linux设备树机制(Device Tree) 一.描述 ARM Device Tree起源于OpenFirmware (OF),在过去的Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx 中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备. resource.i2c_board_info.spi_board_info

linux device driver3 读书笔记(一)

一:机制与策略(转) http://www.51hei.com/bbs/dpj-29441-1.html 机制mechanism,策略policy.如果你看过<linux device drivers>,里面给出了大概的介绍.机制提供了干什么(do what),策略提供如何做(how to do).驱动程序完成机制的功能,把策略的实现留给用户的应用程序. 通常在机制中,驱动程序要完成打开,关闭,读写,控制等功能.这些都是设备使用时最基本的操作.而策略中就要实现一些高级的数据处理或界面功能.通过

关于linux ARM device tree设备树

关于linux ARM device tree设备树 关于linux device tree .dtb文件是如何加载到内核并解析的.见下图: 关于arm device tree的phandle的处理原理,见下图: 详细情况,见下面我的ARM device tree原理视频课程:https://edu.51cto.com/course/17175.html 具体请参考我的免费的linux各种驱动开发课程如下:https://edu.51cto.com/course/17138.html 另外我的相

The Linux usage model for device tree data

Linux and the Device Tree Author: Grant Likely [email protected] 这篇文章介绍了Linux中使用Device Tree的方法.可以在http://devicetree.org/Device_Tree_Usage获取到Device Tree数据格式. Device Tree是一种描述硬件的语言,它可以让操作系统不硬编码硬件的信息. 结构上讲,Device Tree是树形结构,或者非循环的有名节点组成的图.每个节点包含一定数目的属性和键

ARM Linux 3.x的设备树(Device Tree)【转】

转自:http://blog.csdn.net/21cnbao/article/details/8457546 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] ARM Device Tree起源 Device Tree组成和结构 DTS device tree source DTC device tree compiler Device Tree Blob dtb Binding Bootloader Device Tree引发的BSP和驱动变更 常用OF API 总结

ARM Linux 3.x的设备树(Device Tree)

1.    ARM Device Tree起源 在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备.resource.i2c_board_info.spi_board_info以及各种硬件的platform_data.读者有兴趣可以统计下常见的s3c2410.s3c6410等板级目录,代码量在数万行.社区必须改变这种局面,

Linux 3.10 ARM Device Tree 的初始化

转载:http://blog.chinaunix.net/uid-20522771-id-3785808.html 本文代码均来自标准 linux kernel 3.10,可以到这里下载 https://www.kernel.org/     以 arch/arm/mach-msm/board-dt-8960.c 为例,在该文件中的 msm_dt_init 函数的作用就是利用 dt(device tree)结构初始化 platform device. static void __init msm

[转]ARM Linux 3.x Device Tree Usage

此文转自:http://blog.csdn.net/21cnbao/article/details/8457546 更详细内容可以参考官网 Device Tree 官网:http://devicetree.org/Device_Tree_Usage 1.    ARM Device Tree起源 Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称“this whole ARM thing is a f*cking pain in the ass”,引发ARM Li

【转】ARM Linux 3.x的设备树(Device Tree)

原文网址:http://blog.csdn.net/21cnbao/article/details/8457546 1.    ARM Device Tree起源 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/a