[U-Boot] [PATCH v5 12/14] arm: add Faraday specific boot command

Kuo-Jung Su
2013-06-17T12:07:02Z

At the time of writting, none of Faraday NAND & SPI controllers
supports XIP (eXecute In Place). So the Faraday A360/A369 SoC has
to implement a 1st level bootstrap code stored in the embedded ROM
inside the SoC.

After power-on, the ROM code (1st level bootstrap code) would load
the 2nd bootstrap code into SRAM without any SDRAM initialization.

The 2nd bootstrap code would then initialize SDRAM and load the
generic firmware (u-boot/linux) into SDRAM, and finally make
a long-jump to the firmware.

Which means the SPL design of U-boot would never fit to A360/A369,
since it's usually not possible to alter a embedded ROM code.
And because both the 1st & 2nd level bootstrap code use the private
Faraday Firmware Image Format, it would be better to drop U-boot
image support to simplify the design.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
CC: Albert Aribaud <albert.u.boot@aribaud.net>
---
Changes for v5:
- Rename from 'arm: add Faraday firmware image utility'
into 'arm: add Faraday specific boot command'
- Add missing CRC check to the command 'bootfa'.
- Add rationale to the command 'bootfa'.

Changes for v4:
- Coding Style cleanup.
- Break up from [arm: add Faraday A36x SoC platform support]

Changes for v3:
- Coding Style cleanup.
- Always insert a blank line between declarations and code.

Changes for v2:
- Coding Style cleanup.

arch/arm/cpu/faraday/Makefile | 2 +-
arch/arm/cpu/faraday/cmd_bootfa.c | 276 +++++++++++++++++++++++++++++++++++++
arch/arm/cpu/faraday/fwimage.h | 47 +++++++
arch/arm/cpu/faraday/fwimage2.h | 67 +++++++++
arch/arm/cpu/u-boot.lds | 11 ++
5 files changed, 402 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/cpu/faraday/cmd_bootfa.c
create mode 100644 arch/arm/cpu/faraday/fwimage.h
create mode 100644 arch/arm/cpu/faraday/fwimage2.h

diff --git a/arch/arm/cpu/faraday/Makefile b/arch/arm/cpu/faraday/Makefile
index 6b96e32..ef6a72e 100644
--- a/arch/arm/cpu/faraday/Makefile
+++ b/arch/arm/cpu/faraday/Makefile
@@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk

LIB = $(obj)lib$(CPU).o

-src-y := cpu.o
+src-y := cpu.o cmd_bootfa.o
src-$(CONFIG_FTINTC020) += ftintc020.o
src-$(CONFIG_FTTMR010) += fttmr010.o
src-$(CONFIG_FTPWMTMR010) += ftpwmtmr010.o
diff --git a/arch/arm/cpu/faraday/cmd_bootfa.c b/arch/arm/cpu/faraday/cmd_bootfa.c
new file mode 100644
index 0000000..cdc47a3
--- /dev/null
+++ b/arch/arm/cpu/faraday/cmd_bootfa.c
@@ -0,0 +1,276 @@
+/*
+ * arch/arm/cpu/faraday/cmd_bootfa.c
+ *
+ * This command is used to boot faraday firmware from MMC/USB/SPI/NAND/NOR
+ *
+ * (C) Copyright 2013 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * At the time of writting, none of Faraday NAND & SPI controllers
+ * supports XIP (eXecute In Place). So the Faraday A360/A369 SoC has
+ * to implement a 1st level bootstrap code stored in the embedded ROM
+ * inside the SoC.
+ *
+ * After power-on, the ROM code (1st level bootstrap code) would load
+ * the 2nd bootstrap code into SRAM without any SDRAM initialization.
+ *
+ * The 2nd bootstrap code would then initialize SDRAM and load the
+ * generic firmware (u-boot/linux) into SDRAM, and finally make
+ * a long-jump to the firmware.
+ *
+ * Which means the SPL design of U-boot would never fit to A360/A369,
+ * since it's usually not possible to alter a embedded ROM code.
+ * And because both the 1st & 2nd level bootstrap code use the private
+ * Faraday Firmware Image Format, it would be better to drop U-boot
+ * image support to simplify the design.
+ *
+ * The Faraday Firmware Image Format uses a 1 KB (1024 Bytes) header:
+ *
+ * +----------------+ 0x0000
+ * | MAGIC | Magic number
+ * +----------------+ 0x0004
+ * | HDR LENGTH | The size of this header
+ * +----------------+ 0x0008
+ * | |
+ * | SYS PARAMETERS | A set of (addr, data) for 32-bit register write,
+ * | | which is for SDRAM initialization and timing control.
+ * +----------------+ 0x0108
+ * | |
+ * | PART TABLE | A partition table with max. 10 entries.
+ * | |
+ * +----------------+ 0x03D8
+ * | HDR CHECKSUM | Header Checksum (CRC32)
+ * +----------------+ 0x03DC
+ * | HDR REVISION | Header Revision ID
+ * +----------------+ 0x03E0
+ * | HDR TIMESTAMP | Header Creation Timestamp
+ * +----------------+ 0x03E4
+ * | RESERVED |
+ * +----------------+ 0x0400
+ *
+ * The entry of partitoin table is:
+ *
+ * +----------------+ 0x0000
+ * | NAME | The name of the partition
+ * +----------------+ 0x0020
+ * | OFFSET | The offset address of the flash memory.
+ * +----------------+ 0x0024
+ * | LENGTH | The data length of the partition.
+ * +----------------+ 0x0028
+ * | Load Address | The address in SDRAM to store the firmware.
+ * +----------------+ 0x002C
+ * | Quick CRC | An optional CRC32 agains 256KB of TOP & BUTTOM.
+ * +----------------+ 0x0030
+ * | FLAGS | The flags/attribute of the partition
+ * +----------------+ 0x0034
+ * | RESERVED |
+ * +----------------+ 0x0040
+ * | MAGIC=0x1000 | It's always a 0x1000.
+ * +----------------+ 0x0044
+ * | MAGIC=0x0001 | It's always a 0x0001.
+ * +----------------+ 0x0048
+ *
+ * The usage of the command 'bootfa' is:
+ *
+ * bootfa <interface> <fw_name>
+ * - boot from 'interface' with the firmware named as <fw_name>
+ * where 'interface' could be any one of <usb | mmc | nand | sf>
+ *
+ * ex: bootfa usb linux
+ *
+ * The rationale is:
+ *
+ * 1. If 'interface' is either 'usb' or 'mmc/sd', then jumps to 2-a;
+ * else jumps to 2-b.
+ * 2-a. Translate 'fw_name' to 'filename' from the built-in rules
+ * (i.e. 'linux' -> 'zimage'), jumps to 3-a.
+ * 2-b. Use 'nand' or 'sf' or 'cp' to load image header into SDRAM,
+ * and the looking for the 'fw_name' in the partition table.
+ * If part->name equals 'fw_name', then jumps to 3-b;
+ * else report an error and exit.
+ * 3-a. Use 'fatload' to load the firmware into SDRAM by filename,
+ * and then jumps to 4.
+ * 3-b. Use 'nand' or 'sf' or 'cp' to load the firmware into SDRAM
+ * by .offset & .length of the corresponding partition info,
+ * and then jumps to 4.
+ * 4. Use 'go' to shift control to the loaded firmware.
+ */
+#include <common.h>
+#include <command.h>
+
+#include "fwimage2.h"
+
+#undef ROUNDUP
+#define ROUNDUP(len, blksz) \
+ ((len) % (blksz)) ? ((len) + (blksz) - ((len) % (blksz))) : (len)
+
+static int hdr_invalid(struct fwimage2 *img)
+{
+ uint32_t cksum_old, cksum_new;
+
+ if (le32_to_cpu(img->revision) != FWIMAGE2_REVISION)
+ return 0;
+
+ cksum_old = le32_to_cpu(img->hcrc);
+ img->hcrc = 0;
+ cksum_new = crc32_no_comp(0xFFFFFFFF, img, sizeof(struct fwimage2));
+ img->hcrc = cpu_to_le32(cksum_old);
+
+ return (cksum_new != cksum_old);
+}
+
+static int part_invalid(struct fwpart *part, uint8_t *buf)
+{
+ uint32_t len = le32_to_cpu(part->length);
+ uint32_t cksum = 0xFFFFFFFF;
+
+ if (len <= SZ_512K) {
+ cksum = crc32_no_comp(cksum, buf, len);
+ } else {
+ cksum = crc32_no_comp(cksum, buf, SZ_256K);
+ cksum = crc32_no_comp(cksum, buf + len - SZ_256K, SZ_256K);
+ }
+
+ return (cksum != le32_to_cpu(part->qcrc));
+}
+
+static struct fwpart *part_lookup(struct fwimage2 *hdr, char *name)
+{
+ int i;
+ struct fwpart *ppart = hdr->part;
+ static struct fwpart part_local;
+
+ if (hdr_invalid(hdr)) {
+ printf("part_lookup: bad header\n");
+ return NULL;
+ }
+
+ for (i = 0; ppart[i].length > 0 && i < 10; ++i) {
+ if (!strcmp(name, ppart[i].name)) {
+ printf("part_lookup: name=%s, offset=0x%x, size=0x%x\n",
+ ppart[i].name, ppart[i].offset, ppart[i].length);
+
+ memcpy(&part_local, &ppart[i], sizeof(struct fwpart));
+ return &part_local;
+ }
+ }
+
+ return NULL;
+}
+
+static int do_bootfa(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ char *inf, *name , cmd[256];
+ struct fwpart *part = NULL;
+
+ if (argc < 3) {
+#ifdef CONFIG_SYS_LONGHELP
+ printf("Usage:\n"
+ "%s %s\n", argv[0], cmdtp->help);
+#else
+ printf("Usage:\n"
+ "bootfa <interface> <fw_name>\n"
+ " - boot from 'interface' with the firmware named as <fw_name>\n"
+ " where 'interface' could be any one of <usb | mmc | nand | sf>\n"
+ "ex: bootfa usb linux");
+#endif
+ return 1;
+ }
+
+ inf = argv[1];
+ name = argv[2];
+
+ if (!strcmp(inf, "usb")) {
+ if (!strcmp(name, "linux"))
+ name = "zimage";
+ else if (!strcmp(name, "wince"))
+ name = "nk.nb0";
+ sprintf(cmd, "usb start;fatload usb 0 0x%x %s",
+ CONFIG_SYS_LOAD_ADDR, name);
+ run_command(cmd, 0);
+ } else if (!strcmp(inf, "mmc") || !strcmp(inf, "sd")) {
+ if (!strcmp(name, "linux"))
+ name = "zimage";
+ else if (!strcmp(name, "wince"))
+ name = "nk.nb0";
+ sprintf(cmd, "mmcinfo;fatload mmc 0 0x%x %s",
+ CONFIG_SYS_LOAD_ADDR, name);
+ run_command(cmd, 0);
+#ifdef CONFIG_SYS_FLASH_BASE
+ } else if (!strcmp(inf, "nor")) {
+ sprintf(cmd, "cp.l 0x%x 0x%x 0x400",
+ CONFIG_SYS_FLASH_BASE,
+ CONFIG_SYS_LOAD_ADDR);
+ run_command(cmd, 0);
+ part = part_lookup((void *)CONFIG_SYS_LOAD_ADDR, name);
+ if (!part) {
+ printf("firmware not found!\n");
+ return 1;
+ }
+ sprintf(cmd, "cp.l 0x%x 0x%x 0x%x",
+ CONFIG_SYS_FLASH_BASE + part->offset,
+ CONFIG_SYS_LOAD_ADDR, part->length);
+ run_command(cmd, 0);
+#endif
+ } else if (!strcmp(inf, "nand")) {
+ sprintf(cmd, "nand read 0x%x 0 0x400", CONFIG_SYS_LOAD_ADDR);
+ run_command(cmd, 0);
+ part = part_lookup((void *)CONFIG_SYS_LOAD_ADDR, name);
+ if (!part) {
+ printf("firmware not found!\n");
+ return 1;
+ }
+ sprintf(cmd, "nand read 0x%x 0x%x 0x%x",
+ CONFIG_SYS_LOAD_ADDR,
+ part->offset, part->length);
+ run_command(cmd, 0);
+ } else if (!strcmp(inf, "sf")) {
+ sprintf(cmd, "sf probe 0:0 25000000;sf read 0x%x 0 0x400",
+ CONFIG_SYS_LOAD_ADDR);
+ run_command(cmd, 0);
+ part = part_lookup((void *)CONFIG_SYS_LOAD_ADDR, name);
+ if (!part) {
+ printf("firmware not found!\n");
+ return 1;
+ }
+ sprintf(cmd, "sf read 0x%x 0x%x 0x%x",
+ CONFIG_SYS_LOAD_ADDR,
+ part->offset, part->length);
+ run_command(cmd, 0);
+ }
+
+ if (part && part_invalid(part, (void *)CONFIG_SYS_LOAD_ADDR)) {
+ printf("bad partition!\n");
+ return 1;
+ }
+
+ sprintf(cmd, "go 0x%x", CONFIG_SYS_LOAD_ADDR);
+ run_command(cmd, 0);
+
+ return 1;
+}
+
+U_BOOT_CMD(
+ bootfa, 3, 1, do_bootfa,
+ "boot faraday firmware image",
+ "<interface> <fw_name>\n"
+ " - boot from 'interface' with the firmware named as <fw_name>\n"
+ " where 'interface' could be any one of <usb | mmc | nand | sf>\n"
+ "ex: bootfa usb linux"
+);
diff --git a/arch/arm/cpu/faraday/fwimage.h b/arch/arm/cpu/faraday/fwimage.h
new file mode 100644
index 0000000..c6a3e80
--- /dev/null
+++ b/arch/arm/cpu/faraday/fwimage.h
@@ -0,0 +1,47 @@
+/*
+ * linux/arch/arm/plat-faraday/fwimage.h
+ *
+ * (C) Copyright 2013 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ARCH_ARM_PLAT_FARADAY_FWIMAGE_H
+#define _ARCH_ARM_PLAT_FARADAY_FWIMAGE_H
+
+struct fwparam {
+ uint32_t count;
+ uint32_t version; /* ycmo100525: for firmware image version */
+ uint32_t addr[31];
+ uint32_t data[31];
+};
+
+struct fwfile {
+ char name[64];
+ uint32_t size;
+};
+
+struct fwimage {
+ uint32_t magic; /* Image Header Magic */
+ uint32_t length;/* Image Header Length */
+
+ /* 256 bytes, embedded paramters area */
+ struct fwparam param;
+
+ struct fwfile file[1];
+};
+
+#endif /* _ARCH_ARM_PLAT_FARADAY_FWIMAGE_H */
diff --git a/arch/arm/cpu/faraday/fwimage2.h b/arch/arm/cpu/faraday/fwimage2.h
new file mode 100644
index 0000000..52e0b94
--- /dev/null
+++ b/arch/arm/cpu/faraday/fwimage2.h
@@ -0,0 +1,67 @@
+/*
+ * linux/arch/arm/plat-faraday/fwimage2.h
+ *
+ * (C) Copyright 2013 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ARCH_ARM_PLAT_FARADAY_FWIMAGE2_H
+#define _ARCH_ARM_PLAT_FARADAY_FWIMAGE2_H
+
+#include "fwimage.h"
+
+struct fwpart {
+ char name[32];
+
+ uint32_t offset;
+ uint32_t length;
+
+ uint32_t load;
+ uint32_t qcrc; /* Quick checksum againsts 256KB of TOP & BOTTOM */
+
+ uint32_t flags;
+#define FWIMAGE2_PART_FILESYSTEM 0x80000000 /* Is a filesystem ? */
+
+ uint32_t rsvd[3];
+ uint32_t magic1000; /* It's always 0x1000 */
+ uint32_t magic0001; /* It's always 0x0001 */
+}; /* size = 72 bytes */
+
+struct fwimage2 {
+ uint32_t magic; /* Image Header Magic */
+#define FWIMAGE2_MAGIC 0x00484946 /* "FIH\0" */
+
+ uint32_t hlen; /* Image Header Length */
+
+ /* 256 bytes, 32-bit memory write */
+ struct {
+ uint32_t addr;
+ uint32_t data;
+ } mw32[32];
+
+ /* 720 bytes, partition table */
+ struct fwpart part[10];
+
+ uint32_t hcrc; /* Image Header Checksum (CRC32) */
+ uint32_t revision; /* Image Format Revision ID */
+#define FWIMAGE2_REVISION 0x00000202 /* v2.2 */
+
+ uint32_t time; /* Image Creation Timestamp */
+ uint32_t rsvd[7];
+}; /* size = 1024 bytes */
+
+#endif /* _ARCH_ARM_PLAT_FARADAY_FWIMAGE2_H */
diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds
index d9bbee3..ac59fec 100644
--- a/arch/arm/cpu/u-boot.lds
+++ b/arch/arm/cpu/u-boot.lds
@@ -23,6 +23,8 @@
* MA 02111-1307 USA
*/

+#include <config.h>
+
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
@@ -35,6 +37,15 @@ SECTIONS
{
__image_copy_start = .;
CPUDIR/start.o (.text*)
+#ifdef CONFIG_FARADAY
+ /*
+ * Dante Su 2012.10.09:
+ * Reserved for the faimage v2.1.
+ * 1. CPUDIR/start.o: It shall never be > 4KB.
+ * 2. faimage header: It shall always be stored at 0x1000, and <= 1KB.
+ */
+ . = 0x00001400;
+#endif
*(.text*)
}

--
1.7.9.5