As a standard feature of the Linux kernel, device tree overlays (DTOs) allow for easy enabling and configuration of features and drivers, such as those contained within the standard firmware of a Raspberry Pi system. Using these DTOs it’s trivial to set up features like as a soft power-off button, triggering an external power supply and enable drivers for everything from an external real-time clock (RTC) to various displays, sensors and audio devices, all without modifying the operating system or using custom scripts.
It’s also possible to add your own DTOs to create a custom overlay that combines multiple DTO commands into a single one, or create a custom device tree binary (DTB) for the target hardware. Essentially this DTB is loaded by the Linux kernel on boot to let it know which devices are connected and their configuration settings, very similar to what the BIOS component with x86-based architectures handles automatically.
Ultimately, the DTB concept and the use of overlays allow for easy configuration of such optional devices and GPIO pin settings, especially when made configurable through a simple text file as on the Raspberry Pi SBC platform.
The Linux Device Tree
As noted earlier, the device tree is compiled into a binary that is handed to the kernel on boot. Contained in this device tree is the list of connected devices, their driver and other relevant settings. On platforms that do not use a BIOS-like system to auto-detect devices, like graphics cards and network cards, and for devices which do not have an auto-detect and auto-configuration option like I2C and SPI devices, the device tree has to be constructed in this manner.
The use of such an external device tree saves the trouble of recompiling the kernel with every hardware change, with a central DTB file for the system device configuration, like those provided for Raspberry Pi SBCs. Recompiling this DTB for any newly added or changed device would be as much hassle as recompiling the entire kernel. This is where overlays come into play.
After the DTB is loaded by the kernel, DTOs are applied, per the Linux kernel documentation on DTOs. The overlay itself is specified as a device tree source, compiled using the device tree compiler into a device tree binary overlay (DTBO) file. For the Raspberry Pi these DTBOs can be found just like the DTBs on the Raspberry Pi GitHub account.
Over on the Bootlin blog an excellent article explains how to write your own overlays and apply them using the BeagleBone Black platform. This shows how to load DTBOs from the U-Boot bootloader, which is a somewhat more involved process than that offered by other platforms.
With Armbian for example, DTOs can be set and changed from the
armbianEnv.txt file, assuming one uses an image for a platform that supports this feature. Assuming it is a supported feature on the used Armbian image, all it takes is editing the
/boot/armbianEnv.txt to add the requisite overlay names and their parameters. This approach is highly reminiscent of the approach chosen on the Raspberry Pi SBC platform, with a similar text file loaded from
Soft Power Button
As a simple but rather useful example of what DTOs on Raspberry Pi SBCs can accomplish, let’s take a look at how to implement a soft power on and off button using nothing but a default Raspberry Pi OS image, a momentary (normally open) switch and some wiring to connect this switch to two pins on the GPIO header, as in the image on the right.
To make this work, there are two components at play here. The first concerns the soft power on. On Raspberry Pi boards, when the CPU is in halt state (powered, but CPU not running), GPIO3 is kept in high state due to an external pull-up resistor. Whenever GPIO3 is pulled low in this state, the CPU is resumed.
This is accomplished by putting our NO switch on GPIO3 and a ground (GND) pin. When the system enters the halt state and we push the button, GPIO3 is pulled low and the system resumes.
For soft power-off, we need to use the first overlay. Since we’ll be using GPIO3 also for powering off the system, we will add the following device tree overlay (dtoverlay) to
gpio-shutdown DTO is described in the overlay README:
Name: gpio-shutdown Info: Initiates a shutdown when GPIO pin changes. The given GPIO pin is configured as an input key that generates KEY_POWER events. This event is handled by systemd-logind by initiating a shutdown.
If the system is booted when we trigger this event, it will thus act as if we pushed the ‘power’ button on a desktop system, turning the system off and halting the CPU.
Beyond the GPIO pin number, we can also configure the state of the pin when it should trigger the event (here when pulled low), and since we are using a mechanical switch we’d like to have a built-in debounce delay before the event is triggered.
Of course, since we can configure the GPIO pin here, we don’t necessarily have to use GPIO3 here, which may be desirable since GPIO3 is also the (non-remappable) I2C1 SCL pin, and losing access to the primary I2C bus could be a problem. Instead another GPIO pin (like 17) could be used, with the complication that using a single momentary switch as in the previous example would no longer be possible without jumping through some extra hoops.
Another interesting power-related overlay is the
Name: gpio-poweroff Info: Drives a GPIO high or low on poweroff (including halt). Using this overlay interferes with the normal power-down sequence, preventing the kernel from resetting the SoC (a necessary step in a normal power-off or reboot). This also disables the ability to trigger a boot by driving GPIO3 low.
When using an external power supply module, this signal is used to turn off power to the SBC, and likely shutdown the power supply itself until woken up again by some other signal. This could conceivably be useful in industrial and remote locations where some level of automation and/or power savings would be desirable.
Real Time Clock
A sorely missed feature on Raspberry Pi SBCs is a real-time clock (RTC), as this means that without internet access, the system time will be practically meaningless. Fortunately it’s easy enough to install any of a number of RTC modules, such as the ubiquitous PCF8253, DS1307, DS3231 and higher-end options.
Most of these RTC modules communicate using the I2C bus, which means wiring up 3.3V, GND and the I2C SCL/SDA lines. Note that we’d already have a potential conflict here if using the soft power-off feature, since by default it is assumed that we’re using the primary (
i2c_arm) I2C bus. After this we have to enable the appropriate overlay for the module we’re using.
According to the overlay documentation:
Info: Adds support for a number of I2C Real Time Clock devices
With as fallback the software I2C option if we cannot use the primary I2C bus:
Name: i2c-rtc-gpio Info: Adds support for a number of I2C Real Time Clock devices using the software i2c controller Load: dtoverlay=i2c-rtc-gpio,<param>=<val>
With all of this in place, we only need to deal with the fake hardware clock (fake-hwclock) that is used by default, as else we’ll have to manually set the (fake) hwclock time from the RTC. For example on Debian-based OSes like Raspberry Pi OS:
sudo apt-get -y remove fake-hwclock sudo update-rc.d -f fake-hwclock sudo remove systemctl disable fake-hwclock
Just The Beginning
Some may have noticed at this point that this procedure with overlays looks familiar if they have ever installed something like an I2S sound board on a Raspberry Pi system. The reason for this is that DTBOs for these devices are already present and can thus be loaded on boot without further modifications needed.
While a lot of this functionality can be duplicated by various shell- and python-scripts within the OS itself, it’s generally more straightforward to do it in the form of a device tree overlay, if only because everything that is required is already part of the default operating system image. This means that it is also guaranteed to keep working, even across Linux kernel and package upgrades.
A simple glance at the DTBO files that comes with a Raspberry Pi OS image or similar gives a good idea of just how many overlays there are. Everything from DACs, rotary encoders, various (LCD, OLED) screens, official Raspberry Pi devices like the PoE HAT, soundcards and countless GPIO-, SPI- and I2C-related features and devices.
With this in mind, it seems like a good idea to have a read through the overlays README to get some idea of what is supported, and to reference it before embarking on a new project on a Raspberry Pi SBC. Similarly, it can’t hurt to check the available overlays on other (ARM) platforms. For all you know, the feature you would like to have is a simple overlay toggle away.