TS-4900 U-boot Sections

From embeddedTS Manuals

The TS-4900 includes u-boot as the bootloader to launch the full operating system. When the i.MX6 processor starts it loads u-boot from the onboard 8MB SPI flash. This allows you to include your boot image on either the SD, eMMC, SATA, NFS, or USB. U-boot is a general purpose bootloader that is capable of booting into common Linux distributions, Android, QNX, or others.

On a normal boot you should see something similar to this:

U-Boot 2013.10-00034-ga8a4e60 (May 02 2014 - 12:19:18)

CPU:   Freescale i.MX6Q rev1.2 at 792 MHz
Reset cause: POR
Board: TS-4900
DRAM:  2 GiB
MMC:   FSL_SDHC: 0, FSL_SDHC: 1
SF: Detected N25Q64 with page size 256 Bytes, erase size 4 KiB, total 8 MiB
In:    serial
Out:   serial
Err:   serial
Net:   using phy at 7
FEC [PRIME]
Hit any key to stop autoboot:  5

By default the board will boot to SD or eMMC depending on the status of CN1_98/MODE2 on startup. On our off the shelf baseboards this pin is brought out as the "SD Boot" jumper. This can be customized further in u-boot as described below.

U-Boot Environment

The eMMC flash contains both the U-Boot executable binary and U-Boot environment. Our default build has 2 MiB of environment space which can be used for variables and boot scripts. The following commands are examples of how to manipulate the U-Boot environment:

# Print all environment variables
env print -a

# Sets the variable bootdelay to 5 seconds
env set bootdelay 5;

# Variables can also contain commands
env set hellocmd 'led red on; echo Hello world; led green on;'

# Execute commands saved in a variable
env run hellocmd;

# Commit environment changes to the SPI flash
# Otherwise changes are lost
env save

# Restore environment to default
env default -a

# Remove a variable
env delete emmcboot

U-Boot Commands

# The most important command is 
help
# This can also be used to see more information on a specific command
help i2c

# This is a command added to U-Boot by TS to read the baseboard ID on our 
# System on Module devices
bbdetect
echo ${baseboard} ${baseboardid} 
# The echo will return something similar to:
# TS-8390 2

# Boots into the binary at $loadaddr.  The loaded file needs to have
# the U-Boot header from mkimage.  A uImage already contains this.
bootm
# Boots into the binary at $loadaddr, skips the initrd, specifies
# the FDT addrress so Linux knows where to find the device tree
bootm ${loadaddr} - ${fdtaddr}

# Boot a Linux zImage loaded at $loadaddr
bootz
# Boot in to a Linux zImage at $loadaddr, skip initrd, specifies
# the FDT address to Linux knows where to find the device tree
bootz ${loadaddr} - ${fdtaddr}

# Get a DHCP address
dhcp
# This sets ${ipaddr}, ${dnsip}, ${gatewayip}, ${netmask}
# and ${ip_dyn} which can be used to check if the dhcp was successful

# These commands are used for scripting:
false # do nothing, unsuccessfully
true # do nothing, successfully

# This command can set fuses in the processor
# Setting fuses can brick the unit, will void the warranty,
# and should not be done in most cases
fuse

# GPIO can be manipulated from U-Boot.  Keep in mind that the IOMUX 
# in U-Boot is only setup enough to boot the device, so not all pins will
# be set to GPIO mode out of the box.  Boot to the full operating system
# for more GPIO support.
# GPIO are specified in bank and IO in this manual.  U-Boot uses a flat numberspace,
# so for bank 2 DIO 25, this would be number (32*1)+25=89
# The formula thus being (32*(bank-1)+dio)=flattened_dio
# Note that on some products, bank 1 is the first bank
# Set 2_25 low
gpio clear 83
# Set 2_25 high
gpio set 83
# Read 2_25
gpio input 83

# Control LEDs
led red on
led green on
led all off
led red toggle

# This command is used to copy a file from most devices
# Load kernel from SD
load mmc 0:1 ${loadaddr} /boot/uImage
# Load Kernel from eMMC
load mmc 1:1 ${loadaddr} /boot/uImage
# Load kernel from USB
usb start
load usb 0:1 ${loadaddr} /boot/uImage
# Load kernel from SATA
sata init
load sata 0:1 ${loadaddr} /boot/uImage

# View the FDT from U-Boot
load mmc 0:1 ${fdtaddr} /boot/imx6q-ts4900.dtb
fdt addr ${fdtaddr}
fdt print

# It is possible to blindly jump to any memory location
# This is similar to bootm, but it does not require
# the use of the U-Boot header
load mmc 0:1 ${loadaddr} /boot/custombinary
go ${loadaddr}

# Browse fat, ext2, ext3, or ext4 filesystems:
ls mmc 0:1 /

# Access memory like devmem in Linux, read/write arbitrary memory
# using mw and md
# write
mw 0x10000000 0xc0ffee00 1
# read
md 0x10000000 1

# Test memory.
mtest

# Check for new SD card
mmc rescan
# Read SD card size
mmc dev 0
mmcinfo
# Read eMMC Size
mmc dev 1
mmcinfo

# The NFS command is like 'load', but used over the network
dhcp
env set serverip 192.168.0.11
nfs ${loadaddr} 192.168.0.11:/path/to/somefile

# Test ICMP
dhcp
ping 192.168.0.11

# Reboot
reset

# SPI access is through the SF command
# Be careful with sf commands since
# this is where U-Boot and the FPGA bitstream exist
# Improper use can render the board unbootable
sf probe

# Delay in seconds
sleep 10

# Load HUSH scripts that have been created with mkimage
load mmc 0:1 ${loadaddr} /boot/ubootscript
source ${loadaddr}

# Most commands have return values that can be used to test
# success, and HUSH scripting supports comparisons like
# test in Bash, but much more minimal
if load mmc 1:1 ${fdtaddr} /boot/uImage;
	then echo Loaded Kernel
else
	echo Could not find kernel
fi

# Commands can be timed with "time"
time sf probe

# Print U-Boot version/build information
version

Modify Linux Kernel cmdline

The Linux kernel cmdline can be customized by modifying the cmdline_append variable. The variable contents are clobbered when set, so be sure to specify the full desired cmdline string.

env set cmdline_append console=ttymxc0,115200 init=/sbin/init quiet
env save

The kernel command line can also be modified from from the on-board Linux. Debian (and other distributions) provide a U-Boot utilities package that contains the tools necessary to create a U-Boot script:

apt-get update && apt-get install u-boot-tools -y
echo "env set cmdline_append console=ttymxc0,115200 init=/sbin/init quiet" > /boot/boot.scr
mkimage -A arm -T script -C none -n 'tsimx6 boot script' -d /boot/boot.scr /boot/boot.ub

The boot.scr includes the plain text commands to be run in U-Boot on startup. The mkimage tool adds a checksum and header to this file which can be loaded by U-Boot. The .ub file should not be edited directly.

Booting From NFS

U-Boot's NFS support can be used to load a kernel, device tree binary, and root filesystem. The default scripts include an example NFS boot script.

# Set this to your NFS server ip
env set serverip 192.168.0.11;

# Set this to your NFS root path.  The server root should be accessible at this path.
env set nfsroot /path/to/nfs/rootfs/
env save

To boot your NFS root:

# Boot to NFS once
run nfsboot;

# To make the NFS boot the persistent default
env set bootcmd run nfsboot;
env save

Booting From USB

Our U-Boot by default will attempt to read a U-Boot script named /tsinit.ub from a USB drive on bootup. If present on the USB drive, U-Boot will automatically load this script in to memory and execute it. For our bootable USB disk images, no further action is needed.

To make a bootable drive from scratch, create a single ext3 partition on a USB drive and copy over your preferred rootfs just like you would with an SD card. This is described in the Debian and Yocto sections.

The one addition is to create the /tsinit.ub file in the root of the USB drive in order to allow U-Boot to boot from the drive's contents.

Create the file /tsinit.scr in the root of the USB drive with the Linux filesystem:

# Prepare with:
# mkimage -A arm -T script -C none -n 'mx6 usb' -d tsinit.scr tsinit.ub

# DO NOT MANUALLY EDIT THE .UB FILE

# If loading files from a partition other than the first partition on disk, change
# the second number to the partition number
env set bootpart 0:1

if test ${model} = '4900';
	then load usb 0:1 ${loadaddr} /boot/ts4900-fpga.bin;
	ice40 ${loadaddr} ${filesize};

	bbdetect;

	# Check rev, attempt to load the best dtb file for compatibility. If Rev E files
	# are not found, attempt to boot prior dtb. If not Rev E, just boot prior dtb.
	if test ${rev} > 'D'; then
		if load usb ${bootpart} ${fdtaddr} /boot/imx6${cpu}-ts4900-reve-${baseboardid}.dtb
			then echo Baseboard $baseboardid detected;
		elif load usb ${bootpart} ${fdtaddr} /boot/imx6${cpu}-ts4900-reve.dtb
			then echo Booting default Rev E device tree;
		elif load usb ${bootpart} ${fdtaddr} /boot/imx6${cpu}-ts4900-${baseboardid}.dtb
			then echo Baseboard $baseboardid detected;
		elif load usb ${bootpart} ${fdtaddr} /boot/imx6${cpu}-ts4900.dtb
			then echo Booting default device tree;
		fi
	else
		if load usb ${bootpart} ${fdtaddr} /boot/imx6${cpu}-ts4900-${baseboardid}.dtb
			then echo Baseboard $baseboardid detected;
		elif load usb ${bootpart} ${fdtaddr} /boot/imx6${cpu}-ts4900.dtb
			then echo Booting default device tree;
		fi
	fi

	load usb ${bootpart} ${loadaddr} ${uimage};
	setenv bootargs root=/dev/sda1 rootwait rw ${cmdline_append};
	bootm ${loadaddr} - ${fdtaddr};

elif test ${model} = '7970'; then
	# Check for Rev F or newer. If so, load that dtb. If Rev F dtb does not exist
	# fall back to a prior dtb. If earlier Rev PCB, use prior dtb.
	if test ${rev} > 'E'; then
		if load usb ${bootpart} ${fdtaddr} /boot/imx6${cpu}-ts7970-revf.dtb; then
			echo Loaded TS-7970 Rev F device tree;
		elif load usb ${bootdev} ${bootpart} ${fdtaddr} /boot/imx6${cpu}-ts7970.dtb; then
			echo Loaded TS-7970 device tree;
		fi
	else
		if load usb ${bootdev} ${bootpart} ${fdtaddr} /boot/imx6${cpu}-ts7970.dtb; then
			echo Loaded TS-7970 device tree;
		fi
	fi

	load usb 0:1 ${loadaddr} ${uimage};
	setenv bootargs root=/dev/sda1 rootwait rw ${cmdline_append};
	bootm ${loadaddr} - ${fdtaddr};
fi

Then in the same directory generate the tsinit.ub file:

mkimage -A arm -T script -C none -n 'mx6 usb' -d tsinit.scr tsinit.ub

You may need to install u-boot-tools or the equivalent package for your distribution.

Update U-Boot

WARNING: Installing your own u-boot is not recommended and may cause the board to fail to boot.

U-boot requires a different build for Quad/Dual and Solo/Duallite. Flashing the wrong u-boot will cause the board to fail to properly boot. Recovery in this case would require a TS-8550, or submitting an RMA.

On your current u-boot, type "env print imx_type" and this will return the u-boot build you should use. Copy the u-boot.imx to the SD card, and run:

mmc dev 0
load mmc 0:1 ${loadaddr} /u-boot.imx
sf probe
sf erase 0 0x80000
sf write ${loadaddr} 0x400 $filesize

U-boot Recovery

We have several variations of the TS-4900's u-boot which include different RAM configurations for the Quad core, solo commercial, solo industrial, and a few older variants. On a functional board if you run "ech o ${imx_type}" this will show which variant you are running. To recover the system you must get it booting over the USB OTG port.

Boot the TS-4900 on a TS-8550 carrier board and flip the Boot select switch up. This will boot to the TS-8550 SPI flash which includes a blank flash. If the CPU boots up and the internal ROM does not find a valid header on the boot device, it will fall back to the "Serial Downloader". If it is in this mode and the USB1 header on the TS-8550 is connected to your Linux pc you will see this in dmesg:

 hid-generic 0003:15A2:0054.0006: hiddev0,hidraw3: USB HID v1.10 Device [Freescale SemiConductor Inc  SE Blank ARIK] on usb-0000:00:14.0-6.4.2/input0

If this does not show up then the SPI flash may have a valid image programmed, though it is not capable of booting the system. In this case the CPU must be strapped to force this USB serial downloader boot. On Boot:

Name Location Value
BOOT_MODE_0 CN1_52 1
BOOT_MODE_1 CN2_54 0

Normally these BOOT_MODE[1:0] pins are strapped to "10" for Internal boot, so both of these will need to be jumpered with wires to 3.3v/GND to force a serial downloader boot.

Once it is in this mode you can use the imx USB loader to boot the board.

After building imx_usb on your host pc, run "imx_usb u-boot.imx" to get u-boot loaded and running on the cpu. You can then use the instructions #Update u-boot to get the system permanently booting again.

Alternatively, if the u-boot is erased and you need it recovered you can submit an RMA for us to recover the board.

U-boot Development

We do provide our u-boot sources, but we do not recommend rebuilding a custom uboot if it can be avoided. This CPU has a long lifetime which will outlast most RAM chips. If we have to update the RAM timing later in the boards life due to an EOL, die change, or any other change that may require new RAM configuration/timing changes, we will update this in our shipping u-boot. If you are using our u-boot these changes will happen without affecting your application. If you are using a custom u-boot you may need to rebuild to get the updated settings.

Our u-boot includes a variable "imx_type". If you are loading a custom u-boot, make sure you check the value of this before writing. If we are forced to update the RAM configuration we will change this variable. We will also send out a product change to anyone who is subscribed to our PCS system.

If you still want to proceed with building a custom u-boot, use the master branch from the github here: https://github.com/embeddedTS/u-boot

Boot up a TS-4900 into u-boot and run "echo ${imx_type}". This will show you the u-boot config to use for the correct RAM timing. We use the same GCC 4.8 used from yocto Fido to compile the u-boot binary.

source /opt/poky/1.8/environment-setup-cortexa9hf-vfp-neon-poky-linux-gnueabi

# For example, one of the quad core variants.  Replace this with your imx_type
make ts4900-q-2g-1000mhz-c_defconfig
make -j4

This will output a u-boot.imx you can write to the board using the steps in #Update_U-Boot.

Fallback Capable U-boot scripts

U-boot can be configured to have a fallback boot if your application does not acknowledge a successful boot. These scripts can be used in place of the defaults which will require your application to acknowledge a successful boot. If the system returns to u-boot again indicating a failed boot, it will instead boot to the fallback script. The fallback script could be booting to sd, a different read only partition on eMMC, or a small kernel/initramfs on the SPI flash.

env set bootcmd 'sf probe; sf read ${loadaddr} 80000 1; mw.b ${fdtaddr} 0xf0 1; if cmp.b ${loadaddr} ${fdtaddr} 1; 
then run fallback; else sf erase 80000 1000; sf write ${fdtaddr} 80000 1; run emmcboot; fi;';
env set fallback 'run usbprod; sf probe; echo set your fallback action here';
sf erase 80000 1000;

env set sdboot 'echo Booting from the SD; if load mmc 0:1 ${fdtaddr} /boot/imx6${cpu}-ts4900-${baseboardid}.dtb; 
then echo $baseboardid detected; else echo Booting default device tree; 
load mmc 0:1 ${fdtaddr} /boot/imx6${cpu}-ts4900.dtb;
 fi; if fdt addr ${fdtaddr}; then echo "Loaded Device Tree"; else run fallback; fi; 
if load mmc 0:1 ${loadaddr} /boot/ts4900-fpga.bin; then echo "Loaded FPGA"; else run fallback; fi; 
if load mmc 0:1 ${loadaddr} ${uimage}; then echo "Loaded Kernel"; else run fallback; fi; 
setenv bootargs root=/dev/mmcblk1p1 rootwait rw ${cmdline_append}; bootm ${loadaddr} - ${fdtaddr}; 
run fallback;'

env set emmcboot 'echo Booting from the eMMC; if load mmc 1:1 ${fdtaddr} /boot/imx6${cpu}-ts4900-${baseboardid}.dtb; 
then echo $baseboardid detected; else echo Booting default device tree; load mmc 1:1 ${fdtaddr} /boot/imx6${cpu}-ts4900.dtb;
 fi; if fdt addr ${fdtaddr}; then echo "Loaded Device Tree"; else run fallback; fi; 
if load mmc 1:1 ${loadaddr} /boot/ts4900-fpga.bin; then echo "Loaded FPGA"; else run fallback; fi; 
if load mmc 1:1 ${loadaddr} ${uimage}; then echo "Loaded Kernel"; else run fallback; fi; 
setenv bootargs root=/dev/mmcblk2p1 rootwait rw ${cmdline_append}; bootm ${loadaddr} - ${fdtaddr}; run fallback;'

env save;

Once your application has started you can clear the error byte to indicate a successful boot with:

dd if=/dev/zero bs=1 of=/dev/mtdblock0 seek=524288 count=1

Access U-boot Environment from Linux

U-Boot includes a utility fw_printenv which set set/read environment variables from Linux. This must be built and provided with a config file before it will work.

On the board first boot to u-boot by pressing ctrl+c on startup. At the prompt run:

U-Boot > env print imx_type
imx_type=ts4900-s-1g-800mhz-i

Save this output then boot to Linux to build the fw_printenv tool.

cd /usr/src/
git clone --depth 1 https://github.com/embeddedTS/u-boot-imx.git -b imx_v2015.04_3.14.52_1.1.0_ga
cd u-boot-imx

Since u-boot gave us "imx_type=ts4900-s-1g-800mhz-i", the example defconfig is ts4900-s-1g-800mhz-i_defconfig. Your board's defconfig may be different and this build should match.

make ts4900-s-1g-800mhz-i_defconfig
make -j4 env
cp tools/env/fw_printenv /usr/bin/
# The same utility sets environment variables when
# called as fw_setenv
ln -s /usr/bin/fw_printenv /usr/bin/fw_setenv

The board will also need a config file to know where to load the environment. Create a file /etc/fw_env.config

# SPI flash on the TS-4900
# MTD device name	Device offset	Env. size	Flash sector size	Number of sectors
/dev/mtdblock1		0x0		0x2000		0x1000			2

From here you can run "fw_printenv" and read the environment variables. If first line of output is:

Warning: Bad CRC, using default environment

Then the environment is blank, and u-boot is loading the environment compiled into the u-boot binary. This is normal and is how boards are shipped.

You can modify variables with this command as well:

# Set cmdline_append to include "quiet"
fw_setenv cmdline_append console=ttymxc0,115200 ro init=/sbin/init quiet