Contents
Flattened Device Tree
This page is dedicated to the project bringing the flattened device tree (FDT) technology to the FreeBSD. For details contact RafalJaworowski. Work on this project was kindly sponsored by the FreeBSD Foundation.
Abstract
The objective of this project is to provide FreeBSD with the ability to use flattened device trees for description of hardware resources of a computer system and their dependencies, in a platform-neutral and portable way. The primary consumer of this functionality are embedded systems (based on ARM, AVR32, MIPS, PowerPC), but the mechanism can be used in all cases when hardware resources cannot be self-enumerated (as such could be seen as a future replacement for device.hints used in the legacy PC world).
Repository
The code has been committed to the FreeBSD SVN repository as of r210053, and all development is now supposed to happen in the SVN HEAD. The obsolete, no longer maintained original project development P4 branch can be found here.
Introduction
In embedded world there is great variety of systems based on similar silicon chips, but designed into custom boards and devices, where connections of individual components are different and there are no conventions or rules, even across members of the same family of products, for the interconnectios layout and resources allocation. Furthermore, some buses and interconnects are not self-enumerable by definition (unlike PCI or USB), and there has to be some prior knowledge about how they are connected and what their unique identification is. Some of the examples of typical problems are the following:
- memory layout (address offsets/sizes specification)
- assignment of resources and identification on non-enumerable devices (I2C, SPI buses, internal on-chip resources)
- MAC-PHY binding
- interrupts routing
- GPIO/multi-purpose pin routing and assignment
The concept of a flattened device tree (FDT) is an established and mature way of handling such problems and has been successfully used for Linux/powerpc. It has also been adopted as a basis for Power.org's embedded platform reference specification (ePAPR).
The idea is inherited from Open Firmware IEEE 1275 device-tree notion (part of the regular Open Firmware implementation), but it allows to use device tree mechanism on all systems (not based on OF). It boils down to the following:
- hardware platform (board) resources are described in a human readable text source format, where all non self-enumerating info is gathered
- this source description is converted ('compiled') into a binary object (flattened device tree blob), which is passed to the kernel at boot time
- the kernel (driver) learns about hardware resources details and dependencies from this [externally supplied] blob, which eliminates the need for embedding any info about the underlying platform's hardware resources in the kernel
Quick start instructions
This section is meant as a convenient helper for people, who want to quickly check out the idea of using FDT approach. It shows only the statically embedded DTB approach, with SHEEVAPLUG kernel config as an example. For other options and more detailed descriptions please see relevant parts of this manual.
Build FDT-enabled world
$ cd ${SRC} $ make -j 8 buildworld TARGET_ARCH=arm -DWITH_FDT
This creates the dtc compiler utility and FDT-enabled loader(8).
Build FDT-enabled kernel
Enable support in the kernel, make sure the sys/arm/conf/SHEEVAPLUG kernel config file has the following entries:
# Enable FDT support. options FDT makeoptions FDT_DTS_FILE=sheevaplug.dts options FDT_DTB_STATIC
$ make buildkernel TARGET_ARCH=arm KERNCONF=SHEEVAPLUG
The kernel build process includes creation (compilation) of the DTS file into a binary form (DTB), so no explicit steps are required to produce the device tree blob.
Boot
The kernel with statically embedded DTB does not require any additional handling and is ready to be bootstrapped in a standard way.
Definitions
Device tree source (DTS)
The device tree source is a text file which describes hardware resources of a computer system in a human-readable form. See example snippets of a device tree source (DTS) featuring description of all major components (CPU, memory, system-on-chip peripherals, IRQ assignments etc.) as device tree nodes and their properties.
The default locations of DTS files are the following subdirectories of the FreeBSD source repository:
sys/dts sys/gnu/dts
Device tree blob (DTB)
For regular use case the textual device tree description (DTS file) is first converted (compiled) into a binary object (the device tree blob i.e. the DTB), which is handed over to the final consumer (kernel) for parsing and processing of its contents.
Tools, environment
Device tree compiler (dtc)
A stand-alone tool executed on the host, which transforms (compiles) a textual description of a device tree (DTS) into a binary object (DTB).
Supporting library (libfdt)
Helper library providing basic routines for parsing the device tree blob and retrieving data. It is integral part of the dtc tool, and can be used in any other code which needs to process the DTB.
In FreeBSD for FDT-enabled platforms, libfdt is therefore also used by loader(8) and kernel for accessing the device tree blob.
Building the dtc tool
FreeBSD buildworld WITH_FDT
The typical way of building the device tree compiler is supplying WITH_FDT setting during buildworld procedure, either via src.conf(5) or manually. Note the dtc tool has to be built at bootstrap tools building stage of the buildworld procedure, because the build host is not guaranteed to have the compiler installed and readily available. This is similar to other elementary tools required during further stages of the build process (like config, make and others).
$ cd $SRC $ make buildworld -DWITH_FDT
Note the WITH_FDT setting is turned on by default for ARM and PowerPC. In order to opt out FDT support on these platforms supply WITHOUT_FDT during buildworld procedure.
Building FDT-enabled kernel
In order to run a kernel driven by a flattened device tree configuration, there are two required items:
- FDT support enabled in the kernel
- accompanying device tree blob (DTB), dedicated for the given platform
The DTB can either be statically embedded as part of the kernel image or a stand alone file, explicitly loaded and handled by loader(8).
Kernel options
There are a couple of FreeBSD kernel options available to manage FDT support.
# Flattened Device Tree (FDT) support. # # Enable FDT support. options FDT # Provide a preferred (default) device tree source (DTS) file for the kernel. # The indicated DTS file will be converted (compiled) into a binary form # during kernel build stage. makeoptions FDT_DTS_FILE=board.dts # Statically embed device tree blob (DTB) into a kernel image. This option # allows using device tree on platforms which do not (cannot) run loader(8); # in these cases we need to embed the DTB as part of kernel data. This option # requires a DTS file to be specified with FDT_DTS_FILE makeoption. options FDT_DTB_STATIC
Note the FDT_DTS_FILE parameter is a file name specified relative to one of the default directories:
sys/dts sys/gnu/dts
Currently the build system can accept DTS files only from these locations.
Stand-alone DTB
On platforms capable of running loader(8) the default scenario is to use a stand-alone DTB file, which is handled by the loader and handed over to kernel at boot time. In this approach, a DTB should be created at kernel build time:
If FDT_DTS_FILE is specified in the kernel config file, the DTB is compiled automatically as part of the buildkernel stage. The user does not have to perform any explicit steps.
If the default DTS file is not specified in the kernel config file, the DTB needs to be created separately by the user with the builddtb target:
$ make builddtb FDT_DTS_FILE=mpc8572ds.dts
Statically embedded DTB
Platforms without loader(8) support need to include the DTB file as integral part of the kernel image. For this case the FDT_DTB_STATIC option needs to be supplied in kernel config, as well as the FDT_DTS_FILE makeoption (which becomes mandatory in this scenario). At the buildkernel time the DTB file is automatically created from the specified DTS and embedded in the kernel without any user assistance.
Installing FDT-enabled kernel
The kernel installation procedure is no different for an FDT-enabled kernel: standard installkernel procedure applies. However, for the stand-alone DTB approach (non-static) there are caveats regarding DTB file installation:
In case the default DTS file was specified in the kernel config with FDT_DTS_FILE option at build time, the according DTB file is installed automatically by the installkernel target, and no explicit assistance from the user is required. The DTB will be available from /sys/boot/kernel directory on the target file system.
- If the DTB was created manually, it has to be transferred by hand to the /sys/boot/kernel of the target file system.
Running FDT-enabled kernel
Using loader(8)
Basic usage
The following loader(8) commands are available for the device tree support
fdt cd <fdt_path> fdt header fdt ls [fdt_path] fdt mknode [fdt_path/]<node_name> fdt mkprop [node_path/]<property_name> <string | [ byte1 byte2 .. ] | <uint32_1 uint32_2 .. > > fdt prop [node_path/[prop_name value_to_set]] fdt pwd fdt rm [node_path/]<node_name | property_name>
Load blob
loader> load -t dtb boot/mpc8555cds.dtb loader> lsmod ... 0x162f92c: boot/mpc8555cds.dtb (dtb, 0x1eb2) loader>
Inspect blob header
loader> fdt header Flattened device tree header (0x162f92c): magic = 0xd00dfeed size = 7858 off_dt_struct = 0x00000038 off_dt_strings = 0x000018ac off_mem_rsvmap = 0x00000028 version = 17 last compatible version = 16 boot_cpuid = 0 size_dt_strings = 518 size_dt_struct = 6260 loader>
List the device tree
loader> fdt ls /aliases /cpus /cpus/PowerPC,8555@0 /memory /soc8555@e0000000 /soc8555@e0000000/ecm-law@0 /soc8555@e0000000/ecm@1000 /soc8555@e0000000/memory-controller@2000 /soc8555@e0000000/l2-cache-controller@20000 /soc8555@e0000000/i2c@3000 /soc8555@e0000000/dma@21300 /soc8555@e0000000/dma@21300/dma-channel@0 /soc8555@e0000000/dma@21300/dma-channel@80 /soc8555@e0000000/dma@21300/dma-channel@100 /soc8555@e0000000/dma@21300/dma-channel@180 /soc8555@e0000000/ethernet@24000 /soc8555@e0000000/ethernet@24000/mdio@520 /soc8555@e0000000/ethernet@24000/mdio@520/ethernet-phy@0 /soc8555@e0000000/ethernet@24000/mdio@520/ethernet-phy@1 /soc8555@e0000000/ethernet@24000/mdio@520/tbi-phy@11 /soc8555@e0000000/ethernet@25000 /soc8555@e0000000/ethernet@25000/mdio@520 /soc8555@e0000000/ethernet@25000/mdio@520/tbi-phy@11 /soc8555@e0000000/serial@4500 /soc8555@e0000000/serial@4600 /soc8555@e0000000/crypto@30000 /soc8555@e0000000/pic@40000 /soc8555@e0000000/cpm@919c0 /soc8555@e0000000/cpm@919c0/muram@80000 /soc8555@e0000000/cpm@919c0/muram@80000/data@0 /soc8555@e0000000/cpm@919c0/brg@919f0 /soc8555@e0000000/cpm@919c0/pic@90c00 /pci@e0008000 /pci@e0008000/i8259@19000 /pci@e0009000 loader>
Boot kernel
loader> load -t dtb boot/mpc8555cds.dtb loader> boot -s
See full booting log.
The fdtbus and simplebus devices represent FDT hierarchy translated into newbus paradigm:
# devinfo nexus0 fdtbus0 lbc0 cfi0 cfid0 cfi1 cfid1 rtc0 simplebus0 i2c0 iicbus0 iic0 tsec0 miibus0 ciphy0 tsec1 miibus1 ciphy1 uart0 uart1 sec0 openpic0 #
More powerful use cases
Display node properties
loader> fdt prop /soc8555@e0000000 #address-cells = <0x00000001> #size-cells = <0x00000001> device_type = "soc" compatible = "simple-bus" ranges = <0x00000000 0xe0000000 0x00100000> bus-frequency = <0x00000000> loader>
Change existing property value
loader> fdt prop /cpus/PowerPC,8572@0 device_type = "cpu" reg = <0x00000000> d-cache-line-size = <0x00000020> i-cache-line-size = <0x00000020> d-cache-size = <0x00008000> i-cache-size = <0x00008000> timebase-frequency = <0x00000000> bus-frequency = <0x23c34600> clock-frequency = <0x00000000> next-level-cache = <0x00000001>
loader> fdt prop /cpus/PowerPC,8572@0/clock-frequency <15000000>
loader> fdt prop /cpus/PowerPC,8572@0 ... clock-frequency = <0x00e4e1c0> ...
Remove property
loader> fdt prop /aliases ethernet0 = "/soc8572@ffe00000/ethernet@24000" ethernet1 = "/soc8572@ffe00000/ethernet@25000" ethernet2 = "/soc8572@ffe00000/ethernet@26000" ethernet3 = "/soc8572@ffe00000/ethernet@27000" serial0 = "/soc8572@ffe00000/serial@4500" serial1 = "/soc8572@ffe00000/serial@4600" pci0 = "/pcie@ffe08000" pci1 = "/pcie@ffe09000" pci2 = "/pcie@ffe0a000"
loader> fdt rm /aliases/pci2
loader> fdt prop /aliases ethernet0 = "/soc8572@ffe00000/ethernet@24000" ethernet1 = "/soc8572@ffe00000/ethernet@25000" ethernet2 = "/soc8572@ffe00000/ethernet@26000" ethernet3 = "/soc8572@ffe00000/ethernet@27000" serial0 = "/soc8572@ffe00000/serial@4500" serial1 = "/soc8572@ffe00000/serial@4600" pci0 = "/pcie@ffe08000" pci1 = "/pcie@ffe09000"
Add property to a node
loader> fdt mkprop /aliases/pci2 "/pcie@ffe0a000"
loader> fdt prop /aliases pci2 = "/pcie@ffe0a000" ethernet0 = "/soc8572@ffe00000/ethernet@24000" ethernet1 = "/soc8572@ffe00000/ethernet@25000" ethernet2 = "/soc8572@ffe00000/ethernet@26000" ethernet3 = "/soc8572@ffe00000/ethernet@27000" serial0 = "/soc8572@ffe00000/serial@4500" serial1 = "/soc8572@ffe00000/serial@4600" pci0 = "/pcie@ffe08000" pci1 = "/pcie@ffe09000"
Add node
loader> fdt ls /aliases /cpus /cpus/PowerPC,8572@0 /cpus/PowerPC,8572@1 /memory /localbus@ffe05000 /localbus@ffe05000/nor@0,0 /localbus@ffe05000/nor@0,0/ramdisk@0 ...
loader> fdt mknode /chosen
loader> fdt ls /chosen /aliases /cpus /cpus/PowerPC,8572@0 /cpus/PowerPC,8572@1 /memory /localbus@ffe05000 /localbus@ffe05000/nor@0,0 /localbus@ffe05000/nor@0,0/ramdisk@0 ...
Remove node from the device tree
loader> fdt rm /chosen
loader> fdt ls /aliases /cpus /cpus/PowerPC,8572@0 /cpus/PowerPC,8572@1 /memory /localbus@ffe05000 /localbus@ffe05000/nor@0,0 /localbus@ffe05000/nor@0,0/ramdisk@0 ...
Direct kernel boot
Marvell>> tftpboot 900000 sheevaplug/kernel.bin Using egiga0 device TFTP from server 10.0.0.204; our IP address is 10.0.2.26 Filename 'sheevaplug/kernel.bin'. Load address: 0x900000 Loading: ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ######################################## done Bytes transferred = 2864724 (2bb654 hex) Marvell>> go 900000
User space access to the device tree
From the device tree perspective, an FDT-driven platform is not very much different than a genuine Open Firmware. The user can inspect the device tree with ofwdump(8) command in base. The FDT is internally hooked into kernel ofw_bus infrastructure, so it is accessible to user process through the /dev/openfirm node.
# ls -al /dev/openfirm crw------- 1 root wheel 0, 24 Jan 1 00:00 /dev/openfirm
List all devices in the tree
# ofwdump -a Node 0xc06309a0: Node 0xc0630a04: aliases Node 0xc0630b04: cpus Node 0xc0630b30: PowerPC,8555@0 Node 0xc0630bec: memory Node 0xc0630c24: soc8555@e0000000 Node 0xc0630cac: ecm-law@0 Node 0xc0630cfc: ecm@1000 Node 0xc0630d6c: memory-controller@2000 Node 0xc0630dec: l2-cache-controller@20000 Node 0xc0630ea4: i2c@3000 Node 0xc0630f40: dma@21300 Node 0xc0630fd8: dma-channel@0 Node 0xc0631074: dma-channel@80 Node 0xc0631110: dma-channel@100 Node 0xc06311ac: dma-channel@180 Node 0xc063124c: ethernet@24000 Node 0xc0631360: mdio@520 Node 0xc06313c4: ethernet-phy@0 Node 0xc063143c: ethernet-phy@1 Node 0xc06314b4: tbi-phy@11 Node 0xc0631504: ethernet@25000 Node 0xc0631618: mdio@520 Node 0xc0631678: tbi-phy@11 Node 0xc06316c8: serial@4500 Node 0xc063175c: serial@4600 Node 0xc06317f0: crypto@30000 Node 0xc0631898: pic@40000 Node 0xc0631930: cpm@919c0 Node 0xc06319a8: muram@80000 Node 0xc06319f0: data@0 Node 0xc0631a40: brg@919f0 Node 0xc0631aa8: pic@90c00 Node 0xc0631b58: pci@e0008000 Node 0xc0631fa8: i8259@19000 Node 0xc0632068: pci@e0009000 #
List properties of a given node
# ofwdump -p /soc8555 Node 0xc0630c24: soc8555@e0000000 #address-cells: 00 00 00 01 #size-cells: 00 00 00 01 device_type: 73 6f 63 00 'soc' compatible: 73 69 6d 70 6c 65 2d 62 75 73 00 'simple-bus' ranges: 00 00 00 00 e0 00 00 00 00 10 00 00 bus-frequency: 00 00 00 00 #
# ofwdump -p /soc8555/ethernet@24000 Node 0xc063124c: ethernet@24000 #address-cells: 00 00 00 01 #size-cells: 00 00 00 01 cell-index: 00 00 00 00 device_type: 6e 65 74 77 6f 72 6b 00 'network' model: 54 53 45 43 00 'TSEC' compatible: 67 69 61 6e 66 61 72 00 'gianfar' reg: 00 02 40 00 00 00 10 00 ranges: 00 00 00 00 00 02 40 00 00 00 10 00 local-mac-address: 00 00 00 00 00 00 interrupts: 00 00 00 1d 00 00 00 02 00 00 00 1e 00 00 00 02 00 00 00 22 00 00 00 02 interrupt-parent: 00 00 00 02 tbi-handle: 00 00 00 03 phy-handle: 00 00 00 04 #
Dump dts from loaded dtb
There is another option in case you want to see the DTS you booted with:
# sysctl -b hw.fdt.dtb | dtc -I dtb
Advanced topics
How to convert a platform to FDT
Create DTS
- compile, inspect everything is fine
If the dtc has to be (re)built for whatever reason manually, it can be achieved similar to any other user land applications of the base FreeBSD system. In this case:
$ cd gnu/usr.bin/dtc/ $ make ...
$ ./dtc -v Version: DTC 1.2.0-g46b8d261
$ ./dtc -h Usage: dtc [options] <input file> Options: -h This help text -q Quiet: -q suppress warnings, -qq errors, -qqq all -I <input format> Input formats are: dts - device tree source text dtb - device tree blob fs - /proc/device-tree style directory -o <output file> -O <output format> Output formats are: dts - device tree source text dtb - device tree blob asm - assembler source -V <output version> Blob version to produce, defaults to 17 (relevant for dtb and asm output only) -R <number> Make space for <number> reserve map entries (relevant for dtb and asm output only) -S <bytes> Make the blob at least <bytes> long (extra space) -p <bytes> Add padding to the blob of <bytes> long (extra space) -b <number> Set the physical boot cpu -f Force - try to produce output even if the input tree has errors -v Print DTC version and exit
$ ./dtc -O dtb -o mpc8555cds.dtb -b 0 -p 1024 mpc8555cds.dts DTC: dts->dtb on file "mpc8555cds.dts" $ file mpc8555cds.dtb mpc8555cds.dtb: data
$ hexdump -C mpc8555cds.dtb 00000000 d0 0d fe ed 00 00 1e b2 00 00 00 38 00 00 18 ac |...........8....| 00000010 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 |...(............| 00000020 00 00 02 06 00 00 18 74 00 00 00 00 00 00 00 00 |.......t........| 00000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................| 00000040 00 00 00 03 00 00 00 0b 00 00 00 00 4d 50 43 38 |............MPC8| 00000050 35 35 35 43 44 53 00 00 00 00 00 03 00 00 00 16 |555CDS..........| 00000060 00 00 00 06 4d 50 43 38 35 35 35 43 44 53 00 4d |....MPC8555CDS.M| 00000070 50 43 38 35 78 78 43 44 53 00 00 00 00 00 00 03 |PC85xxCDS.......| 00000080 00 00 00 04 00 00 00 11 00 00 00 01 00 00 00 03 |................| 00000090 00 00 00 04 00 00 00 20 00 00 00 01 00 00 00 01 |....... ........| 000000a0 61 6c 69 61 73 65 73 00 00 00 00 03 00 00 00 21 |aliases........!| ...
Use FDT infrastrusture
- fdtbus
- simplebus
Convert individual components (drivers)
Recommended practices
New bindings definitions
References
Flattened Device Trees for Embedded FreeBSD.
Embedded Power Architecture Platform Requirements (ePAPR)
Archives of the devicetree-discuss mailing list