The CPU family functional manual is wonderfully detailed and available at http://www.marvell.com/embedded-processors/kirkwood/assets/FS_88F6180_9x_6281_OpenSource.pdf The processor itself is documented at http://www.marvell.com/embedded-processors/kirkwood/assets/HW_88F6281_OpenSource.pdf Table 26, on page 53, details the pin multiplexing arrangement and is very useful!

Serial console is available on vias. With the power plug component-side-up and top-left, the block of four pins to the left of the power plug are, top-to-bottom, 3.3V, uart0 txd (GPIO 10), uart0 rxd (GPIO 11), and gnd.

I2C is available on pads. With the CPU component-side-up and the RAM chip top-left, the pad to the right of R259 (in the region marked Q12) is SCK (GPIO 9); the pad to the right of R261 (in Q13) is SDA (GPIO 8).

JTAG is available on test-pads. (Not tested; taken from http://wiki.openwrt.org/toh/cloudengines/pogo-v4 .) : TDI on TP19; TDO on TP20; TCK on TP18; TMS_CPU on TP21; RSTn on TP17; TMS_CORE on TP22.

To the right of the SD card socket is J17, which looks to be (not tested; taken from http://wiki.openwrt.org/toh/cloudengines/pogo-v4 and visual inspection), in left-to-right order: 3.3V, GND, GPIO 3 (miso), GPIO 7 (cs), GPIO 1 (mosi), GPIO 2 (sck); this appears to be an SPI breakout!

On the board’s back side, by the processor, LED7 is GPIO 23 and LED8 is GPIO 21, both through resistors. Note that GPIO 21 is by default assigned to SATA0_ACT, which indicates either presence or activity on the SATA link. It could be repurposed.

There is an un-populated button? near the power connector; I do not know what GPIO(s) are there, if any.

There appears to be a GPIO pin, but I am unsure which, on the top right pin of U11 (next to R219, and in fact connected to one of its pads, too).

Processor pin multiplexing configuration values (MPPs) can be investigated from within uboot, by something like md.l f1010000 4.

UART Booting

The kirkwood chips are very friendly and can be fed their initial boot image over a UART. The requisite kwboot tool is even packaged in Debian (in u-boot-tools). Use, for example:

kwboot -b /path/to/uboot.kwb -B 115200 -t /dev/ttyACM0 -p

Building UBoot

With an arm-none-eabi cross-compiler (say, Debian’s gcc-arm-none-eabi), run:

make pogo_v4_defconfig
echo CONFIG_ENV_OFFSET=0x1C0000 >> .config
make oldconfig
CROSS_COMPILE=arm-none-eabi- make KCFLAGS=-fshort-enums

./tools/mkimage -T kwbimage \
  -n ./board/cloudengines/pogo_v4/kwbimage.cfg \
  -a 0x00600000 -e 0x00600000 -d u-boot.bin u-boot.kwb

Or you could grab my binary, if you trust me, at pogo4-uboot.kwb.

Landing uBoot without serial console

Taking over a from-factory device time! Do the pogoplug activation dance, but you can stop just after it finds and updates the device. Then run, on a host

curl -k "https://root:ceadmin@PogoplugMobile/sqdiag/HBPlug?action=command&command=dropbear%20start";

Then you want to run all of this, as taken wholesale from http://blog.qnology.com/2014/07/hacking-pogoplug-v4-series-4-and-mobile.html

#stop my.pogoplug.com service
killall hbwd

#download firmware utilities
cd /tmp
wget http://download.doozan.com/uboot/nanddump
wget http://download.doozan.com/uboot/nandwrite
wget http://download.doozan.com/uboot/flash_erase
wget http://download.doozan.com/uboot/fw_printenv

#make execuable
chmod +x flash_erase fw_printenv nanddump nandwrite

#printenv and setenv are normally symbolic links
cp fw_printenv fw_setenv

#remount '/' as read/write
#by default the Pogoplug OS (internal flash) is read only
#skip if running from Debian/ALARM
mount -o remount,rw /

#setup fw_env.config

echo "/dev/mtd0 0xc0000 0x20000 0x20000">/etc/fw_env.config

scp over your own uBoot image, and then write it:

/tmp/flash_erase /dev/mtd0 0 4
/tmp/nandwrite --pad /dev/mtd0 /tmp/uboot.kwb

Then run whatever commands to adjust the uboot environment.

Loading New Firmware from UBoot

Flash bootloader without nuking environment (at 0x1C0000)

mw 0x800000 0xffff 0x1C0000
tftpboot 0x800000 u-boot.kwb
nand erase 0x0 0x1C0000
nand write.e 0x800000 0x0 0x1C0000

To nuke the environment, too, change the nand erase above to nand erase 0x0 0x200000.

You can also substitute loadb 0x800000 (kermit) or loady 0x800000 (ymodem) to use the serial port. In ckermit on the host, then, use, for example

set prefixing all
send /binary /protocol:kermit u-boot.kwb

Flash UBI-nized image

nand erase 0x200000 0x7e00000
tftpboot 0x800000 ...
nand write 0x800000 0x200000 ${filesize}

The Greatest UBoot Environment Settings

Here are what I consider to be the best uboot environment configuration settings. If you’re pasting along, it may be useful to run :%s!^setenv!/tmp/fw_setenv!g for pasting into a root shell!

Basics. You’ll probably want to keep your ethaddr as it was

setenv ethaddr 00:11:22:33:44:55
setenv baudrate 115200
setenv bootdelay 30
setenv arcNumber 3960
setenv machid f78

Netconsole ala http://forum.doozan.com/read.php?3,14,14 . To use this run nc -u -p 6666 -s 6666 or socat STDIO UDP4-DATAGRAM:,sourceport=6666 on your host:

setenv serverip
setenv ipaddr
setenv if_netconsole 'ping ${serverip}'
setenv start_netconsole 'setenv ncip ${serverip}; setenv stdin nc; setenv stdout nc; setenv stderr nc; version;'
setenv preboot 'run if_netconsole start_netconsole'

Kernel command-line arguments

setenv mtdids nand0=orion_nand
setenv mtdparts 'mtdparts=orion_nand:0x1c0000(uboot),0x40000(uboot_env),0x7e00000(ubi)'
setenv bootargs_console 'console=ttyS0,115200n8 panic=10'


The choice of partition layout is consistent with OpenWRT as of commit c19f811c4d732f634ac18f33ae95e954ac9e8c81 (on 2 Jan 2018, “kirkwood: add pogoplug v4” by Alberto Bursi); earlier editions of this page had smaller uboot partitions and placed the environment elsewhere.

Boot commands and conditional operations. This wad of complexity attempts…

The centerpieces of this mess are the bootcmd_load_* macros, which are pointed to by loadercmd (set in boot_try_*). These expect to find five variables set in the environment:

The boot_cond* wrappers around this know to build loaderwhence from:

setenv nc_disable 'setenv stdin serial; setenv stdout serial; setenv stderr serial'
setenv bootcmd_do_bootm 'setenv bootargs ${bootargs_console} ${mtdids} ${mtdparts} ${loaderbootargs} ${xtrabootargs}; run nc_disable; bootm ${bootmargs}'

setenv bootcmd_load_fat 'fatload $loadermedia $loaderpart $loaderwhere $loaderwhence'
setenv bootcmd_load_ext 'ext4load $loadermedia $loaderpart $loaderwhere $loaderwhence'
setenv bootcmd_load_ubi 'ubi read $loaderwhere $loaderwhence $loaderlimit'

setenv boot_condload_fdt 'if loaderwhere=0x00700000; loaderwhence=${loaderprefix}${loaderdtb}; loaderlimit=0x100000; run ${loadercmd}; then fdt addr 0x00700000; fdt resize; fdt chosen; setenv machid; bootmargs="${bootmargs} 0x00700000"; fi;'
setenv boot_condload_ird 'if loaderwhere=0x01100000; loaderwhence=${loaderprefix}${loaderinitramfs}; loaderlimit=0x300000; run ${loadercmd}; then bootmargs="${bootmargs} 0x01100000"; else bootmargs="${bootmargs} -"; fi;'
setenv boot_condload     'if loaderwhere=0x00800000; loaderwhence=${loaderprefix}${loaderkernel}; loaderlimit=0x300000; run ${loadercmd}; then bootmargs="0x00800000"; run boot_condload_ird; run boot_condload_fdt; run bootcmd_do_bootm; fi'
setenv boot_condscript   'if loaderwhere=0x00700000; loaderwhence=${loaderprefix}boot.scr; loaderlimit=0x100000; run ${loadercmd}; then source 0x700000; fi'

setenv boot_common_pre 'loaderpart="0:1";'
setenv boot_common_fs 'loaderkernel="uImage"; loaderinitramfs="uInitrd"; loaderdtb="pogo4.dtb"; loadercondcmd="boot_condload";'

setenv boot_if_mmc 'if mmc rescan; mmc info; then loadermedia="mmc"; run ${loadercondcmd}; fi'
setenv boot_if_usb 'if usb start; then loadermedia="usb"; run ${loadercondcmd}; fi'

setenv boot_try_mmcE 'loadercmd="bootcmd_load_ext"; loaderbootargs="root=/dev/mmcblk0p1 rootfstype=ext4 rootwait"; run boot_if_mmc'
setenv boot_try_mmcF 'loadercmd="bootcmd_load_fat"; loaderbootargs="root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"; run boot_if_mmc'

setenv boot_try_usbE 'loadercmd="bootcmd_load_ext"; loaderbootargs="root=/dev/sda1 rootfstype=ext4 rootwait"; run boot_if_usb'
setenv boot_try_usbF 'loadercmd="bootcmd_load_fat"; loaderbootargs="root=/dev/sda2 rootfstype=ext4 rootwait"; run boot_if_usb'

setenv boot_try_ubi 'if ubi part ubi && ubi check rootfs; then loadercmd="bootcmd_load_ubi"; loaderbootargs="ubi.mtd=ubi rootfstype=ubifs"; run ${loadercondcmd}; fi'

setenv bootcmd_fs  'echo "trying ${loaderprefix} kernels"; run boot_try_mmcE; run boot_try_mmcF; run boot_try_usbE; run boot_try_usbF;'
setenv bootcmd_owt 'echo "trying OpenWRT recovery..."; loaderkernel="initramfs.bin"; loaderinitramfs="initrd"; loaderdtb="dtb";  run boot_try_usbF;'
setenv bootcmd_ubi 'echo "trying UBI kernels"; loaderkernel="kernel"; loaderinitramfs="initrd"; loaderdtb="dtb";  run boot_try_ubi;'

setenv bootcmd 'run boot_common_pre; loadercondcmd=boot_condscript; echo "trying script"; loaderprefix=/; run boot_try_usbF; run boot_common_fs; run bootcmd_fs; loaderprefix=/boot/; run bootcmd_fs; loaderprefix=""; run bootcmd_owt; run bootcmd_ubi; echo "Boot search failed"'


If you’re wanting to extend the kernel’s command line, the xtrabootargs environment variable is for you.


Boot scripts are useful for making media that pave over the internal flash on boot, offering a kind of less-manual upgrade path.


As of the latest attempt (U-Boot 174ac987), there are two deficiencies:

  • one has to drop netconsole before bootm (see nc_disable, above) or the system will fail to load

  • the standard boot machinery lacks a prober for UBI(fs), which is kind of sad. It would be neat to teach OpenWRT to land a U-Boot script in / or something of that ilk and remove much of the above environmental complexity.

How to Install Debian


Unless you have particularly good reason, you probably want to run a system that’s more designed for… shall we say, politely, low-end devices. May I recommend OpenWRT.


You will want a custom bootloader on the device for booting either USB or MMC, as Debian (or ALARM) will not fit on the onboard flash. I have one in my branch of OpenWRT that I use (https://github.com/nwf/openwrt), but there are others floating around that may be more convenient.

These instructions have been updated as of 2023 Jan to work with bullseye. Sadly, things are now a little more involved than they used to be, since the installation seemingly can no longer take place in an emulator.

Fetch Debian Marvell arm installer images and the dtb we’ll need:

Get these files onto your Pogoplug somehow; possibly the simplest is to land them in a FAT filesystem on a SD card or USB stick (which can be repurposed as your installation target, if desired). Concretely, using a USB stick, I was able to boot the kernel with

usb start
setenv bootargs console=ttyS0,115200n8 panic=10
fatload usb 0:1 0x00800000 vmlinuz-...
fatload usb 0:1 0x01100000 initrd.gz
fatload usb 0:1 0x00700000 kirkwood-pogoplug-series-4.dtb
fdt addr 0x00700000; fdt resize; fdt chosen
bootm 0x00800000 0x01100000 0x00700000