Device Tree 入門

初心者のためのLinuxカーネル 
誰でも理解できるチュートリアル！ 

デバイスツリーチュートリアル（ARM）

概要 

 Linuxカーネルは、起動しているボード（マシンタイプ）、使用しているすべてのデバイスのアドレス（デバイス/バスアドレス）、割り込み番号（irq）、mfpピン設定（ピンマックス）など、ハードウェア全体の説明を必要とします。 / gpios）メモリサイズ、カーネルコマンドラインなどのようなボードレベルの情報も… 

デバイスツリー以前は、これらの情報はすべてボードファイルの巨大なクラスタに設定されていました。 また、コマンドライン、メモリサイズなどの情報は、ブートローダによってATAGSの一部としてレジスタR2（ARM）を介して渡されていました。 マシンタイプは、レジスタR1（ARM）で個別に設定します。 
現時点では、各カーネルコンパイルは特定のチップと特定のボードに対してのみ行われていました。 

そのため、すべてのARMプロセッサ用にカーネルをコンパイルし、カーネルに何らかの方法でハードウェアを検出させ、必要に応じて適切なドライバを適用させたいという待ち望みがありました。 
しかし、どうですか？  PCでは、初期レジスタはハードコードされており、残りの情報はBIOSによって提供されます。 しかしARMプロセッサはBIOSを持っていません。 
選択されたソリューションは、 Open Firmware （略称OF ）またはFlattened Device Tree （ FDT ）とも呼ばれるデバイスツリーでした。 これは基本的に、起動時にカーネルに役立つ情報を含むバイトコード形式のデータ構造です。 

ブートローダは、カーネルイメージとDTBの2つのバイナリをロードします。 
 DTBはデバイスツリーBLOBです。 ブートローダはATAGSではなくR2を介してDTBアドレスを渡します。R1レジスタは現在必要ありません。 

 1行ブックブック定義の場合「デバイスツリーは、システム内の物理デバイスを記述するノードを持つツリーデータ構造です」 

現在、デバイスツリーはARM、x86、Microblaze、PowerPC、およびSparcアーキテクチャでサポートされています。 



 I.デバイスツリーのコンパイル 


 scripts/dtc/にあるデバイスツリーコンパイラとそのソースコード。 
 ARMでは、すべてのデバイスツリーソースは/arch/arm/boot/dts/にあります。 
 Device Tree Blob（.dtb）はコンパイラによって生成され、ブートローダによってロードされ、ブート時にカーネルによって解析されるバイナリです。 


 $ scripts / dtc / dtc -I dts -O dtb -o / path / my_tree.dtb /arch/arm/boot/dts/my_tree.dts 

これはmy_tree.dtbという結果になります。 

 dtbからdtsを作成するため 


 $ scripts / dtc / dtc -I dtb -O dts -o / path / my_tree .dts / path / my_tree .dtb 

これはmy_tree.dtsという結果になります。 



 II.デバイスツリーの基本 



デバイスツリーの各モジュールはノードによって定義され、そのすべてのプロパティはそのノードの下に定義されます。 ドライバに応じて、子ノードまたは親ノードを持つことができます。 
たとえば、i2cバスで接続されたデバイスは、その親ノードとしてi2cを持ち、そのデバイスはi2cノードの子ノードの1つになり、i2cはその親としてapdバスを持つことができます。すべてがルートノードにつながります。ルートノードはすべての親です。（このセクションがより明確にするようになった後で例を心配しないでください。） 
デバイスツリーのルートの下には、通常、次の最も一般的な最上位ノードがあります。 
•  cpus ：システム内の各CPUを記述する各サブノード
•  メモリ ：RAMの場所とサイズを定義します
•  selected ：起動時にシステムファームウェアによって選択または定義されたパラメータを定義します。 実際には、その使用法の1つはカーネルのコマンドラインを渡すことです
•  エイリアス ：特定のノードへのショートカット
•  SoC内のバスを定義する1つ以上のノード 
• オンボードデバイスを定義する1つまたはモードのノード 



 III.デバイスツリー構造の例 


Here will take the example of a dummy dts code for explanation 

 #include "pxa910.dtsi"
 / {
 compatible = "mrvl,pxa910-dkb", "mrvl,pxa910";
 chosen {
 bootargs = "<boot args here>"; 
};
 memory {
 reg = <0x00000000 0x10000000>;
 }; 
soc { 
apb@d4000000 { uart1: uart@d4017000 {
 status = "okay";
 };
 twsi1: i2c@d4011000 {
 #address-cells = <1>
 #size-cells = <0>
 status = "okay";
 pmic: 88pm860x@34 { 

  compatible = "marvell,88pm860x";
 reg = <0x34>;
 interrupts = <4>; 
interrupt-parent = <&intc>; 
interrupt-controller; 
#interrupt-cells = <1>; 

 図1 

各モジュールは、1つのノードの下の1つの波括弧領域で定義されています。サブモジュールは、その内側にさらに定義できます。

最初の行から始めて上記のツリーを説明します： 

 #include：Cファイルと同じように、あらゆる頭のファイルを含む 
 。  dtsi ：拡張dtsファイル、単一のdtsには任意の数のdtsiを含めることができますが、他のdtsファイルを含めることはできませんでした 
 / ：ルートノード、デバイスのツリー構造はここから始まります 


 IV.プロパティ 



カーネルコードによって読み込まれるプロパティの形式としてdtsで定義されているデータがあります。いくつかの主要なプロパティについて読んでみましょう

Compatible

トップレベルのcompatibleプロパティは通常、ボードの互換文字列を定義します。 優先順位は常に最も特定度の高いものから優先度の低いものへと与えられます。 以前はDT_MACHINE構造体のdt_compatフィールドと一致していました。 
ドライバまたはバスノードの内部では、ハードウェアとそのドライバ間のリンクであるため、最も重要なものです。  各ノードは1つの互換性のある文字列に属し、互換性のある文字列に基づいてカーネルのみがデバイスドライバをデバイスツリーノードのデータと照合します。 
 カーネルドライバとそれが接続されるべき「互換性のある」エントリとの間の接続は、ドライバのソースコードの中で次のようなコードセグメントによって行われます。 

  static struct of_device_id dummy_of_match [] = {
   {.compatible = " marvell,88pm860x "、}、
     {}
   ;
 MODULE_DEVICE_TABLE（of、dummy_of_match）; 

 ドライバ内の上記のコードは、それを図1に示されているデバイスツリー構造に示されているpmicノードと照合します。 

 reg 

そのノード/デバイスのアドレスを定義します 

 ＃アドレスセル 

 propertyは、regプロパティのベースアドレス部分を形成するために必要なセル数（つまり32ビット値）を示します。 

 ＃サイズセル 

 regプロパティのサイズ部分 

割り込みコントローラ 

現在のノードが割り込みコントローラであることを示すブール値プロパティです。 

 ＃割り込みセル 

選択した割り込みコントローラによって管理されている割り込みのinterruptsプロパティのセル数を示します 

割り込み親 

現在のノードの割り込みコントローラを指すファンドルです。 一般に、メイン割り込みコントローラにはトップレベルの割り込み親定義があります。 

ラベルとノード名 

まず、ラベル（ "pmic"）とエントリの名前（ "88 pm860x@34"）。 ラベルは完全に省略されている可能性があり、エントリのノード名はこの形式（some-name @ address）に固定されているはずです。 これは、このドライバの名前が88pm860xで、アドレス34（この場合はi2cスレーブアドレス）で親バス（この場合はi2c）に接続されていることをカーネルに伝えます。  PMICは、dts内でこのノードを参照するためのファンドルとして使用できるラベルです。 



 V. DTSからリソースを入手する 



以下は、DTSからさまざまなプロパティを読み取るための、現在のカーネル（4.3）におけるいくつかの主要なAPIです。 

 of_address_to_resource： resプロパティで定義されたデバイスのメモリアドレスを読み込む 

 irq_of_parse_and_map：プロパティinterruptおよびinterrupt-parentによって提供される割り込みハンドラを接続する 

 of_find_property （ np 、 propname 、 NULL ）：    argument2で指定されたプロパティが存在するかどうかを確認します。 

 of_property_read_bool ：   引数2で指定されたブール値プロパティを読み込むには、それがブール値プロパティであるため、そのプロパティが存在するかどうかを検索するのと同じです。  trueまたはfalseを返します 

 of_get_property：引数2に指定されたプロパティを読み込むため 

 of_property_read_u32 ： 32ビットプロパティを読み込むには、3番目の引数に入力します。 エラーが発生した場合、3番目の引数には何も設定しません。 

 of_property_read_string ：文字列プロパティを読み込む 

 of_match_device ：デバイスがノードと一致しているかどうかの妥当性検査、非常にオプションで、あまり使用されていないようです。

https://elinux.org/Device_Tree_Usage
からの翻訳