Nerveware


Flattened device tree

A flattened device tree (FDT) is used to separate the kernel from hardware specific configuration. In older Linux kernels, peripherals where configured in board files in, for instance, arch/arm. Changes to the hardware or kernel configuration requires one to rebuild the entire kernel. Different devices often have different hardware, and thus a new kernel must be maintained. The device tree is a solution to this. Because the device tree is loosly coupled with the kernel, it is possible to run one kernel for several devices.

NOTE: For the full spec, see the ePAPR specification.

There are two types of files, dts and dtsi. The latter one is used when the device tree itself is an interface.

/dts-v1/; #define IRQ_HIGH 0 #define IRQ_LOW 1 #define CLK_I2C0 138 / { model = "Test device tree"; #address-cells = <0>; #size-cells = <1>; aliases { debug = &uart2; }; chosen { stdout-path = &uart2; }; uart2: uart2@0x1 { compatible = "my,uart-device"; reg = <0x1>; status = "okay"; }; clks: clk-master@0x3 { compatible = "my,clocks"; reg = <0x3 0x4000>; interrupts = <0 11 IRQ_HIGH>, <0 12 IRQ_HIGH>; #clock-cells = <1>; }; i2c0: i2c0@0x2 { #address-cells = <1>; #size-cells = <0>; compatible = "my,i2c-bus"; reg = <0x2 0x400>; status = "okay"; interrupts = <0 13 IRQ_HIGH>; clocks = <&clks CLK_I2C0>; i2c_device: i2c-device@0x41 { compatibe = "my,i2c-device"; reg = <0x41>; status = "okay"; }; }; };

Usually, nodes as uart2 and i2c0 (uart2@0x1 and i2c0@0x2) arent direct childern from the root parent, but instead has a bus. For instance, an i2c sensor would be located on the i2c bus, which is in turn located on the aips bus. These nodes have their unit address appended to their name, seperated by the '@' symbol. Although it isn't required, it's good practise to do so.

In the device tree I used a label. It's defined as i2c0: and can be used as &i2c0. It is used to overwrite properties such as 'status', or to add nodes such as i2c-device. Also, try to define lables before you use them.
You can compile the dts with the following command:

$ cpp -Iinclude -E -P -x assembler-with-cpp test.dts tmp.dts $ dtc -I dts -o test.dtb tmp.dts

Turning the dtb into a dts is useful if you want to compare an old and new dts. I usually do this when my kernel doesn't start.

$ dtc -I dtb -o tmp.dts test.dtb

Flattened image tree

A flattened image tree (FIT) in concept is almos the same as a device tree, but instead it describes the combination of image, device tree and/or ramdisk can be used. /dts-v1/; / { description = "Nerveware FIT example"; #address-cells = <0x1>; images { kernel@1 { description = "Kernel"; type = "kernel"; data = /incbin/("./zImage"); arch = "arm"; os = "linux"; compression = "none"; load = <0x11000000>; entry = <0x11000000>; hash@1 { value = <0xbec34b5>; algo = "crc32"; }; }; fdt@1 { description = "Flattened Device Tree"; data = /incbin/("./device-tree.dtb"); type = "flat_dt"; arch = "arm"; compression = "none"; load = <0x12000000>; hash@1 { value = <0x2a0ebcd4>; algo = "crc32"; }; }; ramdisk@1 { description = "Ramdisk buildroot"; data = /incbin/("./buildroot.cpio.gz"); type = "ramdisk"; arch = "arm"; os = "linux"; compression = "gzip"; load = <0x13000000>; entry = <0x13000000>; hash@1 { value = <0xbb627f66>; algo = "crc32"; }; }; ramdisk@2 { description = "Ramdisk Debian"; data = /incbin/("./debian.cpio.gz"); type = "ramdisk"; arch = "arm"; os = "linux"; compression = "gzip"; load = <0x13000000>; entry = <0x13000000>; hash@1 { value = <0xbb627f66>; algo = "crc32"; }; }; }; configurations { default = "buildroot"; buildroot { description = "Boot buildroot"; kernel = "kernel@1"; fdt = "fdt@1"; ramdisk = "ramdisk@1"; }; debian { description = "Boot debian"; kernel = "kernel@1"; fdt = "fdt@1"; ramdisk = "ramdisk@2"; }; }; };

To compile a FIT, you can make use of the follwing command. The commands given in the previous section also work. However, decompiling it is discouraged because the entire kernel is included in the the .itb file, thus will be included in the file when decompiled.

$ mkimage -f fit.its fit.itb

Extracting the kernel or ramdisk is also possible.

$ dumpimage -T flat_dt -i fit.itb -p 0 kernel # For the kernel $ dumpimage -T flat_dt -i fit.itb -p 1 fdt # for the device tree $ dumpimage -T flat_dt -i fit.itb -p 2 ramdisk # For the ramdisk

In U-Boot you're able to either boot Buildroot or Debian with the same kernel and device tree. If the bootm command is used, because Buildroot is configured as default, it will boot Buildroot as default. Append the bootm command with an hashtag following the configuration to boot another configuration.

$ setenv initrd_high 0xffffffff $ setenv loadaddr 0x14000000 $ tftpboot ${loadaddr} /fit.itb $ bootm ${loadaddr}#debian