Mobile i.MX8MQ Device

Christmas time is full of surprises, one of them was a new mobile device I got a Christmas morning, despite all the chip shortage and whatnot problems. The device is powered by NXP i.MX8MQ industrial SoC which combines 4 Arm A53 cores, 1 Arm M4F core and 1 Vivante GC7k Lite video core. And no, it is not Librem5 device which is supposed to be powered by the same SoC.

It is MNT Reform2 DYI Kit - an OpenHW Open Source laptop powered by BoundaryDevices Nitrogen8M SoM which is based on i.MX8MQ SoC. The SoM has 4G LPDDR4 RAM. The DYI Kit is delivered as pre-soldered and partially pre-assembled kit which any amator capable to hold the screwdriver can assemble.

Now, this is not going to be unpacking post or detailed assembling guide. The former is provided already by lucky crowdfunding backers while the later is included in the box (a guide with pictures and step by step assembly process). I didn't join the crowdfunding campaign because I was still hoping to get my L5 any time soon so was thinking I don't need two mobile devices with the same CPU, and Reform was looking promising in the way that soon more SoMs may follow so I was hoping to get then one of those alternative builds later.

But then suddenly I realized this summer I'm not going to get the L5, not this year for sure. So I went ahead and ordered the original build once it popped up in the local store this fall. The kit arrived mid December and was destined to be a Christmas present to myself.

But back to the technical details. The pre-assembled parts include the most sophisitcated elements like

  • SoM and passive cooler pre-mounted on the motherboard - so you cannot screw the SoM by misplacing the cooler,
  • screen assembly with 12" FHD LCD and audio speakers mounted on the frame and frame mounted on the chassis - so you cannot screw the screen lid by wrongly tightening hinges,
  • the keyboard with keycaps - so you cannot screw the caps by forcing them on and accidently breaking,
  • the battery holders - not sure you can screw anything there, it's quite simple, but batteries come in the holder (to save for some space) so I guess to prevent packing each individual cell for safety,
  • the pointer input device (trackball or touchpad) again I guess in assembled form it takes less space, as assembly is quite simple for this device.

So overall assembly is actually just connecting and fixing each of those pre-assembled fragment into place. The spare screws are provided, the connecting cables are provided, all the covers, bezels and fillers are provided. Even bootable SD card with pre-built Debian image is provided and the LiFePO4 cells are pre-charged. So in the end you get fully working laptop which you can turn on and start working with. Just like that:

Several things to note. You can purchase fully pre-assembled version, with either slight price increase if buying from local shop or a bit bigger premium if getting from CrowdSupply since there you get so-called MAX version (fully stuffed). Another thing to note - the DYI kit from CrowdSupply does not include pointer device (so you need to add it explicitly into the basket) while kit from local shop does include pointer (you need to select which one, or none to opt-out).

Then you need to select other optional components such as NVME drive and WIFI mPCIe card. I do have both of those in my component stash from other laptops which kids demolished so I opted out of those elements. I have WD SN550 nvme and two mPCIe WIFI cards - Ralink RT3290 and Intel N2230BN - both with onboard BT component. The kit does include a screw for NVME and it fited flawlessly. The kit also includes acrylic wifi antenna holder but no antenna itself. Luckily when removing cards from old ruined laptops I removed at least one of them with antenna (a u.fl coax cable with small printed pcb). So I just taped that to the acrylic holder. The cards themselves though are half-size while MB is designed for full-size mpcie cards. So I put a filler onto card to push it down to MB by bottom cover.

NVME was recognized without a problem, the WIFI cards though need some tinkering to get in. They are visible in lspci output but the kernel on the image is pre-built with Atheros WIFI (the one you can opt-in for) however no other drivers are included whether as built-in or loadable modules. Which means the necessary modules need to be built separately and side-loaded onto the sd card.

While i.MX8MQ is mostly mainlined in the kernel (with mutual help from NXP and Purism folks) the laptop is using several components which do require some custom patches, mainly in the video path (MIPI DSI converter), but also some other clock drivers specific to the laptop's perephery. Bottomline - you better use the system-image builder (which is currently pinned to 5.12 kernel branch). Interestingly, while enabling Ralink RT3290 module in the kernel menue I noticed the Intel Iwlink driver is already marked as module, however pre-build sdcard image does not include any modules whatsoever, moreover it doesn't even build them! Which explains why even N2230 was not working out of the box.

So what I need to do then is to enable ralink module, build modules and copy them to sd-card. I also need to copy corresponding firmware (as much as I don't like closed-source blobs). So first let me patch the config template with a diff I made by running make menuconfig and enabling rt3290 module. The reason I came to extracting a diff is because menuconfig made way too many changes in the config which I don't think I actually needed.

$ git clone https://source.mnt.re/reform/reform-system-image
$ cd reform-system-image/reform2-imx8mq
$ patch -p2 <<EOF
diff --git a/reform2-imx8mq/template-kernel/kernel-config \
index 91f8ae9..15070a5 100644
--- a/reform2-imx8mq/template-kernel/kernel-config
+++ b/reform2-imx8mq/template-kernel/kernel-config
@@ -2121,7 +2121,29 @@ CONFIG_WLAN_VENDOR_MEDIATEK=y
 # CONFIG_WILC1000_SDIO is not set
 # CONFIG_WILC1000_SPI is not set
+# CONFIG_RT2400PCI is not set
+# CONFIG_RT2500PCI is not set
+# CONFIG_RT61PCI is not set
+# CONFIG_RT2800PCI_RT33XX is not set
+# CONFIG_RT2800PCI_RT35XX is not set
+# CONFIG_RT2800PCI_RT53XX is not set
+# CONFIG_RT2500USB is not set
+# CONFIG_RT73USB is not set
+# CONFIG_RT2800USB is not set
+# CONFIG_RT2X00_LIB_DEBUGFS is not set
+# CONFIG_RT2X00_DEBUG is not set
 # CONFIG_RTL8180 is not set
 # CONFIG_RTL8187 is not set
patching file template-kernel/kernel-config

Now we just run ./mkkernel.sh to build the kernel as normal, and then we'd need to run make modules to actually build them, but we'd need to cross-compile for aarch64 architecture hence:

$ cd linux
$ CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 make -j8 modules
$ less modules.order

That file lists all the modules which were built so we can use it as a source to copy the files over to sdcard image. Assuming the card is inserted and mounted at /mnt/sd1:

$ sudo bsdtar czf /mnt/sd1/boot/kmods.tgz modules.builtin.modinfo modules.builtin \
    modules.order `cat modules.order`

The reason I copied it over to /boot folder is because there's no either /lib/modules or /lib/firmware folders pre-created. Oh, and speaking about firmware:

$ sudo bsdtar czf /mnt/sd1/boot/kmods.tgz modules.builtin.modinfo modules.builtin \
    modules.order `cat modules.order` /lib/firmware/iwlwifi-2030-6.ucode \

This is of course since my laptop is not RYF certified and has all the nasty binary blobs on it (eww, yack!). And now I should have all I need and be ready to boot from this sdcard to see if I can insmod drivers and upload fw. And here is transcript of how I did it on reform laptop itself. First let's check if we have the file and where we need to put it.

root@reform:~# ls -al /boot/
total 14944
drwxr-xr-x  2 root root     4096 Dec 27 13:36 .
drwxr-xr-x 21 root root     4096 Jun 10  2021 ..
-rw-r--r--  1 root root  1079844 Jun 10  2021 flash.bin
-rw-r--r--  1 root root  1079844 Jun 10  2021 flash-rescue.bin
-rwxr-xr-x  1 root root      241 Jun 10  2021 flash-rescue.sh
-rw-r--r--  1 ruff ruff 13421925 Dec 27 13:31 kmods.tgz
root@reform:~# depmod
depmod: ERROR: could not open directory /lib/modules/5.12.0+: No such file or directory
depmod: FATAL: could not search modules: No such file or directory

Now we know where kmodutils are expecting to find it, so let's move on:

root@reform:~# mkdir -p /lib/modules/5.12.0\+
root@reform:~# cd /lib/modules/5.12.0+/
root@reform:/lib/modules/5.12.0+# tar tzf /boot/kmods.tgz
root@reform:/lib/modules/5.12.0+# tar xzf /boot/kmods.tgz
root@reform:/lib/modules/5.12.0+# mv lib/firmware ../..
root@reform:/lib/modules/5.12.0+# depmod
root@reform:/lib/modules/5.12.0+# modprobe rt2800pci
[  806.354313] rt2800pci 0000:01:00.0: enabling device (0000 -> 0002)
[  806.362306] ieee80211 phy0: rt2x00_set_rt: Info - RT chipset 3290, rev 0003 detected
[  806.379656] ieee80211 phy0: rt2x00_set_rf: Info - RF chipset 3290 detected
[  806.409931] rt2800pci 0000:01:00.0 wlp1s0f0: renamed from wlan0
root@reform:/lib/modules/5.12.0+# cd

Ok, now we have the network interface, let's configure it and bring it up:

root@reform:~# vim /etc/wpa_supplicant/wpa_supplicant-wlp1s0f0.conf
root@reform:~# systemctl enable wpa_supplicant@wlp1s0f0
root@reform:~# systemctl start wpa_supplicant@wlp1s0f0
root@reform:~# rfkill unblock 0
[ 1670.434080] ieee80211 phy0: rt2x00lib_request_firmware: Info - Loading firmware file \
[ 1670.448141] ieee80211 phy0: rt2x00lib_request_firmware: Info - Firmware detected - \
		version: 0.37
root@reform:~# [ 1671.676478] wlp1s0f0: authenticate with e0:46:9a:48:e8:c1
[ 1671.688643] wlp1s0f0: send auth to e0:46:9a:48:e8:c1 (try 1/3)
[ 1671.702193] wlp1s0f0: authenticated
[ 1671.711309] wlp1s0f0: associate with e0:46:9a:48:e8:c1 (try 1/3)
[ 1671.723939] wlp1s0f0: RX AssocResp from e0:46:9a:48:e8:c1 (capab=0x431 status=0 aid=2)
[ 1671.737163] IPv6: ADDRCONF(NETDEV_CHANGE): wlp1s0f0: link becomes ready
[ 1671.739308] wlp1s0f0: associated

root@reform:~# vim /etc/systemd/network/wlan.network


root@reform:~# systemctl enable systemd-networkd
root@reform:~# systemctl start systemd-networkd
root@reform:~# networkctl
  1 lo       loopback carrier     unmanaged
  2 sit0     sit      off         unmanaged
  3 eth0     ether    no-carrier  unmanaged
  4 wlp1s0f0 wlan     routable    configured

4 links listed.

And done. RT3290 works kind of ok, I didn't try yet Intel N2230 card. However antenna placement is definitelly not the best. The connectiviy is a bit shaky. However if I lift laptop from the table and turn it a bit (just a bit is enough, eg, to lift the corner) the speed increases tenfolds (I didn't see such speeed even on my Dell Inspirion). So will try IWL of course, just to make sure, but most likely will need to tinker with antenna placement. And btw I'm writing this now from Reform2 laptop.

System Update and graphics

Once network is set up the first thing to do is obviously to do the system update! The update promised to consume extra 700+MB of space - and this topic is very painful to me. I hit mental barier when trying to accept such an offer. So obviosly I've started long winding process of gradual partial upgrades until I figure what is going to eat the extra space and why and whether I'm comfortable with that. Notable space eaters appeared to be blender (200M) LLVM13 (150M) Python3.10(100M) and a little bit of everything for remainder. So I removed blender (I have more powerful machines for it), pinned python (version 3.9 is hard dependency for some packages and 3.10 is only for numpy) removed some other stuff I'm not ever using (eg emacs, wmaker, etc.) which all in all resulted in about 300M of extra space (below mental meltdown threshold).

Now it's time to bring up graphics! And the first problem: sway cannot start, dri cannot find backend, and it is looking for backend at some odd place - /usr/local/lib/aarch64-gnu-linux/dri. I do vaguely remember Lukas mentioned something about some patches and specific versions of various components which are not yet either mainlined or released. But we moved from llvm10 to llvm13 - it's a big leap comparing to base image. Looking at /etc/ld.so.conf it is really looking odd. So let's try to take off on a mainline system runtime. I'll use my standard tty launcher for sway, will just add PATH correction, and will also correct ld.so.conf to prefer system runtime versus custom built.

ruff@reform:~$ vim .bash_profile 
if [[ $(tty) = "/dev/tty1" && "x$WAYLAND_DISPLAY" = "x" ]]
        export LD_LIBRARY_PATH
        export PATH=/bin:/usr/bin:/usr/local/bin:/usr/games:~/bin
        export QT_QPA_PLATFORM=wayland
        export SDL_VIDEODRIVER=wayland
        export MOZ_ENABLE_WAYLAND=1
        export GTK_THEME=Adwaita:dark
        dbus-update-activation-environment --systemd SDL_VIDEODRIVER QT_QPA_PLATFORM \
        exec /usr/bin/sway
ruff@reform:~$ sudo vim /etc/ld.so.conf

And comment out the lines for /usr/local/lib. The rest of the lib paths will be added from ld.so.conf.d/:

ruff@reform:~$ cat  /etc/ld.so.conf.d/aarch64-linux-gnu.conf 
# Multiarch support
ruff@reform:~$ cat  /etc/ld.so.conf.d/libc.conf 
# libc default configuration

Now we can start sway by relogging to tty1 (alt-F1). Sway however behaves strangely, perhaps the result of my meddling with packages. I needed to remove rofi and replace it with wofi, install sway-backgrounds (purely cosmetic) and then correct variables at the beginning of the /etc/sway/config:

# Your preferred terminal emulator
#set $term foot
set $term xfce4-terminal
# Your preferred application launcher
# Note: pass the final command to swaymsg so that the resulting window can be opened
# on the original workspace that the command was run on.
#set $menu dmenu_path | dmenu | xargs swaymsg exec --
set $menu wofi --show run

And here's the result:

Improving network connection

So the connection is indeed making me nervous, so I will try Intel N2230. And while I'm inside - I will also try to lift a bit antenna holder - by rerouting keyboard cable slightly to the side, putting a tape onto usb ports - to fix both cable and antenna holder. Here's a picture to demonstrate what I'm speaking about:

After replacing a card and booting in I realized the card is not properly initialized - the networkd task hanging waiting for a card to appear, and even after timeout and logging in it is obvious the network interface is missing. And indeed, the N2230 is a dual card hence needs dvm module, while existing kernel had only mvm module for iwl. So we need to add the missing module. Shutdown the laptop, remove sdcard and plug it into juicy laptop.

First we enable dvm module. Btw I realized lot of mess around config was due to me missing cross-compile flags while running menuconfig. With those flags it does only what asked to do:

$ CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 make menuconfig
$ diff -u .config.old .config
--- .config.old 2021-12-26 09:46:01.248917810 +0100
+++ .config     2021-12-28 14:37:53.099937285 +0100
@@ -2093,7 +2093,7 @@
 # CONFIG_IWL3945 is not set
-# CONFIG_IWLDVM is not set
$ CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 make -j8 modules
  SYSMAP  System.map
  MODPOST Module.symvers
  CC [M]  drivers/net/wireless/intel/iwlwifi/iwlwifi.mod.o
  LD [M]  drivers/net/wireless/intel/iwlwifi/iwlwifi.ko
  LD [M]  drivers/net/wireless/intel/iwlwifi/mvm/iwlmvm.ko
  CC [M]  drivers/net/wireless/intel/iwlwifi/dvm/iwldvm.mod.o
  LD [M]  drivers/net/wireless/intel/iwlwifi/dvm/iwldvm.ko

So we can just rebuild modules in-place to avoid rebuilding entire kernel. And since we already have directory structure for modules we can simply copy the modules where they should be:

$ sudo cp modules.* /mnt/sd1/lib/modules/5.12.0+/
$ sudo cp drivers/net/wireless/intel/iwlwifi/iwlwifi.ko \
$ sudo cp drivers/net/wireless/intel/iwlwifi/mvm/iwlmvm.ko \
$ sudo mkdir /mnt/sd1/lib/modules/5.12.0+/drivers/net/wireless/intel/iwlwifi/dvm/
$ sudo cp drivers/net/wireless/intel/iwlwifi/dvm/iwldvm.ko \

After booting (still need to wait for network task to timeout) we just need to finish what we missed back there:

root@reform:~# depmod
root@reform:~# modprobe iwldvm
[  148.305298] iwlwifi: module verification failed: signature and/or required key \
	missing - tainting kernel
[  148.324049] Intel(R) Wireless WiFi driver for Linux
[  148.329275] iwlwifi 0000:01:00.0: enabling device (0000 -> 0002)
[  148.335516] iwlwifi 0000:01:00.0: pci_enable_msi failed - -22
[  148.409503] iwlwifi 0000:01:00.0: loaded firmware version 2030-6.ucode \
	op_mode iwldvm
[  148.775268] iwlwifi 0000:01:00.0: CONFIG_IWLWIFI_DEBUG disabled
[  148.781488] iwlwifi 0000:01:00.0: CONFIG_IWLWIFI_DEBUGFS disabled
[  148.787777] iwlwifi 0000:01:00.0: CONFIG_IWLWIFI_DEVICE_TRACING enabled
[  148.794588] iwlwifi 0000:01:00.0: Detected Intel(R) Centrino(R) Wireless-N 2230 BGN, \
root@reform:~# [  148.871484] iwlwifi 0000:01:00.0 wlp1s0: renamed from wlan0
[  148.988018] iwlwifi 0000:01:00.0: Radio type=0x2-0x0-0x0
[  149.272303] iwlwifi 0000:01:00.0: Radio type=0x2-0x0-0x0

Ah snap! I hate these stupid names. But will correct this some other time, for now:

root@reform:~# ln -s wpa_supplicant-wlp1s0f0.conf \
root@reform:~# systemctl enable wpa_supplicant@wlp1s0
root@reform:~# systemctl start wpa_supplicant@wlp1s0
root@reform:~# [  383.774791] wlp1s0: authenticate with e0:46:9a:48:e8:c1
[  383.787492] wlp1s0: send auth to e0:46:9a:48:e8:c1 (try 1/3)
[  383.797608] wlp1s0: authenticated
[  383.803552] wlp1s0: waiting for beacon from e0:46:9a:48:e8:c1
[  383.814484] wlp1s0: associate with e0:46:9a:48:e8:c1 (try 1/3)
[  383.827847] wlp1s0: RX AssocResp from e0:46:9a:48:e8:c1 (capab=0x431 status=0 aid=5)
[  383.858488] wlp1s0: associated
[  383.970910] IPv6: ADDRCONF(NETDEV_CHANGE): wlp1s0: link becomes ready

root@reform:~# networkctl
  1 lo     loopback carrier     unmanaged
  2 sit0   sit      off         unmanaged
  3 eth0   ether    off         unmanaged
  4 wlp1s0 wlan     routable    configured

4 links listed.
root@reform:~# systemctl disable wpa_supplicant@wlp1s0f0

Why disable? Because connectivity works much better now. Don't know whether it is result of intel card or antenna correction, but it feels much better now typing this text over ssh, smoother and sleeker.

Mon Dec 27 17:27:42 2021 Upd.: Tue Dec 28 17:33:56 2021
© ruff 2011