Device Tree Learning (DTS) by SSM
Device Tree Learning (DTS) by SSM
3. DTS、DTB 和 DTC
The code files for the device tree are DTS files and DTSI files.
The DTS file is compiled by the DTC (Device Tree Compiler) into a DTB (Device Tree Block) binary. The file will be flashed to a specific address in
memory (specified by the BootLoader, in principle, as long as it does not overwrite the contents of the boot and kernel). The BootLoader then
passes the address to the kernel as a parameter. The kernel parses valid device information according to the specific format of the DTB file and
passes it to the driver code.
The source code of the dtc tool is located in the scripts/dtc directory of the Linux kernel:
Insert a description of the picture here
# SPDX-License-Identifier: GPL-2.0
# scripts/dtc makefile
hostprogs-$(CONFIG_DTC) := dtc
ifeq ($(DTC_EXT),)
always := $(hostprogs-y)
endif
# Generated files need one more search path to include headers in source tree
HOSTCFLAGS_dtc-lexer.lex.o := -I $(srctree)/$(src)
HOSTCFLAGS_dtc-parser.tab.o := -I $(srctree)/$(src)
The DTC tool relies on files such as dtc.c, flattree.c, fstree.c, etc., and finally compiles the DTC as a host file. If you want to compile a DTS file, you
just need to go to the root directory of the Linux source code and run the following command: make all or make dtbs.
4. DTS syntax
4.1 .dtsi header file
Like C, headers are supported in the device tree, which has a .dtsi extension. At the same time, .dts files can also reference .h files in C, and even
.dts files.
Generally, the .dtsi file is used to describe the internal peripheral information of the SOC, such as the CPU architecture, the main frequency, and the
address range of the peripheral registers, such as UART, IIC, etc.
/include/ "spear3xx.dtsi"
/ {
ahb {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges = <0x60000000 0x60000000 0x50000000
0xd0000000 0xd0000000 0x30000000>;
pinmux@99000000 {
compatible = "st,spear300-pinmux";
reg = <0x99000000 0x1000>;
};
clcd@60000000 {
compatible = "arm,pl110", "arm,primecell";
reg = <0x60000000 0x1000>;
interrupts = <30>;
status = "disabled";
};
fsmc: flash@94000000 {
compatible = "st,spear600-fsmc-nand";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x94000000 0x1000 /* FSMC Register */
0x80000000 0x0010 /* NAND Base DATA */
0x80020000 0x0010 /* NAND Base ADDR */
0x80010000 0x0010>; /* NAND Base CMD */
reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd";
status = "disabled";
};
sdhci@70000000 {
compatible = "st,sdhci-spear";
reg = <0x70000000 0x100>;
interrupts = <1>;
status = "disabled";
};
shirq: interrupt-controller@0x50000000 {
compatible = "st,spear300-shirq";
reg = <0x50000000 0x1000>;
interrupts = <28>;
#interrupt-cells = <1>;
interrupt-controller;
};
apb {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges = <0xa0000000 0xa0000000 0x10000000
0xd0000000 0xd0000000 0x30000000>;
gpio1: gpio@a9000000 {
#gpio-cells = <2>;
compatible = "arm,pl061", "arm,primecell";
gpio-controller;
reg = <0xa9000000 0x1000>;
interrupts = <8>;
interrupt-parent = <&shirq>;
status = "disabled";
};
kbd@a0000000 {
compatible = "st,spear300-kbd";
reg = <0xa0000000 0x1000>;
interrupts = <7>;
interrupt-parent = <&shirq>;
status = "disabled";
};
};
};
};
The basic unit of the device tree is the node, which consists of the root node (/) and its child nodes (name@addr), and the child nodes can also
have child nodes, forming a tree-like structure.
In the example above:
1. "/" is the root node, and each device tree file has only one root node.
2. AHB is a child node, and the node name format in the device tree is as follows:
node-name@unit-address
"node-name" is the name of the node, which is an ASCII string, and the node name should be able to clearly describe the function of the node, for
example, "uart1" means that the node is a UART1 peripheral. "unit-address" generally indicates the address of the device or the first address of the
register, if a node does not have an address or register, "unit-address" can be dispensed with, such as "cpu@0" or "interrupt-
controller@00a01000".
Another format:
label: node-name@unit-address
The purpose of introducing label is to facilitate access to the node, which can be accessed directly through &label, for example, through &cpu0, you
can access the node "cpu@0" without entering the full node name.
"manufacturer,model"
manufacturer indicates the manufacturer, and model is generally the driver name corresponding to the module.
1. model property
The value of the model property is also a string, and the model attribute generally describes the device module information, such as the name.
例如:kernel4.14\arch\arm\boot\dts\sun8i-a33-et-q8-v1.6.dts
/ {
model = "Q8 A33 Tablet";
compatible = "allwinner,q8-a33", "allwinner,sun8i-a33";
};
compatible = “allwinner,q8-a33”, “allwinner,sun8i-a33”; This means that the current device tree supports Allwinner's Q8-A33 platform and Sun8i-A33
platform. When the kernel runs the mach platform file in the corresponding arch directory, it will match the device tree and then load it.
1. status property
The status attribute is related to the state of the device, and the value of the status attribute is also a string, and the string is the state information
of the device.
Each "address length" combination represents an address range, where address is the starting address, length is the address length, #address-
cells indicates the length of the word used by the address data, and #size-cells indicates the length of the word used by the data.
fsmc: flash@94000000 {
compatible = "st,spear600-fsmc-nand";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x94000000 0x1000 /* FSMC Register */
0x80000000 0x0010 /* NAND Base DATA */
0x80020000 0x0010 /* NAND Base ADDR */
0x80010000 0x0010>; /* NAND Base CMD */
reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd";
status = "disabled";
};
The first address of the reg in FSMC is 0x94000000, and its size is 0x1000; The second address is 0x80000000, and its size is 0x0010; The third
address is 0x80020000, which is 0x0010 in size; The fourth address is 0x80010000, which is 0x0010 in size. This means that this device occupies
four blocks of register address space, and the start address and offset of each block are listed in the reg.
1. reg attribute
The value of the reg attribute is usually (address, length) pairs. Generally, it is used to describe the resource information of the device address
space, which is the register address range information of a peripheral.
2. ranges property
The ranges attribute can be empty or a matrix of numbers written in the format (child-bus-address, parent-bus-address, length), ranges is an
address mapping/conversion table, and the ranges attribute consists of three parts: child address, parent address, and address space length:
apb {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges = <0xa0000000 0xa0000000 0x10000000
0xd0000000 0xd0000000 0x30000000>;
gpio1: gpio@a9000000 {
#gpio-cells = <2>;
compatible = "arm,pl061", "arm,primecell";
gpio-controller;
reg = <0xa9000000 0x1000>;
interrupts = <8>;
interrupt-parent = <&shirq>;
status = "disabled";
};
kbd@a0000000 {
compatible = "st,spear300-kbd";
reg = <0xa0000000 0x1000>;
interrupts = <7>;
interrupt-parent = <&shirq>;
status = "disabled";
};
};
child-bus-address: The physical address of the subbus address space, the word length occupied by the #address-cells of the parent node
determines the number of words occupied by this physical address.
parent-bus-address: the physical address of the parent bus address space, which is also determined by the #address-cells of the parent node.
length: the length of the child address space, which is determined by the #size-cells of the parent node.
If the value of the ranges property is null, the child address space and the parent address space are identical, and address translation is not
required.
1. aliases property
aliases means "aliases", so the main function of aliases nodes is to define aliases, and the purpose of defining aliases is to facilitate access to the
node. Nodes that don't use aliases to reference nodes use an absolute path, like /soc/serial0. It is mainly used outside the device tree, and the
internal reference node of the device can use the label. When naming nodes, label will be added, and then the node will be accessed through
&label, which is more convenient, and a lot of the device tree will be used in the form of &label to access nodes.
2. chosen attribute
The chosen node is not a real device, and the chosen node is mainly used to pass data to the Linux kernel for uboot, such as bootargs, which
does not represent the actual device. Its parent node must be the root node. In general, the chosen node in a .dts file is usually empty or has very
little content.
如kernel4.14\arch\arm\boot\dts\sun8i-r16-bananapi-m2m.dts
/dts-v1/;
#include "sun8i-a33.dtsi"
#include <dt-bindings/gpio/gpio.h>
/ {
model = "BananaPi M2 Magic";
compatible = "sinovoip,bananapi-m2m", "allwinner,sun8i-a33";
aliases {
i2c0 = &i2c0;
i2c1 = &i2c1;
i2c2 = &i2c2;
serial0 = &uart0;
serial1 = &uart1;
};
chosen {
stdout-path = "serial0:115200n8";
};
...
1. memory node
The memory node is a required node of the device tree file, and the device_type attribute indicates that the type of the node is memory, which
defines the layout of the system's physical memory, that is, the start address and length. There can be multiple memory nodes, which represent
multiple segments of memory.
/ {
memory {
reg = <0x00000000 0x20000000>;
};
...
5. DTS compilation
5.1 Kernel compilation device tree
Add the compilation option of the dts file to the Makefile in the kernel/arch/arm/boot/dts/ directory, and make dtbs in the kernel directory to get the
corresponding dtb binary.