设备树记录


博客:

http://blog.csdn.net/zqixiao_09/article/details/50822753  

http://blog.csdn.net/21cnbao/article/details/8457546 

http://tinylab.org/embedded-linux-device-tree/ 

http://www.360doc.com/content/15/1113/11/15700426_512794224.shtml 



总结:

采用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等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

     通常由.dts文件以文本方式对系统设备树进行描述,经过Device Tree Compiler(dtc)将dts文件转换成二进制文件binary device tree blob(dtb),.dtb文件可由Linux内核解析,有了device tree就可以在不改动Linux内核的情况下,对不同的平台实现无差异的支持,只需更换相应的dts文件,即可满足。



U-Boot需要将设备树在内存中的存储地址传给内核。该树主要由三大部分组成:头(Header)、结构块(Structure block)、字符串块(Strings block)。


1.1 头(header)
      头主要描述设备树的一些基本信息,例如设备树大小,结构块偏移地址,字符串块偏移地址等。偏移地址是相对于设备树头的起始地址计算的。

1.2 结构块(struct block)

       设备树结构块是一个线性化的结构体,是设备树的主体,以节点node的形式保存了目标单板上的设备信息。

       在结构块中以宏OF_DT_BEGIN_NODE标志一个节点的开始,以宏OF_DT_END_NODE标识一个节点的结束,整个结构块以宏OF_DT_END结束。一个节点主要由以下几部分组成。

(1)节点开始标志:一般为OF_DT_BEGIN_NODE。
(2)节点路径或者节点的单元名(ersion<3以节点路径表示,version>=0x10以节点单元名表示)
(3)填充字段(对齐到四字节)
(4)节点属性。每个属性以宏OF_DT_PROP开始,后面依次为属性值的字节长度(4字节)、属性名称在字符串块中的偏移量(4字节)、属性值和填充(对齐到四字节)。
(5)如果存在子节点,则定义子节点。
(6)节点结束标志OF_DT_END_NODE。

1.3 字符串块

       通过节点的定义知道节点都有若干属性,而不同的节点的属性又有大量相同的属性名称,因此将这些属性名称提取出一张表,当节点需要应用某个属性名称时直接在属性名字段保存该属性名称在字符串块中的偏移量。


1.4 设备树源码 DTS 表示

      设备树源码文件(.dts)以可读可编辑的文本形式描述系统硬件配置设备树,支持 C/C++方式的注释,该结构有一个唯一的根节点“/”,每个节点都有自己的名字并可以包含多个子节点。设备树的数据格式遵循了 Open Firmware IEEE standard 1275。这个设备树中有很多节点,每个节点都指定了节点单元名称。每一个属性后面都给出相应的值。以双引号引出的内容为 ASCII 字符串,以尖括号给出的是 32 位的16进制值。这个树结构是启动 Linux 内核所需节点和属性简化后的集合,包括了根节点的基本模式信息、CPU 和物理内存布局,它还包括通过/chosen 节点传递给内核的命令行参数信息。



1.5 machine_desc结构

      内核提供了一个重要的结构体struct machine_desc ,这个结构体在内核移植中起到相当重要的作用,内核通过machine_desc结构体来控制系统体系架构相关部分的初始化。machine_desc结构体通过MACHINE_START宏来初始化,在代码中, 通过在start_kernel->setup_arch中调用setup_machine_fdt来获取。



设备树初始化及解析

        分析Linux内核的源码,可以看到其对扁平设备树的解析流程如下:

(1)首先在内核入口处将从u-boot传递过来的镜像基地址。

(2)通过调用early_init_dt_scan()函数来获取内核前期初始化所需的bootargs,cmd_line等系统引导参数。

(3)根据bootargs,cmd_line等系统引导参数进入start_kernel()函数,进行内核的第二阶段初始化。

(4)调用unflatten_device_tree()函数来解析dtb文件,构建一个由device_node结构连接而成的单项链表,并使用全局变量of_allnodes指针来保存这个链表的头指针。

(5)内核调用OF提供的API函数获取of_allnodes链表信息来初始化内核其他子系统、设备等。



1. __init start_kernel(void)  (init/main.c) 

      //这个setup_arch就是各个架构自己的设置函数,哪个参与了编译就调用哪个,arm架构应当是arch/arm/kernel/setup.c中的 setup_arch。           

   setup_arch(&command_line);  

2. setup_arch  (setup.c  arch/arm/kernel)

         //setup_machine_fdt函数获取内核前期初始化所需的bootargs,cmd_line等系统引导参数 

      mdesc = setup_machine_fdt(__atags_pointer);//__atags_pointer是bootloader传递参数的物理地址 

            2.1 setup_machine_fdt()  (devtree.c  arch/arm/kernel

                     //bootloader传递参数的物理地址不为空,并将物理地址转化为虚拟地址,     

            //通过函数early_init_dt_scan从设备树中读出bootargs,cmd_line等系统引导参数。    

            if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys)))  

            return NULL;

                        2.1.1  early_init_dt_scan

                               //从设备树中读取chosen节点的信息,包括命令行boot_command_line,initrd location及size      

                 of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);               

                  //得到根节点的{size,address}-cells信息      

                 of_scan_flat_dt(early_init_dt_scan_root, NULL);                

                 //读出设备树的系统内存设置      

                 of_scan_flat_dt(early_init_dt_scan_memory, NULL); 

                                  2.1.1.1  of_scan_flat_dt

                 2.1.1.2  early_init_dt_scan_chosen

                            2.1.1.2.1 early_init_dt_check_for_initrd

                                        ---- of_get_flat_dt_prop

                                             ---- of_fdt_get_property

                                                  ---- of_fdt_get_string

                                        ----     //从该地址读出initrd-end的地址  

                                                 end = of_read_number(prop, len/4);

                 2.1.1.3  early_init_dt_scan_root

                 2.1.1.4  early_init_dt_scan_memory

         //根据设备树中根节点属性"compatible"的属性描述,找到系统中定义的最匹配的machine_desc结构,该结构控制系统体系架构相关部分的初始化  

         mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);  

         2.1.2  of_flat_dt_match_machine(const void *default_match,  const void * (*get_next_compat)(const char * const**))

                  //读出设备树的根节点,dt_root指向根节点的属性地址处  

                  dt_root = of_get_flat_dt_root();  
                  2.1.2.1  of_get_flat_dt_root 

                  //调用arch_get_next_mach,遍历该系统中的所有machine_desc结构,返回给data,并且返回该结构的compatible  

                  while ((data = get_next_compat(&compat))) { 

                  2.1.2.2  arch_get_next_mach(const char *const **match) 
                  //将系统中的所有machine_desc结构的compatible字符串与设备树根节点的compatible属性进行match   

                  score = of_flat_dt_match(dt_root, compat);

                  2.1.2.3  of_flat_dt_match(unsigned long node, const char *const *compat)   //与设备树根节点进行match  
                                                         ----  of_fdt_match

                                 //返回compatible的匹配值  

                                 tmp = of_fdt_is_compatible(blob, node, *compat);  

                                 ---- of_fdt_is_compatible(struct boot_param_header *blob,unsigned long node, const char *compat)  

          //解析设备树  

     unflatten_device_tree();

     2.2 unflatten_device_tree(void)  

         //解析设备树,将所有的设备节点链入全局链表    of_allnodes中  

         __unflatten_device_tree(initial_boot_params, &of_allnodes,early_init_dt_alloc_memory_arch); 

                 2.2.1  __unflatten_device_tree(struct boot_param_header *blob,struct device_node **mynodes,void * (*dt_alloc)(u64 size, u64 align))               //第一次调用mem传0,allnextpp传NULL,实际上是为了计算整个设备树所要的空间  

              size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0); 

                            2.2.1.1 unflatten_dt_node(struct boot_param_header *blob,  

                                      void *mem,void **p,  

                                      struct device_node *dad,  

                                      struct device_node ***allnextpp,  

                                      unsigned long fpsize)  

                                      //分配一个设备节点device_node结构,*mem记录分配了多大空间,最终会累加计算出该设备树总共分配的空间大小  

                     np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,__alignof__(struct device_node)); 

                                          2.2.1.1.1   //当需要为设备指定一个标示符时,操作系统欢迎大家使用别名。  

                              //设置内核输出终端,以及遍历“/aliases”节点下的所有的属性,挂入相应链表  

                              void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) 


           //设置内核输出终端,以及遍历“/aliases”节点下的所有的属性,挂入相应链表  

          of_alias_scan(early_init_dt_alloc_memory_arch);  

                    2.2.2   //当需要为设备指定一个标示符时,操作系统欢迎大家使用别名。  

               //设置内核输出终端,以及遍历“/aliases”节点下的所有的属性,挂入相应链表  

               void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) 



!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


四、OF提供的常用API函数
    OF提供的函数主要集中在drivers/of/目录下,有address.c,base.c,device.c,fdt.c,irq.c,platform.c等等
1. 用来查找在dtb中的根节点
unsigned long __init of_get_flat_dt_root(void)


2. 根据deice_node结构的full_name参数,在全局链表of_allnodes中,查找合适的device_node
struct device_node *of_find_node_by_path(const char *path)
例如:
struct device_node *cpus;
cpus=of_find_node_by_path("/cpus");


3. 若from=NULL,则在全局链表of_allnodes中根据name查找合适的device_node
struct device_node *of_find_node_by_name(struct device_node *from,const char *name)
例如:
struct device_node *np;
np = of_find_node_by_name(NULL,"firewire");


4. 根据设备类型查找相应的device_node
struct device_node *of_find_node_by_type(struct device_node *from,const char *type)
例如:
struct device_node *tsi_pci;
tsi_pci= of_find_node_by_type(NULL,"pci");


5. 根据compatible字符串查找device_node
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)


6. 根据节点属性的name查找device_node
struct device_node *of_find_node_with_property(struct device_node *from,const char *prop_name)


7. 根据phandle查找device_node
struct device_node *of_find_node_by_phandle(phandle handle)


8. 根据alias的name获得设备id号
int of_alias_get_id(struct device_node *np, const char *stem)


9. device node计数增加/减少
struct device_node *of_node_get(struct device_node *node)
void of_node_put(struct device_node *node)


10. 根据property结构的name参数,在指定的device node中查找合适的property
struct property *of_find_property(const struct device_node *np,const char *name,int *lenp)


11. 根据property结构的name参数,返回该属性的属性值
const void *of_get_property(const struct device_node *np, const char *name,int *lenp)


12. 根据compat参数与device node的compatible匹配,返回匹配度
int of_device_is_compatible(const struct device_node *device,const char *compat)


13. 获得父节点的device node
struct device_node *of_get_parent(const struct device_node *node)


14. 将matches数组中of_device_id结构的name和type与device node的compatible和type匹配,返回匹配度最高的of_device_id结构
const struct of_device_id *of_match_node(const struct of_device_id *matches,const struct device_node *node)


15. 根据属性名propname,读出属性值中的第index个u32数值给out_value
int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index, u32 *out_value)


16. 根据属性名propname,读出该属性的数组中sz个属性值给out_values
int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz)
int of_property_read_u16_array(const struct device_node *np,const char *propname, u16 *out_values, size_t sz)
int of_property_read_u32_array(const struct device_node *np,const char *propname, u32 *out_values,size_t sz)


17. 根据属性名propname,读出该属性的u64属性值
int of_property_read_u64(const struct device_node *np, const char *propname,u64 *out_value)


18. 根据属性名propname,读出该属性的字符串属性值
int of_property_read_string(struct device_node *np, const char *propname,const char **out_string)


19. 根据属性名propname,读出该字符串属性值数组中的第index个字符串
int of_property_read_string_index(struct device_node *np, const char *propname,int index, const char **output)


20. 读取属性名propname中,字符串属性值的个数
int of_property_count_strings(struct device_node *np, const char *propname)


21. 读取该设备的第index个irq号
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)


22. 读取该设备的第index个irq号,并填充一个irq资源结构体
int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)


23. 获取该设备的irq个数
int of_irq_count(struct device_node *dev)


24. 获取设备寄存器地址,并填充寄存器资源结构体
int of_address_to_resource(struct device_node *dev, int index,struct resource *r)
const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,unsigned int *flags)


25. 获取经过映射的寄存器虚拟地址
void __iomem *of_iomap(struct device_node *np, int index)


24. 根据device_node查找返回该设备对应的platform_device结构
struct platform_device *of_find_device_by_node(struct device_node *np)


25. 根据device node,bus id以及父节点创建该设备的platform_device结构
struct platform_device *of_device_alloc(struct device_node *np,const char *bus_id,struct device *parent)
static struct platform_device *of_platform_device_create_pdata(struct device_node *np,const char *bus_id,
                                                            void *platform_data,struct device *parent)


26. 遍历of_allnodes中的节点挂接到of_platform_bus_type总线上,由于此时of_platform_bus_type总线上还没有驱动,所以此时不进行匹配
int of_platform_bus_probe(struct device_node *root,const struct of_device_id *matches,struct device *parent)


27. 遍历of_allnodes中的所有节点,生成并初始化platform_device结构
int of_platform_populate(struct device_node *root,const struct of_device_id *matches,
                        const struct of_dev_auxdata *lookup,struct device *parent)
{
    struct device_node *child;
    int rc = 0;
    
    //获得设备树的根节点 
    root = root ? of_node_get(root) : of_find_node_by_path("/







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值