UltraZed 向け Debian GNU/Linux (v2017.3版) の構築(U-Boot編)

## はじめに

UltraZed-EG スターターキットに Debian GNU/Linux (v2017.3版) を構築する方法について、具体的な方法をいくつかに分けて説明します。

 * [Introduction](https://qiita.com/ikwzm/items/a4ebcffdd26ab3e8124d)
 * [Boot Loader](https://qiita.com/ikwzm/items/9d2a76f9665759df58e7)
   - [Sample FPGA Design](https://qiita.com/ikwzm/items/61316ecec2fc68be4c96)
   - [FSBL   (First Stage Boot Loader)](https://qiita.com/ikwzm/items/6c345b0ef7fc397d37bf)
   - [PMUFW  (Platform Manager Unit Firmware)](https://qiita.com/ikwzm/items/9835f591a78e83d6b92b)
   - [BL31   (ARM Trusted Firmware Boot Loader state 3-1)](https://qiita.com/ikwzm/items/6d03b7e5b47721df3ae9)
   - __U-Boot(この記事)__
   - [BOOT.BIN](https://qiita.com/ikwzm/items/45520c8722edbfcd3575)
 * [Linux Kernel](https://qiita.com/ikwzm/items/367f49e9b02102e907d8)
 * [Debian9 Root File System](https://qiita.com/ikwzm/items/a0e88798ed29b30fa003)

この記事では、ZynqMP の Boot Loader で使うための U-Boot の構築について説明をします。

## u-boot の構築

### 必要な環境

* gcc-aarch64-linux-gnu

### Boot Loader 構築環境の準備

次の URL から git clone でリポジトリをダウンロードして v2017.3.0 をチェックアウトします。

 * [https://github.com/ikwzm/ZynqMP-FPGA-Linux](https://github.com/ikwzm/ZynqMP-FPGA-Linux)

```console
shell$ git clone git://github.com/ikwzm/ZynqMP-FPGA-Linux
shell$ cd ZynqMP-FPGA-Linux
shell$ git checkout v2017.3.0
```

### ソースコードのダウンロード

U-Boot のソースコードを以下の URL からダウンロードします。今回は U-Boot 本家からではなく、Xilinx が提供してる u-boot-xlnx を使います。

* [https://github.com/Xilinx/u-boot-xlnx.git](https://github.com/Xilinx/u-boot-xlnx.git)

```
shell$ cd target/UltraZed-EG-IOCC/build
shell$ git clone https://github.com/Xilinx/u-boot-xlnx.git u-boot-xlnx-v2017.3
```

### xilinx-v2017.3 をチェックアウト

u-boot-xlnx の xilinx-v2017.3 というタグをチェックアウトして xilinx-v2017.3-ultrazed-eg-iocc という作業用のブランチを作ります。

```
shell$ cd u-boot-xlnx-v2017.3
shell$ git checkout -b xilinx-v2017.3-ultrazed-eg-iocc refs/tags/xilinx-v2017.3
```

### UltraZed-EG-IOCC 用の各種ファイルを追加

UltraZed-EG-IOCC 用のファイルを追加してコミットします。追加するファイルの詳細は後述します。

```
shell$ patch -p0 < ../../../files/u-boot-xlnx-v2017.3-ultrazed-eg-iocc.diff
shell$ git add arch/arm/dts/zynqmp-uz3eg-iocc.dts
shell$ git add board/xilinx/zynqmp/zynqmp-uz3eg-iocc/psu_init_gpl.c
shell$ git add board/xilinx/zynqmp/zynqmp-uz3eg-iocc/psu_init_gpl.h
shell$ git add configs/xilinx_zynqmp_uz3eg_iocc_defconfig
shell$ git add include/configs/xilinx_zynqmp_uz3eg_iocc.h
shell$ git add --update
shell$ git commit -m "patch for UltraZed-EG-IOCC"
shell$ git tag -a xilinx-v2017.3-ultrazed-eg-iocc-0 -m "release xilinx-v2017.3-ultrazed-eg-iocc release 0"
```

### リブート時に I2C がフリーズする問題を修正

私の作った環境では、電源投入時は問題ないのですがリブート時に I2C のところでシステムがフリーズするという問題が発生しました。この問題に対処するために暫定的にソースコードに手を入れています。詳細は後述します。

```
shell$ patch -p0 < ../../../files/u-boot-xlnx-v2017.3-ultrazed-eg-iocc-i2c-patch.diff
shell$ git add --update
shell$ git commit -m "[fix] bug for I2C freeze when reboot"
shell$ git tag -a xilinx-v2017.3-ultrazed-eg-iocc-1 -m "release xilinx-v2017.3-ultrazed-eg-iocc release 1"
```

### 構築の準備

環境変数を設定します。アーキテクチャ(ARCH)に arm (arm64じゃないことに注意)を指定します。ここでは、クロスコンパイルに使うツールチェーン(CROSS_COMPILE) に aarch64-linux-gnu-を指定します。この変数は構築する環境にあわせてください。その後、UltraZed-EG-IOCC 用のコンフィギュレーション定義ファイルを使って構築の準備をします。

```
shell$ cd u-boot-xlnx-v2017.3
shell$ export ARCH=arm
shell$ export CROSS_COMPILE=aarch64-linux-gnu-
shell$ make xilinx_zynqmp_uz3eg_iocc_defconfig
```

### u-boot の構築

```
shell$ make
```

### Copy u-boot.elf

無事に u-boot.elf が出来たら、それを target/UltraZed-EG-IOCC/build にコピーします。

```
shell$ cp u-boot.elf target/UltraZed-EG-IOCC/build/u-boot.elf
```

## ファイルの説明

ここでは U-Boot を UltraZed-EG-IOCC にポーティングする際に新たに追加したファイルの説明をします。

### configs/xilinx_zynqmp_uz3eg_iocc_defconfig

U-Boot のコンフィギュレーション定義ファイルです。実はこのファイルの中身は u-boot-xlnx にすでにある xilinx_zynqmp_zcu102_revB_defconfig 等とそう変わるところはありません。異なっている箇所は CONFIG_SYS_CONFIG_NAME、CONFIG_IDENT_STRING、CONFIG_DEFAULT_DEVICE_TREE だけです。

なお、xilinx_zynqmp_uz3eg_iocc_defconfig には CONFIG_OF_EMBED=y が設定されています。つまり、U-Boot 用のデバイスツリーは外部ファイルではなく U-Boot の中に組み込まれています。

### arch/arm/dts/zynqmp-uz3eg-iocc.dts

U-Boot 用のデバイスツリーです。実はこのデバイスツリーは Linux Kernel を起動するデバイスツリーとまったく同じものです。このデバイスツリーの説明は Linux Kernel のところで説明します。

### include/configs/xilinx_zynqmp_uz3eg_iocc.h

U-Boot コンフィギュレーション定義ファイルに記述できない各種定数を定義しているファイルです。このファイルには次の定数が設定されています。それ以外の定数は ```include/configs/xilinx_zynqmp.h``` のものをそのまま使っています。

```C:include/configs/xilinx_zynqmp_uz3eg_iocc.h
#ifndef __CONFIG_ZYNQMP_UZ3EG_IOCC_H
#define __CONFIG_ZYNQMP_UZ3EG_IOCC_H

#define CONFIG_ZYNQ_SDHCI0
#define CONFIG_ZYNQ_SDHCI1
#define CONFIG_ZYNQ_I2C1
#define CONFIG_SYS_I2C_ZYNQ

#include <configs/xilinx_zynqmp.h>

#endif /* __CONFIG_ZYNQMP_UZ3EG_IOCC_H */
```

### board/xilinx/zynqmp/zynqmp-uz3eg-iocc/psu_init_gpl.[ch]

これらファイルは[「UltraZed 向け Debian GNU/Linux (v2017.3版) の構築(Sample FPGA Design編)」@Qiita](https://qiita.com/ikwzm/items/61316ecec2fc68be4c96)で作ったハードウェア情報の中に入っています。ただし、U-Boot でハードウェア情報に含まれるファイルが必要になるのは u-boot.spl をビルドする場合です。今回は u-boot.spl は使いませんので、一度作っておいたファイルを使いまわしても問題ありません。

## リブート時に I2C がフリーズする問題

私の作った環境では、電源投入時は問題ないのですがリブート時に I2C のところでシステムがフリーズするという問題が発生しました。

デバッグしたところ、リブート時に CPU が I2C Controller にライトアクセスしているところでフリーズしていました。さらに調べてみると、その時、I2C Controller へのクロックが停止していることが判りました。電源投入時(コールドブート時)は FSBL が I2C Controller へのクロックの設定をするのですが、リブート時(ウォームブート時)は FSBL は何もしないようです。シャットダウン時にデバイスドライバか何かが(気をきかせて?)クロックを停止してしまっているようで、さらにリブート時はクロックの設定を飛ばすので、クロックが停止した状態でライトアクセスを行い、結果としてフリーズします。

結局、ブート時にも強制的に I2C Controller へのクロック供給を開始するように U-Boot のソースコードを修正することで、この問題に対処しました。以下にそのパッチファイルを示します。

```patch:u-boot-xlnx-v2017.3-ultrazed-eg-iocc-i2c-patch.diff
diff --git arch/arm/include/asm/arch-zynqmp/hardware.h arch/arm/include/asm/arch-zynqmp/hardware.h
index ba20bac..f879c4d 100644
--- arch/arm/include/asm/arch-zynqmp/hardware.h
+++ arch/arm/include/asm/arch-zynqmp/hardware.h
@@ -22,6 +22,8 @@
 #define ZYNQMP_TCM_SIZE		0x40000
 
 #define ZYNQMP_CRL_APB_BASEADDR	0xFF5E0000
+#define ZYNQMP_CRL_APB_I2C0_REF_CTRL_CLKACT        	0x1000000
+#define ZYNQMP_CRL_APB_I2C1_REF_CTRL_CLKACT        	0x1000000
 #define ZYNQMP_CRL_APB_TIMESTAMP_REF_CTRL_CLKACT	0x1000000
 #define ZYNQMP_CRL_APB_BOOT_PIN_CTRL_OUT_EN_SHIFT	0
 #define ZYNQMP_CRL_APB_BOOT_PIN_CTRL_OUT_VAL_SHIFT	8
@@ -34,7 +36,9 @@
 struct crlapb_regs {
 	u32 reserved0[36];
 	u32 cpu_r5_ctrl; /* 0x90 */
-	u32 reserved1[37];
+	u32 reserved1[35];
+	u32 i2c0_ref_ctrl;      /* 0x120 */
+	u32 i2c1_ref_ctrl;      /* 0x124 */
 	u32 timestamp_ref_ctrl; /* 0x128 */
 	u32 reserved2[53];
 	u32 boot_mode; /* 0x200 */
diff --git board/xilinx/zynqmp/zynqmp.c board/xilinx/zynqmp/zynqmp.c
index fd80844..ff56e39 100644
--- board/xilinx/zynqmp/zynqmp.c
+++ board/xilinx/zynqmp/zynqmp.c
@@ -416,3 +416,19 @@ int checkboard(void)
 	puts("Board: Xilinx ZynqMP\n");
 	return 0;
 }
+
+void i2c_init_board(void)
+{
+#if defined(CONFIG_ZYNQ_I2C0)
+	{
+		u32 val = readl(&crlapb_base->i2c0_ref_ctrl);
+		writel((val | ZYNQMP_CRL_APB_I2C0_REF_CTRL_CLKACT), &crlapb_base->i2c0_ref_ctrl);
+	}
+#endif
+#if defined(CONFIG_ZYNQ_I2C1)
+	{
+		u32 val = readl(&crlapb_base->i2c1_ref_ctrl);
+		writel((val | ZYNQMP_CRL_APB_I2C1_REF_CTRL_CLKACT), &crlapb_base->i2c1_ref_ctrl);
+	}
+#endif
+}
```