Device Tree - bogics/rpi_gpio_driver GitHub Wiki

Literature:

 

About Device Tree

Device tree is a tree data structure with nodes that describes the physical devices in a system. The Device Tree replaces the legacy platform data where hardware characteristics were hardcoded in the kernel source so that platform devices can be instantiated. Before device trees came into use, the bootloader had to tell the kernel what machine type it was booting. Moreover, it had to pass other information such as memory size and location, kernel command line, and more.
The compiled binary format Device Tree Blob (DTB) is read during the boot up sequence.
Device Tree source filed are stored in arch/arm/boot/dts.

 

Write overlay

test_gpio-overlay.dts is created with the following content:

// Definitions for test_gpio module  
/dts-v1/;  
/plugin/;  

/ {
	compatible = "brcm,bcm2708";

	fragment@0 {
		target = <&soc>;
		__overlay__ {
			#address-cells = <1>;
			#size-cells = <1>;
		
			test_gpio: test_gpio@7e200000 {
				compatible = "test_gpio";
				reg = <0x7e200000 0xa0>;
				interrupts = <2 17>;
				interrupt-controller;
				#interrupt-cells = <2>;
				status = "okay";
			};
		};
	};
};  

 

Compile overlay

Device Tree files are compiled using Device Tree Compiler (DTC), which is included in the kernel source inside scripts/dtc and is compiled along with the kernel itself.

There are two ways to compile Device Tree:

  1. With the Linux kernel kbuild build system:
  • Add test_gpio-overlay.dts to the arch/arm/boot/dts/overlays directory
  • Insert dtbo-$(RPI_DT_OVERLAYS) += test_gpio.dtbo into arch/arm/boot/dts/overlays/Makefile
  • make dtbs
  1. Independently
  • ./dtc -I dts -O dtb -o test_gpio.dtbo test_gpio-overlay.dts

Whatever method is chosen, copy the generated test_gpio.dtbo to the overlays directory of the sd card's boot partition.
Add a dtoverlay=test_gpio line to the config.txt located into the sd card's boot partition.
This ensures that the test_gpio overlay will be loaded after the next reboot.

 

Device Tree syntax

A DT overlay comprises a number of fragments, numbered sequentially from zero, each of which targets one node and its subnodes.

  • compatible = "brcm,bcm2708"; property defines the machine types the device tree is compatible with. It identifies this as being for BCM2708, which is the base architecture of the BCM2835 part.
  • target = <&soc>; property identifies the node to apply the overlay to, using a phandle (reference to another node). soc node is defined in the bcm2708_common.dtsi.
  • __overlay__ represents a body which is added to the target node. It contains description of the gpio hardware.
  • #address-cells = <1>; property indicates how many cells (i.e 32 bits values) are needed to form the base address part in the reg property.
  • #size-cells = <1>; property indicates how many size cells are needed in the reg property.
  • test_gpio: test_gpio@7e200000 defines new subnode into the soc node.
    test_gpio is the node name
    7e200000 is unit address, which is according to the chapter 6 General Purpose I/O (GPIO) of the BCM2835-ARM-Peripherals, address of the first GPIO register. In general, the unit address is the primary address used to access the device, and is listed in the node's reg property.
  • compatible = "test_gpio"; property in non-root node is used to bind device with the driver. It contains a string in the form "manufacturer,model", but here the manufacturer part is omitted.
    The connection between a kernel driver and the “compatible” entries it should be attached to, is made by a code segment as follows in the driver’s source code:
	static struct of_device_id test_gpio_dt_match[] = {
		{ .compatible = "test_gpio", },
		{ },
	};
	MODULE_DEVICE_TABLE(of, test_gpio_dt_match);
  • reg = <0x7e200000 0xb4>; property represents an address range used by the device. Each address value is a list of one or more 32 bit integers called cells. Similarly, the length value can either be a list of cells, or empty. The #address-cells and #size-cells properties in the parent node are used to state how many cells are in each field.
    From the chapter 6 General Purpose I/O (GPIO) of the BCM2835-ARM-Peripherals, 0x7e200000 is address of the firs GPIO register GPFSEL0 while 0x7e2000b0 is address of the last GPIO register denoted as Test. All registers are 32-bits except the last one (Test) which is 4 bits in size. According to this, total size of the all GPIO registers is 0xb4.
  • interrupts = <2 17>; GPIO0 from Bank 2 (interrupts in the "IRQ pending 2" register).
  • #interrupt-cells = <2>; Specifies the number of cells needed to encode an interrupt source. The value shall be 2.
  • interrupt-controller; Identifies the node as an interrupt controller
  • status = "okay"; (or "ok" or absent) property, indicates that the device is enabled. Otherwise, status should be "disabled", so that means the device is disabled.

 

Device Tree on the running system

  • Information about Device Tree on the running system can be obtained from /proc/device-tree or /sys/firmware/devicetree/base.
    gpio_test can be found at:
    /proc/device-tree/soc/test_gpio@7e200000/ and
    /sys/firmware/devicetree/base/soc/test_gpio@7e200000/, with the same content:
	-r--r--r--    1 root     root            10 Jan  1 00:00 compatible
	-r--r--r--    1 root     root            10 Jan  1 00:00 name
	-r--r--r--    1 root     root             4 Jan  1 00:00 phandle
	-r--r--r--    1 root     root             8 Jan  1 00:00 reg
	-r--r--r--    1 root     root             5 Jan  1 00:00 status
  • Get a full device tree from the filesystem:
	# dtc -I fs -O dts /sys/firmware/devicetree/base
	Warning (avoid_default_addr_size): Relying on default #address-cells value for /axi/vc_mem
	Warning (avoid_default_addr_size): Relying on default #size-cells value for /axi/vc_mem
	/dts-v1/;
	
	/ {
		model = "Raspberry Pi Model B Plus Rev 1.2";
		compatible = "brcm,bcm2708";
		memreserve = <0x1c000000 0x4000000>;
		serial-number = "000000002dfd1846";
		...
		...
  • Print dts from the blob:
	# fdtdump /boot/overlays/test_gpio.dtbo 
	/dts-v1/;
	// magic:		0xd00dfeed
	// totalsize:		0x1fa (506)
	// off_dt_struct:	0x38
	// off_dt_strings:	0x1ac
	// off_mem_rsvmap:	0x28
	// version:		17
	// last_comp_version:	16
	// boot_cpuid_phys:	0x0
	// size_dt_strings:	0x4e
	// size_dt_struct:	0x174
	
	/ {
	    compatible = "brcm,bcm2708";
	    fragment@0 {
	        target = <0xdeadbeef>;
	        __overlay__ {
	            #address-cells = <0x00000001>;
	            #size-cells = <0x00000001>;
	            test_gpio@7e200000 {
	                compatible = "test_gpio";
	                reg = <0x7e200000 0x000000b4>;
	                status = "okay";
	                phandle = <0x00000001>;
	            };
	        };
	    };
	    __symbols__ {
	        test_gpio = "/fragment@0/__overlay__/test_gpio@7e200000";
	    };
	    __fixups__ {
	        soc = "/fragment@0:target:0";
	    };
	};

Find explanation for target = <0xdeadbeef>;, __symbols__ and __fixups__ in the www.raspberrypi.org ~ device-tree documentation.

dtc and fdtdump can be cross-compiled using buildroot by selecting the dtc programs package (Target packages ---> Libraries ---> Hardware handling ---> [*] dtc programs)

⚠️ **GitHub.com Fallback** ⚠️