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

 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为例:

 static struct ft5x0x_ts_platform_data ft5x0x_ts_info = {
.irq_gpio_number = GPIO_TOUCH_IRQ,
.reset_gpio_number = GPIO_TOUCH_RESET,
.vdd_name = "vdd28",
}; static struct i2c_board_info i2c0_boardinfo[] = {
{
I2C_BOARD_INFO(FOCALTECH_TS_NAME, FOCALTECH_TS_ADDR),
.platform_data = &ft5x0x_ts_info,
},
}; static struct i2c_board_info i2c1_boardinfo[] = {
{I2C_BOARD_INFO("sensor_main",0x3C),},
{I2C_BOARD_INFO("sensor_sub",0x21),},
}; static int sc8810_add_i2c_devices(void)
{
i2c_register_board_info(, i2c1_boardinfo, ARRAY_SIZE(i2c1_boardinfo));
i2c_register_board_info(, i2c0_boardinfo, ARRAY_SIZE(i2c0_boardinfo));
return ;
} static void __init sc8830_init_machine(void)
{
//...
sc8810_add_i2c_devices();
//...
}

这上面的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 宏控:

 #define CONFIG_OF_LIBFDT

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

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

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

三、解析DTS简要流程

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

3.1 setup_machine_fdt

 //kernel/arch/arm/kernel/devtree.c
struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{
//....
devtree = phys_to_virt(dt_phys); //物理地址转虚拟地址 /* check device tree validity */
if (be32_to_cpu(devtree->magic) != OF_DT_HEADER)
return NULL; /* Search the mdescs for the 'best' compatible value match */
initial_boot_params = devtree;
dt_root = of_get_flat_dt_root(); //获取dts的根 for_each_machine_desc(mdesc) {
score = of_flat_dt_match(dt_root, mdesc->dt_compat);
if (score > && score < mdesc_score) {
mdesc_best = mdesc;
mdesc_score = score;
}
} if (!mdesc_best) {
//....
dump_machine_table(); /* does not return */ //进入了这里是否很麻烦?
} model = of_get_flat_dt_prop(dt_root, "model", NULL);
if (!model)
model = of_get_flat_dt_prop(dt_root, "compatible", NULL);
if (!model)
model = "<unknown>";
pr_info("Machine: %s, model: %s\n", mdesc_best->name, model); /* Retrieve various information from the /chosen node */
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
/* Initialize {size,address}-cells info */
of_scan_flat_dt(early_init_dt_scan_root, NULL);
/* Setup memory, calling early_init_dt_add_memory_arch */
of_scan_flat_dt(early_init_dt_scan_memory, NULL); /* Change machine number to match the mdesc we're using */
__machine_arch_type = mdesc_best->nr; return mdesc_best; //返回目标机器的指针
}

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

 static const char *sprd_boards_compat[] __initdata = {
"sprd,sp8835eb",
NULL,
}; MACHINE_START(SCPHONE, "sc8830")
//...
.dt_compat = sprd_boards_compat,
MACHINE_END

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

 #define MACHINE_START(_type, _name)            \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.name = _name, #define MACHINE_END \
};

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

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

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

 //....
.init.arch.info : {
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
}
//....

说简单了:

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

这段代码,便是循环去 ".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 中:

 void __init unflatten_device_tree(void)
{
__unflatten_device_tree(initial_boot_params, &of_allnodes,
early_init_dt_alloc_memory_arch); /* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
of_alias_scan(early_init_dt_alloc_memory_arch);
}

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

五、机器初始化简要流程

 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

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

  1. linux device tree源代码解析--转

    //Based on Linux v3.14 source code Linux设备树机制(Device Tree) 一.描述 ARM Device Tree起源于OpenFirmware (OF), ...

  2. linux device tree源代码解析【转】

    转自:http://blog.csdn.net/Tommy_wxie/article/details/42806457 //Basedon Linux v3.14 source code Linux设 ...

  3. Linux Device Tree

     原创博文,转载请标明出处--周学伟 http://www.cnblogs.com/zxouxuewei/ 设备树使用手册 基本数据格式 设备树是一个包含节点和属性的简单树状结构.属性就是键-值对,而 ...

  4. Linux and the Device Tree

    来之\kernel\Documentation\devicetree\usage-model.txt Linux and the Device Tree ----------------------- ...

  5. linux下的device tree

    在我个人的理解,device tree就是描述硬件设备的,目前有什么配置,把这些配置信息告诉linux内核,让内核去识别,增强了内核的通用性,不用因为平台不同而每次都要编译新内核了. 配置device ...

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

    http://blog.csdn.net/21cnbao/article/details/8457546 宋宝华 Barry Song <21cnbao@gmail.com> 1.     ...

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

    1. ARM Device Tree起源 Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称“this whole ARM thing is a f*cking pai ...

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

    1.    ARM Device Tree起源 http://blog.csdn.net/21cnbao/article/details/8457546 Linus Torvalds在2011年3月1 ...

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

    原文网址:http://blog.csdn.net/21cnbao/article/details/8457546 1.    ARM Device Tree起源 Linus Torvalds在201 ...

随机推荐

  1. 热门Web开发方式 REST实现原理浅析

    REST 首先只是一种架构样式,不是一种标准.这点和 Ajax 类似,两者都是利用现有的成熟技术.在 REST 的定义中,一个 Web 应用总是使用固定的 URI 向外部世界呈现(或者说暴露)一个资源 ...

  2. check windows return character

    #ifndef _FILE_CHECK_H#define _FILE_CHECK_H#include <string.h>#include <vector> const int ...

  3. hdu 3478(判断奇环)

    题意:给你一个无向图,问你有没有可能存在一个奇环连接所有的节点. 分析:好久没写博客了,这个好习惯还是要继续保持的!这道题通过转化之后就是问你有没有存在一个奇环连接所有的节点,这里用到的方法是染色法, ...

  4. hbm.xml支持的类型

  5. Android数据的四种存储方式SharedPreferences、SQLite、Content Provider和File

    作为一个完成的应用程序,数据存储操作是必不可少的.因此,Android系统一共提供了四种数据存储方式.分别 是:SharePreference.SQLite.Content Provider和File ...

  6. Redis 对String数据类型的操作

    Redis的 Strings 数据结构是简单的key-value类型,value其实不仅是String,也可以是数字.使用Strings类型,你可以完全实现目前 Memcached 的功能,并且效率更 ...

  7. qt信号signal和槽slot机制

    内容: 一.概述 二.信号 三.槽 四.信号与槽的关联 五.元对象工具 六.程序样例 七.应注意的问题 信号与槽作为QT的核心机制在QT编程中有着广泛的应用,本文介绍了信号与槽的一些基本概念.元对象工 ...

  8. SQL存儲過程的調試方法

    1.在vs2010调试存储过程步骤如下:(要點:連接登陸賬號的權限必須是管理員,才能單步調試,否則只能直接執行存儲過程:[因此,此方式適合數據庫和vs裝在同一台電腦上]) 1.1首先,打开vs,点击 ...

  9. Linux_系统信息

    公司里一些仿真软件得进Linux系统,好奇公司用的什么Linux版本,于是搜罗了几个命令如下: 1  uname - Print system info -a, print all info -s, ...

  10. Yii 1.11 获取当前的模块名 控制器名 方法名

    $this->module->id; #模块名$this->action->id; #方法名$this->uniqueId; #控制器名称 Yii: 获取当前模块名.控制 ...