Ultra96-V2 の WiFi で超苦労した話(v2021.1編)

はじめに
--------------------------------------------------

以前、Ultra96-V2 の WiFi を Xilinx 社の提供する linux-xlnx v2019.1 で動かす方法について Qiita に次のような記事を投稿しました。

 * [『Ultra96-V2 の WiFi で超苦労した話』 @Qiita](https://qiita.com/ikwzm/items/2b50d1228282ebf150dd)

上の記事では linux-xlnx v2019.1 (Linux Kernel 5.4 をベース) を対象にしていましたが、linux-xlnx v2021.1 (Linux Kernel 5.10 をベース) では上の記事で紹介した方法では Ultra96-V2 の WiFi を動かすことができませんでした。

この記事では、Ultra96-V2 向け Debian GNU/Linux (v2021.1版) で WiFi を動かすために ATWILC3000 の Linux Driver を組み込む方法を紹介します。

Linux Driver のリポジトリ
--------------------------------------------------

linux-xlnx v2019.1 では ATWILC3000 の Linux Driver は Avnet が提供しているものを使っていましたが、この Linux Driver は linux-xlnx v2021.1 ではコンパイルできませんでした。そこで linux-xlnx v2021.1 では WiFi Chip の製造元である Microchip 社が提供している Linux Driver を使います。

Microchip 社の提供する Linux Driver は github で公開されています。

 * https://github.com/linux4sam/linux-at91

上記のリポジトリは Linux Kernel 全体の Fork になっています(かなりデカいので注意)。該当する Linux Driver のソースコードは drivers/net/wireless/microchip/wilc1000 にあります。また、linux-xlnx v2021.1 からは新に mmc のパワー制御のための Linux Driver が必要になります。ATWILC3000 用のパワー制御用 Linux Driver のソースコードは drivers/mmc/core/pwrseq_wilc.c です。

 * https://github.com/linux4sam/linux-at91/tree/master/drivers/net/wireless/microchip/wilc1000
 * https://github.com/linux4sam/linux-at91/tree/master/drivers/mmc/core/pwrseq_wilc.c

パッチのリポジトリ
--------------------------------------------------

上記の Linux Driver を試したところ、私の Ultra96-V2 では動作しませんでした。いろいろ調べた結果、Avnet の提供するパッチをあてる必要があることがわかりました。パッチは次の URL にあります。

 * https://github.com/Avnet/meta-avnet/blob/2021.1/recipes-modules/wilc/files/0001-ultra96-modifications-15.5.patch

Linux Kernel のソースコードに追加する
--------------------------------------------------

### Microchip 社の Linux Driver をダウンロード

```console
shell$ git clone --depth 1 https://github.com/linux4sam/linux-at91
```

### Avnet 社の meta-avnet をダウンロード

```console
shell$ git clone --depth 1 -b 2021.1 https://github.com/Avnet/meta-avnet
```

### linux-xlnx v2021.1 をダウンロード

linux-xlnx v2021.1 をダウンロードします。ディレクトリは linux-xlnx-v2021.1-zynqmp-fpga としています。

```console
shell$ git clone --depth 1 -b xilinx-v2021.1 https://github.com/Xilinx/linux-xlnx.git linux-xlnx-v2021.1-zynqmp-fpga
shell$ cd linux-xlnx-v2021.1-zynqmp-fpga
```

### ATWILC3000 の Linux Driver を linux-xlnx v2021.1 にコピー

drivers/net/wireless/microchip/wilc1000/ にあるソースファイルを drivers/staging/wilc3000/ にコピーします。

```console
shell$ mkdir drivers/staging/wilc3000
shell$ cp ../linux-at91/drivers/net/wireless/microchip/wilc1000/* drivers/staging/wilc3000
```

drivers/staging/Kconfig と drivers/staging/Makefile に wilc3000 を追加します。

```diff:linux-xlnx-v2021.1-zynqmp-fpga-wilc3000.diff
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 9d424b925..2ea288a5e 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -82,6 +82,8 @@ source "drivers/staging/fbtft/Kconfig"
 
 source "drivers/staging/fsl-dpaa2/Kconfig"
 
+source "drivers/staging/wilc3000/Kconfig"
+
 source "drivers/staging/most/Kconfig"
 
 source "drivers/staging/ks7010/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 55a321d29..f0152d834 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_UNISYSSPAR)	+= unisys/
 obj-$(CONFIG_XILINX_APF)	+= apf/
 obj-$(CONFIG_FB_TFT)		+= fbtft/
 obj-$(CONFIG_FSL_DPAA2)		+= fsl-dpaa2/
+obj-$(CONFIG_WILC)		+= wilc3000/
 obj-$(CONFIG_MOST)		+= most/
 obj-$(CONFIG_KS7010)		+= ks7010/
 obj-$(CONFIG_GREYBUS)		+= greybus/
```

### Avnet のパッチをあてる

drivers/staging/wilc3000 に Avnet が提供しているパッチをあてます。

```console
shell$ patch -d drivers/staging/wilc3000 < ../meta-avnet/recipes-modules/wilc/files/0001-ultra96-modifications-15.5.patch
patching file Kconfig
patching file Makefile
patching file cfg80211.c
patching file debugfs.h
patching file netdev.c
patching file netdev.h
patching file power.c
patching file sdio.c
patching file wlan.c
patching file wlan.h
patching file wlan_cfg.c
```

### ATWILC3000 パワー制御用の Linux Driver を linux-xlnx v2021.1 にコピー

pwrseq_wilc.c を drivers/mmc/core/ にコピーします。

```console
shell$ cp ../linux-at91/drivers/mmc/core/pwrseq_wilc.c drivers/mmc/core/
```

drivers/mmc/core/Kconfig と drivers/mmc/core/Makefile に pwrseq_wilc を追加します。

```diff:linux-xlnx-v2021.1-zynqmp-fpga-pwrseq-wilc.diff
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index c12fe13e4..b65ff34fe 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -34,6 +34,16 @@ config PWRSEQ_SIMPLE
 	  This driver can also be built as a module. If so, the module
 	  will be called pwrseq_simple.
 
+config PWRSEQ_WILC
+	tristate "HW reset support for Microchip WILC BT + WiFi devices"
+	depends on OF
+	help
+	  This selects hardware reset support for Microchip WILC BT + WiFi
+	  devices. By default this option is set to n.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called pwrseq_wilc.
+
 config MMC_BLOCK
 	tristate "MMC block device driver"
 	depends on BLOCK
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 95ffe008e..058ac3fa9 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -13,6 +13,7 @@ mmc_core-$(CONFIG_OF)		+= pwrseq.o
 obj-$(CONFIG_PWRSEQ_SIMPLE)	+= pwrseq_simple.o
 obj-$(CONFIG_PWRSEQ_SD8787)	+= pwrseq_sd8787.o
 obj-$(CONFIG_PWRSEQ_EMMC)	+= pwrseq_emmc.o
+obj-$(CONFIG_PWRSEQ_WILC)	+= pwrseq_wilc.o
 mmc_core-$(CONFIG_DEBUG_FS)	+= debugfs.o
 obj-$(CONFIG_MMC_BLOCK)		+= mmc_block.o
 mmc_block-objs			:= block.o queue.o
```

Linux Kernel のイメージに組み込む
--------------------------------------------------

Ultra96-V2 では ATWILC3000 は ZynqMP の sdio1 に接続しています。ATWILC3000 を SDIO から制御するためのデバイスドライバは ```CONFIG_WILC_SDIO``` です。arch/arm64/configs/xilinx_zynqmp_defconfig に ```CONFIG_WILC_SDIO=y``` を追加します。ここではモジュールではなく Linux Kernel Image に組み込んでしまうために ```=m``` でなく ```=y``` を指定しています。
同様に ATWILC3000 のパワー制御用ドライバを組み込むために ```CONFIG_PWRSEQ_WILC=y``` を arch/arm64/configs/xilinx_zynqmp_defconfig に追加します。

```diff
diff --git a/arch/arm64/configs/xilinx_zynqmp_defconfig b/arch/arm64/configs/xilinx_zynqmp_defconfig
index cd6b1a7d7..a6cdc6600 100644
--- a/arch/arm64/configs/xilinx_zynqmp_defconfig
+++ b/arch/arm64/configs/xilinx_zynqmp_defconfig
@@ -180,6 +180,8 @@ CONFIG_USB_USBNET=y
 CONFIG_WL18XX=y
 CONFIG_WLCORE_SPI=y
 CONFIG_WLCORE_SDIO=y
+CONFIG_WILC_SDIO=y
+CONFIG_PWRSEQ_WILC=y
 CONFIG_INPUT_EVDEV=y
 CONFIG_KEYBOARD_GPIO=y
 CONFIG_KEYBOARD_GPIO_POLLED=y
```

Device Tree に組み込む
--------------------------------------------------

Ultra96-V2 では ATWILC3000 は ZynqMP の sdio1 に接続しています。したがって Device Tree の sdio1 のデバイスドライバである sdhci1 に ATWILC3000 のデバイスドライバをノードとして追加します。下記の例では wlcore というシンボルでノードを追加しています。

```dts:arch/arm64/boot/dts/xilinx/avnet-ultra96v2-rev1.dts
&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; /* This is not compatible with the WILC3000 and means the WILC will always be powered on */
	non-removeable;
	mmc-pwrseq = <&sdio_pwrseq>;
	vqmmc-supply = <&wmmcsdio_fixed>;
	#address-cells = <1>;
	#size-cells = <0>;
	wlcore: wilc_sdio@0 {
		compatible    = "microchip,wilc3000", "microchip,wilc3000";
		status        = "okay";
		reg           = <0>;
		bus-width     = <4>;
	};
};
```

wlcore の ```compatible``` プロパティには "microchip,wilc3000", "microchip,wilc3000" を指定します。

mmc のパワー制御用のドライバは ```mmc-pwrseq``` プロパティで指定します。Ultra96-V2 では ```<&sdio_pwrseq>``` を指定します。 ```<&sdio_pwrseq>``` はデバイスツリーのトップレベルに次のように記述します。

```dts:arch/arm64/boot/dts/xilinx/avnet-ultra96v2-rev1.dts
/ {
	:
	(中略)
	:
	sdio_pwrseq: sdio-pwrseq {
		compatible = "mmc-pwrseq-wilc";
		reset-gpios = <&gpio 7 GPIO_ACTIVE_HIGH>;
		powerdown-gpios = <&gpio 8 GPIO_ACTIVE_HIGH>;
		status = "okay";
	};
};
```

sdio_pwrseq の ```compatible``` プロパティには "mmc-pwrseq-wilc" を指定します。

```reset-gpios```プロパティには ```<&gpio  7 GPIO_ACTIVE_HIGH>``` を指定します。Ultra96-V2 では ATWILC3000 の RESETN ピンは ZynqMP の MIO7 に接続されています。RESETN の最後の N は Negative(Low Active) を意味するのですが、ここでは GPIO_ACTIVE_HIGH を指定しなければなりません。紛らわしいので注意してください。

```powerdown-gpios``` プロパティには ```<&gpio 8 GPIO_ACTIVE_HIGH>``` を指定します。Ultra96-V2 では ATWILC3000 の CHIP_EN ピンは ZynqMP の MIO8 に接続されています。CHIP_EN ピンは High にすることで動作を開始し、Low にすることでパワーダウンします。そういう意味では powerdown-gpios に GPIO_ACTIVE_HIGH を指定するのは少し違和感があるのですが、Linux Driver がそうなっているので仕方がありません。

Firmware を Root File System にインストールしておく
--------------------------------------------------

ATWILC3000 の Linux Driver は、起動時に firmware を ATWILC3000 にロードします。firmware は以下の URL にあります。

 * https://github.com/linux4wilc/firmware

この firmware は /lib/firmware/mchp になければなりません。Root File System を作る時にインストールしておくか、後で追加してください。ネットワークが使えないと後から追加するのにも不便なので、あらかじめインストールしておいたほうが良いでしょう。

```console
debian10-rootfs# mkdir /lib/firmware/mchp
debian10-rootfs# git clone git://github.com/linux4wilc/firmware  linux4wilc-firmware  
debian10-rootfs# cp linux4wilc-firmware/*.bin /lib/firmware/mchp
debian10-rootfs# rm -rf linux4wilc-firmware  
```

苦労した点
--------------------------------------------------

当初は、Avnet 社のパッチを知らずに Microchip 社の提供する Linux Driver のみで動かそうとして全然動かずに難儀しました。もうあきらめて Ultra96-V2 は古いカーネルで動かしていました。そしたら ある方(この方は Yocto でシステムを構築したらしい)から meta-avnet を使って構築した Linux 5.10 では Ultra96-V2 の WiFi が動いているとのメールをいただきました。さっそく調べたところ、meta-avnet ではパッチをあてていることがわかり、このパッチをあてれば Ultra96-V2 の WiFi が動きました。

メールで教えてくださった方、パッチを提供してくれた Avnet 社、もちろん Linux Driver を提供している Microchip 社にも感謝します。


参考
--------------------------------------------------

 * https://github.com/linux4sam/linux-at91
 * https://github.com/Avnet/meta-avnet/
 * https://github.com/linux4wilc/firmware
 * [『Ultra96-V2 の WiFi で超苦労した話』 @Qiita](https://qiita.com/ikwzm/items/2b50d1228282ebf150dd)
 * [『UltraZed/Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux (v2021.1版) ブートイメージの提供』@Qiita](https://qiita.com/ikwzm/items/a9adc5a7329b2eb36895)