Khadas VIM3: Armbian on eMMC With OOWOW Recovery in SPI

Khadas VIM3: Armbian on eMMC With OOWOW Recovery in SPI

Configure a Khadas VIM3 V12 to boot Armbian from eMMC by default and OOWOW from SPI when holding the FUNCTION button.

Khadas VIM3: Armbian on eMMC With OOWOW Recovery in SPI

This guide walks through recovering SPI boot on a Khadas VIM3 V12, installing Armbian on eMMC, and keeping OOWOW available with the FUNCTION button. The final setup starts U-Boot from SPI, boots Armbian by default, and launches OOWOW when FUNCTION is held during startup.

The sequence below documents the configuration verified on the VIM3 V12 described in the original runbook. The I2C bus number and U-Boot device names shown here are specific to that setup; check them on your board before using the commands.

Final boot flow

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
BOOT_MODE = SPI

Power / Reset
    |
    v
SPI U-Boot
    |
    +-- FUNCTION held -----> boot_targets=spi  -----> OOWOW
    |
    +-- FUNCTION not held -> boot_targets=mmc2 -----> Armbian / eMMC

Important: Do not call run bootcmd_spi directly from the FUNCTION-button branch.
The working solution is to change boot_targets to spi and then run the standard distro_bootcmd.


1. Recover from a broken SPI boot loop

Initially, BOOT_MODE was set to SPI while the SPI contents were invalid. The board entered a boot/reset loop and the white LED flashed rapidly.

The existing Ubuntu installation in eMMC could still be started by pressing FUNCTION three times quickly.

1.1 Find the VIM3 MCU I2C bus

Boot the existing Linux installation and run:

1
ls -d /sys/bus/i2c/devices/*-0018 2>/dev/null

For example, if the result is:

1
/sys/bus/i2c/devices/4-0018

then the I2C bus number is 4.

1.2 Check the current MCU boot mode

1
sudo i2cget -f -y 4 0x18 0x20

Values used on this VIM3:

1
2
0x00 = SPI
0x01 = eMMC

1.3 Temporarily bypass the broken SPI

Switch the board to eMMC boot mode:

1
2
3
sudo i2cset -f -y 4 0x18 0x20 0x01
sudo i2cget -f -y 4 0x18 0x20
sudo poweroff

Replace 4 with the I2C bus number reported on your board.


2. Boot OOWOW from microSD using KEY mode

After switching BOOT_MODE to eMMC, OOWOW could be started from a prepared microSD card using VIM3 KEY mode.

  1. Power the VIM3 off completely.
  2. Insert the microSD containing the VIM3 OOWOW SD image.
  3. Hold POWER.
  4. Briefly press RESET while continuing to hold POWER.
  5. Keep holding POWER for a few seconds, then release it.
  6. Wait for OOWOW to boot.

3. Fully restore OOWOW in SPI

During recovery, SPI was first completely erased from OOWOW:

1
Advanced -> SPI flash options -> Erase SPI Flash FULL

The built-in Write system to SPI flash menu showed an empty From field and offered an old version after checking for updates. Therefore, the SPI image was written manually over SSH.

3.1 Connect to OOWOW over SSH

1
ssh root@<OOWOW_IP>

The local OOWOW HTTP helper was not used because this command failed with Bad Gateway on this setup:

1
curl localhost/shell/write ...

3.2 Download and verify the VIM3 SPI image

1
2
3
4
5
6
7
cd /tmp

curl -fL --retry 3 \
  https://dl.khadas.com/products/oowow/system/vim3-oowow-latest-spi.img.gz \
  -o vim3-oowow-latest-spi.img.gz

gzip -t vim3-oowow-latest-spi.img.gz && echo "IMAGE OK"

3.3 Write OOWOW to SPI

1
gzip -dc /tmp/vim3-oowow-latest-spi.img.gz | spi_update

Wait until the SPI write completes successfully. Then run:

1
2
spi off && mmc on
sync

Do not remove power while spi_update is running.
After a full SPI erase, this step restores the SPI contents.


4. Verify that OOWOW boots directly from SPI

Before working on the FUNCTION-button logic, first verify that the SPI image itself is valid.

Boot Linux and temporarily set BOOT_MODE=SPI:

1
2
3
sudo i2cset -f -y 4 0x18 0x20 0x00
sudo i2cget -f -y 4 0x18 0x20
sudo poweroff

Remove the microSD card and power the VIM3 on.

If OOWOW boots, then:

  • the SPI flash is working;
  • the OOWOW SPI image is valid;
  • any remaining problem is only in boot-target selection.

On this VIM3 V12, OOWOW successfully booted directly from SPI.


5. Install Armbian directly to eMMC from OOWOW

On this VIM3 V12, directly writing the Armbian image to eMMC with dd worked correctly. armbian-install was not required.

Connect to the running OOWOW instance over SSH.

5.1 Download the Armbian image

1
2
3
4
5
cd /tmp

curl -fL \
  https://dl.armbian.com/khadas-vim3/Trixie_current_minimal \
  -o armbian-vim3.img.xz

5.2 Verify the downloaded image

1
xz -t armbian-vim3.img.xz && echo "IMAGE OK"

5.3 Confirm the eMMC device

1
mmc_disk

Before writing anything, verify that mmc_disk points to the VIM3 eMMC device. The following command destroys the existing contents of that device.

5.4 Write Armbian to eMMC

1
2
3
4
xz -dc armbian-vim3.img.xz | \
  dd of="$(mmc_disk)" bs=4M conv=fsync status=progress

sync

After sync completes:

  1. Power the board off completely.
  2. Remove the microSD card.
  3. For the first test, use BOOT_MODE=eMMC.
  4. Boot the VIM3.

On this board, Armbian successfully booted from eMMC after the direct dd installation.


6. Verify Armbian boot through SPI U-Boot

The next goal is to keep BOOT_MODE=SPI, start U-Boot from SPI, and then boot Armbian from eMMC.

  1. Set BOOT_MODE=SPI.
  2. Power on the VIM3.
  3. Press Space during the U-Boot countdown to stop autoboot.
  4. Run:
1
2
setenv boot_targets 'mmc2'
run distro_bootcmd

On this VIM3 V12, Armbian booted successfully.

This confirmed that:

  • SPI U-Boot is working;
  • eMMC is exposed as mmc2;
  • the standard distro_bootcmd can boot the installed Armbian system.

7. Verify the FUNCTION button in U-Boot

Stop U-Boot with Space.

List buttons known to U-Boot:

1
button list

Test the FUNCTION button:

1
2
button Function
echo $?

Run the test once with the button released and once while holding FUNCTION.

On this VIM3 V12, the FUNCTION button was detected correctly by U-Boot, so no U-Boot rebuild was required.


8. Configure the final U-Boot boot logic

What did not work

Calling run bootcmd_spi directly inside the FUNCTION-button branch did not start OOWOW reliably.

The working method is:

  1. set boot_targets=spi when FUNCTION is held;
  2. set boot_targets=mmc2 otherwise;
  3. run the standard distro_bootcmd in both cases.

8.1 Test without saving anything

At the U-Boot prompt, enter:

1
2
setenv boot_targets 'mmc2'
setenv bootcmd 'if button Function; then echo FUNCTION pressed - booting OOWOW; setenv boot_targets spi; else setenv boot_targets mmc2; fi; run distro_bootcmd'

Do not run saveenv yet.

8.2 Test normal boot

Without pressing FUNCTION:

1
run bootcmd

Expected result:

1
SPI U-Boot -> mmc2 -> Armbian on eMMC

8.3 Test OOWOW boot

Restart the board and stop autoboot to return to the U-Boot prompt. Because the settings have not been saved, repeat both setenv commands from step 8.1. Then hold FUNCTION and run:

1
run bootcmd

Expected result:

1
FUNCTION pressed - booting OOWOW

U-Boot then sets:

1
boot_targets=spi

and distro_bootcmd launches OOWOW from SPI.

8.4 Save the working configuration

Only after both tests succeed, return to the U-Boot prompt and save the configuration:

1
2
3
setenv boot_targets 'mmc2'
setenv bootcmd 'if button Function; then echo FUNCTION pressed - booting OOWOW; setenv boot_targets spi; else setenv boot_targets mmc2; fi; run distro_bootcmd'
saveenv

9. Normal operation after configuration

ScenarioActionResult
Normal power-on / resetDo not press anythingSPI U-Boot -> mmc2 -> Armbian in eMMC
Boot OOWOWHold FUNCTION during startup/resetSPI U-Boot -> boot_targets=spi -> OOWOW
Enter U-Boot consolePress Space during autobootU-Boot command prompt

10. Quick recovery if SPI prevents normal boot again

A tested emergency recovery path for this board is:

  1. Try pressing FUNCTION three times quickly to bypass the problematic SPI path and boot eMMC.
  2. After Linux starts, find the MCU I2C device (*-0018).
  3. Temporarily switch back to BOOT_MODE=eMMC:
1
2
sudo i2cset -f -y 4 0x18 0x20 0x01
sudo poweroff
  1. If necessary, boot OOWOW from microSD using KEY mode.
  2. Restore SPI again using spi_update.

Replace I2C bus 4 with the actual bus number on your board.


11. Final checklist

  • OOWOW is written to SPI and boots directly when BOOT_MODE=SPI.
  • Armbian is written directly to eMMC from OOWOW using xz -dc ... | dd ... and boots successfully.
  • SPI U-Boot boots Armbian using setenv boot_targets 'mmc2'; run distro_bootcmd.
  • button Function is detected correctly by U-Boot.
  • Without holding FUNCTION, Armbian boots from eMMC.
  • Holding FUNCTION boots OOWOW from SPI.
  • saveenv was executed only after both boot paths were verified.

Final U-Boot configuration — copy/paste block

Run this in SPI U-Boot after verifying that mmc2 is the eMMC device:

1
2
3
setenv boot_targets 'mmc2'
setenv bootcmd 'if button Function; then echo FUNCTION pressed - booting OOWOW; setenv boot_targets spi; else setenv boot_targets mmc2; fi; run distro_bootcmd'
saveenv

Result

1
2
3
4
5
6
7
BOOT_MODE = SPI

Normal boot:
SPI U-Boot -> eMMC (mmc2) -> Armbian

FUNCTION held during boot:
SPI U-Boot -> boot_targets=spi -> OOWOW

Configuration verified on Khadas VIM3 V12.