How To Boot from NAND Flash on the Freescale P2020-RDB Board

NFS-based root file systems are very useful in a development environment. However, once you start to move to a production system, it is no longer a viable option. Instead, you will want your device to boot from some sort of persistent memory, such as NAND flash. This document explains how to boot a complete system from NAND flash on the Freescale P2020-RDB.

Prerequisites

  • Kernel uImage with the following configuration options enabled:
    • CONFIG_MTD_PARTITIONS=y
    • CONFIG_MTD_CMDLINE_PARTS=y (optional)
    • CONFIG_MTD_OF_PARTS=y
    • CONFIG_MTD_BLOCK=y
    • CONFIG_MTD_NAND=y
    • CONFIG_MTD_NAND_FSL_ELBC=y
    • CONFIG_MTD_UBI=y (If using UBI and UBIFS)
    • CONFIG_UBIFS_FS=y (If using UBI and UBIFS)
    • CONFIG_JFFS2_FS=y (If using JFFS2)
    • CONFIG_JFFS2_SUMMARY=y (If using JFFS2 with summary support)
  • RFS file (typically JFFS2 or UBI)

Building RFS Images

Determining your Flash Partitions

The kernel has the ability to divide a single flash device into multiple partitions. We can take advantage of this to keep the kernel from using sections of the flash that have been reserved for the kernel and bootloaders. To determine the partition scheme of the NAND flash, you can look at the kernel output at boot time. Here is an example:

ef000000.nor: Found 1 x16 devices at 0x0 in 16-bit bank
 Amd/Fujitsu Extended Query Table at 0x0040
ef000000.nor: CFI does not contain boot bank location. Assuming top.
number of CFI chips: 1
cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.
RedBoot partition parsing not available
Creating 5 MTD partitions on "ef000000.nor":
0x000000000000-0x000000040000 : "NOR (RO) Vitesse-7385 Firmware" 
0x000000040000-0x000000080000 : "NOR (RO) DTB Image" 
0x000000080000-0x000000400000 : "NOR (RO) Linux Kernel Image" 
0x000000400000-0x000000f00000 : "NOR (RW) JFFS2 Root File System" 
0x000000f00000-0x000001000000 : "NOR (RO) U-Boot Image" 
NAND device: Manufacturer ID: 0xec, Chip ID: 0x75 (Samsung NAND 32MiB 3,3V 8-bit)
RedBoot partition parsing not available
Creating 6 MTD partitions on "ffa00000.flash":
0x000000000000-0x000000080000 : "NAND (RO) U-Boot Image" 
0x000000080000-0x000000100000 : "NAND (RO) DTB Image" 
0x000000100000-0x000000500000 : "NAND (RO) Linux Kernel Image" 
0x000000500000-0x000000900000 : "NAND (RO) Compressed RFS Image" 
0x000000900000-0x000001000000 : "NAND (RW) JFFS2 Root File System" 
0x000001000000-0x000002000000 : "NAND (RW) Writable User area" 
eLBC NAND device at 0xffa00000, bank 1
fsl_m25p80 spi28672.0: s25sl128b (0 Kbytes)
Creating 5 MTD partitions on "SPIFLASH0":
0x000000000000-0x000000080000 : "SPI (RO) U-Boot Image" 
0x000000080000-0x000000100000 : "SPI (RO) DTB Image" 
0x000000100000-0x000000500000 : "SPI (RO) Linux Kernel Image" 
0x000000500000-0x000000900000 : "SPI (RO) Compressed RFS Image" 
0x000000900000-0x000001000000 : "SPI (RW) JFFS2 RFS" 

Based on this, you can determine the partition scheme of the device. The line

0x000000000000-0x000000040000

states that the first partition begins at offset 0 from the start of NOR, and continues until address 0x40000, giving it a size of 0x40000 bytes.

The second partition starts at 0x40000 and is 0x40000 bytes long.

The partitions are numbered in the order that they are detected, starting at 0. In the above example, there are 16 partitions, split between 1 NOR chip, 1 NAND chip, and one SPI Flash chip. This is the default configuration in the Timesys-provided device tree.

Device Number Device Offset Size Name
mtd0 NOR 0x000000000000 0x040000 Vitesse-7385 Firmware
mtd1 0x000000040000 0x040000 DTB Image
mtd2 0x000000080000 0x380000 Linux Kernel Image
mtd3 0x000000400000 0xb00000 JFFS2 Root File System
mtd4 0x000000f00000 0x100000 U-Boot Image
mtd5 NAND Chip 0x000000000000 0x0080000 U-Boot Image
mtd6 0x000000080000 0x0080000 DTB Image
mtd7 0x000000100000 0x0400000 Linux Kernel Image
mtd8 0x000000500000 0x0400000 Compressed RFS Image
mtd9 0x000000900000 0x0700000 JFFS2 Root File System
mtd10 0x000001000000 0x1000000 Writable User Area
mtd11 SPI Flash 0x000000000000 0x080000 U-Boot Image
mtd12 0x000000080000 0x080000 DTB Image
mtd13 0x000000100000 0x400000 Linux Kernel Image
mtd14 0x000000500000 0x400000 Compressed RFS Image
mtd15 0x000000900000 0x700000 JFFS2 RFS

If you are able to boot into a filesystem, you can use the file /proc/mtd to see the exact partitioning scheme for all MTDs on your board:

# cat /proc/mtd
mtd0: 00040000 00020000 "NOR (RO) Vitesse-7385 Firmware" 
mtd1: 00040000 00020000 "NOR (RO) DTB Image" 
mtd2: 00380000 00020000 "NOR (RO) Linux Kernel Image" 
mtd3: 00b00000 00020000 "NOR (RW) JFFS2 Root File System" 
mtd4: 00100000 00020000 "NOR (RO) U-Boot Image" 
mtd5: 00080000 00004000 "NAND (RO) U-Boot Image" 
mtd6: 00080000 00004000 "NAND (RO) DTB Image" 
mtd7: 00400000 00004000 "NAND (RO) Linux Kernel Image" 
mtd8: 00400000 00004000 "NAND (RO) Compressed RFS Image" 
mtd9: 00700000 00004000 "NAND (RW) JFFS2 Root File System" 
mtd10: 01000000 00004000 "NAND (RW) Writable User area" 
mtd11: 00080000 00010000 "SPI (RO) U-Boot Image" 
mtd12: 00080000 00010000 "SPI (RO) DTB Image" 
mtd13: 00400000 00010000 "SPI (RO) Linux Kernel Image" 
mtd14: 00400000 00010000 "SPI (RO) Compressed RFS Image" 
mtd15: 00700000 00010000 "SPI (RW) JFFS2 RFS" 

size corresponds to the density (in bytes), and erasesize corresponds to the Physical Erase Block size (in Bytes).

Use this information to determine where to put your kernel and RFS. The RFS should have its own partition all to itself (e.g. mtd13), while the kernel can share a partition with a bootloader, size permitting. Make a note of the offsets that you are using for each one, since they will be used in a later step.

For this document, we will use the following scheme:

mtd7 kernel
mtd6 device tree
mtd10 RFS

Modifying NAND Partitioning

If the partitions in the kernel are not adequate, as in the example above, you can use command line MTD partitioning to modify the layout:

You can also modify the partitions in the Linux kernel by changing the nand sections in the device tree file.

Writing Kernel and Device Tree to NAND Flash

You can write the kernel and device tree to NAND flash using a number of methods. The simplest way may be to use your JTAG debugger. However, since there are a number of JTAG solutions available, you should consult the documentation for your setup to determine the method for writing these files to flash.

If you do not have a JTAG environment set up, you can use U-Boot to write the kernel and device tree to NAND. The U-Boot NAND routines are bad-block safe, which means they will automatically account for (and skip) bad blocks. The basic method for writing a file to NAND is to erase the region that you wish to write to, transfer your file into RAM, then write the file to NAND.

Writing the Kernel

  1. Activate the proper NAND flash chip using the nand device command.
    => nand device <number>

    Where <number> is the NAND bank.
  2. Erase the region in NAND where you wish to write the kernel using the nand erase command.
    => nand erase <offset> <size>

    Where <offset> is the address relative to the beginning of the NAND chip, and <size> is the number of bytes to erase.
  3. Transfer the kernel file to RAM. This can be done using a number of methods, although TFTP is probably easiest.
  4. Write the image to NAND using the nand write command.
    => nand write <address> <offset> <size>

    Where <address> is the address in RAM where you loaded the kernel to. <offset> and <size> should be the same as those used in the erase procedure above.

Example

We are using mtd7 for our kernel, which corresponds to offset 0x100000 from the beginning of NAND.

=> nand device 0
=> nand erase 100000 400000
=> tftp 1000000 kernel_p2020rdb-1
=> nand write 1000000 100000 400000

Writing the Device Tree

You write the device tree in the same manner as the kernel. Just pick a different NAND offset.

Example

We are using mtd6 for our kernel, which corresponds to offset 0x80000 from the beginning of NAND.

=> nand device 0
=> nand erase 80000 80000
=> tftp c00000 timesys.dtb
=> nand write c00000 80000 80000

Writing RFS to NAND Flash

You can write the RFS to NAND flash using a number of methods. The easiest is using your JTAG, although we do not provide instructions for how to do this. Instead, please consult your JTAG documentation for help. The easiest way to write an RFS to NAND without the aid of a JTAG is to use a kernel booted with an NFS-based RFS.

Using U-Boot

NOTE: Since U-Boot can only write to NAND directly from RAM, you can only use U-Boot if your filesystem is smaller than the available RAM.

You write the file system in the same manner as the kernel and device tree. Just pick a different NAND offset.

Example

We are using mtd10 for our RFS, which corresponds to offset 0x1000000 from the beginning of NAND.

=> nand device 0
=> nand erase 1000000 1000000
=> tftp c00000 rootfs.jffs2
=> nand write c00000 1000000 1000000

Using the Kernel

NOTE: Your NFS-based RFS must have mtd-utils installed in order to modify flash from userspace.

  1. On the host machine, copy your flash image into your NFS directory.
  2. On the target machine, boot Linux into an NFS-based RFS.
  3. Erase the NAND partition that you wish to use using the flash_eraseall command
    # flash_eraseall /dev/mtdN

    Where N is the partition number that you wish to erase.
  4. Write your image to flash using the nandwrite command
    # nandwrite /dev/mtdN <image path>

Example

We are using mtd10 for our RFS, which corresponds to offset 0x1000000 from the beginning of NAND.

# flash_eraseall /dev/mtd10
# nandwrite /dev/mtd10 /rootfs.jffs2

Boot procedure

In order to boot from NAND, you must load the kernel from NAND into memory, and then tell the kernel where to look for the RFS, and how to load it.

  1. Copy the kernel into RAM using the nand read command:
    => nand read <address> <offset> <size>

    This command will read <size> bytes from NAND address <offset> and move it to <address> in RAM. <size> should be greater than or equal to the size of the kernel. <offset> should be the start of the kernel partition in NAND, the same value used in the section Writing Kernel to NAND Flash above. <address> should be some location in RAM. The value 0x1000000 has been tested and works.
  2. Copy the device tree into RAM using the nand read command:
    => nand read <address> <offset> <size>

    The value 0xC00000 as the address has been tested and works.
  3. Set the kernel command line to boot from NAND flash:
    => setenv bootargs console=ttyS0,115200 rootfstype=jffs2 root=/dev/mtdblockN rw

    You should substitute the proper MTD device number for N in the root parameter above. See the UBIFS doc for information about how to use UBI.
  4. Boot the kernel using the bootm command.
    U-Boot> bootm <kernel address> - <device tree address>

    <kernel address> and <device tree address> are the same as the respective values of address used in the NAND read commands above.

Example

=> nand read 1000000 100000 400000
=> nand read c00000 80000 80000
=> setenv bootcmd console=ttyS0,115200 rootfstype=jffs2 root=/dev/mtdblock10 rw
=> bootm 1000000 - c00000

Restoring U-Boot to NOR Flash

U-Boot resides in NOR flash. If it is erased in the process, it must be restored using Freescale's CodeWarrior tool.

Using the CodeWarrior Flash Programmer

  1. Start CodeWarrior
  2. Click Tools->Flash Programmer
  3. Click Load Settings, and select the following file (Your path may vary depending on your CodeWarrior version and installation location):
    C:\Program Files\Freescale\CodeWarrior PA V8.8\bin\plugins\Support\Flash_Programmer\EPPC\QorIQ_P2\P10xx-P20xxRDB_P2020_NOR_FLASH.xml
  4. Modify the Connection field to match your debugger
  5. Erase the flash, if necessary
    • Click Erase / Blank Check
    • Select the regions 0xFFF80000 through 0xFFFE0000
    • Click Erase
  6. Write U-Boot to flash
    • Click Program / Verify
    • Check the box next to Apply Address Offset
    • Enter FFF80000 in the Offset box
    • Select your u-boot.bin file in the Use Selected File field
    • Click Program