The Linux usage model for device tree data

Source: Internet
Author: User

Linux and the Device Tree

The Linux usage model for device tree data

Author:grant likely [email protected]

This article describes how to use device tree in Linux. The Device Tree data format can be obtained from the http://devicetree.org/Device_Tree_Usage.

Device Tree is a language that describes hardware that allows the operating system not to hardcode hardware information.

Structurally, Device tree is a graph of tree-shaped structures, or non-cyclic, well-known nodes. Each node contains a certain number of properties and key-value pairs. A node can be referenced by other nodes in the form of a declaration link, which resembles a pointer to a C language.

Conceptually, the data in what form to describe the hardware features, including bus, interrupt line, GPIO connection, and peripherals, are implemented by a set of conventions, called "bindings".

In the process of use, we use the existing bindings as much as possible to describe the hardware devices so that the existing code can be supported to the fullest extent. However, because property and node names are essentially just literal strings, we can easily extend existing bindings by creating new properties and nodes.

    1. History

    1. Data model

2.1 Top-level view

The first thing to be clear is that DT is just a data structure that describes the hardware, which itself does not have any magic, nor does it have the magic to eliminate all hardware configuration problems. It provides a description of the language that enables the hardware configuration to be decoupled from the platform and device drivers.

Ideally, this data-driven platform setup reduces the reuse of code and enables single-core mirroring to support multiple platforms.

Linux uses DT data for three purposes:

1) Platform identification

2) Run-time configuration

3) Device Enumeration

2.2 Platform Identification

First and foremost, the kernel uses data from DT to identify specific platforms. Ideally, all hardware details are described in a continuous and reliable manner in DT so that the kernel is independent of the specific platform. But the fact that the hardware platform is not so perfect, the kernel must identify the platform during the early boot process, thus having the opportunity to run some hardware-specific repair code. (Software-based patching of hardware defects)

In most cases, machine model recognition is independent of DT, and the kernel chooses the boot code by detecting the machine's core CPU or SOC. In the case of ARM platforms, the Setup_arch () function in ARCH/ARM/KERNEL/SETUP.C calls SETUP_MACHINE_FDT () (in arch/arm/kernel/devtree.c), This function iterates through the MACHINE_DESC table and selects the MACHINE_DESC structure that best matches the device tree data. It selects the most matching MACHINE_DESC structure by comparing the compatible attribute of the DT root node with the Dt_compat linked list in the MACHINE_DESC structure. (struct Machine_desc defined in arch/arm/include/asm/mach/arch.h)

The Compatible property contains a set of strings, in the form of an exact machine name, followed by a series of optional platform names, and the order of the platform names determines the priority of the match. For example:

compatible = "Ti,omap3-beagleboard", "ti,omap3450", "TI,OMAP3";

After traversing all machine_desc linked lists, the SETUP_MACHINE_FDT () function returns the most matching machine_desc struct pointer, which returns NULL if there is no matching machine_desc struct body.

This mechanism is cited because, in most cases, when multiple platforms are using the same SOC, a MACHINE_DESC structure can support multiple platforms. Then, when a particular platform needs to execute specific initialization code, we need to quickly identify the platform that needs to execute the special boot code in the generic initialization function.

In turn, the compatible list allows a generic (in a series of products) Machine_desc to support multiple platforms by inserting less compatible values into the Dt_compat list.

2.3 Run-time configuration

In most cases, DT is the only way to communicate between the boot loader and the kernel. Therefore, DT is also used to pass some runtime data or configuration data to the kernel, such as kernel boot parameters, INITRD image loading address, etc.

Such data is typically contained in/chosen nodes, which are presented to the kernel in the following form when booting Linux:

Chosen {
Bootargs = "console=ttys0,115200 loglevel=8";
Initrd-start = <0xc8000000>;
Initrd-end = <0xc8200000>;
};

The chosen node can also contain a specific number of properties that describe the special configuration of the platform.

During the early boot process, the platform startup code called OF_SCAN_FLAT_DT () multiple times (using a different helper callbacks) to parse the device tree before the memory page table was created. This function traverses the device tree to get the information needed by helper. For example, the Early_init_dt_scan_chosen () callback function is used to parse the chosen node, including the kernel boot parameters, Early_init_dt_scan_root () callback function is used to initialize the DT addressing space, EARLY_INIT_DT The _scan_memory () callback function is used to determine the size and location of available memory.

On the arm platform, SETUP_MACHINE_FDT () is responsible for the initial traversal of the device tree after selecting the correct machine_desc.

2.4 Device Enumeration

At this stage, the Unflatten_device_tree () function is called to present the DT data in real time to the other modules of the kernel in an organized manner. At the same time, the startup hook function for a particular machine is called, such as Init_early (), INIT_IRQ (), Init_machine (), and so on in Machine_desc. The init_early () function is used to execute a specific platform's startup code at the start of an early stage, and INIT_IRQ () is used to set the interrupt processing process. Using DT does not change the behavior of these functions, but DT allows these functions to take advantage of the DT query function (of_* in include/linux/of*.h) to obtain additional platform information.

The most interesting hook function in the DT context is init_machine (), which is primarily responsible for enumerating the Linux device models. Previously implemented by defining a set of static clock structures, platform devices, and other data in the Board support file, and then registering them with the kernel in Init_machine (). After adopting DT, the hardware information is not implemented in this hard-coded way, but the device structure is allocated dynamically by parsing the device tree.

In the simplest case, init_machine () is only responsible for registering a batch of platform_devices. Conceptually, Platform_device is a device that cannot be detected by hardware, is used by Linux as memory or I/O mapping, and is used to represent a combination (composite) or virtual device. In DT, and without the term platform device, platform device only roughly corresponds to the node of the device nodes under the root node and simply the memory map of the bus nodal points.

Speaking of this place, it is time to give you an example:

256/{
257 compatible = "Nvidia,harmony", "nvidia,tegra20";
258 #address-cells = <1>;
259 #size-cells = <1>;
260 Interrupt-parent = <&intc>;
261
262 chosen {};
263 aliases {};
264
265 Memory {
266 device_type = "Memory";
267 reg = <0x00000000 0x40000000>;
268};
269
Soc {
271 compatible = "Nvidia,tegra20-soc", "Simple-bus";
272 #address-cells = <1>;
273 #size-cells = <1>;
274 ranges;
275
276 INTC: [email protected] {
277 compatible = "Nvidia,tegra20-gic";
278 Interrupt-controller;
279 #interrupt-cells = <1>;
280 reg = <0x50041000 0x1000>, < 0x50040100 0x0100 >;
281};
282
283 [email protected] {
284 compatible = "Nvidia,tegra20-uart";
285 reg = <0x70006300 0x100>;
286 interrupts = <122>;
287};
288
289 i2s1: [email protected] {
290 compatible = "NVIDIA,TEGRA20-I2S";
291 reg = <0x70002800 0x100>;
292 interrupts = <77>;
293 codec = <&wm8903>;
294};
295
296 [email protected] {
297 compatible = "NVIDIA,TEGRA20-I2C";
298 #address-cells = <1>;
299 #size-cells = <0>;
Reg = <0x7000c000 0x100>;
301 Interrupts = <70>;
302
303 wm8903: [email protected] {
304 compatible = "wlf,wm8903";
305 reg = <0x1a>;
306 interrupts = <347>;
307};
308};
309};
310
311 Sound {
312 compatible = "Nvidia,harmony-sound";
313 I2s-controller = <&i2s1>;
314 I2s-codec = <&wm8903>;
315};
316};

Looking closely at the above example, the Tegra board-level support code needs to parse the above device tree and decide which nodes are used to create the platform_device. Then, it's hard to see what type of device each node represents, or even whether a node represents a device. For example,/chosen,/aliases,/memory These information class nodes do not represent devices. Although the/SOC node is a memory-mapped device, [email protected] is an i²c device, and the sound node is not a class of devices, but an audio subsystem composed of different device interconnects. I know what each device represents because I understand hardware design, but how does the kernel know how to handle these nodes?

The trick is that the kernel starts with the root node, looking for nodes that contain the compatible attribute. First, it assumes that any node that contains the compatible attribute represents a device. Second, it assumes that the sub-nodes of any root node are directly attached to the processor bus, or a promiscuous system device that cannot be described in any other way. For each of these nodes, Linux allocates and registers a platform_device struct to them, and these structures may be platform_driver adopted by the following bodies.

Why is it a safe strategy to describe these nodes with Platform_device? This is because in the Linux device model, all bus_types assume that a device mounted under a class of bus is a sub-device of a bus controller. For example, each i2c_client device is a sub-device of a i2c_master device, and each spi_device is a sub-device of an SPI bus, as well as for USB, PCI, and Sdio buses. The same hierarchical relationship is shown in DT, for example, all of the I²C device nodes appear only as sub-nodes of the I²C bus node. The only type of device that does not require a specific parent device is platform_device (and amba_devices), Platform_device can happily exist in the/sys/devices tree. Therefore, if a node is under the root junction, it is likely that it will be registered as a platform_device.

Linux board-level support code calls Of_platform_populate (Null,null,null,null) to start discovering devices under the root node in dt. All parameters are null because it is not necessary to specify the starting node (the first parameter), the parent structure device (the last parameter), or the matching table when looking from the root node. For a platform that only needs to register a device, Init_machine () can contain only of_platform_populate () function calls.

For a device tree instance of the above Tegra, the/SOC and/sound nodes under the root node can be registered as Platform_device, but their child nodes should not be registered as Platform_device? It is common practice to register these sub-devices when the parent device-driven probe () function executes. Then, an I²C bus driver registers a i2c_client device structure for each of the sub-nodes, and an SPI bus driver registers the Spi_device device, as do other bus types. Based on this model, you can write a driver that binds to the/SOC node to register its child nodes as platform_device devices, such as/soc/interrupt-controller,/soc/serial,/soc/i2s,/soc/ I²c and so on.

It turns out that registering the Platform_device device's sub-device as a platform_device is a common form that is reflected in the device tree support code. The second parameter of the Of_platform_populate () function is a of_device_id table, and any node that matches the table entry in the table and its child nodes can be registered. In Tegra, for example, the code can:

static void __init harmony_init_machine (void)
{
/* ... */
Of_platform_populate (NULL, of_default_bus_match_table, NULL, NULL);
}

"Simple-bus" is a property defined in the EPAPR 1.0 specification that is used to represent a memory-mapped bus, and the of_platform_populate () function iterates through the nodes under all/SOC by default. However, we can override its default behavior by passing a parameter to it.

Appendix A:amba Devices

Arm Primecell Peripherals are attached to the arm Amba bus, and the arm AMBA bus supports hardware detection and power management. In Linux, ARM Primecell peripherals are described using structural amba_device and Amba_bus_type. However, not all the devices on the AMBA bus are Primecell-type peripherals, so that Linux allows both Amba_device instances and Platform_device instances to exist under the AMBA bus device.

With DT, this can cause problems for the of_platform_populate () function, because it must decide whether to register each node as Amba_device or Platform_device. This obviously introduces trouble to the device creation model, but the actual solution is ingenious. If the compatible property of a node is "Arm,amba-primecell", then the Of_platform_populate () function registers it as Amba_device instead of Platform_device.

The Linux usage model for device tree data

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.