The linux kernel is responsible for managing all the hardware. Which brings us to the question: how does it know what hardware there is to manage? On traditional PC platforms, the kernel can read out a lot of information from the BIOS. But on embedded devices such as the BeagleBone (Black), there is no BIOS to read from.

The answer is that you simply tell the kernel what hardware is present. The way you do that, is by providing a device tree during boot. This tree lists all the hardware that is present on the system, along with the needed parameters.

For example:

memory {
  device_type = "memory";
  reg = <0x80000000 0x10000000>;
};

This tells the kernel that it has 256MB (0x10000000 bytes) of memory, mapped from 0x80000000 onwards. The device tree has such an entry for every hardware part. These nodes are organised in a tree, so that the mailbox for PRU0 is located under the mailboxes node, so it can reuse lots of parameters. Update: I’ve found a really good explanation of the device tree for the Raspberry Pi [local copy]. The device tree is exactly the same, but the way to load overlays is different.

The kernel needs a flattened version of this device tree to boot, aptly called the flattened device tree (fdt). The boot-loader should provide this when booting a kernel (much like the initramdisk). The device tree compiler can be used to compile device trees in to flattened device trees (and the other way around, but you lose some symbolic references in that direction).

Overlays

The device tree is only read at boot, which means it’s not possible to dynamically add/change hardware after boot. To solve this problem, BeagleBone has developed the device tree overlay, a changeset that can be applied (and un-applied!) to the device tree. BeagleBone needed this to support their Capes, hence the name cape manager.

The device tree also checks for exclusive usage of hardware resources. It makes sure that you can’t allocate a single PIN to 2 different functions at once.

This device tree fragment will enable Pin 8-11 as a Dallas 1-wire master:

/dts-v1/;
/plugin/;

/ {
  compatible = "ti,beaglebone", "ti,beaglebone-black";

  part-number = "DS18B20-IO";

  exclusive-use =
    "P8.11", "gpio1_13";

  fragment@0 {
    target = <&am33xx_pinmux>;
    __overlay__ {
      ds18b20_pinmux: pinmux_ds18b20 {
        pinctrl-single,pins = < 0x034 0x37 // P8_11 (GPIO1_13): mode 7 (gpio), pull up, Rx enabled >;
      };
    };
  };

  fragment@1 {
    target = <&ocp>;
    __overlay__ {
      onwire@0 {
        compatible = "w1-gpio";
        pinctrl-names = "default";
        pinctrl-0 = <&ds18b20_pinmux>;
        status = "okay";

        gpios = <&gpio1 13 0>
      };
    };
  };
};

PRU

In order to use GPIO pins from the PRU, you need to load a device tree fragment, at least to let the kernel know that you intend to use hardware components. And while you’re at it, you can ask the kernel to configure them for you as well. I needed 4 GPIO pins for my PRU logic. They were located on GPIO2, which is otherwise unused. Without the device tree, the whole GPIO-hardware was put in shutdown by the kernel, which took me a while to figure out. The following device tree overlay enables these parts.

/dts-v1/;
/plugin/;

/ {
  compatible = "ti,beaglebone", "ti,beaglebone-black";

  part-number = "DHT22-PRU-IO";  // Arbitrary name used for reference

  exclusive-use =
    "P8.7", "gpio2_2",
    "P8.8", "gpio2_3",
    "P8.9", "gpio2_5",
    "P8.10", "gpio2_4";

  fragment@0 {
    target = <&am33xx_pinmux>;
    __overlay__ {
      dht22_pru_pinmux: pinmux_dht22_pru {
        pinctrl-single,pins = <
          0x090 0x37 // P8_07 (TIMER4): mode 7 (gpio), pull up, Rx enabled
          0x094 0x37 // P8_08 (TIMER7): mode 7 (gpio), pull up, Rx enabled
          0x09c 0x37 // P8_09 (TIMER5): mode 7 (gpio), pull up, Rx enabled
          0x098 0x37 // P8_10 (TIMER6): mode 7 (gpio), pull up, Rx enabled
        >;
      };
    };
  };

  fragment@1 {
    target = <&ocp>;
    __overlay__ {
      dht22_pru_helper {
        compatible = "gpio-of-helper";
        pinctrl-names = "default";
        pinctrl-0 = <&dht22_pru_pinmux>;
        status = "okay";

        P8_07 {
          gpio-name = "P8_07";
          gpio = <&gpio2 2 0>;
          input;
          dir-changeable;
        };
        P8_08 {
          gpio-name = "P8_08";
          gpio = <&gpio2 3 0>;
          input;
          dir-changeable;
        };
        P8_09 {
          gpio-name = "P8_09";
          gpio = <&gpio2 5 0>;
          input;
          dir-changeable;
        };
        P8_10 {
          gpio-name = "P8_10";
          gpio = <&gpio2 4 0>;
          input;
          dir-changeable;
        };
      };
    };
  };
};

Next step is to compile this overlay, and apply it to the device tree:

dtc -O dtb -o DHT22-PRU-IO-00A0.dtbo -b 0 -@ overlay.dts
cp DHT22-PRU-IO-00A0.dtbo /lib/firmware/.
echo "DHT22-PRU-IO" > /sys/devices/platform/bone_capemgr/slots
cat /sys/devices/platform/bone_capemgr/slots

You can unload the overlay by echo’ing -$slotnumber to the slots file.