07-05-2017, 06:52 PM
Hello.
I prepared and tested step by step procedure to setup SPI dataflash for booting (u-boot) for everyone interested
.
This is usable for following scenarios
:
Follow the steps:
Theory of operation:
RK3288 processor has embedded Bootrom that can boot from SPI (exactly SPI2 and chipselect CS0) (see TRM pages 13-16).
Step A: Connect SPI dataflash to SPI2/CS0:
Choose, buy and connect supported SPI flash (>=512kB). I used Atmel/Adesto AT45DB041D (~ 1€, 4Gbit = 512kB, in 256B sector mode) and connect it to connector pins #19, #21, #23 and #24, 3.3V and GND (/WP and /RESET are connected to 3.3V) (newer AT45DB041E should work).
Step B: Compile u-boot for SPI dataflash:
I prepared special variant of U-boot to match some limitation (like size down SPL code binary to ~30000B). I published modified sources on github (see last 3 commits!, limited support only to AT45DB041).
Example cross compile on Ubuntu1604:
Step C: Flash u-boot to SPI dataflash:
I created and attached simple python script for basic SPI AT45DB* flashing with TinkerBoard. Beware of bug in SPI driver (UPDATE: resolved with TinkerOS >= 2.0.1).
Step D: optional serial line to interact with u-boot.
I connected serial-usb converter (compatible with 3.3V outputs) to TinkerBoard UART2 connector pins #32 and #33.
Example output from autoboot from USB TikerOS root.
Example output from autoboot with no device attached (see try from MMC, USB OTG, USB, PXE and finally U-boot prompt).
Example output from printenv in u-boot. You can also see "startup script" and correctly setup ethernet address from TinkerBoard i2c eeprom. Actual environment is not saved (can be modified to save too SPI dataflash but untested).
Step E: Modify root mount options on USB with TinkerOS (or other distribution).
Modify kernel command line "extlinux/extlinux.conf" TinkerOS (FAT boot partition) to boot from USB (see changed "root=" to usb disk also "console=" to serial line UART2).
Modify also "/etc/fstab" on rootfs. Change to /dev/sda1 and /dev/sda2 or hide with comment # (root is already mounted over /dev/root and /boot is than mounted to /madia/...).
Happy hacking !
I prepared and tested step by step procedure to setup SPI dataflash for booting (u-boot) for everyone interested
.This is usable for following scenarios
:- broken MicroSD card slot
- booting from USB attached HDD or flash disk
- booting from network (PXE)
- full booting from SPI dataflash (need larger dataflash) - use initramfs or squashfs or unionfs ...
Follow the steps:
- Connect SPI dataflash to SPI2/CS0.
- Compile u-boot for SPI dataflash.
- Flash u-boot to SPI dataflash.
- Connect optional serial line to interact with u-boot.
- Modify root mount options on USB with TinkerOS (or other distribution).
Theory of operation:
RK3288 processor has embedded Bootrom that can boot from SPI (exactly SPI2 and chipselect CS0) (see TRM pages 13-16).
- The Bootrom code scans SPI (addresses 0x0000, 0x2000, 0x4000 ...) with usual command for SPI dataflash "0x03 + 3 bytes address" reads 64 bytes and check valid signature.
- If signature is found then 32kB code is download to embedded SRAM (96kB) from SPI dataflash with special addressing "base+0, base+4k, base+8k, base+12k ... and only 2k are downloaded (this is needed because some SPI flash does not have 2048B page but 2112B page to have space for ECC correction data (for NAND flash type it is required)). This is referred as SPL code (secondary program loader).
- SPL code is run from SRAM and must contain DRAM initialization and can return to Bootrom code or continue to load next stage usually U-boot code.
- U-boot code code is located in SPI dataflash at address 128kB and can be large (as configured). U-boot code has specific header (magic, len, checksum, load address, start entry address). U-boot is read in continuous mode (AT45 command 0xE8).
- U-boot code is executed and parses U-boot variables and tries to load linux kernel with parameters for example from USB FAT formated storage.
- Linux kernel is started.
Step A: Connect SPI dataflash to SPI2/CS0:
Choose, buy and connect supported SPI flash (>=512kB). I used Atmel/Adesto AT45DB041D (~ 1€, 4Gbit = 512kB, in 256B sector mode) and connect it to connector pins #19, #21, #23 and #24, 3.3V and GND (/WP and /RESET are connected to 3.3V) (newer AT45DB041E should work).
Step B: Compile u-boot for SPI dataflash:
I prepared special variant of U-boot to match some limitation (like size down SPL code binary to ~30000B). I published modified sources on github (see last 3 commits!, limited support only to AT45DB041).
Example cross compile on Ubuntu1604:
Code:
# git clone --depth 1 --branch tinkerboard-spi https://github.com/mcerveny/rockchip-u-boot.git
# cd rockchip-u-boot
# cp dot_config .config
# export ARCH=arm
# export CROSS_COMPILE=arm-linux-gnueabihf-
# make all
# ### check SPL+DTB ~ 30000B !
# ls -l spl/
-rw-r--r-- 1 root root 30084 Jul 5 19:19 u-boot-spl-dtb.bin
# ### make image for SPI dataflash
# tools/mkimage -n rk3288 -T rkspi -d spl/u-boot-spl-dtb.bin spl.img
# dd if=spl.img of=all.img bs=128K conv=sync
# cat u-boot-dtb.img >> all.img
# scp all.img "to tinkerboard or other system with SPI flash capability"Step C: Flash u-boot to SPI dataflash:
I created and attached simple python script for basic SPI AT45DB* flashing with TinkerBoard. Beware of bug in SPI driver (UPDATE: resolved with TinkerOS >= 2.0.1).
Code:
# ### convert AT45 from 264B to 256B sector mode
# python spi.py sector
JEDEC: 1f 24 00 00 00 00
sector mode 264->256 ...
# ### power on-off SPI flash
# ### write all image
# python spi.py write all.img
JEDEC: 1f 24 00 00 00 00
write ...
00000000 size 100: 3b 8c dc fc ... wait wait wait wait wait wait
00000100 size 100: 2d f8 ba 7e ... wait wait wait wait wait wait wait wait
00000200 size 100: 00 00 00 00 ... wait wait wait wait wait wait wait wait
00000300 size 100: 00 00 00 00 ... wait wait wait wait wait wait wait wait wait
00000400 size 100: 00 00 00 00 ... wait wait wait wait wait wait wait
00000500 size 100: 00 00 00 00 ... wait wait wait wait wait wait
...
0007e500 size 100: 6e 63 65 00 ... wait wait wait wait wait wait wait
0007e600 size 100: 6c 65 72 00 ... wait wait wait wait wait wait wait
0007e700 size 100: 62 6f 6f 74 ... wait wait wait wait wait wait wait
# ### compare SPI dataflash and sources
# python spi.py compare all.img
JEDEC: 1f 24 00 00 00 00
compare ...
# ### ok if no differences shownStep D: optional serial line to interact with u-boot.
I connected serial-usb converter (compatible with 3.3V outputs) to TinkerBoard UART2 connector pins #32 and #33.
Example output from autoboot from USB TikerOS root.
Code:
U-Boot SPL 2017.05-g6f6fd51 (Jul 05 2017 - 17:27:25)
Trying to boot from SPI
U-Boot 2017.05-g6f6fd51 (Jul 05 2017 - 17:27:25 +0200)
Model: Tinker-RK3288
DRAM: rockchip_sdram_size ff73009c 32a0d2a0
rank 1 col 10 bk 3 cs0_row 15 bw 2 row_3_4 0
rank 1 col 10 bk 3 cs0_row 15 bw 2 row_3_4 0
size 80000000
SDRAM base=0, size=80000000
2 GiB
MMC: dwmmc@ff0c0000: 1
Using default environment
In: serial
Out: serial
Err: serial
Net: eth0: ethernet@ff290000
Hit any key to stop autoboot: 0
MMC Device 0 not found
no mmc device at slot 0
Card did not respond to voltage select!
mmc_init: -95, time 2118
MMC Device 1 not found
no mmc device at slot 1
starting USB...
USB0: Core Release: 3.10a
USB1: Core Release: 3.10a
dwc_otg_core_host_init: Timeout!
...
dwc_otg_core_host_init: Timeout!
scanning bus 0 for devices... 5 USB Device(s) found
scanning bus 1 for devices... 1 USB Device(s) found
scanning usb for storage devices... 1 Storage Device(s) found
USB device 0:
Device 0: Vendor: Generic Rev: 0272 Prod: STORAGE DEVICE
Type: Removable Hard Disk
Capacity: 7580.0 MB = 7.4 GB (15523840 x 512)
... is now current device
Scanning usb 0:1...
Found /extlinux/extlinux.conf
Retrieving file: /extlinux/extlinux.conf
reading /extlinux/extlinux.conf
158 bytes read in 23 ms (5.9 KiB/s)
1: kernel-4.4
Retrieving file: /zImage
reading /zImage
7456672 bytes read in 4168 ms (1.7 MiB/s)
append: earlyprintk console=ttyS2,115200n8 console=tty1 root=/dev/sda2 rw init=/sbin/init
Retrieving file: /rk3288-miniarm.dtb
reading /rk3288-miniarm.dtb
45696 bytes read in 50 ms (891.6 KiB/s)
## Flattened Device Tree blob at 01f00000
Booting using the fdt blob at 0x1f00000
Loading Device Tree to 0fff1000, end 0ffff27f ... OK
Starting kernel ...
[ 0.000000] Booting Linux on physical CPU 0x500
[ 0.000000] Initializing cgroup subsys cpuset
[ 0.000000] Initializing cgroup subsys cpu
[ 0.000000] Initializing cgroup subsys cpuacct
....Example output from autoboot with no device attached (see try from MMC, USB OTG, USB, PXE and finally U-boot prompt).
Code:
U-Boot SPL 2017.05-g0edf75f (Jul 06 2017 - 16:09:59)
Trying to boot from SPI
U-Boot 2017.05-g0edf75f (Jul 06 2017 - 16:09:59 +0200)
Model: Tinker-RK3288
DRAM: rockchip_sdram_size ff73009c 32a0d2a0
rank 1 col 10 bk 3 cs0_row 15 bw 2 row_3_4 0
rank 1 col 10 bk 3 cs0_row 15 bw 2 row_3_4 0
size 80000000
SDRAM base=0, size=80000000
2 GiB
MMC: dwmmc@ff0c0000: 1
Using default environment
In: serial
Out: serial
Err: serial
Net: eth0: ethernet@ff290000
Hit any key to stop autoboot: 0
MMC Device 0 not found
no mmc device at slot 0
Card did not respond to voltage select!
mmc_init: -95, time 2119
MMC Device 1 not found
no mmc device at slot 1
starting USB...
USB0: Core Release: 3.10a
USB1: Core Release: 3.10a
dwc_otg_core_host_init: Timeout!
...
dwc_otg_core_host_init: Timeout!
scanning bus 0 for devices... 4 USB Device(s) found
scanning bus 1 for devices... 1 USB Device(s) found
scanning usb for storage devices... 0 Storage Device(s) found
USB device 0: unknown device
ethernet@ff290000 Waiting for PHY auto negotiation to complete....... done
Speed: 1000, full duplex
BOOTP broadcast 1
BOOTP broadcast 2
BOOTP broadcast 3
*** Unhandled DHCP Option in OFFER/ACK: 2
*** Unhandled DHCP Option in OFFER/ACK: 42
*** Unhandled DHCP Option in OFFER/ACK: 4
*** Unhandled DHCP Option in OFFER/ACK: 252
BOOTP broadcast 4
*** Unhandled DHCP Option in OFFER/ACK: 2
*** Unhandled DHCP Option in OFFER/ACK: 42
*** Unhandled DHCP Option in OFFER/ACK: 4
*** Unhandled DHCP Option in OFFER/ACK: 252
*** Unhandled DHCP Option in OFFER/ACK: 2
*** Unhandled DHCP Option in OFFER/ACK: 42
*** Unhandled DHCP Option in OFFER/ACK: 4
*** Unhandled DHCP Option in OFFER/ACK: 252
DHCP client bound to address 192.168.1.131 (6314 ms)
*** Warning: no boot file name; using 'C0A80183.img'
Using ethernet@ff290000 device
TFTP from server 0.0.0.0; our IP address is 192.168.1.131; sending through gateway 192.168.1.1
Filename 'C0A80183.img'.
Load address: 0x800800
Loading: T T T
TFTP error: 'Access violation' (2)
Not retrying...
missing environment variable: pxeuuid
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/01-2c-4d-54-##-##-##
Speed: 1000, full duplex
*** ERROR: `serverip' not set
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/C0A80183
Speed: 1000, full duplex
*** ERROR: `serverip' not set
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/C0A8018
Speed: 1000, full duplex
*** ERROR: `serverip' not set
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/C0A801
Speed: 1000, full duplex
*** ERROR: `serverip' not set
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/C0A80
Speed: 1000, full duplex
*** ERROR: `serverip' not set
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/C0A8
Speed: 1000, full duplex
*** ERROR: `serverip' not set
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/C0A
Speed: 1000, full duplex
*** ERROR: `serverip' not set
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/C0
Speed: 1000, full duplex
*** ERROR: `serverip' not set
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/C
Speed: 1000, full duplex
*** ERROR: `serverip' not set
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default-arm-rockchip
Speed: 1000, full duplex
*** ERROR: `serverip' not set
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default-arm
Speed: 1000, full duplex
*** ERROR: `serverip' not set
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default
Speed: 1000, full duplex
*** ERROR: `serverip' not set
Config file not found
Speed: 1000, full duplex
BOOTP broadcast 1
BOOTP broadcast 2
BOOTP broadcast 3
*** Unhandled DHCP Option in OFFER/ACK: 2
*** Unhandled DHCP Option in OFFER/ACK: 42
*** Unhandled DHCP Option in OFFER/ACK: 4
*** Unhandled DHCP Option in OFFER/ACK: 252
*** Unhandled DHCP Option in OFFER/ACK: 2
*** Unhandled DHCP Option in OFFER/ACK: 42
*** Unhandled DHCP Option in OFFER/ACK: 4
*** Unhandled DHCP Option in OFFER/ACK: 252
DHCP client bound to address 192.168.1.131 (1296 ms)
Using ethernet@ff290000 device
TFTP from server 0.0.0.0; our IP address is 192.168.1.131; sending through gateway 192.168.1.1
Filename 'boot.scr.uimg'.
Load address: 0x0
Loading: *
TFTP error: 'Access violation' (2)
Not retrying...
Speed: 1000, full duplex
BOOTP broadcast 1
BOOTP broadcast 2
*** Unhandled DHCP Option in OFFER/ACK: 2
*** Unhandled DHCP Option in OFFER/ACK: 42
*** Unhandled DHCP Option in OFFER/ACK: 4
*** Unhandled DHCP Option in OFFER/ACK: 252
BOOTP broadcast 3
*** Unhandled DHCP Option in OFFER/ACK: 2
*** Unhandled DHCP Option in OFFER/ACK: 42
*** Unhandled DHCP Option in OFFER/ACK: 4
*** Unhandled DHCP Option in OFFER/ACK: 252
*** Unhandled DHCP Option in OFFER/ACK: 2
*** Unhandled DHCP Option in OFFER/ACK: 42
*** Unhandled DHCP Option in OFFER/ACK: 4
*** Unhandled DHCP Option in OFFER/ACK: 252
DHCP client bound to address 192.168.1.131 (5304 ms)
Using ethernet@ff290000 device
TFTP from server 0.0.0.0; our IP address is 192.168.1.131; sending through gateway 192.168.1.1
Filename 'boot.scr.uimg'.
Load address: 0x2000000
Loading: T T
TFTP error: 'Access violation' (2)
Not retrying...
=>Example output from printenv in u-boot. You can also see "startup script" and correctly setup ethernet address from TinkerBoard i2c eeprom. Actual environment is not saved (can be modified to save too SPI dataflash but untested).
Code:
=> printenv
arch=arm
baudrate=115200
board=tinker_rk3288
board_name=tinker_rk3288
boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}
boot_efi_binary=load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} efi/boot/bootarm.efi; if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r};else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi
boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}extlinux/extlinux.conf
boot_net_usb_start=usb start
boot_prefixes=/ /boot/
boot_script_dhcp=boot.scr.uimg
boot_scripts=boot.scr.uimg boot.scr
boot_targets=mmc0 mmc1 usb0 pxe dhcp
bootcmd=run distro_bootcmd
bootcmd_dhcp=run boot_net_usb_start; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;setenv efi_fdtfile ${fdtfile}; if test -z "${fdtfile}" -a -n "${soc}"; then setenv efi_fdtfile ${soc}-${board}${boardver}.dtb; fi; setenv efi_old_vci ${bootp_vci};setenv efi_old_arch ${bootp_arch};setenv bootp_vci PXEClient:Arch:00010:UNDI:003000;setenv bootp_arch 0xa;if dhcp ${kernel_addr_r}; then tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r}; else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi;fi;setenv bootp_vci ${efi_old_vci};setenv bootp_arch ${efi_old_arch};setenv efi_fdtfile;setenv efi_old_arch;setenv efi_old_vci;
bootcmd_mmc0=setenv devnum 0; run mmc_boot
bootcmd_mmc1=setenv devnum 1; run mmc_boot
bootcmd_pxe=run boot_net_usb_start; dhcp; if pxe get; then pxe boot; fi
bootcmd_usb0=setenv devnum 0; run usb_boot
bootdelay=2
cpu=armv7
devnum=0
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
efi_dtb_prefixes=/ /dtb/ /dtb/current/
ethact=ethernet@ff290000
ethaddr=2c:4d:54:##:##:##
fdt_addr_r=0x01f00000
fdt_high=0x0fffffff
fdtcontroladdr=7df5bf70
initrd_high=0x0fffffff
kernel_addr_r=0x02000000
load_efi_dtb=load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}${efi_fdtfile}
mmc_boot=if mmc dev ${devnum}; then setenv devtype mmc; run scan_dev_for_boot_part; fi
partitions=uuid_disk=${uuid_gpt_disk};name=loader1,start=32K,size=4000K,uuid=${uuid_gpt_loader1};name=reserved1,size=64K,uuid=${uuid_gpt_reserved1};name=reserved2,size=4M,uuid=${uuid_gpt_reserved2};name=loader2,size=4MB,uuid=${uuid_gpt_loader2};name=atf,size=4M,uuid=${uuid_gpt_atf};name=boot,size=112M,bootable,uuid=${uuid_gpt_boot};name=rootfs,size=-,uuid=${uuid_gpt_rootfs};
pxefile_addr_r=0x00100000
ramdisk_addr_r=0x04000000
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;run scan_dev_for_efi;
scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then run scan_dev_for_boot; fi; done
scan_dev_for_efi=setenv efi_fdtfile ${fdtfile}; if test -z "${fdtfile}" -a -n "${soc}"; then setenv efi_fdtfile ${soc}-${board}${boardver}.dtb; fi; for prefix in ${efi_dtb_prefixes}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${efi_fdtfile}; then run load_efi_dtb; fi;done;if test -e ${devtype} ${devnum}:${distro_bootpart} efi/boot/bootarm.efi; then echo Found EFI removable media binary efi/boot/bootarm.efi; run boot_efi_binary; echo EFI LOAD FAILED: continuing...; fi; setenv efi_fdtfile
scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}extlinux/extlinux.conf; then echo Found ${prefix}extlinux/extlinux.conf; run boot_extlinux; echo SCRIPT FAILED: continuing...; fi
scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done
scriptaddr=0x00000000
soc=rockchip
usb_boot=usb start; if usb dev ${devnum}; then setenv devtype usb; run scan_dev_for_boot_part; fi
vendor=rockchip
Environment size: 4177/8188 bytes
=>Step E: Modify root mount options on USB with TinkerOS (or other distribution).
Modify kernel command line "extlinux/extlinux.conf" TinkerOS (FAT boot partition) to boot from USB (see changed "root=" to usb disk also "console=" to serial line UART2).
Code:
label kernel-4.4
kernel /zImage
fdt /rk3288-miniarm.dtb
append earlyprintk console=ttyS2,115200n8 console=tty1 root=/dev/sda2 rw init=/sbin/initModify also "/etc/fstab" on rootfs. Change to /dev/sda1 and /dev/sda2 or hide with comment # (root is already mounted over /dev/root and /boot is than mounted to /madia/...).
Code:
proc /proc proc defaults 0 0
#/dev/mmcblk0p1 /boot vfat defaults 0 2
#/dev/mmcblk0p2 / ext4 defaults,noatime 0 1Happy hacking !
)and hadn't had time to try it out. You may want to take a look at the Armbian build environment for the Tinker Board U-boot, the SPL+dtb is 26.5k, I'm pretty sure the spl has to be under 32k for the RK3288 to boot from any media.