In default, IMX95-19x19 EVK board enables the PCIe M.2 interface for Wi-Fi modules. But for SDIO interface M.2 Wi-Fi module, we could not use it directly.
This doc is a step by step guide about how to make IW612 SDIO M.2 module(Murata 2EL) working on IMX95-19x19 EVK board based on Android 16.
1.Download I.MX Android BSP package, copy with scp to VMSIS.
16.0.0_1.4.0_ANDROID_SOURCE
2.Decompressing android bsp
tar -xzf imx-android-16.0.0_1.4.0.tar.gz
3. source ./imx_android_setup.sh to download Android source code.
4.Prepare cross compiler
GCC cross compiler:
Download the tool chain for the AArch32 and AArch64 on:
Arm GNU Toolchain
I download the latest version:
Arm GNU Toolchain 15.3.rel1
Need to pay attention, for I.MX95, need both 64 bit and 32 bit GCC compile tool.
64bit:
arm-gnu-toolchain-15.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz
32 bit:
arm-gnu-toolchain-15.3.rel1-x86_64-arm-none-eabi.tar.xz
For AArch64 toolchain
nxf93258@lsv051430:~/GCC_cross_compile_toolchain$sudo tar -xvJf arm-gnu-toolchain-15.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt/
nxf93258@lsv051430:~/Android/imx-android-16.0.0_1.4.0/android_build$export AARCH64_GCC_CROSS_COMPILE=/opt/arm-gnu-toolchain-15.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
For 32bit toolchain:
nxf93258@lsv051430:~/GCC_cross_compile_toolchain$sudo tar -xvJf arm-gnu-toolchain-15.3.rel1-x86_64-arm-none-eabi.tar.xz -C /opt/
nxf93258@lsv051430:~/Android/imx-android-16.0.0_1.4.0/android_build$ export AARCH32_GCC_CROSS_COMPILE=/opt/arm-gnu-toolchain-15.3.rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-
5.Set the external clang, kernel-build-tools, rust, and clang-tools tools for kernel building:
nxf93258@lsv051430:~/Android/imx-android-16.0.0_1.4.0/android_build$ sudo ./device/nxp/common/tools/setup_android_kernel_prebuilts.sh
6.Set up the environment for building.
This only configures the current terminal, if change to another terminal need to re-run the configurations.
nxf93258@lsv051430:~/Android/imx-android-16.0.0_1.4.0/android_build$ source build/envsetup.sh
7.Execute the Android lunch command for i.MX95-EVK.
nxf93258@lsv051430:~/Android/imx-android-16.0.0_1.4.0/android_build$lunch evk_95-nxp_stable-userdebug
8.Optional Steps:
Execute the imx-make.sh script to finish the whole images compiling. Or you can follow below steps to only build required images.
nxf93258@lsv051430:~/Android/imx-android-16.0.0_1.4.0/android_build$ ./imx-make.sh -j4 2>&1 | tee build-log.txt
9.Modify the device tree to disable pcie0 and enable usdhc3 like below:
~/Android/imx-android-16.0.0_1.4.0/android_build/vendor/nxp-opensource/kernel_imx/arch/arm64/boot/dts/freescale$ git diff
diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
index 69508a66f1c8..dfa1d3d51a9c 100644
--- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
@@ -26,10 +26,19 @@ / {
aliases {
mmc0 = &usdhc1;
mmc1 = &usdhc2;
+ mmc2 = &usdhc3;
serial0 = &lpuart1;
ethernet0 = &enetc_port0;
ethernet1 = &enetc_port2;
};
+
+ usdhc3_pwrseq: usdhc3_pwrseq {
+ compatible = "mmc-pwrseq-simple";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc3_pwrseq>;
+ post-power-on-delay-ms = <100>;
+ };
+
bt_sco_codec: audio-codec-bt-sco {
#sound-dai-cells = <1>;
@@ -651,7 +660,7 @@ &pcie0 {
reset-gpio = <&i2c7_pcal6524 5 GPIO_ACTIVE_LOW>;
vpcie-supply = <®_pcie0>;
supports-clkreq;
- status = "okay";
+ status = "disable";
};
&pcie1 {
@@ -733,6 +742,22 @@ &usdhc2 {
status = "okay";
};
+&usdhc3 {
+ pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep";
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ pinctrl-1 = <&pinctrl_usdhc3_100mhz>;
+ pinctrl-2 = <&pinctrl_usdhc3_200mhz>;
+ pinctrl-3 = <&pinctrl_usdhc3>;
+ mmc-pwrseq = <&usdhc3_pwrseq>;
+ vmmc-supply = <®_pcie0>; //Both PCIE0 and USDHC3 use same power supply.
+ bus-width = <4>;
+ keep-power-in-suspend;
+ non-removable;
+ wakeup-source;
+ status = "okay";
+};
+
+
&enetc_port0 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enetc0>;
@@ -1225,6 +1250,45 @@ IMX95_PAD_SD2_DATA3__USDHC2_DATA3 0x138e
IMX95_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e
>;
};
+
+ pinctrl_usdhc3_pwrseq: usdhc3pwrseq {
+ fsl,pins = <
+ IMX95_PAD_XSPI1_SCLK__GPIO5_IO_BIT9 0x31e
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ IMX95_PAD_SD3_CLK__USDHC3_CLK 0x158e
+ IMX95_PAD_SD3_CMD__USDHC3_CMD 0x138e
+ IMX95_PAD_SD3_DATA0__USDHC3_DATA0 0x138e
+ IMX95_PAD_SD3_DATA1__USDHC3_DATA1 0x138e
+ IMX95_PAD_SD3_DATA2__USDHC3_DATA2 0x138e
+ IMX95_PAD_SD3_DATA3__USDHC3_DATA3 0x138e
+ >;
+ };
+
+ pinctrl_usdhc3_100mhz: usdhc3-100mhzgrp {
+ fsl,pins = <
+ IMX95_PAD_SD3_CLK__USDHC3_CLK 0x158e
+ IMX95_PAD_SD3_CMD__USDHC3_CMD 0x138e
+ IMX95_PAD_SD3_DATA0__USDHC3_DATA0 0x138e
+ IMX95_PAD_SD3_DATA1__USDHC3_DATA1 0x138e
+ IMX95_PAD_SD3_DATA2__USDHC3_DATA2 0x138e
+ IMX95_PAD_SD3_DATA3__USDHC3_DATA3 0x138e
+ >;
+ };
+
+ pinctrl_usdhc3_200mhz: usdhc3-200mhzgrp {
+ fsl,pins = <
+ IMX95_PAD_SD3_CLK__USDHC3_CLK 0x15fe
+ IMX95_PAD_SD3_CMD__USDHC3_CMD 0x13fe
+ IMX95_PAD_SD3_DATA0__USDHC3_DATA0 0x13fe
+ IMX95_PAD_SD3_DATA1__USDHC3_DATA1 0x13fe
+ IMX95_PAD_SD3_DATA2__USDHC3_DATA2 0x13fe
+ IMX95_PAD_SD3_DATA3__USDHC3_DATA3 0x13fe
+ >;
+ };
};
&thermal_zones {
10.Build dtbo image.
Need about 40 minutes.
DTBO image holds the device tree binary of the board.
nxf93258@lsv051430:~/Android/imx-android-16.0.0_1.4.0/android_build$ source build/envsetup.sh
nxf93258@lsv051430:~/Android/imx-android-16.0.0_1.4.0/android_build$ lunch evk_95-nxp_stable-userdebug
nxf93258@lsv051430:~/Android/imx-android-16.0.0_1.4.0/android_build$./imx-make.sh dtboimage -j4
Then you will see that the dtbo images time stamp are updated as below:
Below info is for your reference.
~/Android/imx-android-16.0.0_1.4.0/android_build/device/nxp/common/build/dtbo.mk
This makefile includes the logic how to pack dtbo image.
~/Android/imx-android-16.0.0_1.4.0/android_build/device/nxp/imx9/evk_95/SharedBoardConfig.mk
This Macro: SUPPORT_GBL is not enabled in default, so still use dtbo image for the device tree.
~/Android/imx-android-16.0.0_1.4.0/android_build/device/nxp/imx9/evk_95/BoardConfig.mk
Wi-Fi/Bluetooth FW located here:
Wi-Fi/Bluetooth driver located here:
Can modify the Makefile accordingly if needed.
After compiled, the Wi-Fi driver and cfg80211.ko is located here:
11.Download prebuilt Image and decompress it.
16.0.0_1.4.0_DEMO_95//Pay attention to the version, need to match with our Android Source code download version.
12.Replace dtbo image.
Rename the original dtbo-imx95.img to dtbo-imx95_original.img in the prebuilt image directory to be a backup.
Then copy the dtbo-imx95.img from the remote android source code compiled directory:
~/Android/imx-android-16.0.0_1.4.0/android_build/out/target/product/evk_95/dtbo-imx95.img to the prebuilt image directory.
Like below:
13.Flash images into board.
Change the I.MX95-19*19-EVK board's SW7 to 1001 (from 1-4 bit) to enter serial download mode, then connect the board's USB1 port with type C cable to Windows PC.
Download the UUU binary file from GitHub: Releases · nxp-imx/mfgtools
Open the cmd interface in administrator mode, and enter to the prebuilt image directory.
Flash the images into I.MX95-19*19-EVK board with below command:
uuu_imx_android_flash.bat -f imx95 -a -e -u trusty-dual
For the details of each parameter, can refer to:
Android Quick Start Guide
14.Verify Wi-Fi and Bluetooth functions.
After flash finished successfully, power off the board and change the board's SW7 to switch the board back to 1010 (form 1-4 bit) to enter eMMC boot mode.
Check dmesg logs to confirm IW612 Wi-Fi driver is loaded successfully:
Check Wi-fi and Bluetooth work as expected in UI. Can use this application: scrcpy on windows instead of connecting a LCD.
This guide can also be a reference for other M.2 Wi-Fi modules with SDIO interface working on I.MX95 19*19-EVK board with Android or Linux OS.
Reference:
Android Quick Start Guide
Android User's Guide
Linux-Kernel Archive: [PATCH V3 2/3] arm64: dts: imx95-15x15-evk: Disable PCIe bus in the default dt...
Finished by Christine.Li.
Aug 5 2026.