Ultra96 向け Debian GNU/Linux (v2018.2版) の構築(Linux Kernel編)

## はじめに

Ultra96 に Debian GNU/Linux (v2018.2版) を構築する方法について、具体的な方法をいくつかに分けて説明します。

 * [Introduction](https://qiita.com/ikwzm/items/5ae07792143d8018bfea)
 * [Boot Loader](https://qiita.com/ikwzm/items/9c78e83298906447fe5d)
   - [Sample FPGA Design](https://qiita.com/ikwzm/items/0489b02962c6f0455783)
   - [FSBL   (First Stage Boot Loader)](https://qiita.com/ikwzm/items/fe9d6abda60660fc2f87)
   - [PMUFW  (Platform Manager Unit Firmware)](https://qiita.com/ikwzm/items/00024adf956696dd51c6)
   - [BL31   (ARM Trusted Firmware Boot Loader state 3-1)](https://qiita.com/ikwzm/items/3c0d735cd3a605d269f3)
   - [U-Boot](https://qiita.com/ikwzm/items/11853d8cfdaf373beaef)
   - [BOOT.BIN](https://qiita.com/ikwzm/items/af4420edd262e14feef9)
 * __Linux Kernel(この記事)__
 * [Debian9 Root File System](https://qiita.com/ikwzm/items/be06b07e26cbf05fec2b)

この記事では ZynqMP の Linux Kernel について説明をします。

## 注意

Linux Kernel では 起動時に ATF(ARM Trusted Firmware) のバージョンをチェックしています。Debian GNU/Linux (v2018.2版) で構築する Linux Kernel 4.14.0 (xilinx-v2018.2) では ATF(ARM Trusted Firmware) のバージョンは v1.0 である必要があります。これ以外のバージョンでは Kernel が Panic を起こして起動出来ないので注意してください。

## Linux Kernel の構築

### 必要な環境

* gcc-aarch64-linux-gnu

### Linux Kernel 構築環境の準備

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

 * [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 v2018.2.1
```

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

Linux Kernel のソースコードを以下の URL からダウンロードします。今回は Linux のメインラインからではなく、Xilinx が提供してる linux-xlnx を使います。

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

```console
shell$ git clone https://github.com/Xilinx/linux-xlnx.git linux-xlnx-v2018.2-zynqmp-fpga
```

### xilinx-v2018.2 をチェックアウト

linux-xlnx の xilinx-v2018.2 というタグをチェックアウトして linux-xlnx-v2018.2-zynqmp-fpga という作業用のブランチを作ります。

```console
shell$ cd linux-xlnx-v2018.2-zynqmp-fpga
shell$ git checkout -b linux-xlnx-v2018.2-zynqmp-fpga refs/tags/xilinx-v2018.2
```

### ZynqMP 用の各種ファイルを追加

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

```console
shell$ patch -p1 < ../files/linux-xlnx-v2018.2-zynqmp-fpga.diff
shell$ git add --update
shell$ git add arch/arm64/boot/dts/xilinx/zynqmp-ultra96.dts
shell$ git commit -m "[patch] for linux-xlnx-v2018.2-zynqmp-fpga."
```

### ヘッダーパッケージにホスト用の実行ファイルが含まれてしまう問題

チェックアウトした xilinx-v2018.2 には、Debain のパッケージを作る際に arm64 用ではなくクロスコンパイルを実行したプラットフォーム用のパッケージが出来てしまう問題があります。そのための修正を追加します。この件の詳細は後述します。

```console
shell$ patch -p1 < ../files/linux-xlnx-v2018.2-builddeb.diff
shell$ git add --update
shell$ git commit -m "[update] scripts/package/builddeb to add tools/include and postinst script to header package"
```

### FPGA のロード時に raw ファイルをロード出来るように修正

チェックアウトした xilinx-v2018.2 には、FPGA のロード時に raw ファイルを指定するとフリーズするという問題があります。ここではその修正を追加します。この件の詳細は後述します。

```console
shell$ patch -p1 < ../files/linux-xlnx-v2018.2-zynqmp-fpga-patch.diff
shell$ git add --update
shell$ git commit -m "[patch] drivers/fpga/zynqmp-fpga.c for load raw file format"
```

### タグとバージョン番号の設定

```console
shell$ git tag -a xilinx-v2018.2-zynqmp-fpga -m "release xilinx-v2018.2-zynqmp-fpga"
shell$ echo 0 > .version
```

### 構築の準備

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

````console
shell$ cd linux-xlnx-v2018.2-zynqmp-fpga
shell$ export ARCH=arm64
shell$ export CROSS_COMPILE=aarch64-linux-gnu-
shell$ make xilinx_zynqmp_defconfig
````

### カーネルと Debian パッケージの構築

````console
shell$ export DTC_FLAGS=--symbols
shell$ make deb-pkg
````

### 構築したカーネルのイメージとデバイスツリーを target/Ultra96/boot にコピー

無事にカーネルのイメージ(arch/arm64/boot/Image) と Ultra96 用のデバイスツリー(arch/arm64/boot/dts/xilinx/zynqmp-ultra96.dtb) が出来たら、それを target/Ultra96/boot にコピーします。その際、作ったカーネルのバージョン等をわかりやすくするため名前を変更しておきます。
また、dtc(Device Tree Compiler) を使って、デバイスツリーのバイナリファイル (devicetree-4.14.0-xlnx-v2018.2-zynqmp-fpga-ultra96.dtb) をデバイスツリーのソースファイル (devicetree-4.14.0-xlnx-v2018.2-zynqmp-fpga-ultra96.dts) に戻しておきます。何かデバイスツリーを変更したい時は、このデバイスツリーソースファイルを修正して dtc でデバイスツリーバイナリファイルにコンパイルしてください。
なお、[「デバイスツリーにシンボル情報を埋め込む」](https://qiita.com/ikwzm/items/03d518b7c46d1ee49943)で説明したように、古いバージョンの dtc では --symbols オプションをサポートしていない場合があります。その場合は Linux Kernel をビルドした時にホスト用に生成された dtc (scripts/dtc/dtc) を使ってコンパイルすると良いでしょう。

```console
shell$ cp arch/arm64/boot/Image ../target/Ultra96/boot/image-4.14.0-xlnx-v2018.2-zynqmp-fpga
shell$ cp arch/arm64/boot/dts/xilinx/zynqmp-ultra96.dtb ../target/Ultra96/boot/devicetree-4.14.0-xlnx-v2018.2-zynqmp-fpga-ultra96.dtb
shell$ ./scripts/dtc/dtc -I dtb -O dts --symbols -o ../target/Ultra96/boot/devicetree-4.14.0-xlnx-v2018.2-zynqmp-fpga-ultra96.dts ../target/Ultra96/boot/devicetree-4.14.0-xlnx-v2018.2-zynqmp-fpga-ultra96.dtb
```

## Linux Kernel の起動

### uEnv.txt

[「Ultra96 向け Debian GNU/Linux (v2018.2版) の構築(U-Boot編)」](https://qiita.com/ikwzm/items/11853d8cfdaf373beaef) で構築した U-Boot は、起動の際、ストレージに uEnv.txt があればそれを読み込みます。そして ```uenvcmd``` 変数が定義されていれば、その変数で定義されている内容を実行します。そこで uEnv.txt に、この記事で構築したカーネルのイメージとデバイスツリーを指定して実行するように記述します。


```text:target/Ultra96/boot/uEnv.txt
linux_kernel_image=image-4.14.0-xlnx-v2018.2-zynqmp-fpga
linux_fdt_image=devicetree-4.14.0-xlnx-v2018.2-zynqmp-fpga-ultra96.dtb
linux_boot_args=console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait uio_pdrv_genirq.of_id=generic-uio

linux_img_load_cmd=fatload mmc ${sdbootdev} ${kernel_addr} ${linux_kernel_image}
linux_fdt_load_cmd=fatload mmc ${sdbootdev} ${fdt_addr}    ${linux_fdt_image}
linux_load_cmd=run linux_img_load_cmd && run linux_fdt_load_cmd
linux_boot_cmd=setenv bootargs ${linux_boot_args} && booti ${kernel_addr} - ${fdt_addr}

uenvcmd=run linux_load_cmd && run linux_boot_cmd

bootmenu_0=Boot Default=boot
```

 * ```linux_kernel_image``` 変数にカーネルのイメージのファイル名である image-4.14.0-xlnx-v2018.2-zynqmp-fpga を指定。
 * ```linux_fdt_image``` 変数にデバイスツリーファイル名である devicetree-4.14.0-xlnx-v2018.2-zynqmp-fpga-ultra96.dtb を指定。
 * ```linux_boot_args``` 変数に Linux をブートする際の引数を指定。
    - console として ttyPS0 をボーレートと共に指定。
    - root file system として ```/dev/mmcblk0p2``` (SD-Card の第2パーティション) を指定。
    - デバイスツリーオーバーレイで uio を指定出来るように ```uio_pdrv_genirq.of_id=generic-uio``` を指定。
 * ```linux_img_load_cmd``` 変数に ```sdbootdev``` 変数でしていされたデバイスから ```linux_kernel_image``` 変数で指定されたカーネルイメージをメモリ上の ```kernel_addr``` で指定されたアドレスにロードするコマンドを記述。
 * ```linux_dtb_load_cmd``` 変数に ```sdbootdev``` 変数でしていされたデバイスから ```linux_fdt_image``` 変数で指定されたデバイスツリーをメモリ上の ```fdt_addr``` で指定されたアドレスにロードするコマンドを記述。
 * ```linux_load_cmd``` 変数に ```linux_img_load_cmd``` と ```linux_dtb_load_cmd``` を実行するコマンドを記述。
 * ```linux_boot_cmd``` 変数に ```linux_boot_args``` 変数で設定した bootargs を設定し、```booti``` コマンドを使ってメモリ上にロードされたカーネルをブートするコマンドを記述。
 * ```uenvcmd``` 変数に上記各種変数をコマンドとして実行するように記述。
 * ```bootmenu_0``` 変数にブートメニューの最初の選択肢のタイトルと実行するコマンドを記述。

Zynq と異なるのは、ブートするカーネルのイメージが Linux kernel ARM boot executable zImage 形式ではなく、arm64 Linux Image 形式であることです。arm64 用に構築した U-Boot には arm64 Linux Image 形式のイメージをブートするためのコマンド ```booti``` があるので、そちらを使います。

## ファイルの説明

ここでは linux-xlnx を ZynqMP 用に構築する際に新たに追加したファイルおよび修正したファイルの説明をします。

### arch/arm64/configs/xilinx_zynqmp_defconfig

ZynqMP 用のコンフィギュレーション定義ファイルです。基本的には linux-xlnx にすでにある xilinx_zynqmp_defconfig と同じですが、Device Tree Overlay を使うために ```CONFIG_OF_OVERLAY=y```を、さらに Device Tree Overlay を Configuration File System から行うために ```CONFIG_OF_CONFIGFS=y``` を新たに追加しています。

### arch/arm64/boot/dts/xilinx/zynqmp-ultra96.dts

Ultra96 用のデバイスツリーです。実はこれと同じデバイスツリーを U-Boot のビルド時にも流用しています。

<details><summary>arch/arm64/boot/dts/xilinx/zynqmp-ultra96.dts</summary><div>

```dts:arch/arm64/boot/dts/xilinx/zynqmp-ultra96.dts
// SPDX-License-Identifier: GPL-2.0+
/*
 * dts file for Xilinx ZynqMP Ultra96
 *
 */

/dts-v1/;

#include "zynqmp.dtsi"
#include "zynqmp-clk-ccf.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
#include <dt-bindings/phy/phy.h>

/ {
	model = "ZynqMP Ultra96";
	compatible = "xlnx,zynqmp-ultra96", "xlnx,zynqmp";

	aliases {
		gpio0 = &gpio;
		i2c0 = &i2c1;
		rtc0 = &rtc;
		serial0 = &uart1;
		serial1 = &uart0;
		serial2 = &dcc;
		spi0 = &spi0;
		spi1 = &spi1;
		usb0 = &usb0;
		usb1 = &usb1;
		mmc0 = &sdhci0;
		mmc1 = &sdhci1;
	};

	chosen {
		bootargs = "earlycon";
		stdout-path = "serial0:115200n8";
	};

	memory@0 {
		device_type = "memory";
		reg = <0x0 0x0 0x0 0x80000000>;
	};

	amba_pl: amba_pl@0 {
		#address-cells = <2>;
		#size-cells = <2>;
		compatible = "simple-bus";
		ranges ;
	};

	gpio-keys {
		compatible = "gpio-keys";
		#address-cells = <1>;
		#size-cells = <0>;
		autorepeat;
		sw4 {
			label = "sw4";
			gpios = <&gpio 23 GPIO_ACTIVE_LOW>;
			linux,code = <KEY_POWER>;
			gpio-key,wakeup;
			autorepeat;
		};
	};

	iio-hwmon {
		compatible = "iio-hwmon";
		io-channels = <&xilinx_ams 0>, <&xilinx_ams 1>, <&xilinx_ams 2>,
			      <&xilinx_ams 3>, <&xilinx_ams 4>, <&xilinx_ams 5>,
			      <&xilinx_ams 6>, <&xilinx_ams 7>, <&xilinx_ams 8>,
			      <&xilinx_ams 9>, <&xilinx_ams 10>,
			      <&xilinx_ams 11>, <&xilinx_ams 12>;
	};

	leds {
		compatible = "gpio-leds";
		ds2 {
			label = "ds2";
			gpios = <&gpio 20 GPIO_ACTIVE_HIGH>;
			linux,default-trigger = "heartbeat";
		};

		ds3 {
			label = "ds3";
			gpios = <&gpio 19 GPIO_ACTIVE_HIGH>;
			linux,default-trigger = "phy0tx"; /* WLAN tx */
			default-state = "off";
		};

		ds4 {
			label = "ds4";
			gpios = <&gpio 18 GPIO_ACTIVE_HIGH>;
			linux,default-trigger = "phy0rx"; /* WLAN rx */
			default-state = "off";
		};

		ds5 {
			label = "ds5";
			gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
			linux,default-trigger = "bluetooth-power";
		};

		vbus_det { /* U5 USB5744 VBUS detection via MIO25 */
			label = "vbus_det";
			gpios = <&gpio 25 GPIO_ACTIVE_HIGH>;
			default-state = "on";
		};
	};

	ltc2954: ltc2954 { /* U7 */
		compatible = "lltc,ltc2954", "lltc,ltc2952";
		trigger-gpios = <&gpio 26 GPIO_ACTIVE_LOW>; /* INT line - input */
		/* If there is HW watchdog on mezzanine this signal should be connected there */
		watchdog-gpios = <&gpio 35 GPIO_ACTIVE_HIGH>; /* MIO on PAD */
		kill-gpios = <&gpio 34 GPIO_ACTIVE_LOW>; /* KILL signal - output */
	};

	wmmcsdio_fixed: fixedregulator-mmcsdio {
		compatible = "regulator-fixed";
		regulator-name = "wmmcsdio_fixed";
		regulator-min-microvolt = <3300000>;
		regulator-max-microvolt = <3300000>;
		regulator-always-on;
		regulator-boot-on;
	};

	sdio_pwrseq: sdio_pwrseq {
		compatible = "mmc-pwrseq-simple";
		reset-gpios = <&gpio 7 GPIO_ACTIVE_LOW>; /* WIFI_EN */
	};
};

&dcc {
	status = "okay";
};

&gpio {
	status = "okay";
	gpio-line-names = "UART1_TX", "UART1_RX", "UART0_RX", "UART0_TX", "I2C1_SCL",
			  "I2C1_SDA", "SPI1_SCLK", "WLAN_EN", "BT_EN", "SPI1_CS",
			  "SPI1_MISO", "SPI1_MOSI", "I2C_MUX_RESET", "SD0_DAT0", "SD0_DAT1",
			  "SD0_DAT2", "SD0_DAT3", "PS_LED3", "PS_LED2", "PS_LED1",
			  "PS_LED0", "SD0_CMD", "SD0_CLK", "GPIO_PB", "SD0_DETECT",
			  "VBUS_DET", "POWER_INT", "DP_AUX", "DP_HPD", "DP_OE",
			  "DP_AUX_IN", "INA226_ALERT", "PS_FP_PWR_EN", "PL_PWR_EN", "POWER_KILL",
			  "", "GPIO-A", "GPIO-B", "SPI0_SCLK", "GPIO-C",
			  "GPIO-D", "SPI0_CS", "SPI0_MISO", "SPI_MOSI", "GPIO-E",
			  "GPIO-F", "SD1_D0", "SD1_D1", "SD1_D2", "SD1_D3",
			  "SD1_CMD", "SD1_CLK", "USB0_CLK", "USB0_DIR", "USB0_DATA2",
			  "USB0_NXT", "USB0_DATA0", "USB0_DATA1", "USB0_STP", "USB0_DATA3",
			  "USB0_DATA4", "USB0_DATA5", "USB0_DATA6", "USB0_DATA7", "USB1_CLK",
			  "USB1_DIR", "USB1_DATA2", "USB1_NXT", "USB1_DATA0", "USB1_DATA1",
			  "USB1_STP", "USB1_DATA3", "USB1_DATA4", "USB1_DATA5", "USB1_DATA6",
			  "USB_DATA7", "WLAN_IRQ", "PMIC_IRQ", /* MIO end and EMIO start */
			  "", "",
			  "", "", "", "", "", "", "", "", "", "",
			  "", "", "", "", "", "", "", "", "", "",
			  "", "", "", "", "", "", "", "", "", "",
			  "", "", "", "", "", "", "", "", "", "",
			  "", "", "", "", "", "", "", "", "", "",
			  "", "", "", "", "", "", "", "", "", "",
			  "", "", "", "", "", "", "", "", "", "",
			  "", "", "", "", "", "", "", "", "", "",
			  "", "", "", "", "", "", "", "", "", "",
			  "", "", "", "";
};

&gpu {
	status = "okay";
};

&i2c1 {
	status = "okay";
	pinctrl-names = "default", "gpio";
	pinctrl-0 = <&pinctrl_i2c1_default>;
	pinctrl-1 = <&pinctrl_i2c1_gpio>;
	scl-gpios = <&gpio 4 GPIO_ACTIVE_HIGH>;
	sda-gpios = <&gpio 5 GPIO_ACTIVE_HIGH>;
	clock-frequency = <100000>;
	i2c-mux@75 { /* u11 */
		compatible = "nxp,pca9548";
		#address-cells = <1>;
		#size-cells = <0>;
		reg = <0x75>;
		i2csw_0: i2c@0 {
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <0>;
			label = "LS-I2C0";
		};
		i2csw_1: i2c@1 {
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <1>;
			label = "LS-I2C1";
		};
		i2csw_2: i2c@2 {
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <2>;
			label = "HS-I2C2";
		};
		i2csw_3: i2c@3 {
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <3>;
			label = "HS-I2C3";
		};
		i2csw_4: i2c@4 {
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <0x4>;

			pmic: pmic@5e { /* Custom TI PMIC u33 */
				compatible = "ti,tps65086";
				reg = <0x5e>;
				interrupt-parent = <&gpio>;
				interrupts = <77 GPIO_ACTIVE_LOW>;
				#gpio-cells = <2>;
				gpio-controller;
			};
		};
		i2csw_5: i2c@5 {
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <5>;
			/* PS_PMBUS */
			ina226@40 { /* u35 */
				compatible = "ti,ina226";
				reg = <0x40>;
				shunt-resistor = <10000>;
				/* MIO31 is alert which should be routed to PMUFW */
			};
		};
		i2csw_6: i2c@6 {
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <6>;
			/*
			 * Not Connected
			 */
		};
		i2csw_7: i2c@7 {
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <7>;
			/*
			 * usb5744 (DNP) - U5
			 * 100kHz - this is default freq for us
			 */
		};
	};
};

&pinctrl0 {
	status = "okay";
	pinctrl_i2c1_default: i2c1-default {
		mux {
			groups = "i2c1_1_grp";
			function = "i2c1";
		};

		conf {
			groups = "i2c1_1_grp";
			bias-pull-up;
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
		};
	};

	pinctrl_i2c1_gpio: i2c1-gpio {
		mux {
			groups = "gpio0_4_grp", "gpio0_5_grp";
			function = "gpio0";
		};

		conf {
			groups = "gpio0_4_grp", "gpio0_5_grp";
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
		};
	};

	pinctrl_sdhci0_default: sdhci0-default {
		mux {
			groups = "sdio0_3_grp";
			function = "sdio0";
		};

		conf {
			groups = "sdio0_3_grp";
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
			bias-disable;
		};

		mux-cd {
			groups = "sdio0_cd_0_grp";
			function = "sdio0_cd";
		};

		conf-cd {
			groups = "sdio0_cd_0_grp";
			bias-high-impedance;
			bias-pull-up;
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
		};
	};

	pinctrl_sdhci1_default: sdhci1-default {
		mux {
			groups = "sdio1_2_grp";
			function = "sdio1";
		};

		conf {
			groups = "sdio1_2_grp";
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
			bias-disable;
		};
	};

	pinctrl_spi0_default: spi0-default {
		mux {
			groups = "spi0_3_grp";
			function = "spi0";
		};

		conf {
			groups = "spi0_3_grp";
			bias-disable;
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
		};

		mux-cs {
			groups = "spi0_ss_9_grp";
			function = "spi0_ss";
		};

		conf-cs {
			groups = "spi0_ss_9_grp";
			bias-disable;
		};

	};

	pinctrl_spi1_default: spi1-default {
		mux {
			groups = "spi1_0_grp";
			function = "spi1";
		};

		conf {
			groups = "spi1_0_grp";
			bias-disable;
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
		};

		mux-cs {
			groups = "spi1_ss_0_grp";
			function = "spi1_ss";
		};

		conf-cs {
			groups = "spi1_ss_0_grp";
			bias-disable;
		};

	};

	pinctrl_uart0_default: uart0-default {
		mux {
			groups = "uart0_0_grp";
			function = "uart0";
		};

		conf {
			groups = "uart0_0_grp";
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
		};

		conf-rx {
			pins = "MIO2";
			bias-high-impedance;
		};

		conf-tx {
			pins = "MIO3";
			bias-disable;
		};
	};

	pinctrl_uart1_default: uart1-default {
		mux {
			groups = "uart1_0_grp";
			function = "uart1";
		};

		conf {
			groups = "uart1_0_grp";
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
		};

		conf-rx {
			pins = "MIO1";
			bias-high-impedance;
		};

		conf-tx {
			pins = "MIO0";
			bias-disable;
		};
	};

	pinctrl_usb0_default: usb0-default {
		mux {
			groups = "usb0_0_grp";
			function = "usb0";
		};

		conf {
			groups = "usb0_0_grp";
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
		};

		conf-rx {
			pins = "MIO52", "MIO53", "MIO55";
			bias-high-impedance;
		};

		conf-tx {
			pins = "MIO54", "MIO56", "MIO57", "MIO58", "MIO59",
			       "MIO60", "MIO61", "MIO62", "MIO63";
			bias-disable;
		};
	};

	pinctrl_usb1_default: usb1-default {
		mux {
			groups = "usb1_0_grp";
			function = "usb1";
		};

		conf {
			groups = "usb1_0_grp";
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
		};

		conf-rx {
			pins = "MIO64", "MIO65", "MIO67";
			bias-high-impedance;
		};

		conf-tx {
			pins = "MIO66", "MIO68", "MIO69", "MIO70", "MIO71",
			       "MIO72", "MIO73", "MIO74", "MIO75";
			bias-disable;
		};
	};
};

&rtc {
	status = "okay";
};

/* SD0 only supports 3.3V, no level shifter */
&sdhci0 {
	status = "okay";
	no-1-8-v;
	broken-cd; /* CD has to be enabled by default */
	disable-wp;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_sdhci0_default>;
	xlnx,mio_bank = <0>;
};

&sdhci1 {
	status = "okay";
	bus-width = <0x4>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_sdhci1_default>;
	xlnx,mio_bank = <0>;
	non-removable;
	disable-wp;
	cap-power-off-card;
	mmc-pwrseq = <&sdio_pwrseq>;
	vqmmc-supply = <&wmmcsdio_fixed>;
	#address-cells = <1>;
	#size-cells = <0>;
	wlcore: wifi@2 {
		compatible = "ti,wl1831";
		reg = <2>;
		interrupt-parent = <&gpio>;
		interrupts = <76 IRQ_TYPE_EDGE_RISING>; /* MIO76 WLAN_IRQ 1V8 */
	};
};

&serdes {
	status = "okay";
};

&spi0 { /* Low Speed connector */
	status = "okay";
	label = "LS-SPI0";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_spi0_default>;
};

&spi1 { /* High Speed connector */
	status = "okay";
	label = "HS-SPI1";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_spi1_default>;
};

&uart0 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_uart0_default>;
	bluetooth {
		compatible = "ti,wl1831-st";
		enable-gpios = <&gpio 8 GPIO_ACTIVE_HIGH>;
	};

};

&uart1 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_uart1_default>;

};

/* ULPI SMSC USB3320 */
&usb0 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_usb0_default>;
};

&dwc3_0 {
	status = "okay";
	dr_mode = "peripheral";
	phy-names = "usb3-phy";
	phys = <&lane2 PHY_TYPE_USB3 0 0 26000000>;
	maximum-speed = "super-speed";
};

/* ULPI SMSC USB3320 */
&usb1 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_usb1_default>;
};

&dwc3_1 {
	status = "okay";
	dr_mode = "host";
	phy-names = "usb3-phy";
	phys = <&lane3 PHY_TYPE_USB3 1 0 26000000>;
	maximum-speed = "super-speed";
};

&watchdog0 {
	status = "okay";
};

&zynqmp_dpsub {
	status = "okay";
	phy-names = "dp-phy0", "dp-phy1";
	phys = <&lane1 PHY_TYPE_DP 0 1 27000000>,
	       <&lane0 PHY_TYPE_DP 1 1 27000000>;
};

&zynqmp_dp_snd_pcm0 {
	status = "okay";
};

&zynqmp_dp_snd_pcm1 {
	status = "okay";
};

&zynqmp_dp_snd_card0 {
	status = "okay";
};

&zynqmp_dp_snd_codec0 {
	status = "okay";
};

&xlnx_dpdma {
	status = "okay";
};

&xilinx_ams {
	status = "okay";
};

&ams_ps {
	status = "okay";
};
```

</div></detalis>

もともと、このデバイスツリーは arch/arm64/boot/dts/xilinx/zynqmp-zcu100-revC.dts が元になっています。zynqmp-zcu100-revC.dts との違いを説明します。

#### amba_pl@0 を追加

デバイスツリーオーバーレイを使って、各種デバイスドライバのロード/アンロードおよび PL のプログラムを行う際、そのターゲットとなるノードです。このノードは単なるノードで特に対応するデバイスドライバとかはありません。名前は何でも良かったのですが、UltraZed と同じノード名にしています。そうすれば UltraZed といろいろ兼用できて都合が良いからです。

```dts:arch/arm64/boot/dts/xilinx/zynqmp-ultra96.dts
	amba_pl: amba_pl@0 {
		#address-cells = <2>;
		#size-cells = <2>;
		compatible = "simple-bus";
		ranges ;
	};
```

#### pinctrl_uart0_default の conf-rx と conf-tx の pins を交換

zynqmp-zcu100-revC.dts では conf-rx ノードの pins プロパティが "MIO3" に、conf-tx ノードの pins プロパティが "MIO2" に設定されていましたが、改めて Sample Design の I/O pin を調べてみるとどうも逆のようです。

```dts:arch/arm64/boot/dts/xilinx/zynqmp-ultra96.dts
	pinctrl_uart0_default: uart0-default {
		mux {
			groups = "uart0_0_grp";
			function = "uart0";
		};

		conf {
			groups = "uart0_0_grp";
			slew-rate = <SLEW_RATE_SLOW>;
			io-standard = <IO_STANDARD_LVCMOS18>;
		};

		conf-rx {
			pins = "MIO2";
			bias-high-impedance;
		};

		conf-tx {
			pins = "MIO3";
			bias-disable;
		};
	};
```

ZynqMP での pinctrl の ピンアサインについては次の記事を参考にしてください。

  * [「Zynq Ultrascale+ MPSoC の pinctrl のグループとピン番号の対応」@Qiita](https://qiita.com/ikwzm/items/7b4ea8e031d3564b7535)


### ヘッダーパッケージにホスト用の実行ファイルが含まれてしまう問題

詳細は以下の記事を参照してください。

 * [「クロスコンパイルした Debian GNU/Linux でデバイスドライバをセルフコンパイルする際の注意点」@Qiita](https://qiita.com/ikwzm/items/e31ecb099d0d50cb027c)

このパッチを当てる差分ファイルが次に示す files/linux-xlnx-v2018.2-builddeb.diff です。

<details><summary>files/linux-xlnx-v2018.2-builddeb.diff</summary><div>

```diff:files/linux-xlnx-v2018.2-builddeb2.diff
diff --git a/scripts/package/builddeb b/scripts/package/builddeb
index 0bc8747..515963f 100755
--- a/scripts/package/builddeb
+++ b/scripts/package/builddeb
@@ -313,7 +313,7 @@ fi
 
 # Build kernel header package
 (cd $srctree; find . -name Makefile\* -o -name Kconfig\* -o -name \*.pl) > "$objtree/debian/hdrsrcfiles"
-(cd $srctree; find arch/*/include include scripts -type f) >> "$objtree/debian/hdrsrcfiles"
+(cd $srctree; find arch/*/include include tools/include scripts -type f) >> "$objtree/debian/hdrsrcfiles"
 (cd $srctree; find arch/$SRCARCH -name module.lds -o -name Kbuild.platforms -o -name Platform) >> "$objtree/debian/hdrsrcfiles"
 (cd $srctree; find $(find arch/$SRCARCH -name include -o -name scripts -type d) -type f) >> "$objtree/debian/hdrsrcfiles"
 if grep -q '^CONFIG_STACK_VALIDATION=y' $KCONFIG_CONFIG ; then
@@ -330,6 +330,15 @@ mkdir -p "$destdir"
 (cd $objtree; cp $KCONFIG_CONFIG $destdir/.config) # copy .config manually to be where it's expected to be
 ln -sf "/usr/src/linux-headers-$version" "$kernel_headers_dir/lib/modules/$version/build"
 rm -f "$objtree/debian/hdrsrcfiles" "$objtree/debian/hdrobjfiles"
+mkdir -m 755 -p "$kernel_headers_dir/DEBIAN"
+cat <<EOF >> $kernel_headers_dir/DEBIAN/postinst
+#!/bin/sh -e
+
+make -C /usr/src/linux-headers-$version scripts
+
+EOF
+
+chmod 755 $kernel_headers_dir/DEBIAN/postinst
 
 cat <<EOF >> debian/control
 
```

</div></detalis>

### FPGA のロード時に raw ファイルをロード出来るように修正

チェックアウトした xilinx-v2018.2 には、FPGA のロード時に raw ファイルを指定するとフリーズするという問題がありました。ここではその修正を追加します。具体的には ATF(ARM Trusted Firmware) に渡す3番目の引数にファイルサイズを示す物理メモリ上のアドレスを指定します。

このパッチを当てる差分ファイルが次に示す files/linux-xlnx-v2018.2-zynqmp-fpga-patch.diff です。

<details><summary>files/linux-xlnx-v2018.2-zynqmp-fpga-patch.diff</summary><div>

```diff:files/linux-xlnx-v2018.2-zynqmp-fpga-patch.diff
diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
index b0c482f..4a91d64 100644
--- a/drivers/fpga/zynqmp-fpga.c
+++ b/drivers/fpga/zynqmp-fpga.c
@@ -48,7 +48,8 @@ static int zynqmp_fpga_ops_write(struct fpga_manager *mgr,
 {
 	struct zynqmp_fpga_priv *priv;
 	char *kbuf;
-	size_t dma_size;
+	size_t kbuf_size;
+	size_t algn_size = (size + (sizeof(u32)-1)) & (~(sizeof(u32)-1));
 	dma_addr_t dma_addr;
 	int ret;
 	const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
@@ -59,25 +60,30 @@ static int zynqmp_fpga_ops_write(struct fpga_manager *mgr,
 	priv = mgr->priv;
 
 	if (mgr->flags & IXR_FPGA_ENCRYPTION_EN)
-		dma_size = size + ENCRYPTED_KEY_LEN;
+		kbuf_size = algn_size + ENCRYPTED_KEY_LEN;
 	else
-		dma_size = size;
+		kbuf_size = algn_size + sizeof(u32);
 
-	kbuf = dma_alloc_coherent(priv->dev, dma_size, &dma_addr, GFP_KERNEL);
+	kbuf = dma_alloc_coherent(priv->dev, kbuf_size, &dma_addr, GFP_KERNEL);
 	if (!kbuf)
 		return -ENOMEM;
 
 	memcpy(kbuf, buf, size);
 
-	if (mgr->flags & IXR_FPGA_ENCRYPTION_EN)
-		memcpy(kbuf + size, mgr->key, ENCRYPTED_KEY_LEN);
+	if (mgr->flags & IXR_FPGA_ENCRYPTION_EN) {
+		memcpy(kbuf + algn_size, mgr->key, ENCRYPTED_KEY_LEN);
+        }
+	else {
+		u32* size_ptr = (u32*)(kbuf + algn_size);
+		*size_ptr = (u32)size;
+	}
 
 	__flush_cache_user_range((unsigned long)kbuf,
-				 (unsigned long)kbuf + dma_size);
+				 (unsigned long)kbuf + kbuf_size);
 
-	ret = eemi_ops->fpga_load(dma_addr, dma_addr + size, mgr->flags);
+	ret = eemi_ops->fpga_load(dma_addr, dma_addr + algn_size, mgr->flags);
 
-	dma_free_coherent(priv->dev, dma_size, kbuf, dma_addr);
+	dma_free_coherent(priv->dev, kbuf_size, kbuf, dma_addr);
 
 	return ret;
 }
```

</div></detalis>

## 参考

  * [「クロスコンパイルした Debian GNU/Linux でデバイスドライバをセルフコンパイルする際の注意点」@Qiita](https://qiita.com/ikwzm/items/e31ecb099d0d50cb027c)
  * [「Zynq Ultrascale+ MPSoC の pinctrl のグループとピン番号の対応」@Qiita](https://qiita.com/ikwzm/items/7b4ea8e031d3564b7535)