设备树实例(二)

在内核里,如何利用dtb?以下以全志a64为实例讲解。

解析dtb的流程如下:

start_kernel    //  init/main.c

----setup_arch    //  arch/arm64/kernel/setup.c

--------setup_machine_fdt(__fdt_pointer)

--------unflatten_device_tree()

*Note:__fdt_pointer是dtb数据结构的头指针。

在查看setup_machine_fdt之前需要知道fdt数据结构。

scripts/dtc/libfdt/fdt.h:

 1 struct fdt_header {
 2     uint32_t magic;             /* magic word FDT_MAGIC */
 3     uint32_t totalsize;         /* total size of DT block */
 4     uint32_t off_dt_struct;         /* offset to structure */
 5     uint32_t off_dt_strings;     /* offset to strings */
 6     uint32_t off_mem_rsvmap;     /* offset to memory reserve map */
 7     uint32_t version;         /* format version */
 8     uint32_t last_comp_version;     /* last compatible version */
 9
10     /* version 2 fields below */
11     uint32_t boot_cpuid_phys;     /* Which physical CPU id we‘re
12                         booting on */
13     /* version 3 fields below */
14     uint32_t size_dt_strings;     /* size of the strings block */
15
16     /* version 17 fields below */
17     uint32_t size_dt_struct;     /* size of the structure block */
18 };
19
20 struct fdt_reserve_entry {
21     uint64_t address;
22     uint64_t size;
23 };
24
25 struct fdt_node_header {
26     uint32_t tag;
27     char name[0];
28 };
29
30 struct fdt_property {
31     uint32_t tag;
32     uint32_t len;
33     uint32_t nameoff;
34     char data[0];
35 };

dtb由三个区域dt_struct、dt_strings和mem_rsvmap组成,这里暂且忽略mem_rsvmap。

dt_struct表示dtb的结构,其实由若干片数据组成,这里是5个token:FDT_BEGIN_NODE、FDT_END_NODE、FDT_PROP、FDT_NOP和FDT_END,片数据以末端的‘\0‘表示结束。

dt_strings表示dtb的字符串库,利用fdt_property.nameoff+fdt_header.ff_dt_strings+__fdt_pointer获取字符串指针。

dtb的结构可能是:

FDT_BEGIN_NODE

----FDT_PROP x N

----FDT_BEGIN_NODE

--------FDT_PROP x N 

------------FDT_BEGIN_NODE

----------------FDT_PROP x N

------------FDT_END_NODE

----FDT_END_NODE

FDT_END_NODE

FDT_END

在内核里,需要把dtb(非树操作不便)转化成device tree,其由device node组成。

include/linux/of.h:

 1 struct property {
 2     char    *name;
 3     int    length;
 4     void    *value;
 5     struct property *next;
 6     unsigned long _flags;
 7     unsigned int unique_id;
 8 };
 9
10 #if defined(CONFIG_SPARC)
11 struct of_irq_controller;
12 #endif
13
14 struct device_node {
15     const char *name;
16     const char *type;
17     phandle phandle;
18     const char *full_name;
19
20     struct    property *properties;
21     struct    property *deadprops;    /* removed properties */
22     struct    device_node *parent;
23     struct    device_node *child;
24     struct    device_node *sibling;
25     struct    device_node *next;    /* next device of same type */
26     struct    device_node *allnext;    /* next in list of all nodes */
27     struct    proc_dir_entry *pde;    /* this node‘s proc directory */
28     struct    kref kref;
29     unsigned long _flags;
30     void    *data;
31 #if defined(CONFIG_SPARC)
32     const char *path_component_name;
33     unsigned int unique_id;
34     struct of_irq_controller *irq_trans;
35 #endif
36 };

device_node只有一个child,通过child->sibling得到其他child。对于child而言,只有一个parent。

device_node通过一个properties获得一个property,而多个property->next即能获得所有property。

了解dtb和device_node的数据结构,查看setup_machine_fdt(__fdt_pointer)和unflatten_device_tree()问题不大

以下忽略部分代码的setup_machine_fdt:

 1 static void __init setup_machine_fdt(phys_addr_t dt_phys)
 2 {
 3     struct boot_param_header *devtree;
 4     unsigned long dt_root;
 5
 6     devtree = phys_to_virt(dt_phys);        //转化虚拟地址
 7
 8     initial_boot_params = devtree;
 9     dt_root = of_get_flat_dt_root();        //这里return 0
10
11     machine_name = of_get_flat_dt_prop(dt_root, "model", NULL);
12     if (!machine_name)
13         machine_name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
14     if (!machine_name)
15         machine_name = "<unknown>";
16     pr_info("Machine: %s\n", machine_name);
17
18     /* Retrieve various information from the /chosen node */
19     of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
20     /* Initialize {size,address}-cells info */
21     of_scan_flat_dt(early_init_dt_scan_root, NULL);
22     /* Setup memory, calling early_init_dt_add_memory_arch */
23     of_scan_flat_dt(early_init_dt_scan_memory, NULL);
24 }

of_scan_flat_dt函数内容是循环所有node(通过tag是否等于FDT_BEGIN_NODE)调用argv[1]函数,argv[2]为argv[1]的data。由注释可以得知以上三个函数的实质内容。

以下忽略部分代码的unflatten_device_tree和__unflatten_device_tree:

 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 }
 9
10 static void __unflatten_device_tree(struct boot_param_header *blob,
11                  struct device_node **mynodes,
12                  void * (*dt_alloc)(u64 size, u64 align))
13 {
14     unsigned long size;
15     int start;
16     void *mem;
17     struct device_node **allnextp = mynodes;
18
19     /* First pass, scan for size */
20     start = 0;
21     size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
22     size = ALIGN(size, 4);
23
24     /* Allocate memory for the expanded device tree */
25     mem = dt_alloc(size + 4, __alignof__(struct device_node));
26
27     memset((void *)mem, 0, size);
28
29     /* Second pass, do actual unflattening */
30     start = 0;
31     unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
32
33     *allnextp = NULL;
34 }

unflatten_device_tree函数内容把dtb解析成device node。

__unflatten_device_tree中两次unflatten_dt_node,第一次轮询可能递归调用自身(如有子node的话),获得device tree的容量。

第二次调用通过allnextp获得property的内容(NULL则忽略)。

到此,我们的dtb文件已经完全解析成device node,添加设备十分方便。

以下讨论利用device tree如何初始化device。

start_kernel----rest_init----kernel_init----kernel_init_freeable----do_basic_setup----do_initcalls_sync

arch/arm64/kernel/setup.c

1 static int __init arm64_device_init(void)
2 {
3     of_clk_init(NULL);
4     of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
5     return 0;
6 }
7 arch_initcall_sync(arm64_device_init);

of_clk_init通过__clk_of_table初始化一系列时钟,__clk_of_table在drivers/clk/sunxi/clk-sun50iw1.c通过CLK_OF_DECLARE初始化。

of_platform_populate把device node里获取platform资源并add device

以下忽略部分代码的drivers/of/platform.c

  1 int of_platform_populate(struct device_node *root,
  2             const struct of_device_id *matches,
  3             const struct of_dev_auxdata *lookup,
  4             struct device *parent)
  5 {
  6     struct device_node *child;
  7     int rc = 0;
  8
  9     root = root ? of_node_get(root) : of_find_node_by_path("/");
 10
 11
 12     for_each_child_of_node(root, child)
 13         rc = of_platform_bus_create(child, matches, lookup, parent, true);
 14
 15     of_node_put(root);
 16     return rc;
 17 }
 18
 19 static int of_platform_bus_create(struct device_node *bus,
 20                   const struct of_device_id *matches,
 21                   const struct of_dev_auxdata *lookup,
 22                   struct device *parent, bool strict)
 23 {
 24     const struct of_dev_auxdata *auxdata;
 25     struct device_node *child;
 26     struct platform_device *dev;
 27     const char *bus_id = NULL;
 28     void *platform_data = NULL;
 29     int rc = 0;
 30
 31     dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
 32
 33     for_each_child_of_node(bus, child)
 34         rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
 35
 36     return rc;
 37 }
 38
 39 struct platform_device *of_platform_device_create_pdata(
 40                     struct device_node *np,
 41                     const char *bus_id,
 42                     void *platform_data,
 43                     struct device *parent)
 44 {
 45     struct platform_device *dev;
 46
 47     dev = of_device_alloc(np, bus_id, parent);
 48
 49     dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
 50     dev->dev.bus = &platform_bus_type;
 51     dev->dev.platform_data = platform_data;
 52
 53     /* We do not fill the DMA ops for platform devices by default.
 54      * This is currently the responsibility of the platform code
 55      * to do such, possibly using a device notifier
 56      */
 57
 58     if (of_device_add(dev) != 0) {        //device_add
 59         platform_device_put(dev);
 60         return NULL;
 61     }
 62
 63     return dev;
 64 }
 65
 66 struct platform_device *of_device_alloc(struct device_node *np,
 67                   const char *bus_id,
 68                   struct device *parent)
 69 {
 70     struct platform_device *dev;
 71     int rc, i, num_reg = 0, num_irq;
 72     struct resource *res, temp_res;
 73
 74     dev = platform_device_alloc("", -1);
 75
 76     /* count the io and irq resources */
 77     if (of_can_translate_address(np))
 78         while (of_address_to_resource(np, num_reg, &temp_res) == 0)
 79             num_reg++;        //设备树reg属性的个数
 80     num_irq = of_irq_count(np);    //主要调用irq_of_parse_and_map解析和映射中断号
 81
 82     /* Populate the resource table */
 83     if (num_irq || num_reg) {
 84         res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
 85
 86         dev->num_resources = num_reg + num_irq;
 87         dev->resource = res;
 88         for (i = 0; i < num_reg; i++, res++) {
 89             rc = of_address_to_resource(np, i, res);        //若range属性为空则1:1映射地址
 90         }
 91     }
 92
 93     dev->dev.of_node = of_node_get(np);
 94     dev->dev.parent = parent;
 95
 96     if (bus_id)        //bus_id = 0
 97         dev_set_name(&dev->dev, "%s", bus_id);
 98     else
 99         of_device_make_bus_id(&dev->dev);
100
101     return dev;
102 }

经过of_platform_bus_create递归调用自身,已经把每个设备节点device_add。若内核有device对应的driver,则会进入probe函数内platform_set_drvdata设置设备的数据。

时间: 2024-10-13 15:29:55

设备树实例(二)的相关文章

设备树实例(一)

如何单独编译设备树? cd linux-x.xx & make dtbs linux-x.xx/arch/xxx/boot/dts linux-x.xx/scripts/dtc/dtc -I dtb -O dts xxxx.dtb -o xxxx.dts 全志A64实例: linux-3.10/arch/arm64/boot/dts/Makefile: 1 #dtb-$(CONFIG_ARCH_SUN50I) += sun50iw1p1-fpga.dtb sun50iw1p1-soc.dtb 2

设备树(Device Tree)

设备树介绍: 设备树是一个描述设备硬件资源的文件,该文件是由节点组成的树形结构.如下: / { node1 { a-string-property = "A string"; a-string-list-property = "first string", "second string"; // hex is implied in byte arrays. no '0x' prefix is required a-byte-data-proper

Linux设备树使用(二)

一.设备树与驱动的匹配1.设备树会被/scripts中的dtc可执行程序编译成二进制.dtb文件,之前设备树中的节点信息会以单链表的形式存储在这个.dtb文件中:驱动与设备树中compatible属性匹配上后, 驱动中的相应的node节点就映射在这个设备树节点上了,然后以这个node为参数调用of函数来解析这个设备树块上的信息为驱动所用.设备树中的信息是逐条进行获取的(?) 2.例如设备树中有如下定义: flash_SY7803:flashlight { compatible = "qcom,l

我眼中的Linux设备树(二 基础规范概述)

二 基础规范概述 DTS(Device tree syntax,另一种说法是Device tree source)是设备树源文件,为了方便阅读及修改,采用文本格式.DTC(Device tree compiler)是一个小工具,负责将DTS转换成DTB(Device tree blob).DTB是DTS的二进制形式,供机器使用.使用中,我们首先根据硬件修改DTS文件,然后在编译的时候通过DTC工具将DTS文件转换成DTB文件,然后将DTB文件烧写到机器上(如emmc,磁盘等存储介质).系统启动时

基于设备树的TQ2440的中断(1)

作者 姓名:彭东林 E-mail:[email protected] QQ:405728433 平台 板子:TQ2440 内核:Linux-4.9 u-boot: 2015.04 工具链: arm-none-linux-gnueabi-gcc 4.8.3 概述 在博文讓TQ2440也用上設備樹(1)将支持devicetree的Linux4.9移植到了tq2440上面,而在基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(1)中介绍了最新的Linux下中断的知识,下面我们再

Linux设备树语法详解

Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.在设备树出现以前,所有关于设备的具体信息都要写在驱动里,一旦外围设备变化,驱动代码就要重写.引入了设备树之后,驱动代码只负责处理驱动的逻辑,而关于设备的具体信息存放到设备树文件中,这样,如果只是硬件接口信息的变化而没有驱动逻辑的变化,驱动开发者只需要修改设备树文件信息,不需要改写驱动代码.比如在ARM Linux内,一个.dts(device tree source)文件对应一个ARM的machine,一般放置在内核的

我眼中的Linux设备树(四 中断)

四 中断 中断一般包括中断产生设备和中断处理设备.中断控制器负责处理中断,每一个中断都有对应的中断号及触发条件.中断产生设备可能有多个中断源,有时多个中断源对应中断控制器中的一个中断,这种情况中断产生设备的中断源称之为中断控制器中对应中断的子中断.一般情况中断产生设备数量要多于中断控制器,多个中断产生设备的中断都由一个中断控制器处理,这种多对一的关系也很像一个树形结构,所以在设备树中,中断也被描述成树,叫中断树.以下表述的时候为了明确是在说中断树,在父节点和子节点前边我们都加上“中断”二字,是为

数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树

在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉搜索树.红黑树.线索二叉树,它们在解决实际问题中有着非常重要的应用.本文主要从概念和一些基本操作上进行分类和总结. 一.概念总揽 (1) 堆 堆(heap order)是一种特殊的表,如果将它看做是一颗完全二叉树的层次序列,那么它具有如下的性质:每个节点的值都不大于其孩子的值,或每个节点的值都不小于其孩子的值

Linux设备树语法详解【转】

本文转载自:http://www.cnblogs.com/xiaojiang1025/p/6131381.html 概念 Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.在设备树出现以前,所有关于设备的具体信息都要写在驱动里,一旦外围设备变化,驱动代码就要重写.引入了设备树之后,驱动代码只负责处理驱动的逻辑,而关于设备的具体信息存放到设备树文件中,这样,如果只是硬件接口信息的变化而没有驱动逻辑的变化,驱动开发者只需要修改设备树文件信息,不需要改写驱动代码.比如在AR