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

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

    看了韦老师的设备树视频,讲的很好!也在网上找到了根据老师讲课内容整理出来的博文,关于.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 = {
        ,
        "",
        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

    函数调用过程:

attachments-2020-05-8iefYhb85ec0a19b88fbb.png

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.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_nodes -> platform_devices

两个问题:

  1. 哪些device_nodes可以转换为platform_devices?

并非所有的device_nodes都会转换为platform_devices,规则:

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来访问设备树中的信息.”

attachments-2020-05-new7WmCD5ec0a2b56ca02.png

 原来是这么个层级关系!

8.3.6.platform_device跟platform_driver的匹配

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

attachments-2020-05-RZMe9jdZ5ec0a2ccec04e.png

   

    小结: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中。

1 条评论&回复

请先 登录 后评论
zxq
zxq

11 篇文章

作家榜 »

  1. 百问网-周老师 18 文章
  2. st_ashang 14 文章
  3. 渐进 12 文章
  4. zxq 11 文章
  5. helloworld 8 文章
  6. 谢工 5 文章
  7. Litchi_Zheng 5 文章
  8. 星星之火 5 文章