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-v1/;					
						
/ {						
	compatible = "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

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 this14 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

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 Modulation15 (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.16 In the Raspberry Pi world, it is common to control the PWM peripheral from user code, but there is a perfectly functional kernel module17 too.

As with the LED example above, some configuration is needed though. 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 Technologies18 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:19

/*										
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:

Incidentally Jumpnow suggest that the clock shenanigans aren’t necessary with kernel 4.9, but I’ve not verified that.

Permissions, permissions

The overlay 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, and I tried, but failed, to write an analogous script for the PWM devices. I think the problem is that the PWM driver doesn’t generate the necessary udev events.

A crude workaround is to 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.

PWM LED control

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

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. Happily someone has already written an article showing how to do this: see ShiftPlusOne's post in the gpio_keys device tree overlay21 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 evtest22 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 evdev23 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 driver24 to do the decoding: we need only to configure it, for which the documentation25 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 evtest26 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 evdev27 bindings convenient.