Note: When I revisited this in June 2018, I found that I could accomplish most of my goals by simply configuring devicetree overlays included by default in Raspbian.

Introduction

The Raspberry Pi has a little LED which flashes when you access the SD card. The hardware for this is trivial: a LED connected to a GPIO pin. The software is more interesting though. For a start, there isn’t any code in the SD card block device driver which talks to the LED’s GPIO pin. Instead, an instance of the Linux LED device is created which acquires the GPIO pin and hooks into the block device using a well-defined API. A nice design which separates the code for driving the LED from the code for handling the SD card, then composes them to get the desired behaviour.

The code for all this is in the kernel, but we still need to specify details: for example which GPIO pin we are using. One approach is to write a trivial kernel module to instantiate the devices with hard-coded parameters: I did this when I built Seabass,1 a small Intel based computer. However there is a better way: devicetree.2

Devicetree provides a way for the kernel to load configuration data at an early stage of the boot process, which can then be used to bring up the rest of the system. As the name suggests devicetree creates a hierarchical tree of configuration data. Although the examples discussed here are all reasonably peripheral to the system’s operation, devicetree also handles core, system, data. Here’s a snippet showing some facet of interrupt handling:

interrupt-controller@7e00b200 {			
	reg = <0x7e00b200 0x200>;		
	compatible = "brcm,bcm2835-armctrl-ic";	
	#interrupt-cells = <0x2>;		
	phandle = <0x1>;			
	interrupt-controller;			
};

These days (early 2017) you can also load devicetree overlays dynamically, potentially letting us reconfigure a running system. However, not all devices support this: for instance, you can’t dynamically configure the LED subsystem.

Separating configuration and code makes it easier to use kernel modules without having to compile and maintain a local fork. Happily devicetree allows us to split the configuration data across multiple files so it’s easy to separate local changes from the standard system tree: we can patch different bits of the tree with overlay files.

Happily there’s good official support for all of this: both general documentation3 and a dedicated forum4 for specific questions.

sysfs

Devicetree is a good way to configure kernel devices, but how do we talk to them ? Happily, in just the same way we do without devicetree. For example, we can control the GPIO pins via devices in /sys/class/gpio.5 Accessing the GPIO pins this way has some advantages:

We could control a LED this way, but Linux gives us a more abstract route: if we tell the LED subsystem that there’s a LED connected to a particular GPIO pin, we can control it at a higher level. Specifically we can use devicetree to asssociate a LED device with a particular GPIO pin. Having done this, access to the pin via /sys/class/gpio disappears, but we now have a /sys/class/led8 device to play with instead:

$ cd /sys/class/leds/
$ ls
led0  led1

We can see that led0 indicates disc activity on mmc0:

$ cat led0/trigger
none kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock
kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock
kbd-ctrlllock kbd-ctrlrlock [mmc0] timer oneshot heartbeat backlight
gpio cpu0 default-on input rfkill0

Jumping forward slightly, here’s a snippet from the devicetree which accomplishes it all:

leds {							
	compatible = "gpio-leds";			
	phandle = <0x36>;				
							
	act {						
		gpios = <0xa 0x10 0x1>;			
		label = "led0";				
		linux,default-trigger = "mmc0";		
		phandle = <0x20>;			
	};						
							
};

Roughly:

I used the LED subsystem on seabass11 where the LED/GPIO map was embedded in a bespoke kernel module, but the sysfs API is the same.

Caveats

Many people used to writing embedded code on tiny devices would doubtless recoil in horror at the inefficiency involved in controlling a LED through so many levels of indirection. Such people do have a point: if you want to toggle the LED really quickly, this isn’t the way to go. On the other hand, if you’re writing that kind of code, perhaps user-mode Linux isn’t the right environment anyway.

On the other hand, most of the time it seems only sensible to put code so closely tied to the hardware on the kernel side of an API. If we can afford the inefficiencies, it is also good engineering to establish a clear divide between application code and hardware drivers.

A Devicetree cookbook

You can read proper documentation on the Raspberry Pi website12 but roughly speaking, the kernel consults files in /boot during boot. In particular it loads an appropriate devicetree blob from one of the /boot/*.dtb files, then consults /boot/config.txt for more information.

Typically it will then load devicetree overlays from /boot/overlays to satisy dtoverlay commands in /boot/config.txt. For example dtoverlay=foo will load /boot/overlays/foo.dtbo

Both the initial blob and the overlays are binary files, but happily you can compile them losslessly from text files with the dtc command (note the odd filename convention):

$ dtc -@ -I dts -O dtb -o foo.dtbo foo-overlay.dts

Having compiled the overlay, you’ll probably want to copy it into /boot then reboot to load it:

$ sudo cp foo.dtbo /boot/overlays/
$ shutdown -r now

The lossless nature of the compilation means we can disassemble a binary file too:

$ dtc -I dtb -O dts /boot/overlays/i2s-mmap.dtbo
Warning (unit_address_vs_reg): Node /fragment@0 has a unit name, but no reg property
/dts-compatible/;					
						
/ {						
	v1 = "brcm,bcm2708";		
						
	fragment@0 {				
		target = <0xdeadbeef>;		
						
		__overlay__ {			
			brcm,enable-mmap;	
		};				
	};					
						
	__fixups__ {				
		i2s = "/fragment@0:target:0";	
	};					
};						

Or even parse the devicetree of a running kernel:

$ dtc -I fs -O dts /proc/device-tree
...
/dts-v1/;

/ {
	model = "Raspberry Pi Model B Rev 2";
	compatible = "brcm,bcm2708";
	memreserve = <0x1c000000 0x4000000>;
...

Devicetree in practice (2018 edition)

When I started playing with devicetree in 2017, I did most things from first principles. Revisiting things in June 2018, I think most simple things can be done by simply using the overlays included by default in e.g. raspbian. To be precise here, the notes which follow are based on the 2018-06-27 release of Raspbian Stretch Lite.

The bottom line is that if you just want to use devicetree on the Raspberry Pi, begin by perusing /boot/overlays/README13 and see if your problem is covered.

LED devicetree

There is only minimal support for this, so I roll my own.

PWM devicetree

Rather than just switching a pin on or off, sometimes it’s helpful to wiggle it around so that it’s on for say 80% of the time. The proper name for this is Pulse-Width Modulation14 (PWM) and happily we have both hardware and software support for this on the Raspberry Pi.

On the hardware side the BCM2835 has a dedicated PWM peripheral, documented in chapter 9 of the datasheet.15 In the Raspberry Pi world, it is common to control the PWM peripheral from user code, but there is a perfectly functional kernel module16 too.

There are couple of overlays: one to enable a single PWM channel; the other to enable two of them for twice the fun.

The relevant incantations for /boot/config.txt are:

dtoverlay pwm
dtoverlay pwm-2chan

There is scope to use different pins, but the hardware has some constraints. Refer to the README for more details.

The overlays above will make working PWM devices in /sys/class/pwm but they will be owned by root. By contrast, gpio devices are mapped into the gpio group by a udev script. In the past (mid-2017) the kernel didn’t generate the necessary events for udev, but happily today (June 2018) it does!

Sadly though udev doesn’t have suitable rules. I added these to /etc/udev/rules.d/99-com.rules:

SUBSYSTEM=="pwm*", PROGRAM="/bin/sh -c '\				
        chown -R root:gpio /sys/class/pwm && chmod -R 770 /sys/class/pwm;\
        chown -R root:gpio /sys/devices/platform/soc/*.pwm/pwm/pwmchip* \
          && chmod -R 770 /sys/devices/platform/soc/*.pwm/pwm/pwmchip*\	
'"

An alternative is the rude workaround I used in the past: simply run a script to fix the permissions before running the client code, systemd has a hook for such tasks which maintains the split between system provided permissions and client code running under a normal user.

Having installed the pwm overlay, you can test it by connecting an LED between GPIO 18 and ground (via a suitable resistor) and then:

$ echo 0 > /sys/class/pwm/pwmchip0/export 		
$ echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period 	
$ echo  500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
$ echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable 		
$ echo  900000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
$ echo  100000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle

Button devicetree

Devicetree isn’t just for outputs. It is easy to connect a push button to a GPIO pin and sense it, but someone has to debounce the input so that each press generates but a single event. This is a fairly general problem, so unsurprisingly the kernel has support for it: we just need to configure it.

dtoverlay=gpio-key,gpio=25,label=MYBTN,keycode=0x101

The line above sets up a new button, which generates events when pressed:

$ evtest /dev/input/event1
Input driver version is 1.0.1
Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100
Input device name: "19.button"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 257 (BTN_1)
Properties:
Testing ... (interrupt to exit)
Event: time 1530301202.391360, type 1 (EV_KEY), code 257 (BTN_1), value 1
Event: time 1530301202.391360, ————-- SYN_REPORT ————
Event: time 1530301202.571365, type 1 (EV_KEY), code 257 (BTN_1), value 0
Event: time 1530301202.571365, ————-- SYN_REPORT ————

It isn’t clear to me what the label argument does, nor if it’s possible to give the device a nicer path. The latter can be done if you write your own overlay.

To write applications, I found the python evdev17 bindings convenient.

Rotary encoder devicetree

Rotary encoders are another popular input device: they generate a pair of quadrature pulse-trains which can be decoded to tell us how much the encoder has been turned. To instantiate the device add this to /boot/config.txt:

dtoverlay=rotary-encoder,pin_a=7,pin_b=8,relative_axis=1

Then watch the events as the knob gets turned:

$ evtest /dev/input/event0
Input driver version is 1.0.1
Input device ID: bus 0x19 vendor 0x0 product 0x0 version 0x0
Input device name: "7.rotary"
Supported events:
  Event type 0 (EV_SYN)
  Event type 2 (EV_REL)
    Event code 0 (REL_X)
Properties:
Testing ... (interrupt to exit)
Event: time 1530301590.130047, type 2 (EV_REL), code 0 (REL_X), value 1
Event: time 1530301590.130047, ————-- SYN_REPORT ————
Event: time 1530301590.679675, type 2 (EV_REL), code 0 (REL_X), value 1
Event: time 1530301590.679675, ————-- SYN_REPORT ————
Event: time 1530301591.295263, type 2 (EV_REL), code 0 (REL_X), value 1
Event: time 1530301591.295263, ————-- SYN_REPORT ————
Event: time 1530301592.056198, type 2 (EV_REL), code 0 (REL_X), value -1
Event: time 1530301592.056198, ————-- SYN_REPORT ————
Event: time 1530301592.578682, type 2 (EV_REL), code 0 (REL_X), value -1
Event: time 1530301592.578682, ————-- SYN_REPORT ————
Event: time 1530301593.355554, type 2 (EV_REL), code 0 (REL_X), value 1
Event: time 1530301593.355554, ————-- SYN_REPORT ————
Event: time 1530301593.822017, type 2 (EV_REL), code 0 (REL_X), value 1
Event: time 1530301593.822017, ————-- SYN_REPORT ————

To write applications, I found the python evdev18 bindings convenient.

1-Wire devicetree

Maxim Integrated make nice little digital thermometers which sit on a 1-wire bus, of these, the DS18S2019 is reasonably common.

Happily there’s an overlay to bit-bang a 1-wire interface:

dtoverlay=w1-gpio,gpiopin=4

Then you can attach a bunch of sensors and read the temperatures (in milliCelsius):

$ cat /sys/bus/w1/devices/*/w1_slave | fgrep t=
a9 01 4b 46 7f ff 07 10 85 t=26562
a0 01 4b 46 7f ff 10 10 6e t=26000
a4 01 4b 46 7f ff 0c 10 da t=26250
9f 01 4b 46 7f ff 01 10 40 t=25937

Devicetree from first principles

These days (June 2018) you might find that you don’t need to do these anymore: see the section above. I’ve left the notes below not because I recommend them now, but because they might be useful. Note also that the PWM system has improved somewhat over the last year.

There is considerable overlap and duplication with the previous section: apologies for that.

Happily, enough people use the Raspberry Pi that not only are the general principles well tested and documented, but you can also often find someone who has implemented something close to the specific thing you want.

LED devicetree

Above we saw a snippet from the devicetree which handles the disc activity LED. Here’s the full source of an overlay for a system heartbeat display:

/dts-v1/;						
/plugin/;						
							
/ {							
  compatible = "brcm,bcm2835", "brcm,bcm2708";		
							
        fragment@0 {					
          target = <&leds>;				
          __overlay__ {					
            hb_led: led {				
              label = "led1";				
              linux,default-trigger = "heartbeat";	
              gpios = <&gpio 17 0>;			
            };						
          };						
        };						
							
};

As you can guess this LED:

The &gpio is a reference to the Pi’s main GPIO controller. In principle we could add other GPIO controllers, e.g. via SPI, create a GPIO controller node for them with a devicetree overlay, then put the heartbeat LED on the new GPIO controller by using a different reference.

Expansion HATs should implement this21 transparently, by including a suitable devicetree overlay in an EEPROM. I’ve not played with this though.

The remaining difference between this overlay and the snippet above is that the overlay has to say where it fits in the existing devicetree. That’s done by target=<&leds> line: another symbolic reference.

More complicated overlays might want to modify different parts of the devicetree: happily they can do that by including more than one fragment.

PWM devicetree

The PWM subsystem is reasonably complicated. Not only do we need a GPIO pin on which to generate the signal, but we also need to configure the clock going into the PWM hardware.

Happily people have already worked all this out and written it up. I found an article by Jumpnow Technologies22 helpful, but that refers to other, earlier, work.

In case of link rot, here’s the code I cribbed, though you’re probably better off getting it from github:23

/*										
Legal pin,function combinations for each channel:				
  PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0)            52,5(Alt1)			
  PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1)			
										
N.B.:										
  1) Pin 18 is the only one available on all platforms, and			
     it is the one used by the I2S audio interface.				
     Pins 12 and 13 might be better choices on an A+, B+ or Pi2.		
  2) The onboard analogue audio output uses both PWM channels.			
  3) So be careful mixing audio and PWM.					
*/										
										
/dts-v1/;									
/plugin/;									
										
/ {										
        compatible = "brcm,bcm2835", "brcm,bcm2708";				
										
        fragment@0 {								
                target = <&gpio>;						
                __overlay__ {							
                        pwm_pins: pwm_pins {					
                                brcm,pins = <18>;				
                                brcm,function = <2>; /* Alt5 */			
                        };							
                };								
        };									
										
        fragment@1 {								
                target = <&clk_pwm>;						
                __overlay__ {							
                        // Rename the fixed "pwm" clock to avoid a clash	
                        clock-output-names = "fake_pwm";			
                };								
        };									
										
        fragment@2 {								
                target = <&pwm>;						
                __overlay__ {							
                        #clock-cells = <1>;					
                        clocks = <&cprman 30>; /* 30 is the BCM2835_CLOCK_PWM */
                        assigned-clocks = <&cprman 30>;				
                        assigned-clock-rates = <10000000>;			
                        pinctrl-names = "default";				
                        pinctrl-0 = <&pwm_pins>;				
                        status = "okay";					
                };								
        };									
										
        fragment@3 {								
                target = <&cprman>;						
                __overlay__ {							
                        status = "okay";					
                };								
        };									
										
        __overrides__ {								
                pin   = <&pwm_pins>,"brcm,pins:0";				
                func  = <&pwm_pins>,"brcm,function:0";				
        };									
};

This is clearly much more complicated than the LED example above, and it includes four fragments each patching a different part of the devicetree.

Ignoring the implementation details, three things are important:

As of 2018, the clock shenanigans above are no longer necessary. In practice I just use the standard PWM overlays though, so I’ve not revised this.

You’ll still have to worry about the permissions/owner of the sysfs device. See above for a discussion.

PWM LED control

In principle we could create a Linux LED device with a PWM backend. The code is in the kernel tree24 but the module isn’t compiled in the current version of Raspbian. A fun future project.

Button devicetree

Happily someone has already written an article showing how to do this: see ShiftPlusOne's post in the gpio_keys device tree overlay25 thread.

I reduced his example to a single-button which generates keycode 256 when a button connected to GPIO 25 is pressed.

/dts-v1/;							
/plugin/;							
								
/ {								
   compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709";	
   								
    fragment@0 {						
        target-path = "/soc/gpio";				
        __overlay__ {						
            butt_pins: butt_pins {				
                brcm,pins = <25>;				
                brcm,function = <0>;				
                brcm,pull = <2>;				
            };							
        };							
    };								
								
   fragment@1 {							
      target-path = "/soc";					
      __overlay__ {						
         keypad: keypad {					
            compatible = "gpio-keys";				
            #address-cells = <1>;				
            #size-cells = <0>;					
            pinctrl-names = "default";				
            pinctrl-0 = <&butt_pins>;				
            button@13 {						
               label = "Test BTN0";				
               linux,code = <0x100>;				
               gpios = <&gpio 25 1>;				
            };							
         };							
      };							
   };								
};

We need a couple of fragments here: the first configures the GPIO pin; the second interprets the input as a keypad. The latter refers to the former with the &butt_pins reference.

Note that when we configure our GPIO pin, we use the brcm,pull line to configure the pin’s internal pull-up resistor, which saves installing one on the board.

Watching the events

Having loaded the overlay, an input device appears at /dev/input/by-path/platform-soc\:keypad-event. It’s convenient to view the events with the evtest26 package.

$ sudo evtest /dev/input/by-path/platform-soc\:keypad-event 		
Input driver version is 1.0.1						
Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100		
Input device name: "soc:keypad"						
Supported events:							
  Event type 0 (EV_SYN)							
  Event type 1 (EV_KEY)							
    Event code 256 (BTN_0)						
Properties:								
Testing ... (interrupt to exit)						
Event: time 1493118156.530675, type 1 (EV_KEY), code 256 (BTN_0), value 1
Event: time 1493118156.530675, ————-- EV_SYN ————	
Event: time 1493118156.700677, type 1 (EV_KEY), code 256 (BTN_0), value 0
Event: time 1493118156.700677, ————-- EV_SYN ————	
Event: time 1493118157.470660, type 1 (EV_KEY), code 256 (BTN_0), value 1
Event: time 1493118157.470660, ————-- EV_SYN ————	

To write applications, I found the python evdev27 bindings convenient.

Rotary encoder devicetree

Rotary encoders are another popular input device: they generate a pair of quadrature pulse-trains which can be decoded to tell us how much the encoder has been turned. The kernel has a driver28 to do the decoding: we need only to configure it, for which the documentation29 is helpful.

Here’s the overlay file I used:

/dts-v1/;								
/plugin/;								
									
/ {									
    compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709";	
									
    fragment@0 {							
        target-path = "/soc/gpio";					
        __overlay__ {							
            knob_pins: knob_pins {					
                brcm,pins = <7 8>;					
                brcm,function = <0>;					
                brcm,pull = <2>;					
            };								
        };								
    };									
									
    fragment@1 {							
        target-path = "/soc";						
        __overlay__ {							
            knob: knob {						
                compatible = "rotary-encoder";				
                #address-cells = <1>;					
                #size-cells = <0>;					
                pinctrl-names = "default";				
                pinctrl-0 = <&knob_pins>;				
                gpios = <&gpio 7 1>, <&gpio 8 1>; 			
                linux,axis = <0>; /* REL_X */				
                rotary-encoder,relative-axis;				
            };								
        };								
    };									
    __overrides__ {							
        relative_axis =  <&knob>,"rotary-encoder,relative-axis";	
        linux_axis =  <&knob>,"linux,axis";				
        rollover =  <&knob>,"rotary-encoder,rollover";			
        half-period =  <&knob>,"rotary-encoder,half-period";		
        steps =  <&knob>,"rotary-encoder,steps";			
    };									
};

You can see that it’s similar the keypad example above, but uses a couple of GPIO lines: 7 and 8.

There’s also a new overrides section which lets us change the configuration of the device by adding dt_param options to /boot/config.txt

Watching the events

Having loaded the overlay, an input device appears at /dev/input/by-path/platform-soc\:knob-event. It’s convenient to view the events with the evtest30 package.

$ sudo evtest /dev/input/by-path/platform-soc\:knob-event 		
Input driver version is 1.0.1						
Input device ID: bus 0x19 vendor 0x0 product 0x0 version 0x0		
Input device name: "soc:knob"						
Supported events:							
  Event type 0 (EV_SYN)							
  Event type 2 (EV_REL)							
    Event code 0 (REL_X)						
Properties:								
Testing ... (interrupt to exit)						
Event: time 1493118601.236576, type 2 (EV_REL), code 0 (REL_X), value -1
Event: time 1493118601.236576, ————-- EV_SYN ————	
Event: time 1493118601.335016, type 2 (EV_REL), code 0 (REL_X), value -1
Event: time 1493118601.335016, ————-- EV_SYN ————	
Event: time 1493118601.393779, type 2 (EV_REL), code 0 (REL_X), value -1
Event: time 1493118601.393779, ————-- EV_SYN ————	
Event: time 1493118601.456277, type 2 (EV_REL), code 0 (REL_X), value -1
Event: time 1493118601.456277, ————-- EV_SYN ————	
Event: time 1493118602.207050, type 2 (EV_REL), code 0 (REL_X), value 1	
Event: time 1493118602.207050, ————-- EV_SYN ————	
Event: time 1493118602.259268, type 2 (EV_REL), code 0 (REL_X), value 1	
Event: time 1493118602.259268, ————-- EV_SYN ————	

To write applications, I found the python evdev31 bindings convenient.