Raspberry PI CM4 MHI enablement

Through the past few days I’ve been trying to build a Raspberry PI CM4 MHI kernel in order to use it with a PCIe modem: everything went quickly fine, but there are a few not immediate tricks that I had to use. Let’s see which ones…

Raspberry PI CM4 MHI

Raspberry PI CM4 MHI kernel building

The default Raspberry PI OS distribution does not have a kernel with the MHI and WWAN drivers, so we need to build it. You can directly build the kernel on the Raspberry, but it will take time, so I prefer cross-compiling on a x64 PC.

The procedure is described here, but in short, after having cloned the Raspberry Linux kernel repo and installed the required build tools, type in the kernel root:

$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig

Before building, we need to add the MHI and WWAN drivers. Menuconfig can be used for that task:

$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig

Add the following items:

Device Drivers > Bus devices
    <M> Modem Host Interface (MHI) bus
    <M> MHI PCI controller driver
Raspberry PI CM4 MHI
Device Drivers > Network device support
    <M> MHI network driver
Device Drivers > Network device support > Ethernet driver support
    <M> RmNet MAP driver ----
Raspberry PI CM4 MHI
Device Drivers > Network device support > Wireless WAN
    <M> WWAN Driver Core
    <M> MHI WWAN control driver for QCOM-based PCIe modems
    <M> MHI WWAN MBIM network driver for QCOM-based PCIe modems

Then, start the build:

$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image modules dtbs

After a while the build will be available.

Updating the kernel on the target

Most of the documentation focuses on the Raspberry using the kernel image loaded from an external SD card. However the Raspberry CM4 comes with an eMMC, so the procedure to update the kernel is a bit different.

As the first step, power-off the board, and shortcut the jumper J2 “Fit jumper to disable eMMC Boot” as in the following picture:

Download and build the usbboot tool.

Run the tool, power-on the board and plug the USB cable: the output of the usbboot tool should be something like:

$ sudo ./rpiboot
RPIBOOT: build-date Mar 12 2024 version 20221215~105525 1cf92dbf
Waiting for BCM2835/6/7/2711/2712…
Loading embedded: bootcode4.bin
Sending bootcode.bin
Successful read 4 bytes
Waiting for BCM2835/6/7/2711/2712…
Loading embedded: bootcode4.bin
Second stage boot server
Cannot open file config.txt
Cannot open file pieeprom.sig
Loading embedded: start4.elf
File read: start4.elf
Cannot open file fixup4.dat
Second stage boot server done

You should now see two new storage volumes: mount and open both (bootfs and rootfs).

It’s time to install the new kernel, but first move the old image to a backup:

$ sudo mv ./kernel8.img ./kernel8-backup.img

Copy the required files in the bootfs partition:

$ sudo cp arch/arm64/boot/Image /media/danielepa/bootfs/kernel8.img
$ sudo cp arch/arm64/boot/dts/broadcom/.dtb /media/bootfs/ $ sudo cp arch/arm64/boot/dts/overlays/.dtb* /media/bootfs/overlays/
$ sudo cp arch/arm64/boot/dts/overlays/README /media/bootfs/overlays/

Install the kernel modules in the rootfs partition:

$ sudo env PATH=$PATH make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=/media/danielepa/rootfs modules_install

You can now unmount the volumes, power-off the board, remove the jumper and power-on it again: the kernel should now load properly.

All done? Unfortunately, not yet…

MHI failed to power-up

After having done the procedure described above, I tried to use the newly built Raspberry PI CM4 MHI kernel with a Telit FN980, but the wwan devices were not available…

Looking at the kernel log, the MHI module was reporting a memory error during the initialization phase returned by function dma_alloc_coherent, something that I had never seen previously.

After a bit of googling I found that the PCIe window on the PI can’t be addressed via a 32 bit mask, something that the MHI driver seems to be doing for all the modems supported in the driver.

The solution is to use the following overlay:

Name: pcie-32bit-dma
Info: Force PCIe config to support 32bit DMA addresses at the expense of having to bounce buffers.
Load: dtoverlay=pcie-32bit-dma
Params: <none>

that should be added to the file config.txt in the boot partition.

Once fixed that, the modem is ready to be used.