Device Tree Overlay 入門 新構文, Buildroot, U-BOOTでの使い方

実際組込みLinuxを始めると、基本的なデバイスツリーの読み書きの次に、デバイスツリーの追加方法が出てくると思います。 バイナリ化されて既に組み込まれているベースDevice Tree(dtb)のデバイス設定をrun timeでも上書きできるのがOverlayです。 しかしrun timeで変更するのはクラッシュの心配もあるので、通常はブート時にOverlay dtboで上書きするのが一般的のようです。

Syntax(構文)

新しい Syntax(推奨)

今までのOverlayの構文と違いずいぶん簡単になっているのでいいですね。 dtcでコンパイルすると、fragment や __overlay__を付け加えてくれます。  この構文は、/plugin/;を除けば、階層化されたDevice Treeの上位でincludeするDevice Treeのデバイスの設定を書き換えるのと同じ構文になっています。 因みに、ルートノードはbase Device Treeの最上位のdtsには必須です。  Overlay は上書きするだけなのでルートノードに新規ノードを追加しなければ/{}は省略できます。

/dts-v1/;
/plugin/;
&some_node {
  some_prop = "okay";
  ...
};

fragmentを使う旧Syntax(非推奨)

ちょっと知っておくには過去のコード解析時に役に立ちますが、自分で作る時は分かりやすく間違えにくい新しい構文を使ってます。

/dts-v1/;
/plugin/;
/ {
  fragment@0 {
    target = <&some_node>;
      __overlay__ {
        some_prop = "okay";
        ...
      };
  };
};

例 node、propertyの変更・追加

Overlay

/dts-v1/;
/plugin/;
&mydev0{
      status = "disabled";
};

&{/mydev-1}{
    ichiri,serial-number = "henkou shita ";
  bar {
        ichiri = "test";
    }
};

&で参照します。

&mydev0は、ラベル参照します。階層パスが深くなっっても簡単なラベルで参照できるので便利ですが、この下で出てくる-@オプション出ないといけません。

status property は全てのノードが持っていて、記述しなければ”ok”となるので、ここでは”disabled”を指定しています。

&{/mydev-1}は直接階層パスを指定しています。階層が深くなれば、そのパスを記述して指定します。mydev-1のichiri,serial-number propertyを上書きして、ichiriというpropertyを持ったbarノードを新たに追加しています。

ベースdts

/dts-v1/;
/{
    // struct platform_device
    mydev0: mydev-0 {
        compatible = "my-dev100","my-dev0";
        ichiri,size=<512>;
        ichiri,serial-number="TEKITOUNA123";
        ichiri,perm=<0x11>;
    };

    mydev1: mydev-1 {
        compatible = "my-dev1";
        ichiri,size=<1024>;
        ichiri,serial-number="TEKITOUNA456";
        ichiri,perm=<0x11>;
    };
};

厳密には、このファイルはdtbiでベースdtbに組み込まれるインクルードファイル。 mydev0とmydev1はラベルで、-@オプションでコンパイルすれば__symbol__として残り参照に使えますが、ラベルは消えてしまい、Overlayから参照できません。

例 node、propertyの削除

不要なnodeやpropertyを削除して、dtbを最小にする事で高速起動に寄与できます。

Overlay

/dts-v1/;
/plugin/;
&mydev0{
     /delete-property/ ichiri,perm;
};

&{/mydev-1}{
    /delete-node/ foo@0xa0;
         // 或いは
    /delete-node/ foo_label;
};
  • /delete-property/ プロパティ削除:これを/delete-node/と同じ参照ノード内に記述する場合は、参照ノードをもう一つ書いて、その中に/delete-property/を記述すしないと『must precede subnodes』となる。
  • /delete-node/ ノード削除
  • /omit-if-no-ref/ ノード名;  — Kernel 4.16までは使用できない

ベースdts

/dts-v1/;
/{
    // struct platform_device
    mydev0: mydev-0 {
        compatible = "my-dev100","my-dev0";
        ichiri,size=<512>;
        ichiri,serial-number="TEKITOUNA123";
        ichiri,perm=<0x11>;
    };

    mydev1: mydev-1 {
        compatible = "my-dev1";
        ichiri,size=<1024>;
        ichiri,serial-number="TEKITOUNA456";
        ichiri,perm=<0x11>;
        foo_label:foo@0xa0{
            prooerty=<0xa0 0xff>
        }
    };
};

ヘッダ

Overlayファイルでは、新旧Syntax共に必ず/dts-v1/;と/plugin/;で始まるのが決まりです。

/plugin/;

コンパイル時に存在しないノードへの未定義の参照を可能にするには、オーバーレイ DT の .dts ファイルのヘッダーにタグ /plugin/ を挿入する必要があります。

Overlayとdtsi違い

構文はOverlayもdtsiも同じです。 違いは、Overlayは既にビルドされてバイナリ化となったdtbに適用する事ができます。 of_overlay_fdt_apply()でdtbに簡単にOverlayを追加する事ができます。 既に存在するシステムの.dtbを逆変換してシンボルやパスを確認して、dtboで上書きや新規追加ができます。

dtsiはベースとなるdtsやその他のdtsiのソースの状態からビルドしなければなりません。 既にdtbとなっていたら適用できません。(dtbを逆変換してdtsにして、dtsiをincludeしたらできる気がしますが検証していないので確かでは無いです。)

なのでdtsや他のdtsiからビルドできるときは、最上位のdtsかdtsiで作りビルドするのが良いです。 dtsやdtsiがなくdtbしか無いときは、dtboを使うのが良いと思います。

Overlay使用要件

  1. Version 1.44以上のdtc(Device Tree Compiler)で、-@オプション付けてコンパイルします。
  2. そしてLinux Kernelは4.14以上でないといけません。
  3. ベースdtbとOverlayのdtboの両方に-@(__symbols__)を渡さないといけません。

Overlay コンパイル

ベースとなるdtbは対象ボード用にクロスコンパイルしなければなりませんが、Overlayはクロスコンパイルでなくて構いません。 私の場合、uBuntu PCでdtc実行をして、nfsでBBB(Beaglebone black)のDDRに送っています。

Overlay方法

  1. コンパイルするpcのLinux Kernelバージョンが4.14以上であることを確認。
  2. dtcのバージョンが1.4以上であることを確認。
  3. dtcがなければインストールする。
  4. dtcに-@を付けてコンパイル。
  5. 生成したdtboファイルを、DDRの指定アドレスに入れて先に読み込んだベースdtbに付け加える。(御述のU-BOOTのboot.scr参照)
$ uname -r
5.15.0-60-generic

$ dtc -v
Version: DTC 1.5.0

$ sudo apt update
$ sudo apt install device-tree-compiler
$ dtc -@ -I dts -O dtb -o my_overlay.dtbo my_overlay.dts

Buildroot使用時

Buildroot v2 のデフォルトでは生成されるベースdtbに-@を渡さないのでOverlayを使えません。 多分、Buildrootを使わずKernelをビルドする時も、下に書いてあるDTC_FLAGS=–symbolsが無いとOverlayは使えないと思います。

Buildrootのmenuconfigで『Build Device Tree with overlay support [=n]』にチェックを入れて[=y]にしなければなりません。 これをしないと、Buildrootで生成されたBase dtbに__symbols__ノードが入らず、U-BOOTでOverlayをapply (適用)する時に、以下のU-BOOTのエラーで__symbols__が無いと言われりKernelを起動できません。 (でも、md ${fdtovaddr}で見たら、最初のアドレスのmagic領域にはFDT_MAGICマクロ値の0xd00dfeedが入ってたからBADMAGICじゃないんだけど、FDT_ERR_BADMAGICになっている。 多分、このエラーのエラーコードマクロがないからかな?)

=> fdt apply ${fdtovaddr}                                                       
failed on fdt_overlay_apply(): FDT_ERR_BADMAGIC                                 
base fdt does did not have a /__symbols__ node                                  
make sure you've compiled with -@

__symbols__

以下は、Buildroot menuconfigで、『Build Device Tree with overlay support [=y]』にして、再ビルドして生成された、ベースdtbをdtsに逆変換して中を見ると、[=n]の時には無かった『__symbols__』が出来ています。

デバイス追加

Buildrootで生成したdtbをそのまま使う場合は、Buildroot menuconfigで『Build Device Tree with overlay support [=y]』にしてdtbを生成したらOKですが、デバイスを追加したい場合、Buildrootで生成されたdtsに自分のdtsiをincludeして、dtbを再コンパイルしなければなりません。 この時、dtcではコンパイル出来ず、make ARCH=arm CROSS-COMPILE= … arm-linux-gnueabihf-gcc でコンパイルしなければなりません。 そのままでは、__symbols__が入らないので、DTC_FLAGS=–symbolsを設定してコンパイルすると、__symbols__が入りました。

export DTC_FLAGS=--symbols

Makefile

Buildrootで生成したファイルの再コンパイルには、ディレクトリ階層が深く、毎回記述するのが面倒さいので、Makefileを使っています。

Buildrootで使用したコンパイラやLinux Kernelソースは、Buildrootディレクトリの下のoutput以下に入っています。 再コンパイルする際、同じコンパイラで同じKernelソースを使ってビルドしなければ動作しないので、コンパイラとKernelの場所を指定して、makeにDTC_FLAGS=–symbolsをつけてコンパイルするとちゃんと__symbols__が付いていました。 因みにBuildrootで生成されたdtsやdtsi群はLinuxディレクトリ下の./arch/arm/boot/dts/にあります。 

Makefile
DTS_FILE=am335x-boneblack
_X_CC=~/BBB_Workspace/Downloads/buildroot-2022.11/output/host/opt/ext-toolchain/bin/arm-linux-gnueabihf-
KERNEL_SRC_DIR=~/BBB_Workspace/Downloads/buildroot-2022.11/output/build/linux-5.10.145-cip17-rt7/

dtb:
# cp $(DTS_FILE)-mydev.dtsi $(DTS_DIR)
	cp $(DTS_FILE)-mydev.dtsi $(DTS_DIR)
	make ARCH=arm CROSS_COMPILE=$(_X_CC) -C $(KERNEL_SRC_DIR) DTC_FLAGS=--symbols dtbs $(DTS_FILE).dtb

以下は逆変換してa.dtsを生成しています。 a.dts生成後、viでa.dtsを開いてsymbolsを検索してみました。 DTC_FLAGS=–symbolsを付けた時は__symbols__が存在していました。

//dtc -I dtb -O dts -o 出力ファイル名 逆変換したいバイナリファイル名 
$ dtc -I dtb -O dts -o a.dts am335x-boneblack.dtb 

__symbols__ ありのフィルサイズ

ファイルサイズが40%ほど増えていますね。 しかし容量的には小さいので、Overlayをあててデバッグもできるので、私は常に__symbols__ありが良いと思います。 

-rwxr-xr-x 1 root   root     88832  2月 17 09:21 am335x-boneblack.dtb   <--- __symbols__  あり DTC_FLAGS=--symbols 設定
-rwxr-xr-x 1 root   root     63293  2月 17 09:24 am335x-boneblack.dtb   <--- __symbols__  なし

それは、__symbols__ノード無いにOverlayが&ラベル名で参照できるように、全てのノードのフルパスリンクを記載しているからです。 逆に、この__symbols__がなければOverlayは参照できないので動作しません。 なので、Overlayを使わない時は、__symbols__が入らないようにした方がメモリーの限りのある組込みにはいいですね。

__symbols__の一番最後に、includeしたdtsiファイルのデバイスのmydev0~mydev3がちゃんと入っていますね。

	__symbols__ {
		mpu_gate = "/cpus/idle-states/mpu_gate";
		cpu0_opp_table = "/opp-table";
		ocp = "/ocp";
		l4_wkup = "/ocp/interconnect@44c00000";
		wkup_m3 = "/ocp/interconnect@44c00000/wkup_m3@100000";
		prcm = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0";
		prcm_clocks = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks";
		clk_32768_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/clk_32768_ck";
		clk_rc32k_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/clk_rc32k_ck";
		virt_19200000_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/virt_19200000_ck";
		virt_24000000_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/virt_24000000_ck";
		virt_25000000_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/virt_25000000_ck";
		virt_26000000_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/virt_26000000_ck";
		tclkin_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/tclkin_ck";
		dpll_core_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_core_ck@490";
		dpll_core_x2_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_core_x2_ck";
		dpll_core_m4_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_core_m4_ck@480";
		dpll_core_m5_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_core_m5_ck@484";
		dpll_core_m6_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_core_m6_ck@4d8";
		dpll_mpu_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_mpu_ck@488";
		dpll_mpu_m2_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_mpu_m2_ck@4a8";
		dpll_ddr_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_ddr_ck@494";
		dpll_ddr_m2_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_ddr_m2_ck@4a0";
		dpll_ddr_m2_div2_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_ddr_m2_div2_ck";
		dpll_disp_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_disp_ck@498";
		dpll_disp_m2_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_disp_m2_ck@4a4";
		dpll_per_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_per_ck@48c";
		dpll_per_m2_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_per_m2_ck@4ac";
		dpll_per_m2_div4_wkupdm_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_per_m2_div4_wkupdm_ck";
		dpll_per_m2_div4_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_per_m2_div4_ck";
		clk_24mhz = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/clk_24mhz";
		clkdiv32k_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/clkdiv32k_ck";
		l3_gclk = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/l3_gclk";
		pruss_ocp_gclk = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/pruss_ocp_gclk@530";
		mmu_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/mmu_fck@914";
		timer1_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/timer1_fck@528";
		timer2_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/timer2_fck@508";
		timer3_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/timer3_fck@50c";
		timer4_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/timer4_fck@510";
		timer5_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/timer5_fck@518";
		timer6_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/timer6_fck@51c";
		timer7_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/timer7_fck@504";
		usbotg_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/usbotg_fck@47c";
		dpll_core_m4_div2_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/dpll_core_m4_div2_ck";
		ieee5000_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/ieee5000_fck@e4";
		wdt1_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/wdt1_fck@538";
		l4_rtc_gclk = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/l4_rtc_gclk";
		l4hs_gclk = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/l4hs_gclk";
		l3s_gclk = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/l3s_gclk";
		l4fw_gclk = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/l4fw_gclk";
		l4ls_gclk = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/l4ls_gclk";
		sysclk_div_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/sysclk_div_ck";
		cpsw_125mhz_gclk = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/cpsw_125mhz_gclk";
		cpsw_cpts_rft_clk = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/cpsw_cpts_rft_clk@520";
		gpio0_dbclk_mux_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/gpio0_dbclk_mux_ck@53c";
		lcd_gclk = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/lcd_gclk@534";
		mmc_clk = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/mmc_clk";
		gfx_fclk_clksel_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/gfx_fclk_clksel_ck@52c";
		gfx_fck_div_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/gfx_fck_div_ck@52c";
		sysclkout_pre_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/sysclkout_pre_ck@700";
		clkout2_div_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/clkout2_div_ck@700";
		clkout2_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks/clkout2_ck@700";
		prcm_clockdomains = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clockdomains";
		per_cm = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/per-cm@0";
		l4ls_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/per-cm@0/l4ls-clkctrl@38";
		l3s_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/per-cm@0/l3s-clkctrl@1c";
		l3_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/per-cm@0/l3-clkctrl@24";
		l4hs_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/per-cm@0/l4hs-clkctrl@120";
		pruss_ocp_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/per-cm@0/pruss-ocp-clkctrl@e8";
		cpsw_125mhz_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/per-cm@0/cpsw-125mhz-clkctrl@0";
		lcdc_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/per-cm@0/lcdc-clkctrl@18";
		clk_24mhz_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/per-cm@0/clk-24mhz-clkctrl@14c";
		wkup_cm = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/wkup-cm@400";
		l4_wkup_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/wkup-cm@400/l4-wkup-clkctrl@0";
		l3_aon_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/wkup-cm@400/l3-aon-clkctrl@14";
		l4_wkup_aon_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/wkup-cm@400/l4-wkup-aon-clkctrl@b0";
		mpu_cm = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/mpu-cm@600";
		mpu_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/mpu-cm@600/mpu-clkctrl@0";
		l4_rtc_cm = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/l4-rtc-cm@800";
		l4_rtc_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/l4-rtc-cm@800/l4-rtc-clkctrl@0";
		gfx_l3_cm = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/gfx-l3-cm@900";
		gfx_l3_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/gfx-l3-cm@900/gfx-l3-clkctrl@0";
		l4_cefuse_cm = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/l4-cefuse-cm@a00";
		l4_cefuse_clkctrl = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/l4-cefuse-cm@a00/l4-cefuse-clkctrl@0";
		prm_per = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/prm@c00";
		prm_wkup = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/prm@d00";
		prm_device = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/prm@f00";
		prm_gfx = "/ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/prm@1100";
		gpio0_target = "/ocp/interconnect@44c00000/segment@200000/target-module@7000";
		gpio0 = "/ocp/interconnect@44c00000/segment@200000/target-module@7000/gpio@0";
		uart0 = "/ocp/interconnect@44c00000/segment@200000/target-module@9000/serial@0";
		i2c0 = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0";
		tps = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/tps@24";
		dcdc1_reg = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/tps@24/regulators/regulator@0";
		dcdc2_reg = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/tps@24/regulators/regulator@1";
		dcdc3_reg = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/tps@24/regulators/regulator@2";
		ldo1_reg = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/tps@24/regulators/regulator@3";
		ldo2_reg = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/tps@24/regulators/regulator@4";
		ldo3_reg = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/tps@24/regulators/regulator@5";
		ldo4_reg = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/tps@24/regulators/regulator@6";
		baseboard_eeprom = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/baseboard_eeprom@50";
		baseboard_data = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/baseboard_eeprom@50/baseboard_data@0";
		tda19988 = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/tda19988@70";
		hdmi_0 = "/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/tda19988@70/ports/port@0/endpoint@0";
		tscadc = "/ocp/interconnect@44c00000/segment@200000/target-module@d000/tscadc@0";
		am335x_adc = "/ocp/interconnect@44c00000/segment@200000/target-module@d000/tscadc@0/adc";
		scm = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0";
		am33xx_pinmux = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800";
		user_leds_s0 = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/user_leds_s0";
		i2c0_pins = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/pinmux_i2c0_pins";
		i2c2_pins = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/pinmux_i2c2_pins";
		uart0_pins = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/pinmux_uart0_pins";
		clkout2_pin = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/pinmux_clkout2_pin";
		cpsw_default = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/cpsw_default";
		cpsw_sleep = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/cpsw_sleep";
		davinci_mdio_default = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/davinci_mdio_default";
		davinci_mdio_sleep = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/davinci_mdio_sleep";
		mmc1_pins = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/pinmux_mmc1_pins";
		emmc_pins = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/pinmux_emmc_pins";
		nxp_hdmi_bonelt_pins = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/nxp_hdmi_bonelt_pins";
		nxp_hdmi_bonelt_off_pins = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/nxp_hdmi_bonelt_off_pins";
		mcasp0_pins = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800/mcasp0_pins";
		scm_conf = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0";
		phy_gmii_sel = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/phy-gmii-sel";
		scm_clocks = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks";
		sys_clkin_ck = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/sys_clkin_ck@40";
		adc_tsc_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/adc_tsc_fck";
		dcan0_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/dcan0_fck";
		dcan1_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/dcan1_fck";
		mcasp0_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/mcasp0_fck";
		mcasp1_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/mcasp1_fck";
		smartreflex0_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/smartreflex0_fck";
		smartreflex1_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/smartreflex1_fck";
		sha0_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/sha0_fck";
		aes0_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/aes0_fck";
		rng_fck = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/rng_fck";
		ehrpwm0_tbclk = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/ehrpwm0_tbclk@44e10664";
		ehrpwm1_tbclk = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/ehrpwm1_tbclk@44e10664";
		ehrpwm2_tbclk = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/scm_conf@0/clocks/ehrpwm2_tbclk@44e10664";
		usb_ctrl_mod = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/control@620";
		wkup_m3_ipc = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/wkup_m3_ipc@1324";
		edma_xbar = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/dma-router@f90";
		scm_clockdomains = "/ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/clockdomains";
		timer1_target = "/ocp/interconnect@44c00000/segment@200000/target-module@31000";
		timer1 = "/ocp/interconnect@44c00000/segment@200000/target-module@31000/timer@0";
		wdt2 = "/ocp/interconnect@44c00000/segment@200000/target-module@35000/wdt@0";
		rtc = "/ocp/interconnect@44c00000/segment@200000/target-module@3e000/rtc@0";
		l4_per = "/ocp/interconnect@48000000";
		uart1 = "/ocp/interconnect@48000000/segment@0/target-module@22000/serial@0";
		uart2 = "/ocp/interconnect@48000000/segment@0/target-module@24000/serial@0";
		i2c1 = "/ocp/interconnect@48000000/segment@0/target-module@2a000/i2c@0";
		spi0 = "/ocp/interconnect@48000000/segment@0/target-module@30000/spi@0";
		mcasp0 = "/ocp/interconnect@48000000/segment@0/target-module@38000/mcasp@0";
		mcasp1 = "/ocp/interconnect@48000000/segment@0/target-module@3c000/mcasp@0";
		timer2_target = "/ocp/interconnect@48000000/segment@0/target-module@40000";
		timer2 = "/ocp/interconnect@48000000/segment@0/target-module@40000/timer@0";
		timer3 = "/ocp/interconnect@48000000/segment@0/target-module@42000/timer@0";
		timer4 = "/ocp/interconnect@48000000/segment@0/target-module@44000/timer@0";
		timer5 = "/ocp/interconnect@48000000/segment@0/target-module@46000/timer@0";
		timer6 = "/ocp/interconnect@48000000/segment@0/target-module@48000/timer@0";
		timer7 = "/ocp/interconnect@48000000/segment@0/target-module@4a000/timer@0";
		gpio1 = "/ocp/interconnect@48000000/segment@0/target-module@4c000/gpio@0";
		mmc1 = "/ocp/interconnect@48000000/segment@0/target-module@60000/mmc@0";
		elm = "/ocp/interconnect@48000000/segment@0/target-module@80000/elm@0";
		mailbox = "/ocp/interconnect@48000000/segment@0/target-module@c8000/mailbox@0";
		mbox_wkupm3 = "/ocp/interconnect@48000000/segment@0/target-module@c8000/mailbox@0/wkup_m3";
		hwspinlock = "/ocp/interconnect@48000000/segment@0/target-module@ca000/spinlock@0";
		i2c2 = "/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0";
		cape_eeprom0 = "/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/cape_eeprom0@54";
		cape0_data = "/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/cape_eeprom0@54/cape_data@0";
		cape_eeprom1 = "/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/cape_eeprom1@55";
		cape1_data = "/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/cape_eeprom1@55/cape_data@0";
		cape_eeprom2 = "/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/cape_eeprom2@56";
		cape2_data = "/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/cape_eeprom2@56/cape_data@0";
		cape_eeprom3 = "/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/cape_eeprom3@57";
		cape3_data = "/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/cape_eeprom3@57/cape_data@0";
		spi1 = "/ocp/interconnect@48000000/segment@100000/target-module@a0000/spi@0";
		uart3 = "/ocp/interconnect@48000000/segment@100000/target-module@a6000/serial@0";
		uart4 = "/ocp/interconnect@48000000/segment@100000/target-module@a8000/serial@0";
		uart5 = "/ocp/interconnect@48000000/segment@100000/target-module@aa000/serial@0";
		gpio2 = "/ocp/interconnect@48000000/segment@100000/target-module@ac000/gpio@0";
		gpio3_target = "/ocp/interconnect@48000000/segment@100000/target-module@ae000";
		gpio3 = "/ocp/interconnect@48000000/segment@100000/target-module@ae000/gpio@0";
		dcan0 = "/ocp/interconnect@48000000/segment@100000/target-module@cc000/can@0";
		dcan1 = "/ocp/interconnect@48000000/segment@100000/target-module@d0000/can@0";
		mmc2 = "/ocp/interconnect@48000000/segment@100000/target-module@d8000/mmc@0";
		epwmss0 = "/ocp/interconnect@48000000/segment@300000/target-module@0/epwmss@0";
		ecap0 = "/ocp/interconnect@48000000/segment@300000/target-module@0/epwmss@0/ecap@100";
		ehrpwm0 = "/ocp/interconnect@48000000/segment@300000/target-module@0/epwmss@0/pwm@200";
		epwmss1 = "/ocp/interconnect@48000000/segment@300000/target-module@2000/epwmss@0";
		ecap1 = "/ocp/interconnect@48000000/segment@300000/target-module@2000/epwmss@0/ecap@100";
		ehrpwm1 = "/ocp/interconnect@48000000/segment@300000/target-module@2000/epwmss@0/pwm@200";
		epwmss2 = "/ocp/interconnect@48000000/segment@300000/target-module@4000/epwmss@0";
		ecap2 = "/ocp/interconnect@48000000/segment@300000/target-module@4000/epwmss@0/ecap@100";
		ehrpwm2 = "/ocp/interconnect@48000000/segment@300000/target-module@4000/epwmss@0/pwm@200";
		lcdc = "/ocp/interconnect@48000000/segment@300000/target-module@e000/lcdc@0";
		lcdc_0 = "/ocp/interconnect@48000000/segment@300000/target-module@e000/lcdc@0/port/endpoint@0";
		rng = "/ocp/interconnect@48000000/segment@300000/target-module@10000/rng@0";
		l4_fw = "/ocp/interconnect@47c00000";
		l4_fast = "/ocp/interconnect@4a000000";
		mac = "/ocp/interconnect@4a000000/segment@0/target-module@100000/ethernet@0";
		davinci_mdio = "/ocp/interconnect@4a000000/segment@0/target-module@100000/ethernet@0/mdio@1000";
		ethphy0 = "/ocp/interconnect@4a000000/segment@0/target-module@100000/ethernet@0/mdio@1000/ethernet-phy@0";
		cpsw_emac0 = "/ocp/interconnect@4a000000/segment@0/target-module@100000/ethernet@0/slave@200";
		cpsw_emac1 = "/ocp/interconnect@4a000000/segment@0/target-module@100000/ethernet@matab0/slave@300";
		pruss_tm = "/ocp/interconnect@4a000000/segment@0/target-module@300000";
		l4_mpuss = "/ocp/interconnect@4b140000";
		intc = "/ocp/interrupt-controller@48200000";
		edma = "/ocp/target-module@49000000/dma@0";
		edma_tptc0 = "/ocp/target-module@49800000/dma@0";
		edma_tptc1 = "/ocp/target-module@49900000/dma@0";
		edma_tptc2 = "/ocp/target-module@49a00000/dma@0";
		mmc3 = "/ocp/target-module@47810000/mmc@0";
		usb = "/ocp/target-module@47400000";
		usb0_phy = "/ocp/target-module@47400000/usb-phy@1300";
		usb0 = "/ocp/target-module@47400000/usb@1400";
		usb1_phy = "/ocp/target-module@47400000/usb-phy@1b00";
		usb1 = "/ocp/target-module@47400000/usb@1800";
		cppi41dma = "/ocp/target-module@47400000/dma-controller@2000";
		ocmcram = "/ocp/sram@40300000";
		pm_sram_code = "/ocp/sram@40300000/pm-code-sram@0";
		pm_sram_data = "/ocp/sram@40300000/pm-data-sram@1000";
		emif = "/ocp/emif@4c000000";
		gpmc = "/ocp/gpmc@50000000";
		sham_target = "/ocp/target-module@53100000";
		sham = "/ocp/target-module@53100000/sham@0";
		aes_target = "/ocp/target-module@53500000";
		aes = "/ocp/target-module@53500000/aes@0";
		vmmcsd_fixed = "/fixedregulator0";
		clk_mcasp0_fixed = "/clk_mcasp0_fixed";
		clk_mcasp0 = "/clk_mcasp0";
		dailink0_master = "/sound/simple-audio-card,cpu";
		mydev0 = "/mydev-0";
		mydev1 = "/mydev-1";
		mydev2 = "/mydev-2";
		mydev3 = "/mydev-3";
	};

U-BOOT

ここにブートスクリプトとしてテキストソースファイルを紹介します。 これをboot.scrに変換して、boot.scrをブートパーティションに入れておくとU-BOOT使用時にmy_overlay.dtboファイルをベースdtbに追加します。 私の場合、

  • microSDのブートパーティションに以下のファイルを入れています。
    • MLO(TI用のspl)
    • u-boot.img
    • boot.scr
  • uBuntu PCのTFTP用ディレクトリに以下ファイルを入れて、U-BOOT時にBBBのDDRに転送しています。
    • uImage — Linux Kernelイメージ
    • am335x-boneblack.dtb — クロスコンパイルされたベースdtb
  • uBuntu PCのNFS用ディレクトリに以下を入れています。 nfs設定で/srv/nfs/bbbを登録しているので、 uBuntu PCでは/srv/nfs/bbbと見えるのが、BBBからは/(ルート)と見えます。
    • ルートファイルシステム — BBBが立ち上がった時、このディレクトリをルートファイルシステムとして使います。 
    • (/srv/nfs/bbb)/lib/firm に my_overlay.dtboを入れています。 結果的に、TFTPでも良かったのですが、全てのoverly dtboファイルは/lib/firmwareに入れることと書かれてたので、ここに入れています。

スクリプトの説明

  1. echoは、boot.scrがちゃんと動作しているか確認できるように一応入れています。
  2. TFTPとNFS用に、自分(BBB)のipaddrとserverip(uBuntu)を設定。
  3. uImage(Kernel)を格納するDDRアドレス loadaddrを設定 (これらのアドレスはBBB用)。
  4. am335x-boneblack.dtbを格納するDDRアドレス fdtaddrを設定。
  5. my_overlay.dtboを格納するDDRアドレス fdtovaddrを設定。
  6. Kernelが立ち上がった時に、ルートファイルシステムをNFS通信でマウントする設定。
  7. 勝手にどんなイメージもDDRに格納しないように、autoload noに設定。
  8. TFTPでuImageを転送しDDRのアドレス${loadaddr}からに格納。
  9. TFTPでam335x-boneblack.dtbをDDRのアドレス${fdtaddr}からに格納。
  10. fdt addrで${fdtaddr}に入っているのがベースdtbである事を宣言。
  11. fdt resize 8192で、ベースdtbにoverlayを追加する為の予約スペースを確保。
  12. NFSでmy_overlay.dtboを転送しDDRのアドレス${fdtovaddr}からに格納。(U-BOOTは設定なしでデフォルトでNFSやTFTPを使えます。)
  13. fdt apply で${fdtovaddr}に入っているoverlayをベースdtbに適用します。 この時、ベースdtbに__symbols__がないと、エラーになります。
  14. bootargsはKernel Command LineとしてKernelが起動した時に使う設定を入れています。
  15. 14まで終わったら、uImageとベースdtbのアドレスを指定しKernelをブートします。
boot_nfs_ov.cmd
echo ********** my boot.scr start ***************
setenv ipaddr    192.168.11.11
setenv serverip  192.168.11.5
setenv loadaddr  0x82000000
setenv fdtaddr   0x87f00000
setenv fdtovaddr 0x87fc0000

echo ********* Booting from microSD ...*********
setenv rootpath /srv/nfs/bbb,nfsvers=3,tcp ip=dhcp,nolock,wsize=1024,rsize=1024 rootwait
setenv autoload no

tftpboot ${loadaddr} uImage
tftpboot ${fdtaddr}  am335x-boneblack.dtb
fdt addr ${fdtaddr}
fdt resize 8192

nfs ${fdtovaddr} /srv/nfs/bbb/lib/firmware/my_overlay.dtbo
fdt apply ${fdtovaddr}

setenv bootargs console=ttyS0,115200n8 root=/dev/nfs rw rootfstype=nfs ip=${ipaddr} nfsroot=${serverip}:${rootpath} nfsrootdebug debug
bootm ${loadaddr} - ${fdtaddr}

これを以下のコマンドでboot.scrに変換。

S mkimage -A arm -O linux -T script -C none -a 0 -e 0 -d boot_nfs_ov.cmd boot.scr

Overlayの例

以下のデバイスツリーコードは正確にはOverlayでなくdevice tree include ファイルなのですが、書き方はOverlayと全く同じです。

&で始まる部分は、下の階層のデバイスツリーの__symbol__に書かれた、ノードを参照して、そのノードのプロパティを追加・変更しています。

2〜3行目の/{ }の中にノードを書くと新たなノードが追加されます。 ここでは何も書いてないので省略できるのですが、一応残しています。

以下のコードはUltra96V2のWifiを起動させるために実際に書いたコードです。

/include/ "system-conf.dtsi"
/ {
};
&sdhci1 {
    status = "okay";
    bus-width = <0x4>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_sdhci1_default>;
    xlnx,mio_bank = <0>;
    non-removable;
    disable-wp;
    #address-cells = <1>;
    #size-cells = <0>;
};
&wlcore{
        compatible    = "microchip,wilc3000";
        irq-gpios     = <&gpio 76 GPIO_ACTIVE_HIGH>; /* MIO76 WLAN_IRQ    1V8 */
        reset-gpios   = <&gpio  7 GPIO_ACTIVE_HIGH>; /* MIO7  RADIO_RST_N 1V8 */
        post-power-on-delay-ms = <0x0a>;
        chip_en-gpios = <&gpio  8 GPIO_ACTIVE_HIGH>; /* MIO8  RADIO_EN    1V8 */
        status        = "okay";
        reg           = <0>;
        bus-width     = <4>;
};

&sdio_pwrseq{
    chip_en_gpios=<&gpio 8 1>;
};

&uart0 {
    bluetooth {
        compatible = "microchip,wilc3000";
    };  
};

参考

Devicetree Overlay Notes — The Linux Kernel documentation

その他 syntax

  • /plugin/ directive
  • __overlay__ node
  • __symbols__ node
  • __fixups__ node
  • __local_fixups__ node

ラベルが無いnodeの書き換え

以下は、petalinuxの場合にラベルが無いchosenノードのbootargsの値を上書きする際に例。 検証していないので今後の為のメモ書きです。

$ vi ./project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
/{
	chosen {
		bootargs = "earlycon console=ttyPS0,115200 clk_ignore_unused cpuidle.off=1";
	};
};

コメント