Linux device tree < reprint >

Posted by MidOhioIT on Sat, 18 Sep 2021 14:54:26 +0200

Reprint address:

1. Introduction to device tree

Linus Torvalds declared in the ARM Linux mailing list on March 17, 2011 that "this whole ARM thing is a fusing pain in the ass", which triggered an earthquake in the ARM Linux community, and then the ARM community made a series of major amendments. In the past ARM Linux, arch / ARM / plat XXX and arch / ARM / Mach XXX were filled with a large amount of garbage code. A considerable number of codes only described board level details, which were just garbage for the kernel, such as platform devices, resource s and I2C on the board_ board_ info,spi_board_info and various hardware platforms_ data. The community must change this situation, so the flatted Device Tree (FDT) already used under other architectures such as PowerPC has entered the vision of the ARM community. Device Tree is a data structure describing hardware, which originated from OpenFirmware(OF). In Linux 2.6, the board hardware details of ARM architecture are too hard coded in arch / ARM / plat XXX and arch / ARM / Mach XXX. After using Device Tree, many hardware details can be directly transmitted to Linux through it without a lot of redundant coding in the kernel.

Device Tree is composed of a series of named nodes and properties, and the node itself can contain child nodes. The so-called attribute is actually a pair of name and value. In the Device Tree, the describable information includes (previously, most of these information was hard code d into the kernel):

  • Number and category of CPU s
  • Memory base address and size
  • Bus and Bridge
  • Peripheral connection
  • Interrupt controller and interrupt usage
  • GPIO controller and GPIO usage

It basically draws a tree composed of CPU, bus and equipment on the circuit board. The Bootloader will pass the tree to the kernel, and then the kernel can identify the tree and expand the platform in the Linux kernel according to it_ device,i2c_client,spi_device and other devices. The memory, IRQ and other resources used by these devices are also passed to the kernel, which will bind these resources to the corresponding deployed devices.

2. Device Tree compilation

The format of Device Tree file is dts, and the format of header file is dtsi. dts file is an encoding format that people can understand. But uboot and Linux can't recognize directly. They can only recognize binary files, so they need to compile dts files into dtb files. dtb file is a binary file that can be recognized by kernel and uboot. The tool for compiling dts into dtb files is dtc. The scripts/dtc directory under the Linux source directory contains the source code of dtc tools. In addition to providing dtc tools in the scripts/dtc directory of Linux, you can also install dtc tools yourself. Execute sudo apt get install Device Tree compiler under Linux to install dtc tools. It also provides a tool of fdtdump, which can decompile dtb files. The transformation of dts and dtb files is shown in Figure 1.  
The use method of dtc tool is: dtc – I dts – O dtb – o xxx.dtb xxx.dts to generate the DTB file corresponding to the DTS file.  

3. Device Tree header information

Use the fdtdump tool, execute ftddump – h on the Linux terminal, and output the following information:  
fdtdump -h 
Usage: fdtdump [options] 
Options: -[dshV] 
-d, –debug Dump debug information while decoding the file 
-s, –scan Scan for an embedded fdt in file 
-h, –help Print this help and exit 
-V, –version Print version and exit 
This paper adopts s5pv21_smc.dtb file as an example to illustrate the use of fdtdump tool. Linux terminal execution fdtdump – SD s5pv21_smc.dtb > s5pv21_smc.txt, open s5pv21_smc.txt file, some output information is as follows:  
// magic: 0xd00dfeed 
// totalsize: 0xce4 (3300) 
// off_dt_struct: 0x38 
// off_dt_strings: 0xc34 
// off_mem_rsvmap: 0x28 
// version: 17 
// last_comp_version: 16 
// boot_cpuid_phys: 0x0 
// size_dt_strings: 0xb0 
// size_dt_struct: 0xbfc 
The above information is the header information of the Device Tree file, which is stored at the beginning of the dtb file. Using struct FDT in Linux kernel_ Header structure description. struct fdt_ The header structure is defined in the scripts\dtc\libfdt\fdt.h file.

  1. struct fdt_header {
  2. fdt32_t magic; /* magic word FDT_MAGIC */
  3. fdt32_t totalsize; /* total size of DT block */
  4. fdt32_t off_dt_struct; /* offset to structure */
  5. fdt32_t off_dt_strings; /* offset to strings */
  6. fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
  7. fdt32_t version; /* format version */
  8. fdt32_t last_comp_version; /* last compatible version */
  9. /* version 2 fields below */
  10. fdt32_t boot_cpuid_phys; /* Which physical CPU id we're booting on */
  11. /* version 3 fields below */
  12. fdt32_t size_dt_strings; /* size of the strings block */
  13. /* version 17 fields below */
  14. fdt32_t size_dt_struct; /* size of the structure block */
  15. };

The output information of the fdtdump tool is the value of each member in the above structure, struct fdt_ The header structure contains the private information of the Device Tree. For example: fdt_header.magic is the magic number of fdt. The fixed value is 0xd00dfeed, fdt_header.totalsize is the size of the fdt file. Open s5pv21 using binary tools_ Smc.dtb validation. s5pv21_ The header information of the smc.dtb binary file is shown in Figure 2. It can be seen from Figure 2 that the file of Device Tree is stored in big end mode. Moreover, the header information is consistent with the output information of fdtdump.  

An example of node information in the Device Tree is shown in Figure 3.  

The above. dts file has no real purpose, but it basically represents the structure of a Device Tree source file. 1 root node "/"; There are a series of child nodes under the root node. In this example, it is " node@0 ”And " node@1 ”; "Node" node@0 ”The following contains a series of child nodes, in this case "child"- node@0 ”; Each node has a series of attributes. These properties may be empty, such as "an empty property"; It may be A string, such as "a-string-property"; It may be A string array, such as "a-string-list-property"; It may be Cells (composed of u32 integers), such as "second child property", or binary numbers, such as "a-byte-data-property". The structure of the Device Tree source file is divided into header and fill_area,dt_struct and dt_string four regions. Header is the header information, fill_area is the filled area, filled with the number 0, dt_struct stores node value and name related information, dt_string stores the property name. For example, a-string-property is stored in dt_string area, "A string" and node1 are stored in dt_struct area.  
We can add a label to a device node, and then access the label in the form of & label. This reference is made through the phase (pointer handle). For example, node1 in Figure 3 is a label, node@0 Child of- node@0 Reference via & node1 node@1 Node. For a node like this phase, after being compiled by the DTC tool, & node1 will become a special integer number n. assuming that the value of n is 1, then node@1 Two attributes are automatically generated under the node. The attributes are as follows:  
linux,phandle = <0x00000001>; 
phandle = <0x00000001>; 
node@0 Child of- node@0 A-reference-to-something = < & node1 > in becomes a-reference-to-something = < 0x00000001 >. 0x00000001 here is the value of a phase. Each phase has a unique integer value. The referenced node is indirectly found in the subsequent kernel through this special number. By viewing the output information of FDT dump and the information of dtb binary file, the struct FDT is obtained_ The relationship information between header and file structure is shown in.  

4. Device Tree file structure

Through the above analysis, the Device Tree file structure can be obtained, as shown in Figure 5. The first thing stored in the head of dtb is FDT_ The structure information of the header, followed by the filling area, and the filling size is off_dt_struct – sizeof(struct fdt_header), the filled value is 0. Then struct fdt_property structure. Finally, dt_string part.  

The structure of the Device Tree source file is divided into header and fill_area,dt_struct and dt_string four regions. fill_ The area area is filled with a value of 0. The node information uses struct fdt_node_header structure description. Use struct FDT for attribute information_ Property structure description. The information of each structure is as follows:

  1. struct fdt_node_header {
  2. fdt32_t tag;
  3. char name[ 0];
  4. };
  5. struct fdt_property {
  6. fdt32_t tag;
  7. fdt32_t len;
  8. fdt32_t nameoff;
  9. char data[ 0];
  10. };

struct fdt_node_header describes node information. Tag is a flag bit identifying the start and end of node, and name points to the first address of node name. The values of tag are as follows:

  1. #define FDT_BEGIN_NODE 0x1 /* Start node: full name */
  2. #define FDT_END_NODE 0x2 /* End node */
  3. #define FDT_PROP 0x3 /* Property: name off, size, content */
  4. #define FDT_NOP 0x4 /* nop */
  5. #define FDT_END 0x9

FDT_BEGIN_NODE and FDT_END_NODE identifies the start and end of node node, FDT_PROP identifies the attribute initiator under the node node, FDT_END identifies the end identifier of the Device Tree. Therefore, the tag identifier for each node node is generally FDT_BEGIN_NODE, the tag identifier of the attribute under each node node is generally FDT_PROP.  
The description attribute adopts struct fdt_property description, tag ID is a property, and the value is FDT_PROP; len is the length of the attribute value (including '\ 0', unit: bytes); nameoff is the attribute name, and the storage location is relative to off_ dt_ Offset address of strings.  
For example: compatible = "sampling, GONI", "sampling, s5pv210"; Compatible is the attribute name, "sampling, GONI" and "sampling, s5pv210" is the attribute value. The storage area of the compatible property name string is dt_string. ” The storage location of "sampling, GONI" and "sampling, s5pv210" is FDT_ After So points to the property value. fdt_ The value of property.tag is the property ID, and Len is the length of the property value (including '\ 0', unit: bytes), where len = 29. Nameoff is the position of the compatible string relative to off_ dt_ Offset address of strings, i.e. & compatible = nameoff + off_dt_strings.  
dt_ The structure of struct in Device Tree is shown in Figure 6. The nesting of nodes also leads to the nesting of tag identifiers.  

5. kernel parsing Device Tree

The Device Tree file structure is described in the above struct fdt_header,struct fdt_node_header and struct fdt_property three structure descriptions. The kernel will parse the struct property structure that can be used by the kernel according to the structure of the Device Tree. The kernel parses the data according to all the properties in the Device Tree and fills in the struct property structure. The struct property structure is described as follows:

  1. struct property {
  2. char *name; /* property full name */
  3. int length; /* property value length */
  4. void *value; /* property value */
  5. struct property *next; /* next property under the same node */
  6. unsigned long _flags;
  7. unsigned int unique_id;
  8. struct bin_attribute attr; /* Properties file, attached to sysfs file system */
  9. };

In general, the kernel converts the file structure information of the Device Tree into a struct property structure, and links all the properties under the same node through the pointer to form a single linked list.  
How to parse the Device Tree in the kernel? Let's analyze the function parsing process. The function calling process is shown in Figure 7. The entry function of the C language phase of the kernel is init/main.c/stsrt_kernel() function, in early_init_dt_scan_nodes() does the following three things:

  • Scan / chosen or/ chose@0 The value of the bootargs attribute under the node is to boot_command_line. In addition, it also processes the properties related to initrd and saves them in initrd_start and initrd_end in these two global variables;
  • Scan the root node to obtain {size,address}-cells information and save it in dt_root_size_cells and dt_root_addr_cells global variable;
  • Scan with device_type = "memory" attribute or/ memory@0 The reg attribute value under the node and save the relevant information in meminfo. The global variable meminfo saves the information related to the system memory.  

    Each node node in the Device Tree will generate a struct device after being processed by the kernel_ Structure of node, struct device_node is usually attached to a specific struct device structure. struct device_ The node structure is described as follows:
  1. struct device_node {
  2. const char *name; /* node The name of the last substring between "/" and "@" */
  3. const char *type; /* device_type Property name of, not < null > */
  4. phandle phandle; /* phandle Attribute value */
  5. const char *full_name; /* Point to the end of the structure and store the full path name of the node, for example: / chosen */
  6. struct fwnode_handle fwnode;
  7. struct property *properties; /* Point to the first attribute under the node, and other attributes are connected with the attribute linked list */
  8. struct property *deadprops; /* removed properties */
  9. struct device_node *parent; /* Parent node */
  10. struct device_node *child; /* Child node */
  11. struct device_node *sibling; /* Sister node, node of the same level as itself */
  12. struct kobject kobj; /* sysfs File system directory representation */
  13. unsigned long _flags; /* For the current node status flag bit, see / include/linux/of.h line124-127 */
  14. void *data;
  15. };
  16. /* flag descriptions (need to be visible even when !CONFIG_OF) */
  17. #define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */
  18. #define OF_DETACHED 2 /* node has been detached from the device tree*/
  19. #define OF_POPULATED 3 /* device already created for the node */
  20. #define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */

struct device_ The function of each member in the node structure has been annotated with annotation information. Let's analyze how the above information is obtained. The Device Tree is first parsed from unflatten_ device_ Starting with tree(), the code is listed as follows:

  1. /**
  2. * unflatten_device_tree - create tree of device_nodes from flat blob
  3. *
  4. * unflattens the device-tree passed by the firmware, creating the
  5. * tree of struct device_node. It also fills the "name" and "type"
  6. * pointers of the nodes so the normal device-tree walking functions
  7. * can be used.
  8. */
  9. void __init unflatten_device_tree( void )
  10. {
  11. __unflatten_device_tree(initial_boot_params, &of_root,
  12. early_init_dt_alloc_memory_arch);
  13. /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
  14. of_alias_scan(early_init_dt_alloc_memory_arch);
  15. }
  16. /**
  17. * __unflatten_device_tree - create tree of device_nodes from flat blob
  18. *
  19. * unflattens a device-tree, creating the
  20. * tree of struct device_node. It also fills the "name" and "type"
  21. * pointers of the nodes so the normal device-tree walking functions
  22. * can be used.
  23. * @blob: The blob to expand
  24. * @mynodes: The device_node tree created by the call
  25. * @dt_alloc: An allocator that provides a virtual address to memory
  26. * for the resulting tree
  27. */
  28. static void __unflatten_device_tree( const void *blob,
  29. struct device_node **mynodes,
  30. void * (*dt_alloc)(u64 size, u64 align))
  31. {
  32. unsigned long size;
  33. int start;
  34. void *mem;
  35. /* Omit the unimportant part */
  36. /* First pass, scan for size */
  37. start = 0;
  38. size = ( unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);
  39. size = ALIGN(size, 4);
  40. /* Allocate memory for the expanded device tree */
  41. mem = dt_alloc(size + 4, __alignof__( struct device_node));
  42. memset(mem, 0, size);
  43. /* Second pass, do actual unflattening */
  44. start = 0;
  45. unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
  46. }

Analyze the above code in unflatten_ device_ In tree (), calling function unflatten_device_tree(), parameter initial_boot_params refers to the first address of the Device Tree in memory, of_ After being processed by this function, root will point to the root node, early_init_dt_alloc_memory_arch is a function pointer, which is struct device_node and struct property structures are callback functions that allocate memory. In__ unflatten_ device_ In the tree() function, unflatten is called twice_ dt_ Node() function, the first time is to get the Device Tree converted to struct device_ The memory size to be allocated for node and struct property structures. The second call is to fill each struct device_node and struct property structures__ unflatten_ device_ The tree () code is listed below:

  1. /**
  2. * unflatten_dt_node - Alloc and populate a device_node from the flat tree
  3. * @blob: The parent device tree blob
  4. * @mem: Memory chunk to use for allocating device nodes and properties
  5. * @poffset: pointer to node in flat tree
  6. * @dad: Parent struct device_node
  7. * @nodepp: The device_node tree created by the call
  8. * @fpsize: Size of the node path up at the current depth.
  9. * @dryrun: If true, do not allocate device nodes but still calculate needed
  10. * memory size
  11. */
  12. static void * unflatten_dt_node( const void *blob,
  13. void *mem,
  14. int *poffset,
  15. struct device_node *dad,
  16. struct device_node **nodepp,
  17. unsigned long fpsize,
  18. bool dryrun)
  19. {
  20. const __be32 *p;
  21. struct device_node *np;
  22. struct property *pp, **prev_pp = NULL;
  23. const char *pathp;
  24. unsigned int l, allocl;
  25. static int depth;
  26. int old_depth;
  27. int offset;
  28. int has_name = 0;
  29. int new_format = 0;
  30. /* Get the name pointer of node node into pathp */
  31. pathp = fdt_get_name(blob, *poffset, &l);
  32. if (!pathp)
  33. return mem;
  34. allocl = ++l;
  35. /* version 0x10 has a more compact unit name here instead of the full
  36. * path. we accumulate the full path size using "fpsize", we'll rebuild
  37. * it later. We detect this because the first character of the name is
  38. * not '/'.
  39. */
  40. if ((*pathp) != '/') {
  41. new_format = 1;
  42. if (fpsize == 0) {
  43. /* root node: special case. fpsize accounts for path
  44. * plus terminating zero. root node only has '/', so
  45. * fpsize should be 2, but we want to avoid the first
  46. * level nodes to have two '/' so we use fpsize 1 here
  47. */
  48. fpsize = 1;
  49. allocl = 2;
  50. l = 1;
  51. pathp = "";
  52. } else {
  53. /* account for '/' and path size minus terminal 0
  54. * already in 'l'
  55. */
  56. fpsize += l;
  57. allocl = fpsize;
  58. }
  59. }
  60. /* Allocate struct device_node memory, including full path size */
  61. np = unflatten_dt_alloc(&mem, sizeof( struct device_node) + allocl,
  62. __alignof__( struct device_node));
  63. if (!dryrun) {
  64. char *fn;
  65. of_node_init(np);
  66. /* Fill full_name,full_ Name refers to the full path name string of the node node */
  67. np->full_name = fn = (( char *)np) + sizeof(*np);
  68. if (new_format) {
  69. /* rebuild full path for new format */
  70. if (dad && dad->parent) {
  71. strcpy(fn, dad->full_name);
  72. fn += strlen(fn);
  73. }
  74. *(fn++) = '/';
  75. }
  76. memcpy(fn, pathp, l);
  77. /* The node is attached to the corresponding parent node, child node and sister node */
  78. prev_pp = &np->properties;
  79. if (dad != NULL) {
  80. np->parent = dad;
  81. np->sibling = dad->child;
  82. dad->child = np;
  83. }
  84. }
  85. /* Process all the properties under the node */
  86. for (offset = fdt_first_property_offset(blob, *poffset);
  87. (offset >= 0);
  88. (offset = fdt_next_property_offset(blob, offset))) {
  89. const char *pname;
  90. u32 sz;
  91. if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {
  92. offset = -FDT_ERR_INTERNAL;
  93. break;
  94. }
  95. if (pname == NULL) {
  96. pr_info( "Can't find property name in list !\n");
  97. break;
  98. }
  99. if ( strcmp(pname, "name") == 0)
  100. has_name = 1;
  101. pp = unflatten_dt_alloc(&mem, sizeof( struct property),
  102. __alignof__( struct property));
  103. if (!dryrun) {
  104. /* We accept flattened tree phandles either in
  105. * ePAPR-style "phandle" properties, or the
  106. * legacy "linux,phandle" properties. If both
  107. * appear and have different values, things
  108. * will get weird. Don't do that. */
  109. /* Process the phase to get the phase value */
  110. if (( strcmp(pname, "phandle") == 0) ||
  111. ( strcmp(pname, "linux,phandle") == 0)) {
  112. if (np->phandle == 0)
  113. np->phandle = be32_to_cpup(p);
  114. }
  115. /* And we process the "ibm,phandle" property
  116. * used in pSeries dynamic device tree
  117. * stuff */
  118. if ( strcmp(pname, "ibm,phandle") == 0)
  119. np->phandle = be32_to_cpup(p);
  120. pp->name = ( char *)pname;
  121. pp->length = sz;
  122. pp->value = (__be32 *)p;
  123. *prev_pp = pp;
  124. prev_pp = &pp->next;
  125. }
  126. }
  127. /* with version 0x10 we may not have the name property, recreate
  128. * it here from the unit name if absent
  129. */
  130. /* Add a name attribute for each node node */
  131. if (!has_name) {
  132. const char *p1 = pathp, *ps = pathp, *pa = NULL;
  133. int sz;
  134. /* The value value of the attribute name is the name of the node node, taking the substring between "/" and "@" */
  135. while (*p1) {
  136. if ((*p1) == '@')
  137. pa = p1;
  138. if ((*p1) == '/')
  139. ps = p1 + 1;
  140. p1++;
  141. }
  142. if (pa < ps)
  143. pa = p1;
  144. sz = (pa - ps) + 1;
  145. pp = unflatten_dt_alloc(&mem, sizeof( struct property) + sz,
  146. __alignof__( struct property));
  147. if (!dryrun) {
  148. pp->name = "name";
  149. pp->length = sz;
  150. pp->value = pp + 1;
  151. *prev_pp = pp;
  152. prev_pp = &pp->next;
  153. memcpy(pp->value, ps, sz - 1);
  154. (( char *)pp->value)[sz - 1] = 0;
  155. }
  156. }
  157. /* Fill device_ name and type members in the node structure */
  158. if (!dryrun) {
  159. *prev_pp = NULL;
  160. np->name = of_get_property(np, "name", NULL);
  161. np->type = of_get_property(np, "device_type", NULL);
  162. if (!np->name)
  163. np->name = "<NULL>";
  164. if (!np->type)
  165. np->type = "<NULL>";
  166. }
  167. old_depth = depth;
  168. *poffset = fdt_next_node(blob, *poffset, &depth);
  169. if (depth < 0)
  170. depth = 0;
  171. /* Recursively call the child nodes below the node node */
  172. while (*poffset > 0 && depth > old_depth)
  173. mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
  174. fpsize, dryrun);
  175. if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
  176. pr_err( "unflatten: error %d processing FDT\n", *poffset);
  177. /*
  178. * Reverse the child list. Some drivers assumes node order matches .dts
  179. * node order
  180. */
  181. if (!dryrun && np->child) {
  182. struct device_node *child = np->child;
  183. np->child = NULL;
  184. while (child) {
  185. struct device_node *next = child->sibling;
  186. child->sibling = np->child;
  187. np->child = child;
  188. child = next;
  189. }
  190. }
  191. if (nodepp)
  192. *nodepp = np;
  193. return mem;
  194. }

All struct devices are obtained through the above function processing_ Node structure. For each node, a property named "name" will be automatically added. The value of property.length is the name of the current node. Take the last substring between "/" and "@" (including '\ 0'). For example:/ serial@e2900800 , then length = 7, property.value = = “serial”.

6. platform_device and device_node binding

After the above analysis, all the data of Device Tree have been parsed into a specific struct device_node and struct property structures need to be bound with specific devices. First, explain the platform_device and device_node binding process. In the arch/arm/kernel/setup.c file, customize_ The machine () function is responsible for populating the struct platform_device structure. The function calling process is shown in Figure 8.  

The code analysis is as follows:

  1. const struct of_device_id of_default_bus_match_table[] = {
  2. { .compatible = "simple-bus", },
  3. { .compatible = "simple-mfd", },
  4. #ifdef CONFIG_ARM_AMBA
  5. { .compatible = "arm,amba-bus", },
  6. #endif /* CONFIG_ARM_AMBA */
  7. {} /* Empty terminated list */
  8. };
  9. int of_platform_populate( struct device_node *root,
  10. const struct of_device_id *matches,
  11. const struct of_dev_auxdata *lookup,
  12. struct device *parent)
  13. {
  14. struct device_node *child;
  15. int rc = 0;
  16. /* Get root node */
  17. root = root ? of_node_get(root) : of_find_node_by_path( "/");
  18. if (!root)
  19. return -EINVAL;
  20. /* Create a platform for each node below the root node_ Device structure */
  21. for_each_child_of_node(root, child) {
  22. rc = of_platform_bus_create(child, matches, lookup, parent, true);
  23. if (rc) {
  24. of_node_put(child);
  25. break;
  26. }
  27. }
  28. /* Update device_node flag flag bit */
  29. of_node_set_flag(root, OF_POPULATED_BUS);
  30. of_node_put(root);
  31. return rc;
  32. }
  33. static int of_platform_bus_create( struct device_node *bus,
  34. const struct of_device_id *matches,
  35. const struct of_dev_auxdata *lookup,
  36. struct device *parent, bool strict)
  37. {
  38. const struct of_dev_auxdata *auxdata;
  39. struct device_node *child;
  40. struct platform_device *dev;
  41. const char *bus_id = NULL;
  42. void *platform_data = NULL;
  43. int rc = 0;
  44. /* Only the node node containing the "compatible" attribute will generate the corresponding platform_device structure */
  45. /* Make sure it has a compatible property */
  46. if (strict && (!of_get_property(bus, "compatible", NULL))) {
  47. return 0;
  48. }
  49. /* Omit some codes */
  50. /*
  51. * For the node with status = "ok" or status = "okay" or without status attribute
  52. * The node allocates memory and populates the platform_device structure
  53. */
  54. dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
  55. if (!dev || !of_match_node(matches, bus))
  56. return 0;
  57. /* Recursively call the node parsing function to continue to generate the platform for the child nodes_ Device structure, provided that the parent node
  58. * "compatible" = "simple bus", that is, matching of_ default_ bus_ match_ Data in table structure
  59. */
  60. for_each_child_of_node(bus, child) {
  61. rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
  62. if (rc) {
  63. of_node_put(child);
  64. break;
  65. }
  66. }
  67. of_node_set_flag(bus, OF_POPULATED_BUS);
  68. return rc;
  69. }

In general, when of_ platform_ After the populate () function is executed, the kernel creates a platform for all the first level nodes in the DTB that contain the compatible attribute name_ Device structure, and register the device information with the platform device bus. If the compatible attribute value of the first level node is equal to "simple bus", "simple MFD" or "arm, AMBA bus", the kernel will continue to create a platform for the second level node containing the compatible attribute of the current node_ Device structure and register the device. Most devices in the Linux system are attached to the platform bus. Therefore, after the platform bus is registered, it will be registered according to the of_ The tree structure of the root node to find the child nodes of the bus. All child nodes will be registered on the bus as devices.

7. i2c_client and device_node binding

After customize_ After the initialization of the machine () function, DTB has been converted to platform_device structure, which contains i2c adapter devices. Different SOCS need to drive i2c adapter devices by means of platform device bus. For example: I2C_ I2C will be called in the probe function driven by adapter_ add_ numbered_ Adapter () registers the adapter driver, and the function flow is executed as shown in Figure 9.  

In of_ i2c_ register_ The devices() function internally facilitates each child node under the I2C node and creates I2C for the child nodes (except those with status = "disable")_ The client structure and the device of the child node_ Node hook. Where I2C_ The client is populated in i2c_new_device(), the last device_register(). Building I2C_ When using client, the vendor name of the compatible attribute name under node will be removed as I2C_ The name of the client. For example, if compatible = "maximum, ds1338", then i2c_client->name = “ds1338”.

8. Device_Tree and sysfs

The kernel startup process is start_kernel()→rest_init()→kernel_thread():kernel_init()→do_basic_setup()→driver_init()→of_core_init() in of_ core_ In the init() function, under the sys/firmware/devicetree/base directory, expand the device tree into the directory and binary property file of sysfs. All node nodes are a directory and all property properties are a binary property file.

Topics: Linux