この記事の前提
- C-5/C-6で、ブートローダーがカーネルとDTBを準備する流れを見た方を想定します
- Device Treeの読み方と小さな変更までを扱い、ドライバ実装は扱いません
- コード例とコマンド出力は説明用です。実機のモデル、OS、接続デバイスに合わせて確認してください
はじめに
C-6の説明用ログには、.dtbというファイルが登場しました。今回は、このDevice Treeが何を記述し、Linuxがどう使うかを整理します。
DTS、DTSI、DTB、overlayと似た用語が並びますが、押さえる流れは「ハードウェアの事実を記述し、起動時にカーネルへ渡す」です。
Device Treeが必要な理由
PCIやUSBのように列挙の仕組みを持つバスでは、OSが接続デバイスから情報を取得できます。一方、SoC内蔵ペリフェラルや基板直結のデバイスには、OSが実装状態を完全には自動判定できないものがあります。
I2Cでは応答するアドレスを走査できますが、その結果だけで正確な型番、割り込み線、リセットGPIO、電源、クロックまで確定できません。そこで、基板上の構成をデータとしてOSへ渡します。
Linuxカーネルの公式資料では、Device Treeを「OSが機械の詳細をハードコードせずに済むよう、ハードウェアを記述するデータ構造」と説明しています。Linux and the Devicetree
Device Tree自体がレジスタを設定して機器を動かすわけではありません。記述を読んだブートローダーやカーネル、デバイスドライバが、必要な初期化を行います。
DTS、DTSI、DTB、overlay
| 用語 | 役割 | 注意点 |
|---|---|---|
| DTS | Device Tree Source。ボードや構成を記述するソース | .dts |
| DTSI | 複数DTSからincludeする共通記述 |
.dtsi。SoC共通部などに使われる |
| DTB | DTSをdtcで変換したFlattened Device Treeのバイナリ |
カーネルへ渡される形式 |
| overlay/DTBO | ベースツリーへ適用する差分 | ブート前適用と実行時適用があり、仕組みは環境依存 |
| binding | 各デバイスのノード名・プロパティ・制約を定める仕様 | LinuxではYAML schemaによる検証が進んでいる |
典型的には、SoC共通のDTSIをボードDTSがincludeし、ボードで使用するI/O、接続デバイス、GPIOなどの差分を記述します。ただし、ファイル分割の方針はベンダーBSPやカーネル版で異なります。
DTSの中身を読む
UARTとI2C接続OLEDを説明用に簡略化した例です。実際に必要なプロパティは、対象カーネルのbindingを確認してください。
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&uart1_pins>;
status = "okay";
};
&i2c0 {
status = "okay";
oled@3c {
compatible = "solomon,ssd1306";
reg = <0x3c>;
};
};
status
ノードを利用可能として扱うかを示します。よく見る値は"okay"と"disabled"です。statusが存在しない場合の扱いも仕様で定義されているため、「すべてのノードに必須」ではありません。
compatible
ハードウェアがどのbindingに適合するかを示す文字列です。Linuxはこの値などを用いて、対応するドライバとのマッチングを行います。
「ドライバ名を直接指定する文字列」ではありません。ハードウェアを表す安定した識別子であり、利用できる文字列と必須プロパティはカーネルのDocumentation/devicetree/bindings/で確認します。
regなどのプロパティ
regは親バスにおけるアドレスなどを表します。値のセル数と意味は、親ノードの#address-cells、#size-cellsやbindingで決まります。割り込み、クロック、リセット、GPIO、電源なども、bindingに従って記述します。
pinctrl
コントローラーを有効にしても、信号が目的の端子へ割り当てられているとは限りません。pinmux、プルアップ/プルダウン、ドライブ能力などをpinctrlで指定する構成があります。
Raspberry PiでI2Cを有効にする
Raspberry Pi OSでは、config.txtからDevice Treeパラメーターとoverlayを利用できます。Bookworm世代の標準パスは/boot/firmware/config.txtです。
dtparam=i2c_arm=on
再起動後、I2Cデバイスファイルを確認します。
ls -l /dev/i2c*
i2c-toolsを導入した環境では、バス番号を確認してから走査できます。
sudo apt install i2c-tools
sudo i2cdetect -y 1
SSD1306系モジュールがアドレス0x3cへ応答する構成なら、説明上は次のように見えます。
0 1 2 3 4 5 6 7 8 9 a b c d e f
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
これはそのアドレスに応答があったことを示すだけで、SSD1306だと識別した結果ではありません。カーネルドライバがアドレスを使用中の場合はUUと表示されることもあります。また、走査処理が不具合を起こすデバイスもあるため、対象デバイスの仕様を確認し、製品運用中にむやみに実行しません。
Raspberry Piのdtparamやdtoverlayの適用方法は、公式Device Tree設定資料と/boot/firmware/overlays/READMEで確認できます。
カーネルが受け取ったツリーを確認する
ソースファイルではなく、実行中のカーネルが受け取った内容を確認します。
ls /sys/firmware/devicetree/base
device-tree-compilerが入っていれば、ファイルシステム表現からDTS形式へ変換できます。
sudo apt install device-tree-compiler
dtc -I fs -O dts /sys/firmware/devicetree/base | less
逆変換時の警告が、直ちに実機の異常を意味するとは限りません。元のラベルやinclude構造はDTBに残らないため、逆変換結果は元ソースと同じ見た目にはなりません。
確認したいノードがある場合は、次も組み合わせます。
dmesg | grep -i -E 'i2c|ssd130|probe|defer'
ls -l /sys/bus/i2c/devices/
変更時のハマりどころ
- bindingを確認せずプロパティを書く:似たデバイスでも必須項目が異なります。対象カーネルのYAML schemaとドライバを確認します
-
compatibleの誤記:ドライバがマッチしません。ビルド時のdtbs_checkや起動後のdmesgで確認します - コントローラーと子デバイスを混同する:I2Cバスを有効にする記述と、バス上のデバイスを記述する作業は別です
-
pinctrlや電源を見落とす:
status = "okay"だけでは、端子、クロック、レギュレーターがそろわない場合があります - BSPの構成要素を別々に更新する:カーネル、DTB、overlay、ブートローダーを、ベンダーが検証した組み合わせから外す場合は互換性を評価します
-
編集対象を間違える:別パーティションや別ファイルの
config.txt、別DTBを編集していることがあります。ブートログと実行中ツリーで確認します
Device Treeを契約書として扱う
SoMと自社キャリアを組み合わせる場合、回路図の変更とDevice Treeの変更を対応づけます。
| 回路側の変更 | Device Treeで確認する項目 |
|---|---|
| UARTの接続先を変更 | ノード、pinctrl、alias、console指定 |
| I2Cデバイスを追加 | 親バス、compatible、reg、割り込み |
| GPIO極性を変更 | GPIO指定子とactive high/low |
| PHYやクロックを変更 | PHYノード、clock、reset、電源 |
| 基板リビジョン追加 | 対応DTS/overlayと識別方法 |
Device Treeは回路図の代わりではなく、Linuxが必要とするハードウェア情報の表現です。回路図、部品表、DTS、ドライバ、評価項目をまとめてレビューすると、基板改版時の漏れを減らせます。
実行中のモデルとaliasを追う
手元のLinuxボードで、モデル名と起動引数を確認してみてください。
tr -d '\0' < /sys/firmware/devicetree/base/model; echo
cat /proc/cmdline
次に/sys/firmware/devicetree/base/aliasesを開き、serial0やi2cが実際のどのノードを指すかを追うと、Device TreeがLinuxのデバイス名へつながる感覚をつかめます。
まとめと次回
- Device Treeは、OSが自動判定できない基板構成を記述するデータ
- DTS/DTSIからDTBを作り、必要に応じてoverlayを適用する
- 読む鍵は
status、compatible、reg、pinctrlとbinding - I2Cアドレス走査は型番識別ではなく、ドライバ使用中は
UUになる場合もある - 実行中のツリーと
dmesgを確認し、編集したソースだけで判断しない
次回C-8では、rootfsと自動起動を扱います。自作アプリを、電源投入後にサービスとして起動する構成へ進めます。
