TESTCape. Understanding the Device Tree (PART I)


In our NULLCape tutorial (LINK) we explored the basics of BeagleBone cape development. Now it is time to go into the real stuff, and for that, we need to understand how the Device Tree infrastructure works. Here begins the development of the TESTCape and our journey into this black magic stuff!

DEVELOPMENT ENVIRONMENT

Several readers of the previous tutorial wrote us and also added comments regarding different problems when trying to make their own NULLCapes work. This time we will start defining a development and run-time environment to try to avoid diversions whenever our readers try to follow this new tutorial.

So, for this tutorial we had chosen Robert Nelson's awesome tutorial on how to setup a complete development environment and working system for a BeagleBone.

http://eewiki.net/display/linuxonarm/BeagleBone+Black

The page above provides precise instructions on how to compile everything from scratch and build a bootable SD-Card containing a minimal Debian image. The instructions "just works" and all the scripts developed by Mr Nelson are just great taking into account most of the problems you might have.

RUN-TIME ENVIRONMENT

So, basically or run-time platform will be an SD-CARD running the minimal Debian 7 image you can found here:

http://eewiki.net/display/linuxonarm/BeagleBone+Black#BeagleBoneBlack-De...

We had applied the modifications suggested in the page and then just installed a couple of additional packages. However there are a couple of recommendations.

  • It is highly recommended to get access to the default serial port on the Beaglebone. If something goes wrong, specially at the beginning this is the best way to find out what is going on.
  • If you are not using the network do not follow the steps to configure it. If you do so, the DHCP client will block the boot
    process for a long time With this configuration our NULLCape works flawless. Just copy the firmware (.dtbo) file in the /lib/firmware and boot the BeagleBone from the SD-Card (keep the Boot switch close to the SD-Card socket pressed while powering it up) and the Cape will work as expected.

DEVICE TREE

OK, now it is time to start our voyage through the full understanding of the device tree stuff. There are plenty of good explanation on the web and we are not going to repeat them here. You can check this two links to get to know the basics.

http://elinux.org/Device_Tree
http://learn.adafruit.com/introduction-to-the-beaglebone-black-device-tr...

There are good resources in there and the reading is highly recommended. But, to be honest, I had read those documents and I've got a general idea of what it is all about but I had no clue on how to write my own Device Tree Overlay after reading them and that is the reason I'm writing this.

We will not start with in-depth explanations (at least not too deep). We will just target (at the beginning) how to write those .dts files you need to make your cape work. So, we will just start analysis the default Device Tree overlays for our BeagleBones. Try to derive a structure for them and the make sense of that structure.

Several of those overlays can be found here:

https://github.com/beagleboard/cape-firmware/tree/master/dts

GENERAL STRUCTURE OF A DEVICE TREE OVERLAY

After browsing through most of the files in the repository above, the following generic structure naturally arise:

/dts-v1/;
/plugin/;

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

        /* identification */
        part-number = "BB-UART2";
        version = "00A0";

	 exclusive-use = ...

	 fragment@0 ....
	 fragment@N ....
};
In other words. A device tree overlay is composed of the following items:
  • A generic header (fixed)
  • A compatible attribute that, for now, would be fixed for us. It will always refer to the beaglebone
  • A part-number and a version. In principle these values shall be the same we flash in our Cape EEPROM. We will come back on this later but for now that is the safest choice.
  • A list of the pins and hardware devices this overlay refers to. Next section will explain this in detail
  • A list of fragment. Those are the real configuration of our hardware.
The elements above seems to be common to most of the BeagleBone Cape device tree overlays. There is quite some flexibility on how all these elements can be defined but, for this initial part of this tutorial, we will assume a fix structure so it would be easier (we hope) to better understand how they work. Now we will go in detail for each of the ellipsis in our generic structure above.

THE EXCLUSIVE-USE SECTION

The first thing we need to define in our overlay is the list of pins our cape will use. At this point you probably know that each of the pins in the BeagleBone headers may have different functions. You should also know that you can stack up to four capes in a single BeagleBone. Therefore, we need to make sure that two different capes does not access the same pin at the same time expecting different functionalities. Now is a good time to take a look to the different functions each pin can assume. The page below is a great resource for this: http://elinux.org/Beagleboard:Cape_Expansion_Headers For instance, let's take pin 21 on connector P9. This pin can be configured as:
  • A GPIO (GPIO_3)
  • A PWM capable pin (EHRPWM0B)
  • A serial port pin (UART2_TXD)
  • A SPI data pin (SPI0_D0)
  • A I2C Clock pin (I2C2_SCL)
So, if our cape needs to use pin 21 as part of an SPI interface and another cape needs to use pin 21 as part of an I2C interface we cannot use those two capes at the same time. Adding this information in the exclusive-pin section will allow the capemanager (the software that interprets our device tree overlays - http://elinux.org/Capemgr -) to produce an error and prevent one of the capes to be active. Well, enough of background. How does this section looks like?. It will be a list of pins, followed by a list of hardware elements. The list of resources used by our cape To illustrate this, let's take a look to the Device Tree overlay to configure UART2, which, by chance ;), makes use of pin 21 on P9. This is what we will see:
        /* state the resources this cape uses */
        exclusive-use =
                /* the pin header uses */
                "P9.21",        /* uart2_txd */
                "P9.22",        /* uart2_rxd */
                /* the hardware ip uses */
                "uart2";
First, everything between /* and */ is a comment and can be ignored. Second there is a list of the pins required to use the UART2; P9.21 and P9.22 or, in other words, pin 21 on connector P9 and pin 22 also on connector 22. And finally, a list of the hardware devices that will be used by the cape. The list of the pins is pretty obvious... but what does those hardware devices come from.

HARDWARE DEVICES ON OVERLAYS

Note: I have done my best here. What is written below seems to match reality but I'm not completely sure that this is the right explnations.... Be aware of that To understand were those hardware devices come from and how can we know about their names we need to look into the kernel sources. Let's assume that ROOT_DIR is the directory where you deployed your development environment. $ cd $ROOT_DIR/linux-dev/KERNEL/arch/arm/boot/dts This directory is part of the Linux kernel and contains the base device trees for several different platforms. That's enough for now. What we are interested in is the BeagleBone base device tree which is a composition of the following files:
  • skeleton.dsi. This is the minimum required to boot. Not interesting for us right now
  • am335x-boneblack.dts. This is the main device tree for the BeagleBone. This is not interested right now
  • am335x-bone-common.dtsi. This is an "include" file incorporated by am335x-boneblack.dts. From out point of view, this file includes the declarations required by the official capes. It also includes the reservation of the cape I2C addresses (as we had seen in the NULLCape tutorial) and the association of the at24 driver to access the capes EEPROM. Anyhow, this is also not interesting for us.
  • am33xx.dtsi. This is another "include" file, and this is the one we are actually interested on at this point.
In this file (am33x.dtsi) we will find the "hardware devices" we can mention in the exclusive-use section of our device tree overlay. Take a look to get an idea. If you do not want to take a look, this is a list of the devices relevant during this tutorial (not all of them):
  • Serial: uart1, uart2, uart4, uart5
  • I2C: i2c1, i2c2
  • SPI: spi0, spi1
  • GMP: gmpc
  • Analogue: tscadc
If you take a look at the file, you will check that there are a lot more devices but some of them are not accessible through the BeagleBone expansion connector and therefore cannot be used by our Capes. Anyhow, the devices listed above, together with the GPIOs will cover most of our needs and will cover, for sure, the contents of this tutorial.

FRAGMENTS

Once we had declared the resources we need for our cape (the pins and the interfaces in the SOC that our cape will interface to), it is time to configure them. We will do this configuration using the so-called fragments. In general, we will find two main kinds of fragments. They are actually the same thing, but we believe this separation will be more appropriated for the cape hackers. The first kind of fragments are intended to configure the function of each pin in the BeagleBone headers. As we mentioned above, each single pin in our BeagleBone can have different functions. The function of the pin is selected through the so called pin multiplexer. In the exclusive-use section we had listed the pins we want to use but, in order to use them, we need to configure the proper function. This mux configuration is a fragment. The second kind of fragments are intended to configure the hardware devices we discussed above. In the simplest case, they will just enable the hardware and refer to the pins configuration defined in previous fragment. In the most complex case specific hardware parameters can be provided here, but we will not talk about this in this part of the tutorial.

PIN MULTIPLEXING

As we have just discussed there is a kind of fragments, intended to configure the pin mux mode, or the pin function. Those fragments also follows a very specific structure:
fragment@N {
	   target = <&am33xx_pinmux>;
	   __overlay__ {
	   	       CAPENAME_pins: pinmux_CAPENAME_pins {
		       		      pinctrl-single,pins = <
				      			  REG MUX_MASK
				      			  REG MUX_MASK
							  ...
				      >;
			};
	  };
};
It is pretty straightforward. The only tricky part is that REG MUX_MASK list. These are a list of hexadecimal numbers that we need to figure out. To better illustrate this, let's take our UART2 overlay and take a look to this fragment on it:
fragment@0 {
                target = <&am33xx_pinmux>;
                __overlay__ {
                        bb_uart2_pins: pinmux_bb_uart2_pins {
                                pinctrl-single,pins = <
                                        0x150 0x21        /* spi0_sclk.uart2_rxd | MODE1 */
                                        0x154 0x01        /* spi0_d0.uart2_txd | MODE1 */
                                >;
                        };
                };
        };
So, where those mysterious numbers come from.

THE MAGIC NUMBERS

To know what those magic number means we have to go to the system reference manual... Yes, that document 4161 pages long full of information on the AM335X SoC. Don't panic, right now we are only interested in two parts of the document: Table 9-10, Control_Module Registers in section 9.3 and section 9.3.51 conf_<module>_<pin> Register. The table will tell us about the first magic number. It is actually an offset referencing a conf_* registers. We will have one of those registers for each multiplexed signal. The table will show all the offsets for the control module. The am33xx_pinmux component seems to expect the offset with respect to the first conf_* register, that is 0x800.... OK, this is an assumption based on the information gather so far, but basically it matches what is expected. If we come back to our UART2 example, the offset 0x150 will correspond to the control module offset 0x950 (0x800 + 0x150). If we check Table 9-10 we will find that the offset corresponds to conf_spi0_clk. Now we can check the multiplexing table for expansion header P9 and we will find that UART2_RXD is mode 1 for SPI0_SCLK that corresponds with pin 22. You can find this multiplexing table in the webpage mentioned above (http://elinux.org/Beagleboard:Cape_Expansion_Headers) or in the SRM (System Reference Manual) for the Beaglebone. Note that this information is not provided by Technical Reference Manual, because this manual is for the Soc (AM335x), the processor in the BeagleBone, not the BeagleBone itself. Well, this is the first magical number. The second mysterious number is explained in section 9.3.51, in the Technical Reference Manual. Basically it is a byte, containing the information required for the pin configuration. You can check the section for the details. For the time being, we just need to know that, the last 3 bits are the mux mode (in our UART2 example mode 1), and the first nibble with value 2 (that is bit 5 in the byte) indicates whether the pin is an input or an output. We can check that the Rx pin is configured as an input and the Tx pin as an output. So far so good.

THE HARDWARE DEVICE FRAGMENT

OK, we are almost done. We just need to analyse the final fragment, the one in charge of the initialisation of the hardware inside the SoC. We have already make the Rx and Tx pins available to the external world (that was the previous fragment), now we need to configure the hardware within the Soc behind those pins. In our example the UART (or serial port). The general structure of this fragment is something like this:
fragment@1 {
	   target = ;
	   __overlay__ {
	   	       status= "okay";
		       pinctrl-name = "default";
		       pinctrl-0 = <&CAPENAME_pins>

		       //Specific driver stuff will follow
	   }
};
There are two things we need to now about this fragment. The first one is the target. For now, the target will be the hardware device we want to initialise, the same thing we included at the end of the exclusive-use. The other one is to map this device to the pins we had defined. This is the content of the pinctl-0, which, as you probably had already figured out is whatever we had defined in the previous fragment. So, for our UART2 example, this fragment will look like this:
        fragment@1 {
                target = <&uart3>;        /* really uart2 */
                __overlay__ {
                        status = "okay";
                        pinctrl-names = "default";
                        pinctrl-0 = <&bb_uart2_pins>;
                };
        };
Did you noticed?... yes, sure... it says uart3, instead of uart2. OK, serial port numbering has always been an issue, some systems number them starting on 0 and some starting on 1. Here we've got worst of both words, and we have to use uart2 for everything but this part. Note that this only happen with the serial ports. For I2C, SPI you will always refer to the same device in the device tree... but there are some diversions when you try to access the real device (usually those start with 0). Anyhow, this is a quite long entry and if you have been so brave to reach this point we do not want to bother you any more. So we will finish with some good news.

BBCape_EEPROM UPDATE

Maybe you remember our simple tool to generate Cape EEPROMs files... No?. It doesn't matter. You can find it here: https://github.com/picoflamingo/BBCape_EEPROM We had updated it with a new menu option (c -> Pin Configuration) to generate this overlays. Right now it only works with serial ports, but we hope we will update it as we progress on this tutorial. So, if you cape needs two serial ports... let's say UART2 and UART4, you can just launch the tool and do like this:
  • Press b to go into the board info mode if you want to update the part-number and version
  • Press c to go into pin configuration mode
  • Press a to add a device to your Cape
  • Press 1 to add UART2
  • Press a again to add a second device
  • Press 3 to add UART4
  • Press w to save the device tree overlay
  • type a name for your file, for instance: two_serials.dts
  • Press q to exit.
Now you already know how to work with this file, and all this is stuff is a little less black magic than before :) CU The picoFlamingo Team