デバイスツリーにシンボル情報を埋め込む

## 背景

デバイスツリーオーバーレイを使って新たにデバイスツリーを追加する際、追加するデバイスツリーが別のデバイスを参照する場合があります。例えば次のように uio を新たにデバイスツリーオーバーレイを使って追加する際、割り込みコントローラーを指定することがあります。

```uio0.dts
/dts-v1/;
/ {
	fragment@0 {
		target-path = "/amba";
		__overlay__ {
			#address-cells = <0x1>;
			#size-cells = <0x1>;
			pump-uio@43c10000 {
				compatible = "generic-uio";
				reg = <0x43c10000 0x1000>;
				interrupt-parent = <0x3>;
				interrupts = <0x0 0x1d 0x4>;
			};
		};
	};
};
```

上記のデバイスツリーの ```interrupt-parent = <0x3>;``` が割り込みコントローラーを指定しているところです。

上の例では割り込みコントローラーの phandle の値を指定しています。しかし、phandle の値はデバイスツリーを生成するときに Device Tree Compiler(dtc) によって自動的に割り振られます。そのため、上記のデバイスツリーをオーバーレイで追加する際には phandle の値をなんらかの方法で知っておく必要があります。これは、デバイスツリーのソースコードを見て phandle の値を知る必要があり不便、かつ間違えた値を指定するとシステムが動かなくなる可能性があり危険です。

## 解決方法

デバイスツリーにシンボル情報を埋め込むことで、次のようにオーバーレイするデバイスツリーを記述することができます。

```uio0.dts
/dts-v1/; /plugin/;
/ {
	fragment@0 {
		target-path = "/amba";
		__overlay__ {
			#address-cells = <0x1>;
			#size-cells = <0x1>;
			pump-uio@43c10000 {
				compatible = "generic-uio";
				reg = <0x43c10000 0x1000>;
				interrupt-parent = <&intc>;
				interrupts = <0x0 0x1d 0x4>;
			};
		};
	};
};
```

上記のデバイスツリーで ```/plugin/;``` を指定していることと、```interrupt-parent = <&intc>;``` で割り込みコントローラーのシンボルを指定していることに着目してください。

ただし、Linux を起動する際に利用するデバイスツリーには次のようにシンボル情報が埋め込まれている必要があります。

```devicetree.dts
/dts-v1/;

/ {
	#address-cells = <0x1>;
	#size-cells = <0x1>;
	compatible = "digilent,zynq-pynqz1", "xlnx,zynq-7000";
	model = "Zynq PYNQ-Z1 Development Board";
    :
  (中略)
    :
	amba {
    :
  (中略)
    :
		interrupt-controller@f8f01000 {
			compatible = "arm,cortex-a9-gic";
			#interrupt-cells = <0x3>;
			interrupt-controller;
			reg = <0xf8f01000 0x1000 0xf8f00100 0x100>;
			linux,phandle = <0x3>;
			phandle = <0x3>;
		};
    :
  (中略)
    :
	};
	__symbols__ {
		cpu0 = "/cpus/cpu@0";
		cpu1 = "/cpus/cpu@1";
		regulator_vccpint = "/fixedregulator";
		amba = "/amba";
		adc = "/amba/adc@f8007100";
		can0 = "/amba/can@e0008000";
		can1 = "/amba/can@e0009000";
		gpio0 = "/amba/gpio@e000a000";
		i2c0 = "/amba/i2c@e0004000";
		i2c1 = "/amba/i2c@e0005000";
		intc = "/amba/interrupt-controller@f8f01000";
		L2 = "/amba/cache-controller@f8f02000";
		mc = "/amba/memory-controller@f8006000";
		uart0 = "/amba/serial@e0000000";
		uart1 = "/amba/serial@e0001000";
		spi0 = "/amba/spi@e0006000";
		spi1 = "/amba/spi@e0007000";
		gem0 = "/amba/ethernet@e000b000";
		ethernet_phy = "/amba/ethernet@e000b000/ethernet-phy@0";
		gem1 = "/amba/ethernet@e000c000";
		sdhci0 = "/amba/sdhci@e0100000";
		sdhci1 = "/amba/sdhci@e0101000";
		slcr = "/amba/slcr@f8000000";
		clkc = "/amba/slcr@f8000000/clkc@100";
		rstc = "/amba/slcr@f8000000/rstc@200";
		pinctrl0 = "/amba/slcr@f8000000/pinctrl@700";
		dmac_s = "/amba/dmac@f8003000";
		devcfg = "/amba/devcfg@f8007000";
		global_timer = "/amba/timer@f8f00200";
		ttc0 = "/amba/timer@f8001000";
		ttc1 = "/amba/timer@f8002000";
		scutimer = "/amba/timer@f8f00600";
		usb0 = "/amba/usb@e0002000";
		usb1 = "/amba/usb@e0003000";
		watchdog0 = "/amba/watchdog@f8005000";
		usb_phy0 = "/phy0";
	};
};	
```

## シンボル情報埋め込む方法

### Device Tree Compiler(dtc) を直接使う場合

Device Tree Compiler(dtc) を使って Device Tree を作る際、```-@``` または ```--symbols``` を指定します。

例えば次のような Device Tree Source(dts) から

```devicetree.dts
/dts-v1/;
/ {
	amba {
		example: example@f8f01000 {
		};
	};
};
```

シンボル付きの Device Tree Blob(dtb) を生成する場合、次のように dtc を使います。

```
shell$ dtc -I dts -O dtb -@ -o devicetree.dtb devicetree.dts
```

ここで生成した Device Tree Blob(dtb) を Device Tree Source(dts) に戻してみると、シンボル情報や phandle が追加されていることがわかります。

```
shell$ dtc -I dtb -O dts -@ devicetree.dtb
/dts-v1/;

/ {

	amba {

		example@f8f01000 {
			linux,phandle = <0x1>;
			phandle = <0x1>;
		};
	};

	__symbols__ {
		example = "/amba/example@f8f01000";
	};
};
```

### Linux Kernel のビルド時にシンボル情報付きのデバイスツリーを作る

Linux Kernel のビルド時にはあらかじめ DTC_FLAGS 環境変数に ```--symbols``` オプションを指定しておきます。

```
shell$ export LINUX_BUILD_DIR=linux-4.12.2-armv7-fpga
shell$ export DTC_FLAGS=--symbols
shell$ cd $LINUX_BUILD_DIR
shell$ make zynq-zybo.dtb
```

### Device Tree Compiler(dtc) のバージョンに注意

デバイスツリーにシンボル情報を埋め込んだり、オーバーレイするデバイスツリーに ```/plugin/;``` を指定したり、シンボルを使えるようにするには、Device Tree Compiler(dtc) のバージョンが 1.4.2 以降である必要があります。

残念ながら debian8 で提供されている Device Tree Compiler パッケージは 1.4.0 なので使えません。

また、Linux Kernel に付属している Device Tree Compiler(dtc) を使う場合、Linux Kernel v4.11 以降である必要があります。 Linux Kernel v4.10 以前の Device Tree Compiler(dtc) では --symbol オプションが使えません。

### Device Tree Compiler 1.4.4 のビルド

手持ちの Device Tree Compiler が古くて ```-@``` または ```--symbols``` を指定できない場合は次の URL からソースコードをとってきてビルドします。

 * [https://git.kernel.org/pub/scm/utils/dtc/dtc.git](https://git.kernel.org/pub/scm/utils/dtc/dtc.git)

具体的には次の URL のいずれかから git clone でダウンロードします。

 * git://git.kernel.org/pub/scm/utils/dtc/dtc.git
 * https://git.kernel.org/pub/scm/utils/dtc/dtc.git
 * https://kernel.googlesource.com/pub/scm/utils/dtc/dtc.git

次に make を使ってビルドします

```
shell$ cd dtc
shell$ make
	 DEP tests/dumptrees.c
	 DEP tests/trees.S
	 DEP tests/testutils.c
	 DEP tests/value-labels.c
	 DEP tests/asm_tree_dump.c
	 DEP tests/truncated_property.c
	 DEP tests/check_path.c
	 DEP tests/overlay_bad_fixup.c
	 DEP tests/overlay.c
	 DEP tests/subnode_iterate.c
	 DEP tests/property_iterate.c
	 DEP tests/integer-expressions.c
	 DEP tests/utilfdt_test.c
	 DEP tests/path_offset_aliases.c
	 DEP tests/add_subnode_with_nops.c
	 DEP tests/dtbs_equal_unordered.c
	 DEP tests/dtb_reverse.c
	 DEP tests/dtbs_equal_ordered.c
	 DEP tests/extra-terminating-null.c
	 DEP tests/incbin.c
	 DEP tests/boot-cpuid.c
	 DEP tests/phandle_format.c
	 DEP tests/path-references.c
	 DEP tests/references.c
	 DEP tests/string_escapes.c
	 DEP tests/propname_escapes.c
	 DEP tests/appendprop2.c
	 DEP tests/appendprop1.c
	 DEP tests/del_node.c
	 DEP tests/del_property.c
	 DEP tests/setprop.c
	 DEP tests/set_name.c
	 DEP tests/rw_tree1.c
	 DEP tests/open_pack.c
	 DEP tests/nopulate.c
	 DEP tests/mangle-layout.c
	 DEP tests/move_and_save.c
	 DEP tests/sw_tree1.c
	 DEP tests/nop_node.c
	 DEP tests/nop_property.c
	 DEP tests/setprop_inplace.c
	 DEP tests/stringlist.c
	 DEP tests/addr_size_cells.c
	 DEP tests/notfound.c
	 DEP tests/sized_cells.c
	 DEP tests/char_literal.c
	 DEP tests/get_alias.c
	 DEP tests/node_offset_by_compatible.c
	 DEP tests/node_check_compatible.c
	 DEP tests/node_offset_by_phandle.c
	 DEP tests/node_offset_by_prop_value.c
	 DEP tests/parent_offset.c
	 DEP tests/supernode_atdepth_offset.c
	 DEP tests/get_path.c
	 DEP tests/get_phandle.c
	 DEP tests/getprop.c
	 DEP tests/get_name.c
	 DEP tests/path_offset.c
	 DEP tests/subnode_offset.c
	 DEP tests/find_property.c
	 DEP tests/root_node.c
	 DEP tests/get_mem_rsv.c
	 DEP libfdt/fdt_overlay.c
	 DEP libfdt/fdt_addresses.c
	 DEP libfdt/fdt_empty_tree.c
	 DEP libfdt/fdt_strerror.c
	 DEP libfdt/fdt_rw.c
	 DEP libfdt/fdt_sw.c
	 DEP libfdt/fdt_wip.c
	 DEP libfdt/fdt_ro.c
	 DEP libfdt/fdt.c
	 DEP util.c
	 DEP fdtoverlay.c
	 DEP fdtput.c
	 DEP fdtget.c
	 DEP fdtdump.c
	 LEX convert-dtsv0-lexer.lex.c
	 DEP convert-dtsv0-lexer.lex.c
	 DEP srcpos.c
	 BISON dtc-parser.tab.c
	 DEP dtc-parser.tab.c
	 LEX dtc-lexer.lex.c
	 DEP dtc-lexer.lex.c
	 DEP treesource.c
	 DEP livetree.c
	 DEP fstree.c
	 DEP flattree.c
	 DEP dtc.c
	 DEP data.c
	 DEP checks.c
	CHK version_gen.h
	UPD version_gen.h
	 DEP util.c
	CHK version_gen.h
	 CC srcpos.o
	 CC util.o
	 CC convert-dtsv0-lexer.lex.o
	 LD convert-dtsv0
	 CC dtc.o
	 CC checks.o
	 CC data.o
	 CC flattree.o
	 CC fstree.o
	 CC livetree.o
	 CC treesource.o
	 CC dtc-lexer.lex.o
	 CC dtc-parser.tab.o
	 LD dtc
	 CC fdtdump.o
	 LD fdtdump
	 CC fdtget.o
	 CC libfdt/fdt.o
	 CC libfdt/fdt_ro.o
	 CC libfdt/fdt_wip.o
	 CC libfdt/fdt_sw.o
	 CC libfdt/fdt_rw.o
	 CC libfdt/fdt_strerror.o
	 CC libfdt/fdt_empty_tree.o
	 CC libfdt/fdt_addresses.o
	 CC libfdt/fdt_overlay.o
	 AR libfdt/libfdt.a
	 LD fdtget
	 CC fdtput.o
	 LD fdtput
	 CC fdtoverlay.o
	 LD fdtoverlay
	 LD libfdt/libfdt-1.4.4.so
## Skipping pylibfdt (install python dev and swig to build)
```

dtc を指定したパスにインストールします。
Makefile には次のような箇所があって、HOME 変数で示されるパスの下にディレクトリを作る様です。

```Makefile
INSTALL = /usr/bin/install
DESTDIR =
PREFIX = $(HOME)
BINDIR = $(PREFIX)/bin
LIBDIR = $(PREFIX)/lib
INCLUDEDIR = $(PREFIX)/include
```

したがって、例えば /usr/local/bin に dtc を置きたい場合は次の様にインストールします。

```
shell$ sudo make HOME=/usr/local install-bin
	CHK version_gen.h
## Skipping pylibfdt (install python dev and swig to build)
	 INSTALL-BIN
```