A database approach to describing device hardware - Embedded.com

A database approach to describing device hardware

When moving an operating system to a new platform, one of the major headaches is describing the hardware. Where are the peripheral registers located? What interrupts are the devices connected to? How are devices interconnected? Historically, these issues have been addressed by modifying the device driver code. The result is a unique kernel image for each platform.

Suppose we could describe the hardware in a database that could be passed to the kernel at boot time? The kernel and its driver code are now generic and can be easily adapted to new platforms. Device trees are a database mechanism for describing a system’s hardware independent of the operating system and its device drivers. Hardware is described in plain text and translated to a binary “blob” by the Device Tree Compiler.

Introducing the problem
One of the biggest problems with porting an operating system such as Linux to a new platform is describing the hardware. That’s because the hardware description is scattered over a dozen or so device drivers, the kernel, and the boot loader just to name a few. The ultimate result is that these various pieces of software become unique to each platform and the number of configuration options grows.

There have been a number of approaches to addressing this problem. The notion of a “board support package” or BSP attempts to gather all of the hardware dependent code in a few files in one place. It could be argued that the entire arch/ subtree of the Linux kernel source tree is a gigantic board support package.

Take a look at the arch/arm/ subtree. In there you’ll find a large number of directories of the form mach-* and plat-* , presumably short for “machine” and “platform” respectively. Most of the files in these directories provide configuration information for a specific implementation of the ARM architecture. And of course, each implementation describes its configuration differently.

Wouldn’t it be nice to have a single language that could be used to unambiguously describe the hardware of a computer system? That’s the premise, and promise, of device trees.

Background
The device tree concept evolved in the Open Firmware project that became IEEE 1275. The IEEE subsequently withdrew support for the standard and the project ultimately morphed in UEFI. Nevertheless, Open Firmware became the de facto method of booting Power PCs, including Apple MacIntoshes.

When the 32-bit and 64-bit Power PC arch/ trees in the Linux kernel were merged, a decision was made to require device trees as the only mechanism for describing hardware. The idea has since spread to several other architectures supported by Linux including ARM.

Platform devices vs. “discoverable” devices
The peripheral devices in a system can be characterized along a number of dimensions. There are, for example, character vs. block devices. There are memory mapped devices and those that connect through an external bus such as I2C or USB. Then there are platform devices and discoverable devices.

Discoverable devices are those that live on external busses such as PCI and USB that can tell the system what they are and how they are configured. That is, they can be “discovered” by the kernel. Having identified a device, it’s a fairly simple matter to load the corresponding driver, which then interrogates the device to determine its precise configuration.

Platform devices, on the other hand, lack any mechanism to identify themselves. SoC (System on Chip) implementations are rife with these platform devices—system clocks, interrupt controllers, GPIO, serial ports, to name a few. The device tree mechanism is particularly useful for managing platform devices.

Basics of a device tree
A device tree is a hierarchical data structure that describes the collection of devices and interconnecting busses of a computer system. It is organized as nodes that begin at a root represented by “/”. Every node has a name and consists of “properties” that are name-value pairs. It may also contain “child” nodes.

Figure 1 is a sample device tree taken from the devicetree.org website. It doesn’t do anything beyond illustrating the structure. Here we have two nodes named node1 and node2 . node1 has two child nodes and node2 has one child. Properties are represented by name=value. Values can be strings, lists of strings, one or more numeric values enclosed by square brackets, or one or more “cells” enclosed in angle brackets. The value can also be empty if the property conveys a Boolean value by its presence or absence.

Figure 1: Sample Device Tree

Figure 2 shows how elements of a real machine are represented inthe device tree. Let’s start with the compatible property, which everynode is required to have. The value of compatible is a list of strings.The first string exactly identifies the device by both manufacturer andmodel name. Any subsequent strings identify what the node might be“compatible” with. For example, many serial ports implement the NationalSemiconductor register interface and could be specified as compatiblewith the “ns16550”.

#address-cells and #size-cells say how many 32-bit cells are required to specify a node’s starting address and range respectively. interrupt_parent points to a node that identifies the interrupt controller on this system.

This brings us to the first child node named cpus . In here we find two child nodes, each describing a CPU core. Note the naming. Most nodes have a name of the form @ where is specified by the reg property and represents the beginning of thenode’s addressable range, whatever that is. CPUs don’t have a range, so #size_cells is set to zero and we simply identify the CPUs by an index number. In this case, the system has two Cortex-A9 cores.

Figure 2: A “real” device tree

Next up is the description of a serial port at address 0x101f0000. The reg property tells us it uses 0x1000 bytes. The reg property requires at least #address_cells plus #size_cells cells. Multiple ranges are specified by adding more cells.

This node also indicates that this device uses an interrupt at <2 0> .We don’t necessarily know what that means until we encounter thedescription of the interrupt controller and see that the definition ofan interrupt requires two cells.

The compatible property is whatmakes the connection between an entry in the device tree and a devicedriver that can manage the device. Most contemporary device driversannounce what they are capable of by registering some sort of “devicetable”. For the serial device in particular, the driver would have codelike this:

   struct of_device_id my_of_match[] {
      {.compatible = “arm,pl011”}
   }

   MODULE_DEVICE_TABLE (of, my_of_match);

The MODULE_DEVICE_TABLE() macro makes the id table externally visible so the kernel can scan it to learn that this driver handles the “arm,pl011” device.

Theinterrupt controller node has a label that allows us to reference itfrom elsewhere in the tree. It has a Boolean property,interrupt-controller, that says this device receives interrupts. Notethat it takes two cells to specify an interrupt. The interrupts propertyof other nodes then specifies which interrupt the device is attachedto.

Some devices aren’t memory mapped but are instead connectedto some external “bus”. In this example it requires two cells to specifythe address, in this case a chip select and an offset from the base ofthe chip select. The size is still one cell.

One of the devicesattached to the bus is an I2C controller. The children of this node areI2C devices, which don’t have a size, only a device number.

Special nodes
Thereare a couple of “special” nodes that perform functions other thandescribing hardware. The aliases node provides a way to represent thefull path to a node by a shorter string as shown here:

   aliases {
      rtc = “/external_bus/i2c@1,0/rtc@58”;
   };

The chosen node provides a way to pass environment data to the kernel. It strikes me as an odd name, but there it is. Here’s an example:

   chosen {
      bootargs = “rootfs=/dev/nfs rw nfsroot=192.168.1.50
      console=ttySAC0,115200”;
   };

The Flattened Device Tree and the Device Tree Compiler
Theoriginal Open Firmware specification uses the text-based device treedirectly. That’s fine in desktops and servers, but could becomeproblematic in more resource-constrained embedded systems. That led tothe development of the flattened device tree , a more compact, binary representation. This is what Linux operates on.

TheDevice Tree Compiler, or DTC, converts the textual representation tothe flattened device tree, called a device tree “blob”. The DTC is partof the kernel source tree and may be invoked by an appropriate makecommand. For boardexample, the command:

   make .dtb

will find the source file .dts in arch//boot/dts and run it through the DTC to create .dtb . Note that this only works if you’ve built the kernel with device tree support.

Once the DTB has been read by the kernel, there is a rich set of kernel APIs for accessing nodes and properties.

Doug Abbott is a firmware/software engineer and the principal of Intellimetrix ,which specializes in hardware and software for industrial andscientific data acquisition and embedded product applications. He’s alsoa popular instructor and seminar leader, teaching classes on Linux andreal-time programming in general. Doug has taught the techniques ofembedded programming and multi-tasking operating systems to hundreds ofprofessional engineers. This paper was part of a class he taught at theEmbedded Systems Conference (ESC-331 ) at 2012 DESIGN West.

References
1. devicetree.org – home page for the device tree project. Among other things, this sitedocuments bindings that aren’t covered by the Linux kernel or the ePAPR(see next reference). There’s also a good tutorial on device tree usage.

2.Standard for Embedded Power Architecture Platform Requirements ePAPR –available at www.power.org. Probably the most complete definition of thedevice tree.

3. Device Trees Everywhere , David Gibson, Benjamin Herrenschmidt.

4. A Symphony of Flavours: Using the device tree to describe embedded hardware , Grant Likely, Josh Boyer.

5. A Tutorial on the Device Tree (Zynq)   – Interesting 5-part tutorial on device trees.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.