第八章 设备树详解-8.3内核对设备树的处理

    看了韦老师的设备树视频,讲的很好!也在网上找到了根据老师讲课内容整理出来的博文,关于.dts转换成.dtb文件的内容可以参考这篇博文:https://blog.csdn.net/huanting_123/article/details/90142745

    单板上电之后uboot引导内核启动的流程,也参考该作者的另一篇博文:https://blog.csdn.net/huanting_123/article/details/89931329,中间涉及汇编和好多看了头晕的代码,自己写起来有些吃力,等后面深入学习了再来分析。

 后面会陆续讲到内核对设备树的处理,也可以参考上面的这位作者的博文,这里自己写一下自己的理解。只能是自己的一些浅显理解,深入代码内部的理解暂时还做不到。

8.3.2.对设备树中平台信息的处理(选择machine_desc)

    可参考:

    内核中有很多machine_desc结构体描述单板信息,其中有个字符串变量为dt_compat,它可以为单个字符串也可以为多个字符串,字符串内容为该machine_desc可以支持的单板信息。.dts中的根节点的compatiple中申明了该设备树可以运行的单板类型,可以是单个字符串或多个字符串,依次与dt_compat对比,排在前面的compatiple属性优先匹配。看下6ull的dts和machine_desc。

//.dts:
/ {
	model = "Freescale i.MX6 ULL 14x14 EVK Board";
	compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
}
********************************************************
//arch/arm/mach-imx6ul.c

static const char * const imx6ul_dt_compat[] __initconst = {
    "fsl,imx6ul",
    "fsl,imx6ull",
    NULL,
};
DT_MACHINE_START(IMX6UL, "Freescale i.MX6 UltraLite (Device Tree)")
    .map_io = imx6ul_map_io,
    .init_irq = imx6ul_init_irq,
    .init_machine = imx6ul_init_machine,
    .init_late = imx6ul_init_late,
    .dt_compat = imx6ul_dt_compat,
MACHINE_END

函数调用过程:

第八章 设备树详解-8.3内核对设备树的处理_第1张图片

8.3.3.对设备树中运行时配置信息的提取

    可参考:https://blog.csdn.net/huanting_123/article/details/89978923

    主要是讲内核如何提取几个特殊节点和属性:

a. /chosen节点中bootargs属性的值, 存入全局变量: boot_command_line
b. 确定根节点的这2个属性的值: #address-cells, #size-cells,存入全局变量: dt_root_addr_cells, dt_root_size_cells
c. 解析/memory中的reg属性, 提取出"base, size", 最终调用memblock_add(base, size); 

第八章 设备树详解-8.3内核对设备树的处理_第2张图片

8.3.4.dtb转换为device_node(unflatten)

    内核先计算出整个树的大小并为其分配内存空间,然后将设备树中的每个node转化为device_node,再为每个device_node填入device_properties。

unflatten_device_tree(); //解压设备树
    __unflatten_device_tree(initial_boot_params, NULL, &of_root,
        early_init_dt_alloc_memory_arch, false); //执行解压

            /* First pass, scan for size 计算设备树的大小*/
            size = unflatten_dt_nodes(blob, NULL, dad, NULL);

            /* Allocate memory for the expanded device tree 为设备树分配内存*/
            mem = dt_alloc(size + 4, __alignof__(struct device_node));

            /* Second pass, do actual unflattening */
            unflatten_dt_nodes(blob, mem, dad, mynodes); //解压设备树节点
                fdt_next_node(); //遍历设备树找节点
                populate_node(); //填充设备树节点

                |-->fdt_get_name(); //获取name属性
                |-->np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node)); //为node分配内存
                |-->of_node_init(np); //初始化节点
                |-->np->full_name = fn = ((char *)np) + sizeof(*np);
                 //将device_node的full_name指向结构体结尾处,即将一个节点的unit name放置在一个struct device_node的结尾处。
                |-->populate_properties(); //填充属性

                    ||-->fdt_next_property_offset(); //
                    ||-->pp = unflatten_dt_alloc(mem, sizeof(struct property),
__alignof__(struct property)); //为property分配内存
                    ||-->pp->name = (char *)pname;
                    ||-->pp->length = sz;
                    ||-->pp->value = (__be32 *)val;

    关于韦老师的那个node和特性的图是很好的,但是太长了,不贴出来了。

8.3.5.device_node转换为platform_device

一路转化过来就是:dts -> dtb -> device_node -> platform_device

两个问题:

  1. 哪些device_node可以转换为platform_device?

并非所有的device_node都会转换为platform_device,只有以下的device_node会转换:

a.1 根节点不会转化成platform_device;

a.2 根节点的子节点(父节点)必须含有compatible属性才可以转化成platform_device;

a.3 孙节点可以转化为platform_device的前提是自身必须含有compatible属性而且父节点必须含有特殊的compatible属性,如"simple-bus","simple-mfd","isa","arm,amba-bus"。

举例:

/ {
      mytest {
            /*父节点中的"simple-bus"属性会导致其子节点也会构建出平台设备的设备端*/
          compatile = "mytest", "simple-bus";
          mytest@0 {
                compatile = "mytest_0";
          };
      };
      
      i2c {
          compatile = "samsung,i2c";
          at24c02 {
                compatile = "at24c02";                      
          };
      };
};

(1)节点 /mytest 会被转换为 platform_device。由于它兼容 "simple-bus", 它的子节点 /mytest/mytest@0 也会被转换为platform_device. 也就是说一个节点的 compatible 属性中只要有 "simple-bus",其子节也会转换为 platform_device,若没有是不会转换的。

(2)/i2c 节点一般表示i2c控制器,它会被转换为platform_device,在内核中有对应的 platform_driver; 其子节点 /i2c/at24c02 节点不会被转换为 platform_device,它被如何处理完全由父节点的 platform_driver 决定, 一般是被创建为一个i2c_client

b. 怎么转换?

    首先看一个结构体platform_device,找到一篇很好的博文:https://www.cnblogs.com/downey-blog/p/10486568.html,引用:

“在老版本的内核中,platform_device部分是静态定义的,其实最主要的部分就是resources部分,这一部分描述了当前驱动需要的硬件资源,一般是IO,中断等资源。

    在设备树中,这一类资源通常通过reg属性来描述,中断则通过interrupts来描述,所以,设备树中的reg和interrupts资源将会被转换成platform_device内的struct resources资源。

那么,设备树中其他属性是怎么转换的呢?答案是:不需要转换,在platform_device中有一个成员struct device dev,这个dev中又有一个指针成员struct device_node *of_node,linux的做法就是将这个of_node指针直接指向由设备树转换而来的device_node结构。

例如,有这么一个struct platform_device* of_test.我们可以直接通过of_test->dev.of_node来访问设备树中的信息.”

第八章 设备树详解-8.3内核对设备树的处理_第3张图片

 原来是这么个调用关系!

8.3.6.platform_device跟platform_driver的匹配

    发现写的很不错的博文记录下:https://blog.csdn.net/richard_liujh/article/details/45825333

第八章 设备树详解-8.3内核对设备树的处理_第4张图片

  

小结:uboot把设备树编译成的.dtb文件的地址传递给内核,内核提取.dtb根节点的compatible属性来匹配machine_desc,解析.dtb的chosen节点、memory节点、#address-cells和#size-cells属性,为其分配内存。再去遍历整个.dtb文件的节点,将其转换成device_nodes,填充到各个device_nodes的properties,最终构建出device-tree。再根据compatible属性的转化原则将某些device_nodes转化成platform_devices,不能转化成device_nodes的节点内核提供专门的处理函数。platform_devices被挂载到不同的设备总线上,等待platform_drivers的注册。到这里设备树到内核的转化过程已经清楚了,回归到leddrv.c中。

你可能感兴趣的:(I.MX6)