Devicetree is a representation of a hardware configuration, most especially non-discoverable hardware devices, passed from a bootstrap firmware to an OS .  It is formatted as a series of nodes containing name:value pairs (think: JSON-like) that get compiled into a binary blob.  This binary blob is incorporated into a bootloader which hands it off to the OS.  The OS can then parse the blob to figure out how the hardware is connected, electrically, before addressing said hardware.  The advantage here is to free the OS from needing to know, a priori, the hardware implementation details of every supported computer or embedded system design.

History

The seed that grew into what we know today as Devicetree began at Sun Microsystems in 1988 as the Open Firmware initiative.  Trademarked by Sun as OpenBoot, Open Firmware eventually became an IEEE standard in 1994. (IEEE-1275)

Open Firmware was used in many Sun SPARC and Apple Power PC-based Macintosh designs, but with the market failure of those architectures, Open Firmware also went by the wayside.  IEEE withdrew IEEE-1275 in January 2000.  (I know I am oversimplifying a bit, but bear with me)

However, several reference platform architectures were derived from IEEE-1275, namely Power PC's Common Hardware Reference Platform (CHRP) and Power Architecture Platform Reference (PAPR), which specified the use of Open Firmware.

The component of Open Firmware that survived the fate of the larger project is the subject of this article, Devicetree.  Embedded systems designers needed a way to describe their hardware to Linux without hardcoding hardware implementation details into the kernel and compiling custom kernels for every device running Linux.  A seminal paper coming out of IBM, Device Trees Everywhere, proposed in 2006 a solution using Open Firmware's "device tree" concept, and this idea later was formalized into the Embedded Power Architecture Platform Requirements (ePAPR) specification.

ePAPR was focused on the Power ISA, but the "device tree" concept was too good to keep Power-specific, and so from ePAPR was derived the ISA-agnostic Devicetree specification, currently v0.3 at time of writing.  Today, the Linux kernel supports Devicetree in ARM, MIPS, Power, SPARC, and x86, among other supported architectures.

How Does it Work?

First, the problem:  some buses support device enumeration, like USB and PCIe, whereby the host can interrogate the bus to discover what devices are present and how they're configured.  Other buses do not support enumeration, like I2C and SPI.  So, how can an OS talk to I2C devices, for example, if it has no way to know their address, or even if they're present?  One might be tempted to try the brute-force method of hard-coding the system's implementation of I2C devices directly into the Linux kernel.  That approach has been tried, and unfortunately, for obvious reasons, is not scalable.  What Devicetree provides is a way to describe these non-discoverable devices so the kernel can use them without a priori knowledge.  Furthermore, even for a bus like PCIe, while connected devices can be discovered, the kernel must first know something about the host bridge before it can attempt an enumeration of the bus beneath the host bridge—again, Devicetree provides the solution by describing the host bridge to the kernel.  A Devicetree illustration:


Source File

The hardware is represented in a source file, per convention with the extension .dts.  The file is a kind of tree data structure, (acyclic graph with named nodes) where each node consists of name:value pairs.  An example:
i2c@3000 {
    #address-cells = <1>;
        #size-cells = <0>;
        cell-index = <0>;
        compatible = "fsl-i2c";
        reg = <0x3000 0x100>;
        interrupts = <43 2>;
        interrupt-parent = <&mpic>;
        dfsrr;

dtt@48 {
        compatible = "national,lm75";
        reg = <0x48>;
};

rtc@68 {
        compatible = "dallas,ds1337";
        reg = <0x68>;
        };
};
Using the syntax described above, we can make the following observations about this example node:
• The I2C controller is located at offset 0x3000 from its parent
• The driver for the I2C controller is fsl-i2c
• The first child is named dtt, at offset 0x48 from its parent; the driver is national lm75
• The second child is named rtc, at offset 0x68 from its parent; the driver is Dallas ds1337
• The interrupt parent is the mpic, and interrupt number 0x43 is used. Because this is OpenPIC, an offset of 16 is added to the interrupt number for internal interrupts. 43 - 16 = 27, so this is actually SoC interrupt 0x27

Binary Blob

The .dts source gets turned into a binary blob with a .dtb extension via the Devicetree compiler, dtc.  Here's how you can get it:






The .dtb file gets loaded by the bootloader and parsed by the kernel at boot.  The compiler has a variety of options I won't detail here, but I'll just mention briefly that the compiler can output to several different formats, and can disassemble binary .dtb blobs back into .dts source.

Devicetree Includes

It is oftentimes advantageous to represent a device tree with not one, monolithic, .dst source file, but with a collection of sub-.dst source files.  This allows building a top-level device tree made up of independent files describing the system subcomponents.  Like the C programming language preprocessor #include directive, Devicetree has a compiler directive called include, used in the format:

/include/ "FILE"

With this directive, the compiler will include sub-.dst files, named with the extension .dsti, into a main .dst file.

Devicetree Bindings

Devicetree bindings remind me of "schemas" as that concept is defined in XML or JSON.  A binding's job is to make sure, for any particular device, that a required set of properties/values for the device are supplied by a device tree.  For example, a serial device is an extremely well-known quantity—we've been doing serial ports, as an industry, for longer than most people in the industry have been alive.  Therefore, with all the knowledge we have about how serial ports work, Devicetree has a binding that specifies which properties are required or not required for serial ports.  This helps to ensure valid device trees that the kernel can understand.  There exists a plethora of bindings for a wide array of devices and buses.

Devicetree Overlays

An issue that arose was how best to support single board computers (SBCs) with configurable components.  It would be sub-optimal to have to create a collection of binary .dtb files to represent all possible peripheral configurations for the SBC and swap them out as necessary.  What was needed was to maintain one foundational binary blob with a way to modify the binary blob, at run-time, and from user-mode.  This is accomplished via a Devicetree overlay.  Overlays act like applying a "patch" to a device tree.  See Mr. Grant Likely's proposal here that helped to jumpstart the overlay feature.

Conclusion

My goal here was to give you a high-level description of where Devicetree came from and the problem it solves so that you can make a decision on whether or not to use it.  Here are some pointers to more information:
Did I miss anything obvious?  If so, please leave a comment, thanks!

Post a Comment

Be sure to select an account profile (e.g. Google, OpenID, etc.) before typing your comment!