This article was reproduced from: http://blog.csdn.net/KjfureOne/article/details/51972854
1. Why should the ARM Linux community introduce the device tree
The father of Linux Linus Torvalds Idle, when looking at the arm Linux code, one day finally can not help. He said in an ARM Linux mailing list on March 17, 2011: "This whole arm thing is a f*cking pain in the". This sentence forces the ARM Linux community to introduce a device tree.
Why would Linus Torvalds be mad? And why are the cows in the arm Linux community so obedient? You have to first understand a very good design in the Linux device driver framework: Device information and drive separation.
To illustrate the concept of device information and drive separation, here is a simple analog code to explain:
"Example 1" implements a code that simply writes the information to be used to die in the code:
int Add ()/* Simulate driver code */
{
return 3+5; /* Simulate device information */
}
Advantages: Simple
Cons: Once addend and Summand change, you have to change the code.
The improved design is as follows:
Example 2 implements a code that separates the information to be used and the operation code:
struct dev{
int id;
int x;
int y;
}; /* Simulate device information structure */
Strcut drv{
int id;
Int (*add) (struct dev *info);
}; /* Analog Drive structure */
int add (struct dev *info)/*/Analog Drive Code */
{
return info->x + info->y; /* Analog Device information-passed in via parameters */
}
struct DRV DRV = {
. id = 1,
. Add = Add,
};
/* Simulate device information */
struct Dev dev = {
. id = 1,
. x = 3,
. y = 5,
};
/* Analog bus initialization matches device information and driver code */
int bus ()
{
if (dev.id = = drv.id) {
Return Drv.add (&dev);
}
...
}
Pros: No need to modify code, no matter how addend and summand change, only need to modify the information
Cons: The structure is more complex
What does the device information and drive separation design have to do with the drive? The students familiar with hardware programming know that the general composition of hardware can be used simple expression:
The driver code logic of the operating peripherals, as long as the hardware is the same, will not change. But the peripheral hangs to the different host, may have the I/O address change, if has the interruption also is same, the interrupt number may also be different. These I/O addresses and interrupts are device information, and the code that uses this information to manipulate the control hardware is the driver.
If the "Example 1" design method, then the same hardware peripherals to different host, or change the address line/in the disconnection, the device information changes, you have to modify the driver. However, using the "Example 2" approach to design, the problem is solved: no matter where the same peripheral hardware received or the platform, its driver code logic does not need to change, but only need to modify the device information, the main is the I/O address and the interrupt number.
What does it have to do with introducing a plant tree? The Development Board (A8/A9) used in Huaqing teaching uses the DM9000 card chip. DM9000 Drive is open source, in the mainline kernel source code is there. Each time we based on the A8/A9 board transplant, the DM9000 driver has not been modified, just optional, the main task is to add device information in the board level file. The DM9000 driver uses the platform framework, so a platform_device message is added for the DM9000 NIC chip. The problem comes, if the use of C code to describe the device information, then in the kernel source code, there will be multiple copies of DM9000 Platform_device device information, resulting in kernel code redundancy.
The solution to this problem is to introduce the device tree and transform "Example 2" to illustrate the role of the device tree.
"Example 3" implements a code that separates not only the information to be used but also the operation code, and the information is not written in C code but is saved by the text configuration file:
struct dev{
int id;
int x;
int y;
}; /* Simulate device information structure */
Strcut drv{
int id;
Int (*add) (struct dev *info);
}; /* Analog Drive structure */
int add (struct dev *info)/*/Analog Drive Code */
{
return info->x + info->y; /* Analog Device information-passed in via parameters */
}
struct DRV DRV = {
. id = 1,
. Add = Add,
};
/* analog Device Tree-a special configuration file, Xxx.dtbs text file */
/{
......
dm9000{
x = 3;
y = 5;
};
......
};
/* Analog bus initialization matches device information and driver code */
int bus ()
{
/* Simulation of Device tree initialization processing */
Read the document (XXX.DTBS);
Parse the contents of the file (according to the rules of the device tree);
Generate the struct dev device information;
if (dev.id = = drv.id) {
Return Drv.add (&dev);
}
...
}
If you like "Example 3", you can solve the problem of code redundancy for large amounts of device information.
By and by, the system's hardware and software information can be described using the device tree. In this way, the ARM Linux community will not support the board and drive more and more cause the kernel source code in a lot of redundant codes (mainly board-level files), only need to transplant, the system hardware and software information through the device tree to provide, optional kernel code, you can.
2. Overview of the device tree
2.1. References
Kernel source directory Documentation\devicetree device Tree Description document
Kernel source code drivers/of/source Analysis
2.2. Basic Concepts
A device tree is a tree structure that describes soft/hardware information, including nodes and attributes. Nodes are used to classify a hardware or software information (like a directory of a file system). The node describes one or more attributes, which are key-value pairs that describe the specific soft/hard information. The simple form is as follows:
/{
node{
Property=value;
...
child_node{
Child_property=value;
...
};
...
};
...
};
The description is as follows:
/: Root node, node using "{};" The scope of the syntax description
Node: A child node under the root node
A child node under the Child_node:node node
The attributes that are described in the Property:node node are the value of the property (any byte data, which can be integer, string, array, and so on). Description line with ";" End
2.3. Storage form
The use of the device tree has been discussed in why the ARM Linux community is introducing the device tree. In short: When the kernel is initialized, the contents of the device tree file are read as a configured file, and the corresponding soft/hardware information is parsed for use by the appropriate kernel code.
The Authoring device tree file is stored as a. DTS text file, primarily for modification and for added editing convenience.
So the problem is, if plain text parsing, it's obviously slow and troublesome. For example, if the property value is an I/O address: 0x80000000, if it is stored as a string, then "0x80000000" is a string, the kernel code parsing this information is also converted to an integer number, not only slow, the intangible device tree file size will increase a lot, You have to add more initialization code.
So. DTS device tree files, which need to be converted once before the kernel is used, are mainly converting complex syntactic forms and attribute values into byte data (special data structures), rather than symbols. The. DTB binaries are converted after the DTS file.
3. Node
3.1. Naming
The name of the node is composed of letters, numbers, _, and so on. Common command methods are as follows:
A, with "device name" as the node name, example:
DM9000 is named as follows:
/{
...
dm9000{
...
};
...
};
B, with "Device @i/o address" as "Node name @i/o address", Example:
DM9000 the I/O address on the host side is 0x8000 0000, which can be named as follows:
/{
...
[Email protected] {
...
};
...
};
C, with "Device type @i/o address" as "Node name @i/o address", Example:
DM9000 the I/O address on the host side is 0x8000 0000, which can be named as follows:
/{
...
[Email protected] {
...
};
...
};
3.2. Node path
A
/{
...
dm9000{
...
};
...
};
Node Name: dm9000
Node path:/dm9000
B
/{
...
[Email protected] {
...
};
...
};
Node Name: dm9000
Node path:/[email protected]
C
/{
...
[Email protected] {
...
};
...
};
Node Name: Ethernet
Node path:/[email protected]
3.3. Node reference
/{
aliases {
Demo = &demo;
};
...
Demo:[email protected]{
...
};
...
};
Node Name: Demo
Node path:/[email protected]
Reference Path: Demo (equivalent/[email protected], troubleshooting path name process)
Example of reference node "/[email protected]" In the device tree:
&demo{
...
};
3.4. Node Lookup
Sometimes, when sharing kernel code or writing kernel code, it may involve using the Find node function. The kernel provides a number of kernel functions to find (parse the device tree) a specified node:
A, Path Lookup
/*
* Function: Find the specified node by path
Parameters
* const char *path-node path, which can be a path or a path reference
* Return value:
* Success: Gets the first address of the node object; failed: NULL
*/
struct Device_node *of_find_node_by_path (const char *path);
B, node name lookup
/*
* Function: To find the specified node by nodes name
Parameters
* struct Device_node *from-node pointing to the start path, if NULL, starting from the root node
* const char *name-node name
* Return value:
* Success: Gets the first address of the node object; failed: NULL
*/
struct Device_node *of_find_node_by_name (struct device_node *from, const char *name);
C. Find by Compatible property
/*
* Function: Find the specified node through the Compatible property
Parameters
* struct Device_node *from-node pointing to the start path, if NULL, starting from the root node
* const char *type-node type, can be null
* const CHAR *compat-The first address of the value (string) that points to the compatible property of the node
* Return value:
* Success: Gets the first address of the node object; failed: NULL
*/
struct Device_node *of_find_compatible_node (struct Device_node *from,
const char *type, const char *compat);
Device ID Table structure
struct OF_DEVICE_ID {
Char name[32]; /* Device Name */
Char type[32]; /* Device Type */
Char compatible[128]; /* String to match the property value of the device tree compatible */
const void *data; /* Private Data */
};
/*
* Function: Find the specified node through the Compatible property
Parameters
* struct Device_node *from-node pointing to the start path, if NULL, starting from the root node
* Const struct OF_DEVICE_ID *matches-point to Device ID table
* Note that the ID table must end with NULL
* Example: const struct OF_DEVICE_ID mydemo_of_match[] = {
{. compatible = "Fs4412,mydemo",},
{}
};
* Return value:
* Success: Gets the first address of the node object; failed: NULL
*/
struct Device_node *of_find_matching_node (struct Device_node *from,
const struct OF_DEVICE_ID *matches);
D. Find child nodes
/*
* Function: Find child nodes of the specified node
Parameters
* Const struct Device_node *node-point to parent node to find child nodes
* const CHAR *name-sub-node name
* Return value:
* Success: Gets the first address of the child node object; failed: NULL
*/
struct Device_node *of_get_child_by_name (const struct Device_node *node,
const char *name);
3.5. Node Content Merging
Sometimes, some of the information in a hardware device does not change, but some of the information is likely to change, and node content merging occurs. That is: First write a good node, just describe some of the property values, the user and then add a portion of the property value.
Under a sibling path, the "two" nodes with the same node name are actually one.
/* The already compiled node node of the reference board */
/{
node{
Item1=value;
};
};
/* Node nodes added by the transplant */
/{
node{
Item2=value;
};
};
Equivalent to:
/{
node{
Item1=value;
Item2=value;
};
};
3.6. Node content substitution
Sometimes, some of the property information of a hardware device may change, but the device tree has already described all the attribute values, the user can add the existing property values to replace the original attribute values, there is a node content substitution.
In addition, the content of a node may not be used, even if it does not change.
Under a sibling path, the "two" nodes with the same node name are actually one.
One of the common forms of content substitution:
/* The already compiled node node of the reference board */
/{
node{
item=value1;
};
};
/* Node nodes added by the transplant */
/{
node{
Item=value2;
};
};
Equivalent to:
/{
node{
Item=value2;
};
};
Two common forms of content substitution:
/* The already compiled node node of the reference board */
/{
node{
Item=value;
Status = "Disabled";
};
};
/* Node nodes added by the transplant */
/{
node{
Status = "Okay";
};
};
Equivalent to:
/{
node{
Item=value;
Status = "Okay";
};
};
3.7. Node Content Reference
Sometimes a node needs to use the attribute value of another node, and it needs to refer to the concept. Sometimes when a device tree is written, a reference is used to replace a node property value or to merge a node's property value.
A, the reference node completes the substitution and merging of property values:
/* The already compiled node node of the reference board */
/{
Node:[email protected]{
Item1=value;
Status = "Disabled";
};
};
/* Node nodes added by the transplant */
&node{
Item2=value;
Status = "Okay";
};
Equivalent to:
/{
Node: [Email protected]{
Item1=value;
Item2=value;
Status = "Okay";
};
};
B. Node refers to another node:
/* The already compiled node node of the reference board */
/{
Node:[email protected]{
Item=value;
};
};
/* The demo node added by the transplant */
/{
demo{
item=<&node>;
};
};
Description
The properties of the demo node, item, refer to the node's attribute value, and how to use the attribute value of the nodes node to discuss it in the Properties section.
Linux device tree and node reference "Go"