U-Boot
Threads by month
- ----- 2025 -----
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2000 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
October 2016
- 203 participants
- 644 discussions

[U-Boot] [PATCH v3] Add nand bootupdate command for i.MX6 and similar platforms
by Sergey Kubushyn 08 Oct '16
by Sergey Kubushyn 08 Oct '16
08 Oct '16
This one adds nand_bootupdate command for i.MX6 and similar MCUs. It
generates proper NAND boot structures (FCB, DBBT, etc) and writes those
along with U-Boot imx image to where they belong so system would be able
to boot off of raw NAND.
As of now the only way to do it is by using user-space kobs-ng utility
that has lots of unnecessary bells and whistles and only runs from
Linux so it is impossible to update raw NAND U-Boot from U-Boot itself.
It does not give any choice for the actual data placement in NAND but
that is done deliberately -- there is no reason to put it anywhere but
the only location i.MX6 Boot ROM expects it to be.
Signed-off-by: Sergey Kubushyn <ksi(a)koi8.net>
Cc: Scott Wood <oss(a)buserror.net>
Cc: Stefano Babic <sbabic(a)denx.de>
---
Changes for v3:
- Renamed some functions to more appropriate names
- Files moved to where they belong
- Code cleanup
- Converted to Kconfig
arch/arm/include/asm/imx-common/mxs_nand.h | 37 ++
cmd/Kconfig | 9 +
cmd/nand.c | 61 ++++
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/mxs_nand.c | 7 +-
drivers/mtd/nand/mxs_nand_bootupdate.c | 556 +++++++++++++++++++++++++++++
6 files changed, 668 insertions(+), 3 deletions(-)
create mode 100644 arch/arm/include/asm/imx-common/mxs_nand.h
create mode 100644 drivers/mtd/nand/mxs_nand_bootupdate.c
diff --git a/arch/arm/include/asm/imx-common/mxs_nand.h b/arch/arm/include/asm/imx-common/mxs_nand.h
new file mode 100644
index 0000000..7826a9f
--- /dev/null
+++ b/arch/arm/include/asm/imx-common/mxs_nand.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 PHYTEC Messtechnik GmbH,
+ * Author: Stefan Christ <s.christ at phytec.de>
+ *
+ * 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.
+ */
+
+#ifndef __NAND_MXS_H
+#define __NAND_MXS_H
+
+/*
+ * Functions are definied in drivers/mtd/nand/nand_mxs.c. They are used to
+ * calculate the ECC Strength, BadBlockMarkerByte and BadBlockMarkerStartBit
+ * which are placed into the FCB structure. The i.MX6 ROM needs these
+ * parameters to read the firmware from NAND.
+ *
+ * The parameters depends on the pagesize and oobsize of NAND chips and are
+ * different for each combination. To avoid placing hardcoded values in the bbu
+ * update handler code, the generic calculation from the driver code is used.
+ */
+
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+ uint32_t page_oob_size);
+
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd);
+
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd);
+
+#endif /* __NAND_MXS_H */
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 86554ea..520d3c3 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -571,6 +571,15 @@ config CMD_TIME
help
Run commands and summarize execution time.
+config CMD_NAND_BOOTUPDATE
+ bool "Update NAND bootloader on iMX6 and its brethen"
+ depends on ARCH_MX6 && NAND_BOOT && CMD_NAND
+ help
+ Having iMX6 NAND U-Boot image in memory creates all necessary
+ structures and writes those where they belong along with that
+ U-Boot image so system is able to boot off of raw NAND. Kinda
+ like kobs-ng utility but simpler.
+
# TODO: rename to CMD_SLEEP
config CMD_MISC
bool "sleep"
diff --git a/cmd/nand.c b/cmd/nand.c
index c16ec77..d10b794 100644
--- a/cmd/nand.c
+++ b/cmd/nand.c
@@ -38,6 +38,11 @@ int find_dev_and_part(const char *id, struct mtd_device **dev,
u8 *part_num, struct part_info **part);
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+/* This comes from a separate file in drivers/mtd/nand */
+int mxs_do_nand_bootupdate(ulong addr, size_t len);
+#endif
+
static int nand_dump(struct mtd_info *mtd, ulong off, int only_oob,
int repeat)
{
@@ -372,6 +377,9 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
loff_t off, size, maxsize;
char *cmd, *s;
struct mtd_info *mtd;
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ size_t cnt;
+#endif
#ifdef CONFIG_SYS_NAND_QUIET
int quiet = CONFIG_SYS_NAND_QUIET;
#else
@@ -777,6 +785,48 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ if (strncmp(cmd, "bootupdate", 10) == 0) {
+
+ if (argc < 3) {
+ /* All default values */
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+ if (argc == 3) {
+ /* 'addr' only, file size from environment */
+ if ((addr = simple_strtoul(argv[2], NULL, 16)) == 0UL)
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+ if (argc > 3) {
+ /* 'addr', 'size', and possibly more */
+ if ((addr = simple_strtoul(argv[2], NULL, 16)) == 0UL)
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+
+ if ((cnt = simple_strtoul(argv[3], NULL, 16)) == 0UL)
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+
+ if (addr == 0 || cnt == 0) {
+ puts("Invalid arguments to nand bootupdate!\n");
+ return 1;
+ }
+
+ if (mxs_do_nand_bootupdate(addr, cnt)) {
+ puts("NAND bootupdate failed!\n");
+ return 1;
+ }
+
+ puts("NAND bootupdate successful\n");
+ return 0;
+ }
+#endif
+
usage:
return CMD_RET_USAGE;
}
@@ -798,6 +848,17 @@ static char nand_help_text[] =
" 'addr', skipping bad blocks and dropping any pages at the end\n"
" of eraseblocks that contain only 0xFF\n"
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ "nand bootupdate - [addr] [size]\n"
+ " write U-Boot into NAND in board/SoC specific manner creating all\n"
+ " required headers and other bits and pieces as required for the\n"
+ " system to be able to boot off of NAND. 'addr' is the address\n"
+ " where U-Boot image has been loaded at, 'size' is its size.\n"
+ " If any of 'addr'/'size' is missing it is taken from environment\n"
+ " for the last file loaded. U-Boot image must be of a proper type\n"
+ " for the target platform (only IMX image supported at the moment)\n"
+ " binary without U-Boot image headers (e.g. u-boot.imx file.)\n"
+#endif
"nand erase[.spread] [clean] off size - erase 'size' bytes "
"from offset 'off'\n"
" With '.spread', erase enough for given file size, otherwise,\n"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1df9273..b1b98dc 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o
obj-$(CONFIG_NAND_PLAT) += nand_plat.o
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
+obj-$(CONFIG_CMD_NAND_BOOTUPDATE) += mxs_nand_bootupdate.o
else # minimal SPL drivers
diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c
index 9200544..333ee09 100644
--- a/drivers/mtd/nand/mxs_nand.c
+++ b/drivers/mtd/nand/mxs_nand.c
@@ -26,6 +26,7 @@
#include <asm/imx-common/regs-gpmi.h>
#include <asm/arch/sys_proto.h>
#include <asm/imx-common/dma.h>
+#include <asm/imx-common/mxs_nand.h>
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
@@ -150,7 +151,7 @@ static uint32_t mxs_nand_aux_status_offset(void)
return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3;
}
-static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
uint32_t page_oob_size)
{
int ecc_strength;
@@ -226,14 +227,14 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size,
return block_mark_bit_offset;
}
-static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3;
}
-static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
diff --git a/drivers/mtd/nand/mxs_nand_bootupdate.c b/drivers/mtd/nand/mxs_nand_bootupdate.c
new file mode 100644
index 0000000..89323fb
--- /dev/null
+++ b/drivers/mtd/nand/mxs_nand_bootupdate.c
@@ -0,0 +1,556 @@
+/*
+ * mxs_nand_bootupdate.c - write U-Boot to MXS NAND to make it bootable
+ *
+ * Copyright (c) 2016 Sergey Kubushyn <ksi(a)koi8.net>
+ *
+ * Most of the source shamelesly stolen from barebox.
+ *
+ * Here is the original copyright:
+ *
+ *=== Cut ===
+ * Copyright (C) 2014 Sascha Hauer, Pengutronix
+ *
+ * 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.
+ *=== Cut ===
+ */
+
+
+#include <common.h>
+#include <linux/sizes.h>
+#include <linux/mtd/mtd.h>
+#include <linux/compat.h>
+#include <command.h>
+#include <console.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <jffs2/jffs2.h>
+#include <nand.h>
+#include <errno.h>
+#include <asm/imx-common/mxs_nand.h>
+#include <asm/imx-common/imximage.cfg>
+
+
+#ifndef CONFIG_CMD_MTDPARTS
+#error "CONFIG_CMD_MTDPARTS is not set, mtd partition support missing"
+#endif
+
+static const char *uboot_tgt = "nand0,0";
+
+/* partition handling routines */
+int mtdparts_init(void);
+int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+ u8 *part_num, struct part_info **part);
+
+struct dbbt_block {
+ uint32_t Checksum; /* reserved on i.MX6 */
+ uint32_t FingerPrint;
+ uint32_t Version;
+ uint32_t numberBB; /* reserved on i.MX6 */
+ uint32_t DBBTNumOfPages;
+};
+
+struct fcb_block {
+ uint32_t Checksum; /* First fingerprint in first byte */
+ uint32_t FingerPrint; /* 2nd fingerprint at byte 4 */
+ uint32_t Version; /* 3rd fingerprint at byte 8 */
+ uint8_t DataSetup;
+ uint8_t DataHold;
+ uint8_t AddressSetup;
+ uint8_t DSAMPLE_TIME;
+ /* These are for application use only and not for ROM. */
+ uint8_t NandTimingState;
+ uint8_t REA;
+ uint8_t RLOH;
+ uint8_t RHOH;
+ uint32_t PageDataSize; /* 2048 for 2K pages, 4096 for 4K pages */
+ uint32_t TotalPageSize; /* 2112 for 2K pages, 4314 for 4K pages */
+ uint32_t SectorsPerBlock; /* Number of 2K sections per block */
+ uint32_t NumberOfNANDs; /* Total Number of NANDs - not used by ROM */
+ uint32_t TotalInternalDie; /* Number of separate chips in this NAND */
+ uint32_t CellType; /* MLC or SLC */
+ uint32_t EccBlockNEccType; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0Size; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSize; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccType; /* Ecc level for Block 0 - BCH */
+ uint32_t MetadataBytes; /* Metadata size - BCH */
+ uint32_t NumEccBlocksPerPage; /* Number of blocks per page for ROM use - BCH */
+ uint32_t EccBlockNEccLevelSDK; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0SizeSDK; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSizeSDK; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccLevelSDK; /* Ecc level for Block 0 - BCH */
+ uint32_t NumEccBlocksPerPageSDK;/* Number of blocks per page for SDK use - BCH */
+ uint32_t MetadataBytesSDK; /* Metadata size - BCH */
+ uint32_t EraseThreshold; /* To set into BCH_MODE register */
+ uint32_t BootPatch; /* 0 for normal boot and 1 to load patch starting next to FCB */
+ uint32_t PatchSectors; /* Size of patch in sectors */
+ uint32_t Firmware1_startingPage;/* Firmware image starts on this sector */
+ uint32_t Firmware2_startingPage;/* Secondary FW Image starting Sector */
+ uint32_t PagesInFirmware1; /* Number of sectors in firmware image */
+ uint32_t PagesInFirmware2; /* Number of sector in secondary FW image */
+ uint32_t DBBTSearchAreaStartAddress; /* Page address where dbbt search area begins */
+ uint32_t BadBlockMarkerByte; /* Byte in page data that have manufacturer marked bad block marker, */
+ /* this will be swapped with metadata[0] to complete page data. */
+ uint32_t BadBlockMarkerStartBit;/* For BCH ECC sizes other than 8 and 16 the bad block marker does not */
+ /* start at 0th bit of BadBlockMarkerByte. This field is used to get to */
+ /* the start bit of bad block marker byte with in BadBlockMarkerByte */
+ uint32_t BBMarkerPhysicalOffset;/* FCB value that gives byte offset for bad block marker on physical NAND page */
+ uint32_t BCHType;
+
+ uint32_t TMTiming2_ReadLatency;
+ uint32_t TMTiming2_PreambleDelay;
+ uint32_t TMTiming2_CEDelay;
+ uint32_t TMTiming2_PostambleDelay;
+ uint32_t TMTiming2_CmdAddPause;
+ uint32_t TMTiming2_DataPause;
+ uint32_t TMSpeed;
+ uint32_t TMTiming1_BusyTimeout;
+
+ uint32_t DISBBM; /* the flag to enable (1)/disable(0) bi swap */
+ uint32_t BBMarkerPhysicalOffsetInSpareData; /* The swap position of main area in spare area */
+};
+
+struct fw_write_data {
+ int fw1_blk;
+ int fw2_blk;
+ int part_blks;
+ void *buf;
+ size_t len;
+};
+
+#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET)
+#define GETBIT(v,n) (((v) >> (n)) & 0x1)
+
+
+static uint8_t calculate_parity_13_8(uint8_t d)
+{
+ uint8_t p = 0;
+
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^ GETBIT(d, 1)) << 1;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 2;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
+ return p;
+}
+
+static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
+{
+ int i;
+ uint8_t *src = _src;
+ uint8_t *ecc = _ecc;
+
+ for (i = 0; i < size; i++)
+ ecc[i] = calculate_parity_13_8(src[i]);
+}
+
+static uint32_t calc_chksum(void *buf, size_t size)
+{
+ u32 chksum = 0;
+ u8 *bp = buf;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ chksum += bp[i];
+
+ return ~chksum;
+}
+
+
+static ssize_t raw_write_page(struct mtd_info *mtd, void *buf, loff_t offset)
+{
+ struct mtd_oob_ops ops;
+ ssize_t ret;
+
+ ops.mode = MTD_OPS_RAW;
+ ops.ooboffs = 0;
+ ops.datbuf = buf;
+ ops.len = mtd->writesize;
+ ops.oobbuf = buf + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ret = mtd_write_oob(mtd, offset, &ops);
+
+ return ret;
+}
+
+static int fcb_create(struct fcb_block *fcb, struct mtd_info *mtd)
+{
+ fcb->FingerPrint = 0x20424346;
+ fcb->Version = 0x01000000;
+ fcb->PageDataSize = mtd->writesize;
+ fcb->TotalPageSize = mtd->writesize + mtd->oobsize;
+ fcb->SectorsPerBlock = mtd->erasesize / mtd->writesize;
+
+ /* Divide ECC strength by two and save the value into FCB structure. */
+ fcb->EccBlock0EccType =
+ mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1;
+
+ fcb->EccBlockNEccType = fcb->EccBlock0EccType;
+
+ /* Also hardcoded in kobs-ng */
+ fcb->EccBlock0Size = 0x00000200;
+ fcb->EccBlockNSize = 0x00000200;
+ fcb->DataSetup = 80;
+ fcb->DataHold = 60;
+ fcb->AddressSetup = 25;
+ fcb->DSAMPLE_TIME = 6;
+ fcb->MetadataBytes = 10;
+
+ /* DBBT search area starts at second page on first block */
+ fcb->DBBTSearchAreaStartAddress = 1;
+
+ fcb->BadBlockMarkerByte = mxs_nand_mark_byte_offset(mtd);
+ fcb->BadBlockMarkerStartBit = mxs_nand_mark_bit_offset(mtd);
+
+ fcb->BBMarkerPhysicalOffset = mtd->writesize;
+
+ fcb->NumEccBlocksPerPage = mtd->writesize / fcb->EccBlock0Size - 1;
+
+ fcb->Checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
+
+ return 0;
+}
+
+/* Erase entire U-Boot partition */
+static int mxs_nand_uboot_erase(struct mtd_info *mtd, struct part_info *part)
+{
+ uint64_t offset = 0;
+ struct erase_info erase;
+ int len = part->size;
+ int ret;
+
+ while (len > 0) {
+ pr_debug("erasing at 0x%08llx\n", offset);
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("erase skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ continue;
+ }
+
+ memset(&erase, 0, sizeof(erase));
+ erase.addr = offset;
+ erase.len = mtd->erasesize;
+
+ ret = mtd_erase(mtd, &erase);
+ if (ret)
+ return ret;
+
+ offset += mtd->erasesize;
+ len -= mtd->erasesize;
+ }
+
+ return 0;
+}
+
+/* Write the U-Boot proper (2 copies) to where it belongs. */
+/* This is U-Boot binary image itself, no FCB/DBBT here yet. */
+static int mxs_nand_uboot_write_fw(struct mtd_info *mtd, struct fw_write_data *fw)
+{
+ uint64_t offset;
+ int ret;
+ int blk;
+ size_t dummy;
+ size_t bytes_left;
+ int chunk;
+ void *p;
+
+ bytes_left = fw->len;
+ p = fw->buf;
+ blk = fw->fw1_blk;
+ offset = blk * mtd->erasesize;
+
+ while (bytes_left > 0) {
+ chunk = min(bytes_left, mtd->erasesize);
+
+ pr_debug("fw1: writing %p at 0x%08llx, left 0x%08x\n",
+ p, offset, bytes_left);
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("fw1: write skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ blk++;
+ continue;
+ }
+
+ if (blk >= fw->fw2_blk)
+ return -ENOSPC;
+
+ ret = mtd_write(mtd, offset, chunk, &dummy, p);
+ if (ret)
+ return ret;
+
+ offset += chunk;
+ bytes_left -= chunk;
+ p += chunk;
+ blk++;
+ }
+
+ bytes_left = fw->len;
+ p = fw->buf;
+ blk = fw->fw2_blk;
+ offset = blk * mtd->erasesize;
+
+ while (bytes_left > 0) {
+ chunk = min(bytes_left, mtd->erasesize);
+
+ pr_debug("fw2: writing %p at 0x%08llx, left 0x%08x\n",
+ p, offset, bytes_left);
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("fw2: write skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ blk++;
+ continue;
+ }
+
+ if (blk >= fw->part_blks)
+ return -ENOSPC;
+
+ ret = mtd_write(mtd, offset, chunk, &dummy, p);
+ if (ret)
+ return ret;
+
+ offset += chunk;
+ bytes_left -= chunk;
+ p += chunk;
+ blk++;
+ }
+
+ return 0;
+}
+
+static int dbbt_data_create(struct mtd_info *mtd, void *buf, int num_blocks)
+{
+ int n;
+ int n_bad_blocks = 0;
+ uint32_t *bb = buf + 0x8;
+ uint32_t *n_bad_blocksp = buf + 0x4;
+
+ for (n = 0; n < num_blocks; n++) {
+ loff_t offset = n * mtd->erasesize;
+ if (mtd_block_isbad(mtd, offset)) {
+ n_bad_blocks++;
+ *bb = n;
+ bb++;
+ }
+ }
+
+ *n_bad_blocksp = n_bad_blocks;
+
+ return n_bad_blocks;
+}
+
+/********************************************************************/
+/* This is where it is all done. Takes pointer to a U-Boot image in */
+/* RAM and image size, creates FCB/DBBT and writes everything where */
+/* it belongs into NAND. Image must be an IMX image built for NAND. */
+/********************************************************************/
+static int mxs_nand_uboot_update(const void *img, size_t len)
+{
+ int i, ret;
+
+ size_t dummy;
+ loff_t offset = 0;
+
+ void *fcb_raw_page;
+ void *dbbt_page;
+ void *dbbt_data_page;
+ void *ecc;
+
+ uint32_t num_blocks_fcb_dbbt;
+ uint32_t num_blocks_fw;
+
+ struct mtd_info *mtd;
+ struct fcb_block *fcb;
+ struct dbbt_block *dbbt;
+
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 pnum;
+
+ struct fw_write_data fw;
+
+ if ((mtdparts_init() == 0) &&
+ (find_dev_and_part(uboot_tgt, &dev, &pnum, &part) == 0)) {
+ if (dev->id->type != MTD_DEV_TYPE_NAND) {
+ puts("Not a NAND device\n");
+ return -ENODEV;
+ }
+ }
+
+ nand_curr_device = dev->id->num;
+
+#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
+ board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
+#endif
+
+ /* Get a pointer to mtd_info for selected device */
+
+ mtd = get_mtd_device_nm("nand0"); /* We always boot off of nand0 */
+
+ if (IS_ERR(mtd)) {
+ /* Should not happen */
+ puts("No nand0 device...\n");
+ return -ENODEV;
+ }
+
+ put_mtd_device(mtd);
+
+ /* Quick and dirty check if we have 2Mbytes of good blocks in nand0,0 */
+ /* Not sure if it is needed at all but won't hurt so here it is... */
+
+ i = 0;
+ offset = 0; /* It is the first partition so it starts at block 0 */
+
+ while (offset < part->size) {
+ if (!mtd_block_isbad(mtd, offset)) {
+ i += mtd->erasesize;
+ }
+ offset += mtd->erasesize;
+ }
+
+ if (i < SZ_2M) {
+ puts("Partition too small for U-Boot!\n");
+ return -EINVAL;
+ }
+
+ /* We will use 4 first blocks for FCB/DBBT copies. */
+ /* The rest of partition is split in half and used */
+ /* for two U-Boot copies. We don't care if those */
+ /* start on good or bad block - bad blocks will be */
+ /* skipped on write, ROM boot code will also skip */
+ /* bad blocks on bootup when loading U-Boot image. */
+
+ fw.part_blks = part->size / mtd->erasesize;
+ num_blocks_fcb_dbbt = 4;
+ num_blocks_fw = (fw.part_blks - num_blocks_fcb_dbbt) / 2;
+ fw.fw1_blk = num_blocks_fcb_dbbt;
+ fw.fw2_blk = fw.fw1_blk + num_blocks_fw;
+
+ /* OK, now create FCB structure for bootROM NAND boot */
+
+ fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+
+ fcb = fcb_raw_page + 12;
+ ecc = fcb_raw_page + 512 + 12;
+
+ dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ dbbt = dbbt_page;
+
+ /* Write one additional page to make the ROM happy. */
+ /* Maybe the PagesInFirmwarex fields are really the */
+ /* number of pages - 1. kobs-ng does the same. */
+
+ fw.len = ALIGN(len + FLASH_OFFSET_STANDARD + mtd->writesize, mtd->writesize);
+ fw.buf = kzalloc(fw.len, GFP_KERNEL);
+ memcpy(fw.buf + FLASH_OFFSET_STANDARD, img, len);
+
+ /* Erase entire partition */
+ ret = mxs_nand_uboot_erase(mtd, part);
+ if (ret)
+ goto out;
+
+ /* Now write 2 copies of the U-Boot proper to where they belong. */
+ /* Headers (FCB, DBBT) will be generated and written after that. */
+ ret = mxs_nand_uboot_write_fw(mtd, &fw);
+ if (ret < 0)
+ goto out;
+
+ /* Create FCB, calculate ECC (we don't/can't use hardware ECC */
+ /* here so we do it ourselves and then write _RAW_ pages. */
+
+ fcb->Firmware1_startingPage = fw.fw1_blk * mtd->erasesize / mtd->writesize;
+ fcb->Firmware2_startingPage = fw.fw2_blk * mtd->erasesize / mtd->writesize;
+ fcb->PagesInFirmware1 =
+ ALIGN(len + FLASH_OFFSET_STANDARD, mtd->writesize) / mtd->writesize;
+ fcb->PagesInFirmware2 = fcb->PagesInFirmware1;
+
+ fcb_create(fcb, mtd);
+ encode_hamming_13_8(fcb, ecc, 512);
+
+ /*
+ * Set the first and second byte of OOB data to 0xFF, not 0x00. These
+ * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since
+ * the FCB is mostly written to the first page in a block, a scan for
+ * factory bad blocks will detect these blocks as bad, e.g. when
+ * function nand_scan_bbt() is executed to build a new bad block table.
+ * We will _NOT_ mark a bad block as good -- we skip the bad blocks.
+ */
+ memset(fcb_raw_page + mtd->writesize, 0xff, 2);
+
+ /* Now create DBBT */
+ dbbt->Checksum = 0;
+ dbbt->FingerPrint = 0x54424244;
+ dbbt->Version = 0x01000000;
+
+ if ((ret = dbbt_data_create(mtd, dbbt_data_page, fw.part_blks)) < 0)
+ goto out;
+
+ if (ret > 0)
+ dbbt->DBBTNumOfPages = 1;
+
+ offset = 0;
+
+ if (mtd_block_isbad(mtd, offset)) {
+ puts("Block 0 is bad, NAND unusable\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Write FCB/DBBT to first 4 blocks. Skip bad blocks if any. */
+ /* Less than 4 copies will be written if there were BBs !!! */
+ for (i = 0; i < 4; i++) {
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_err("Block %d is bad, skipped\n", i);
+ continue;
+ }
+
+
+ ret = raw_write_page(mtd, fcb_raw_page, mtd->erasesize * i);
+ if (ret)
+ goto out;
+
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
+ mtd->writesize, &dummy, dbbt_page);
+ if (ret)
+ goto out;
+
+ /* DBBTNumOfPages == 0 if no bad blocks */
+ if (dbbt->DBBTNumOfPages > 0) {
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize * 5,
+ mtd->writesize, &dummy, dbbt_data_page);
+ if (ret)
+ goto out;
+ }
+ }
+
+out:
+ kfree(dbbt_page);
+ kfree(dbbt_data_page);
+ kfree(fcb_raw_page);
+ kfree(fw.buf);
+
+ return ret;
+}
+
+
+int mxs_do_nand_bootupdate(ulong addr, size_t len)
+{
+ /* KSI: Unlock NAND first if it is locked... */
+
+ return mxs_nand_uboot_update((const void *)addr, len);
+}
--
2.5.5
1
0

08 Oct '16
Unify the partitions of each chip then it will be more easy for us to write scripts, tools or guides
for rockchip chips.
Those extra partitions mostly are used to be compatible with our internal loaders (such as miniloader
which was same as spl, or android loader then we can support dual boot)
Signed-off-by: Jacob Chen <jacob2.chen(a)rock-chips.com>
---
Changes in v2:
- put macro in a rockchip_common.h
include/configs/rk3036_common.h | 17 +----------------
include/configs/rk3288_common.h | 17 +----------------
include/configs/rk3399_common.h | 16 ++--------------
include/configs/rockchip-common.h | 36 ++++++++++++++++++++++++++++++++++++
4 files changed, 40 insertions(+), 46 deletions(-)
create mode 100644 include/configs/rockchip-common.h
diff --git a/include/configs/rk3036_common.h b/include/configs/rk3036_common.h
index 73830e4..29d56ff 100644
--- a/include/configs/rk3036_common.h
+++ b/include/configs/rk3036_common.h
@@ -7,6 +7,7 @@
#define __CONFIG_RK3036_COMMON_H
#include <asm/arch/hardware.h>
+#include "rockchip-common.h"
#define CONFIG_SYS_NO_FLASH
#define CONFIG_NR_DRAM_BANKS 1
@@ -79,11 +80,6 @@
#define CONFIG_G_DNL_VENDOR_NUM 0x2207
#define CONFIG_G_DNL_PRODUCT_NUM 0x310a
-/* Enable gpt partition table */
-#define CONFIG_CMD_GPT
-
-#include <config_distro_defaults.h>
-
#define ENV_MEM_LAYOUT_SETTINGS \
"scriptaddr=0x60000000\0" \
"pxefile_addr_r=0x60100000\0" \
@@ -91,17 +87,6 @@
"kernel_addr_r=0x62000000\0" \
"ramdisk_addr_r=0x64000000\0"
-#define CONFIG_RANDOM_UUID
-#define PARTS_DEFAULT \
- "uuid_disk=${uuid_gpt_disk};" \
- "name=boot,start=8M,size=64M,bootable,uuid=${uuid_gpt_boot};" \
- "name=rootfs,size=-,uuid=${uuid_gpt_rootfs};\0" \
-
-/* First try to boot from SD (index 0), then eMMC (index 1 */
-#define BOOT_TARGET_DEVICES(func) \
- func(MMC, mmc, 0) \
- func(MMC, mmc, 1)
-
#include <config_distro_bootcmd.h>
/* Linux fails to load the fdt if it's loaded above 512M on a evb-rk3036 board,
diff --git a/include/configs/rk3288_common.h b/include/configs/rk3288_common.h
index 9ddfe1d..630f218 100644
--- a/include/configs/rk3288_common.h
+++ b/include/configs/rk3288_common.h
@@ -8,6 +8,7 @@
#define __CONFIG_RK3288_COMMON_H
#include <asm/arch/hardware.h>
+#include "rockchip-common.h"
#define CONFIG_SKIP_LOWLEVEL_INIT_ONLY
#define CONFIG_SYS_NO_FLASH
@@ -96,11 +97,6 @@
#define CONFIG_G_DNL_VENDOR_NUM 0x2207
#define CONFIG_G_DNL_PRODUCT_NUM 0x320a
-/* Enable gpt partition table */
-#define CONFIG_CMD_GPT
-
-#include <config_distro_defaults.h>
-
#define ENV_MEM_LAYOUT_SETTINGS \
"scriptaddr=0x00000000\0" \
"pxefile_addr_r=0x00100000\0" \
@@ -108,17 +104,6 @@
"kernel_addr_r=0x02000000\0" \
"ramdisk_addr_r=0x04000000\0"
-#define CONFIG_RANDOM_UUID
-#define PARTS_DEFAULT \
- "uuid_disk=${uuid_gpt_disk};" \
- "name=boot,start=8M,size=64M,bootable,uuid=${uuid_gpt_boot};" \
- "name=rootfs,size=-,uuid=${uuid_gpt_rootfs};\0" \
-
-/* First try to boot from SD (index 0), then eMMC (index 1 */
-#define BOOT_TARGET_DEVICES(func) \
- func(MMC, mmc, 0) \
- func(MMC, mmc, 1)
-
#include <config_distro_bootcmd.h>
/* Linux fails to load the fdt if it's loaded above 512M on a Rock 2 board, so
diff --git a/include/configs/rk3399_common.h b/include/configs/rk3399_common.h
index b026122..68b9391 100644
--- a/include/configs/rk3399_common.h
+++ b/include/configs/rk3399_common.h
@@ -7,6 +7,8 @@
#ifndef __CONFIG_RK3399_COMMON_H
#define __CONFIG_RK3399_COMMON_H
+#include "rockchip-common.h"
+
#define CONFIG_SYS_NO_FLASH
#define CONFIG_NR_DRAM_BANKS 1
#define CONFIG_ENV_SIZE 0x2000
@@ -53,7 +55,6 @@
#define CONFIG_SF_DEFAULT_SPEED 20000000
#ifndef CONFIG_SPL_BUILD
-#include <config_distro_defaults.h>
#define ENV_MEM_LAYOUT_SETTINGS \
"scriptaddr=0x00000000\0" \
@@ -62,19 +63,6 @@
"kernel_addr_r=0x02000000\0" \
"ramdisk_addr_r=0x04000000\0"
-#define CONFIG_CMD_GPT
-#define CONFIG_RANDOM_UUID
-#define CONFIG_PARTITION_UUIDS
-#define PARTS_DEFAULT \
- "uuid_disk=${uuid_gpt_disk};" \
- "name=boot,start=16M,size=32M,bootable;" \
- "name=rootfs,size=-,uuid=${uuid_gpt_rootfs};\0" \
-
-/* First try to boot from SD (index 0), then eMMC (index 1) */
-#define BOOT_TARGET_DEVICES(func) \
- func(MMC, mmc, 0) \
- func(MMC, mmc, 1)
-
#include <config_distro_bootcmd.h>
#define CONFIG_EXTRA_ENV_SETTINGS \
ENV_MEM_LAYOUT_SETTINGS \
diff --git a/include/configs/rockchip-common.h b/include/configs/rockchip-common.h
new file mode 100644
index 0000000..86c81b0
--- /dev/null
+++ b/include/configs/rockchip-common.h
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 2010-2012
+ * NVIDIA Corporation <www.nvidia.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef _ROCKCHIP_COMMON_H_
+#define _ROCKCHIP_COMMON_H_
+#include <linux/sizes.h>
+
+#ifndef CONFIG_SPL_BUILD
+#include <config_distro_defaults.h>
+
+/* First try to boot from SD (index 0), then eMMC (index 1 */
+#define BOOT_TARGET_DEVICES(func) \
+ func(MMC, mmc, 0) \
+ func(MMC, mmc, 1)
+
+ /* Enable gpt partition table */
+#define CONFIG_CMD_GPT
+#define CONFIG_RANDOM_UUID
+#define CONFIG_PARTITION_UUIDS
+#define PARTS_DEFAULT \
+ "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=128M,bootable,uuid=${uuid_gpt_boot};" \
+ "name=rootfs,size=-,uuid=${uuid_gpt_rootfs};\0" \
+
+#endif
+
+#endif /* _ROCKCHIP_COMMON_H_ */
--
1.9.1
2
2

[U-Boot] [PATCH v3] Add nand bootupdate command for i.MX6 and similar platforms
by Sergey Kubushyn 08 Oct '16
by Sergey Kubushyn 08 Oct '16
08 Oct '16
This one adds nand_bootupdate command for i.MX6 and similar MCUs. It
generates proper NAND boot structures (FCB, DBBT, etc) and writes those
along with U-Boot imx image to where they belong so system would be able
to boot off of raw NAND.
As of now the only way to do it is by using user-space kobs-ng utility
that has lots of unnecessary bells and whistles and only runs from
Linux so it is impossible to update raw NAND U-Boot from U-Boot itself.
It does not give any choice for the actual data placement in NAND but
that is done deliberately -- there is no reason to put it anywhere but
the only location i.MX6 Boot ROM expects it to be.
Signed-off-by: Sergey Kubushyn <ksi(a)koi8.net>
Cc: Scott Wood <oss(a)buserror.net>
Cc: Stefano Babic <sbabic(a)denx.de>
---
Changes for v3:
- Renamed some functions to more appropriate names
- Files moved to where they belong
- Code cleanup
- Converted to Kconfig
arch/arm/include/asm/imx-common/mxs_nand.h | 37 ++
cmd/Kconfig | 9 +
cmd/nand.c | 61 ++++
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/mxs_nand.c | 7 +-
drivers/mtd/nand/mxs_nand_bootupdate.c | 556 +++++++++++++++++++++++++++++
6 files changed, 668 insertions(+), 3 deletions(-)
create mode 100644 arch/arm/include/asm/imx-common/mxs_nand.h
create mode 100644 drivers/mtd/nand/mxs_nand_bootupdate.c
diff --git a/arch/arm/include/asm/imx-common/mxs_nand.h b/arch/arm/include/asm/imx-common/mxs_nand.h
new file mode 100644
index 0000000..7826a9f
--- /dev/null
+++ b/arch/arm/include/asm/imx-common/mxs_nand.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 PHYTEC Messtechnik GmbH,
+ * Author: Stefan Christ <s.christ at phytec.de>
+ *
+ * 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.
+ */
+
+#ifndef __NAND_MXS_H
+#define __NAND_MXS_H
+
+/*
+ * Functions are definied in drivers/mtd/nand/nand_mxs.c. They are used to
+ * calculate the ECC Strength, BadBlockMarkerByte and BadBlockMarkerStartBit
+ * which are placed into the FCB structure. The i.MX6 ROM needs these
+ * parameters to read the firmware from NAND.
+ *
+ * The parameters depends on the pagesize and oobsize of NAND chips and are
+ * different for each combination. To avoid placing hardcoded values in the bbu
+ * update handler code, the generic calculation from the driver code is used.
+ */
+
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+ uint32_t page_oob_size);
+
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd);
+
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd);
+
+#endif /* __NAND_MXS_H */
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 86554ea..520d3c3 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -571,6 +571,15 @@ config CMD_TIME
help
Run commands and summarize execution time.
+config CMD_NAND_BOOTUPDATE
+ bool "Update NAND bootloader on iMX6 and its brethen"
+ depends on ARCH_MX6 && NAND_BOOT && CMD_NAND
+ help
+ Having iMX6 NAND U-Boot image in memory creates all necessary
+ structures and writes those where they belong along with that
+ U-Boot image so system is able to boot off of raw NAND. Kinda
+ like kobs-ng utility but simpler.
+
# TODO: rename to CMD_SLEEP
config CMD_MISC
bool "sleep"
diff --git a/cmd/nand.c b/cmd/nand.c
index c16ec77..d10b794 100644
--- a/cmd/nand.c
+++ b/cmd/nand.c
@@ -38,6 +38,11 @@ int find_dev_and_part(const char *id, struct mtd_device **dev,
u8 *part_num, struct part_info **part);
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+/* This comes from a separate file in drivers/mtd/nand */
+int mxs_do_nand_bootupdate(ulong addr, size_t len);
+#endif
+
static int nand_dump(struct mtd_info *mtd, ulong off, int only_oob,
int repeat)
{
@@ -372,6 +377,9 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
loff_t off, size, maxsize;
char *cmd, *s;
struct mtd_info *mtd;
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ size_t cnt;
+#endif
#ifdef CONFIG_SYS_NAND_QUIET
int quiet = CONFIG_SYS_NAND_QUIET;
#else
@@ -777,6 +785,48 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ if (strncmp(cmd, "bootupdate", 10) == 0) {
+
+ if (argc < 3) {
+ /* All default values */
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+ if (argc == 3) {
+ /* 'addr' only, file size from environment */
+ if ((addr = simple_strtoul(argv[2], NULL, 16)) == 0UL)
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+ if (argc > 3) {
+ /* 'addr', 'size', and possibly more */
+ if ((addr = simple_strtoul(argv[2], NULL, 16)) == 0UL)
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+
+ if ((cnt = simple_strtoul(argv[3], NULL, 16)) == 0UL)
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+
+ if (addr == 0 || cnt == 0) {
+ puts("Invalid arguments to nand bootupdate!\n");
+ return 1;
+ }
+
+ if (mxs_do_nand_bootupdate(addr, cnt)) {
+ puts("NAND bootupdate failed!\n");
+ return 1;
+ }
+
+ puts("NAND bootupdate successful\n");
+ return 0;
+ }
+#endif
+
usage:
return CMD_RET_USAGE;
}
@@ -798,6 +848,17 @@ static char nand_help_text[] =
" 'addr', skipping bad blocks and dropping any pages at the end\n"
" of eraseblocks that contain only 0xFF\n"
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ "nand bootupdate - [addr] [size]\n"
+ " write U-Boot into NAND in board/SoC specific manner creating all\n"
+ " required headers and other bits and pieces as required for the\n"
+ " system to be able to boot off of NAND. 'addr' is the address\n"
+ " where U-Boot image has been loaded at, 'size' is its size.\n"
+ " If any of 'addr'/'size' is missing it is taken from environment\n"
+ " for the last file loaded. U-Boot image must be of a proper type\n"
+ " for the target platform (only IMX image supported at the moment)\n"
+ " binary without U-Boot image headers (e.g. u-boot.imx file.)\n"
+#endif
"nand erase[.spread] [clean] off size - erase 'size' bytes "
"from offset 'off'\n"
" With '.spread', erase enough for given file size, otherwise,\n"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1df9273..b1b98dc 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o
obj-$(CONFIG_NAND_PLAT) += nand_plat.o
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
+obj-$(CONFIG_CMD_NAND_BOOTUPDATE) += mxs_nand_bootupdate.o
else # minimal SPL drivers
diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c
index 9200544..333ee09 100644
--- a/drivers/mtd/nand/mxs_nand.c
+++ b/drivers/mtd/nand/mxs_nand.c
@@ -26,6 +26,7 @@
#include <asm/imx-common/regs-gpmi.h>
#include <asm/arch/sys_proto.h>
#include <asm/imx-common/dma.h>
+#include <asm/imx-common/mxs_nand.h>
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
@@ -150,7 +151,7 @@ static uint32_t mxs_nand_aux_status_offset(void)
return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3;
}
-static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
uint32_t page_oob_size)
{
int ecc_strength;
@@ -226,14 +227,14 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size,
return block_mark_bit_offset;
}
-static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3;
}
-static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
diff --git a/drivers/mtd/nand/mxs_nand_bootupdate.c b/drivers/mtd/nand/mxs_nand_bootupdate.c
new file mode 100644
index 0000000..89323fb
--- /dev/null
+++ b/drivers/mtd/nand/mxs_nand_bootupdate.c
@@ -0,0 +1,556 @@
+/*
+ * mxs_nand_bootupdate.c - write U-Boot to MXS NAND to make it bootable
+ *
+ * Copyright (c) 2016 Sergey Kubushyn <ksi(a)koi8.net>
+ *
+ * Most of the source shamelesly stolen from barebox.
+ *
+ * Here is the original copyright:
+ *
+ *=== Cut ===
+ * Copyright (C) 2014 Sascha Hauer, Pengutronix
+ *
+ * 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.
+ *=== Cut ===
+ */
+
+
+#include <common.h>
+#include <linux/sizes.h>
+#include <linux/mtd/mtd.h>
+#include <linux/compat.h>
+#include <command.h>
+#include <console.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <jffs2/jffs2.h>
+#include <nand.h>
+#include <errno.h>
+#include <asm/imx-common/mxs_nand.h>
+#include <asm/imx-common/imximage.cfg>
+
+
+#ifndef CONFIG_CMD_MTDPARTS
+#error "CONFIG_CMD_MTDPARTS is not set, mtd partition support missing"
+#endif
+
+static const char *uboot_tgt = "nand0,0";
+
+/* partition handling routines */
+int mtdparts_init(void);
+int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+ u8 *part_num, struct part_info **part);
+
+struct dbbt_block {
+ uint32_t Checksum; /* reserved on i.MX6 */
+ uint32_t FingerPrint;
+ uint32_t Version;
+ uint32_t numberBB; /* reserved on i.MX6 */
+ uint32_t DBBTNumOfPages;
+};
+
+struct fcb_block {
+ uint32_t Checksum; /* First fingerprint in first byte */
+ uint32_t FingerPrint; /* 2nd fingerprint at byte 4 */
+ uint32_t Version; /* 3rd fingerprint at byte 8 */
+ uint8_t DataSetup;
+ uint8_t DataHold;
+ uint8_t AddressSetup;
+ uint8_t DSAMPLE_TIME;
+ /* These are for application use only and not for ROM. */
+ uint8_t NandTimingState;
+ uint8_t REA;
+ uint8_t RLOH;
+ uint8_t RHOH;
+ uint32_t PageDataSize; /* 2048 for 2K pages, 4096 for 4K pages */
+ uint32_t TotalPageSize; /* 2112 for 2K pages, 4314 for 4K pages */
+ uint32_t SectorsPerBlock; /* Number of 2K sections per block */
+ uint32_t NumberOfNANDs; /* Total Number of NANDs - not used by ROM */
+ uint32_t TotalInternalDie; /* Number of separate chips in this NAND */
+ uint32_t CellType; /* MLC or SLC */
+ uint32_t EccBlockNEccType; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0Size; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSize; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccType; /* Ecc level for Block 0 - BCH */
+ uint32_t MetadataBytes; /* Metadata size - BCH */
+ uint32_t NumEccBlocksPerPage; /* Number of blocks per page for ROM use - BCH */
+ uint32_t EccBlockNEccLevelSDK; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0SizeSDK; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSizeSDK; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccLevelSDK; /* Ecc level for Block 0 - BCH */
+ uint32_t NumEccBlocksPerPageSDK;/* Number of blocks per page for SDK use - BCH */
+ uint32_t MetadataBytesSDK; /* Metadata size - BCH */
+ uint32_t EraseThreshold; /* To set into BCH_MODE register */
+ uint32_t BootPatch; /* 0 for normal boot and 1 to load patch starting next to FCB */
+ uint32_t PatchSectors; /* Size of patch in sectors */
+ uint32_t Firmware1_startingPage;/* Firmware image starts on this sector */
+ uint32_t Firmware2_startingPage;/* Secondary FW Image starting Sector */
+ uint32_t PagesInFirmware1; /* Number of sectors in firmware image */
+ uint32_t PagesInFirmware2; /* Number of sector in secondary FW image */
+ uint32_t DBBTSearchAreaStartAddress; /* Page address where dbbt search area begins */
+ uint32_t BadBlockMarkerByte; /* Byte in page data that have manufacturer marked bad block marker, */
+ /* this will be swapped with metadata[0] to complete page data. */
+ uint32_t BadBlockMarkerStartBit;/* For BCH ECC sizes other than 8 and 16 the bad block marker does not */
+ /* start at 0th bit of BadBlockMarkerByte. This field is used to get to */
+ /* the start bit of bad block marker byte with in BadBlockMarkerByte */
+ uint32_t BBMarkerPhysicalOffset;/* FCB value that gives byte offset for bad block marker on physical NAND page */
+ uint32_t BCHType;
+
+ uint32_t TMTiming2_ReadLatency;
+ uint32_t TMTiming2_PreambleDelay;
+ uint32_t TMTiming2_CEDelay;
+ uint32_t TMTiming2_PostambleDelay;
+ uint32_t TMTiming2_CmdAddPause;
+ uint32_t TMTiming2_DataPause;
+ uint32_t TMSpeed;
+ uint32_t TMTiming1_BusyTimeout;
+
+ uint32_t DISBBM; /* the flag to enable (1)/disable(0) bi swap */
+ uint32_t BBMarkerPhysicalOffsetInSpareData; /* The swap position of main area in spare area */
+};
+
+struct fw_write_data {
+ int fw1_blk;
+ int fw2_blk;
+ int part_blks;
+ void *buf;
+ size_t len;
+};
+
+#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET)
+#define GETBIT(v,n) (((v) >> (n)) & 0x1)
+
+
+static uint8_t calculate_parity_13_8(uint8_t d)
+{
+ uint8_t p = 0;
+
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^ GETBIT(d, 1)) << 1;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 2;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
+ return p;
+}
+
+static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
+{
+ int i;
+ uint8_t *src = _src;
+ uint8_t *ecc = _ecc;
+
+ for (i = 0; i < size; i++)
+ ecc[i] = calculate_parity_13_8(src[i]);
+}
+
+static uint32_t calc_chksum(void *buf, size_t size)
+{
+ u32 chksum = 0;
+ u8 *bp = buf;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ chksum += bp[i];
+
+ return ~chksum;
+}
+
+
+static ssize_t raw_write_page(struct mtd_info *mtd, void *buf, loff_t offset)
+{
+ struct mtd_oob_ops ops;
+ ssize_t ret;
+
+ ops.mode = MTD_OPS_RAW;
+ ops.ooboffs = 0;
+ ops.datbuf = buf;
+ ops.len = mtd->writesize;
+ ops.oobbuf = buf + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ret = mtd_write_oob(mtd, offset, &ops);
+
+ return ret;
+}
+
+static int fcb_create(struct fcb_block *fcb, struct mtd_info *mtd)
+{
+ fcb->FingerPrint = 0x20424346;
+ fcb->Version = 0x01000000;
+ fcb->PageDataSize = mtd->writesize;
+ fcb->TotalPageSize = mtd->writesize + mtd->oobsize;
+ fcb->SectorsPerBlock = mtd->erasesize / mtd->writesize;
+
+ /* Divide ECC strength by two and save the value into FCB structure. */
+ fcb->EccBlock0EccType =
+ mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1;
+
+ fcb->EccBlockNEccType = fcb->EccBlock0EccType;
+
+ /* Also hardcoded in kobs-ng */
+ fcb->EccBlock0Size = 0x00000200;
+ fcb->EccBlockNSize = 0x00000200;
+ fcb->DataSetup = 80;
+ fcb->DataHold = 60;
+ fcb->AddressSetup = 25;
+ fcb->DSAMPLE_TIME = 6;
+ fcb->MetadataBytes = 10;
+
+ /* DBBT search area starts at second page on first block */
+ fcb->DBBTSearchAreaStartAddress = 1;
+
+ fcb->BadBlockMarkerByte = mxs_nand_mark_byte_offset(mtd);
+ fcb->BadBlockMarkerStartBit = mxs_nand_mark_bit_offset(mtd);
+
+ fcb->BBMarkerPhysicalOffset = mtd->writesize;
+
+ fcb->NumEccBlocksPerPage = mtd->writesize / fcb->EccBlock0Size - 1;
+
+ fcb->Checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
+
+ return 0;
+}
+
+/* Erase entire U-Boot partition */
+static int mxs_nand_uboot_erase(struct mtd_info *mtd, struct part_info *part)
+{
+ uint64_t offset = 0;
+ struct erase_info erase;
+ int len = part->size;
+ int ret;
+
+ while (len > 0) {
+ pr_debug("erasing at 0x%08llx\n", offset);
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("erase skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ continue;
+ }
+
+ memset(&erase, 0, sizeof(erase));
+ erase.addr = offset;
+ erase.len = mtd->erasesize;
+
+ ret = mtd_erase(mtd, &erase);
+ if (ret)
+ return ret;
+
+ offset += mtd->erasesize;
+ len -= mtd->erasesize;
+ }
+
+ return 0;
+}
+
+/* Write the U-Boot proper (2 copies) to where it belongs. */
+/* This is U-Boot binary image itself, no FCB/DBBT here yet. */
+static int mxs_nand_uboot_write_fw(struct mtd_info *mtd, struct fw_write_data *fw)
+{
+ uint64_t offset;
+ int ret;
+ int blk;
+ size_t dummy;
+ size_t bytes_left;
+ int chunk;
+ void *p;
+
+ bytes_left = fw->len;
+ p = fw->buf;
+ blk = fw->fw1_blk;
+ offset = blk * mtd->erasesize;
+
+ while (bytes_left > 0) {
+ chunk = min(bytes_left, mtd->erasesize);
+
+ pr_debug("fw1: writing %p at 0x%08llx, left 0x%08x\n",
+ p, offset, bytes_left);
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("fw1: write skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ blk++;
+ continue;
+ }
+
+ if (blk >= fw->fw2_blk)
+ return -ENOSPC;
+
+ ret = mtd_write(mtd, offset, chunk, &dummy, p);
+ if (ret)
+ return ret;
+
+ offset += chunk;
+ bytes_left -= chunk;
+ p += chunk;
+ blk++;
+ }
+
+ bytes_left = fw->len;
+ p = fw->buf;
+ blk = fw->fw2_blk;
+ offset = blk * mtd->erasesize;
+
+ while (bytes_left > 0) {
+ chunk = min(bytes_left, mtd->erasesize);
+
+ pr_debug("fw2: writing %p at 0x%08llx, left 0x%08x\n",
+ p, offset, bytes_left);
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("fw2: write skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ blk++;
+ continue;
+ }
+
+ if (blk >= fw->part_blks)
+ return -ENOSPC;
+
+ ret = mtd_write(mtd, offset, chunk, &dummy, p);
+ if (ret)
+ return ret;
+
+ offset += chunk;
+ bytes_left -= chunk;
+ p += chunk;
+ blk++;
+ }
+
+ return 0;
+}
+
+static int dbbt_data_create(struct mtd_info *mtd, void *buf, int num_blocks)
+{
+ int n;
+ int n_bad_blocks = 0;
+ uint32_t *bb = buf + 0x8;
+ uint32_t *n_bad_blocksp = buf + 0x4;
+
+ for (n = 0; n < num_blocks; n++) {
+ loff_t offset = n * mtd->erasesize;
+ if (mtd_block_isbad(mtd, offset)) {
+ n_bad_blocks++;
+ *bb = n;
+ bb++;
+ }
+ }
+
+ *n_bad_blocksp = n_bad_blocks;
+
+ return n_bad_blocks;
+}
+
+/********************************************************************/
+/* This is where it is all done. Takes pointer to a U-Boot image in */
+/* RAM and image size, creates FCB/DBBT and writes everything where */
+/* it belongs into NAND. Image must be an IMX image built for NAND. */
+/********************************************************************/
+static int mxs_nand_uboot_update(const void *img, size_t len)
+{
+ int i, ret;
+
+ size_t dummy;
+ loff_t offset = 0;
+
+ void *fcb_raw_page;
+ void *dbbt_page;
+ void *dbbt_data_page;
+ void *ecc;
+
+ uint32_t num_blocks_fcb_dbbt;
+ uint32_t num_blocks_fw;
+
+ struct mtd_info *mtd;
+ struct fcb_block *fcb;
+ struct dbbt_block *dbbt;
+
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 pnum;
+
+ struct fw_write_data fw;
+
+ if ((mtdparts_init() == 0) &&
+ (find_dev_and_part(uboot_tgt, &dev, &pnum, &part) == 0)) {
+ if (dev->id->type != MTD_DEV_TYPE_NAND) {
+ puts("Not a NAND device\n");
+ return -ENODEV;
+ }
+ }
+
+ nand_curr_device = dev->id->num;
+
+#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
+ board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
+#endif
+
+ /* Get a pointer to mtd_info for selected device */
+
+ mtd = get_mtd_device_nm("nand0"); /* We always boot off of nand0 */
+
+ if (IS_ERR(mtd)) {
+ /* Should not happen */
+ puts("No nand0 device...\n");
+ return -ENODEV;
+ }
+
+ put_mtd_device(mtd);
+
+ /* Quick and dirty check if we have 2Mbytes of good blocks in nand0,0 */
+ /* Not sure if it is needed at all but won't hurt so here it is... */
+
+ i = 0;
+ offset = 0; /* It is the first partition so it starts at block 0 */
+
+ while (offset < part->size) {
+ if (!mtd_block_isbad(mtd, offset)) {
+ i += mtd->erasesize;
+ }
+ offset += mtd->erasesize;
+ }
+
+ if (i < SZ_2M) {
+ puts("Partition too small for U-Boot!\n");
+ return -EINVAL;
+ }
+
+ /* We will use 4 first blocks for FCB/DBBT copies. */
+ /* The rest of partition is split in half and used */
+ /* for two U-Boot copies. We don't care if those */
+ /* start on good or bad block - bad blocks will be */
+ /* skipped on write, ROM boot code will also skip */
+ /* bad blocks on bootup when loading U-Boot image. */
+
+ fw.part_blks = part->size / mtd->erasesize;
+ num_blocks_fcb_dbbt = 4;
+ num_blocks_fw = (fw.part_blks - num_blocks_fcb_dbbt) / 2;
+ fw.fw1_blk = num_blocks_fcb_dbbt;
+ fw.fw2_blk = fw.fw1_blk + num_blocks_fw;
+
+ /* OK, now create FCB structure for bootROM NAND boot */
+
+ fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+
+ fcb = fcb_raw_page + 12;
+ ecc = fcb_raw_page + 512 + 12;
+
+ dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ dbbt = dbbt_page;
+
+ /* Write one additional page to make the ROM happy. */
+ /* Maybe the PagesInFirmwarex fields are really the */
+ /* number of pages - 1. kobs-ng does the same. */
+
+ fw.len = ALIGN(len + FLASH_OFFSET_STANDARD + mtd->writesize, mtd->writesize);
+ fw.buf = kzalloc(fw.len, GFP_KERNEL);
+ memcpy(fw.buf + FLASH_OFFSET_STANDARD, img, len);
+
+ /* Erase entire partition */
+ ret = mxs_nand_uboot_erase(mtd, part);
+ if (ret)
+ goto out;
+
+ /* Now write 2 copies of the U-Boot proper to where they belong. */
+ /* Headers (FCB, DBBT) will be generated and written after that. */
+ ret = mxs_nand_uboot_write_fw(mtd, &fw);
+ if (ret < 0)
+ goto out;
+
+ /* Create FCB, calculate ECC (we don't/can't use hardware ECC */
+ /* here so we do it ourselves and then write _RAW_ pages. */
+
+ fcb->Firmware1_startingPage = fw.fw1_blk * mtd->erasesize / mtd->writesize;
+ fcb->Firmware2_startingPage = fw.fw2_blk * mtd->erasesize / mtd->writesize;
+ fcb->PagesInFirmware1 =
+ ALIGN(len + FLASH_OFFSET_STANDARD, mtd->writesize) / mtd->writesize;
+ fcb->PagesInFirmware2 = fcb->PagesInFirmware1;
+
+ fcb_create(fcb, mtd);
+ encode_hamming_13_8(fcb, ecc, 512);
+
+ /*
+ * Set the first and second byte of OOB data to 0xFF, not 0x00. These
+ * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since
+ * the FCB is mostly written to the first page in a block, a scan for
+ * factory bad blocks will detect these blocks as bad, e.g. when
+ * function nand_scan_bbt() is executed to build a new bad block table.
+ * We will _NOT_ mark a bad block as good -- we skip the bad blocks.
+ */
+ memset(fcb_raw_page + mtd->writesize, 0xff, 2);
+
+ /* Now create DBBT */
+ dbbt->Checksum = 0;
+ dbbt->FingerPrint = 0x54424244;
+ dbbt->Version = 0x01000000;
+
+ if ((ret = dbbt_data_create(mtd, dbbt_data_page, fw.part_blks)) < 0)
+ goto out;
+
+ if (ret > 0)
+ dbbt->DBBTNumOfPages = 1;
+
+ offset = 0;
+
+ if (mtd_block_isbad(mtd, offset)) {
+ puts("Block 0 is bad, NAND unusable\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Write FCB/DBBT to first 4 blocks. Skip bad blocks if any. */
+ /* Less than 4 copies will be written if there were BBs !!! */
+ for (i = 0; i < 4; i++) {
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_err("Block %d is bad, skipped\n", i);
+ continue;
+ }
+
+
+ ret = raw_write_page(mtd, fcb_raw_page, mtd->erasesize * i);
+ if (ret)
+ goto out;
+
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
+ mtd->writesize, &dummy, dbbt_page);
+ if (ret)
+ goto out;
+
+ /* DBBTNumOfPages == 0 if no bad blocks */
+ if (dbbt->DBBTNumOfPages > 0) {
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize * 5,
+ mtd->writesize, &dummy, dbbt_data_page);
+ if (ret)
+ goto out;
+ }
+ }
+
+out:
+ kfree(dbbt_page);
+ kfree(dbbt_data_page);
+ kfree(fcb_raw_page);
+ kfree(fw.buf);
+
+ return ret;
+}
+
+
+int mxs_do_nand_bootupdate(ulong addr, size_t len)
+{
+ /* KSI: Unlock NAND first if it is locked... */
+
+ return mxs_nand_uboot_update((const void *)addr, len);
+}
--
2.5.5
1
0

[U-Boot] [PATCH v2] Added nand bootupdate command for i.MX6 and similar systems
by Sergey Kubushyn 08 Oct '16
by Sergey Kubushyn 08 Oct '16
08 Oct '16
This one adds nand_bootupdate command for i.MX6 and similar MCUs. It
generates proper NAND boot structures (FCB, DBBT, etc) and writes those
along with U-Boot imx image to where they belong so system would be able
to boot off of raw NAND.
As of now the only way to do it is by using user-space kobs-ng utility
that has lots of unnecessary bells and whistles and only runs from
Linux so it is impossible to update raw NAND U-Boot from U-Boot itself.
It does not give any choice for the actual data placement in NAND but
that is done deliberately -- there is no reason to put it anywhere but
the only location i.MX6 Boot ROM expects it to be.
This is my FOURTH attempt to get it into the main U-Boot tree. I do
really hope it would get applied before I retire.
Signed-off-by: Sergey Kubushyn <ksi(a)koi8.net>
Cc: Scott Wood <oss(a)buserror.net>
Cc: Stefano Babic <sbabic(a)denx.de>
---
Changes for v2:
- Renamed some functions to more appropriate names
- Files moved to where they belong
- Code cleanup
- Converted to Kconfig
arch/arm/include/asm/imx-common/mxs_nand.h | 37 ++
cmd/Kconfig | 10 +
cmd/nand.c | 61 ++++
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/mxs_nand.c | 7 +-
drivers/mtd/nand/mxs_nand_bootupdate.c | 556 +++++++++++++++++++++++++++++
6 files changed, 669 insertions(+), 3 deletions(-)
create mode 100644 arch/arm/include/asm/imx-common/mxs_nand.h
create mode 100644 drivers/mtd/nand/mxs_nand_bootupdate.c
diff --git a/arch/arm/include/asm/imx-common/mxs_nand.h b/arch/arm/include/asm/imx-common/mxs_nand.h
new file mode 100644
index 0000000..7826a9f
--- /dev/null
+++ b/arch/arm/include/asm/imx-common/mxs_nand.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 PHYTEC Messtechnik GmbH,
+ * Author: Stefan Christ <s.christ at phytec.de>
+ *
+ * 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.
+ */
+
+#ifndef __NAND_MXS_H
+#define __NAND_MXS_H
+
+/*
+ * Functions are definied in drivers/mtd/nand/nand_mxs.c. They are used to
+ * calculate the ECC Strength, BadBlockMarkerByte and BadBlockMarkerStartBit
+ * which are placed into the FCB structure. The i.MX6 ROM needs these
+ * parameters to read the firmware from NAND.
+ *
+ * The parameters depends on the pagesize and oobsize of NAND chips and are
+ * different for each combination. To avoid placing hardcoded values in the bbu
+ * update handler code, the generic calculation from the driver code is used.
+ */
+
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+ uint32_t page_oob_size);
+
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd);
+
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd);
+
+#endif /* __NAND_MXS_H */
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 86554ea..6ef7253 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -571,6 +571,16 @@ config CMD_TIME
help
Run commands and summarize execution time.
+config CMD_NAND_BOOTUPDATE
+ bool "Update NAND bootloader on iMX6 and its brethen"
+ depends on ARCH_MX6 && NAND_BOOT && CMD_NAND
+ help
+ Having iMX6 NAND U-Boot image in memory creates all necessary
+ structures and writes those where they belong along with that
+ U-Boot image so system is able to boot off of raw NAND. Kinda
+ like kobs-ng utility but simpler.
+
+
# TODO: rename to CMD_SLEEP
config CMD_MISC
bool "sleep"
diff --git a/cmd/nand.c b/cmd/nand.c
index c16ec77..c25f1ef 100644
--- a/cmd/nand.c
+++ b/cmd/nand.c
@@ -38,6 +38,11 @@ int find_dev_and_part(const char *id, struct mtd_device **dev,
u8 *part_num, struct part_info **part);
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+/* This comes from a separate file in drivers/mtd/nand */
+int mxs_do_nand_bootupdate(ulong addr, size_t len);
+#endif
+
static int nand_dump(struct mtd_info *mtd, ulong off, int only_oob,
int repeat)
{
@@ -372,6 +377,9 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
loff_t off, size, maxsize;
char *cmd, *s;
struct mtd_info *mtd;
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ size_t cnt;
+#endif
#ifdef CONFIG_SYS_NAND_QUIET
int quiet = CONFIG_SYS_NAND_QUIET;
#else
@@ -777,6 +785,48 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ if (strncmp(cmd, "bootupdate", 10) == 0) {
+
+ if (argc < 3) {
+ /* All default values */
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+ if (argc == 3) {
+ /* 'addr' only, file size from environment */
+ if ((addr = simple_strtoul(argv[2], NULL, 16)) == 0UL)
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+ if (argc > 3) {
+ /* 'addr', 'size', and possibly more */
+ if ((addr = simple_strtoul(argv[2], NULL, 16)) == 0UL)
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+
+ if ((cnt = simple_strtoul(argv[3], NULL, 16)) == 0UL)
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+
+ if (addr == 0 || cnt == 0) {
+ puts("Invalid arguments to nand bootupdate!\n");
+ return 1;
+ }
+
+ if (mxs_do_nand_bootupdate(addr, cnt)) {
+ puts("NAND bootupdate failed!\n");
+ return 1;
+ }
+
+ puts("NAND bootupdate successful\n");
+ return 0;
+ }
+#endif
+
usage:
return CMD_RET_USAGE;
}
@@ -798,6 +848,17 @@ static char nand_help_text[] =
" 'addr', skipping bad blocks and dropping any pages at the end\n"
" of eraseblocks that contain only 0xFF\n"
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ "nand bootupdate - [addr] [size]\n"
+ " write U-Boot into NAND in board/SoC specific manner creating all\n"
+ " required headers and other bits and pieces as required for the\n"
+ " system to be able to boot off of NAND. 'addr' is the address\n"
+ " where U-Boot image has been loaded at, 'size' is its size.\n"
+ " If any of 'addr'/'size' is missing it is taken from environment\n"
+ " for the last file loaded. U-Boot image must be of a proper type\n"
+ " for the target platform (only IMX image supported at the moment)\n"
+ " binary without U-Boot image headers (e.g. u-boot.imx file.)\n"
+#endif
"nand erase[.spread] [clean] off size - erase 'size' bytes "
"from offset 'off'\n"
" With '.spread', erase enough for given file size, otherwise,\n"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1df9273..b1b98dc 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o
obj-$(CONFIG_NAND_PLAT) += nand_plat.o
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
+obj-$(CONFIG_CMD_NAND_BOOTUPDATE) += mxs_nand_bootupdate.o
else # minimal SPL drivers
diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c
index 9200544..333ee09 100644
--- a/drivers/mtd/nand/mxs_nand.c
+++ b/drivers/mtd/nand/mxs_nand.c
@@ -26,6 +26,7 @@
#include <asm/imx-common/regs-gpmi.h>
#include <asm/arch/sys_proto.h>
#include <asm/imx-common/dma.h>
+#include <asm/imx-common/mxs_nand.h>
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
@@ -150,7 +151,7 @@ static uint32_t mxs_nand_aux_status_offset(void)
return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3;
}
-static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
uint32_t page_oob_size)
{
int ecc_strength;
@@ -226,14 +227,14 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size,
return block_mark_bit_offset;
}
-static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3;
}
-static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
diff --git a/drivers/mtd/nand/mxs_nand_bootupdate.c b/drivers/mtd/nand/mxs_nand_bootupdate.c
new file mode 100644
index 0000000..9442553
--- /dev/null
+++ b/drivers/mtd/nand/mxs_nand_bootupdate.c
@@ -0,0 +1,556 @@
+/*
+ * mxs_nand_bootupdate.c - write U-Boot to MXS NAND to make it bootable
+ *
+ * Copyright (c) 2016 Sergey Kubushyn <ksi(a)koi8.net>
+ *
+ * Most of the source shamelesly stolen from barebox.
+ *
+ * Here is the original copyright:
+ *
+ *=== Cut ===
+ * Copyright (C) 2014 Sascha Hauer, Pengutronix
+ *
+ * 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.
+ *=== Cut ===
+ */
+
+
+#include <common.h>
+#include <linux/sizes.h>
+#include <linux/mtd/mtd.h>
+#include <linux/compat.h>
+#include <command.h>
+#include <console.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <jffs2/jffs2.h>
+#include <nand.h>
+#include <errno.h>
+#include <asm/imx-common/mxs_nand.h>
+#include <asm/imx-common/imximage.cfg>
+
+
+#ifndef CONFIG_CMD_MTDPARTS
+#error "CONFIG_CMD_MTDPARTS is not set, mtd partition support missing"
+#endif
+
+static const char *uboot_tgt = "nand0,0";
+
+/* partition handling routines */
+int mtdparts_init(void);
+int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+ u8 *part_num, struct part_info **part);
+
+struct dbbt_block {
+ uint32_t Checksum; /* reserved on i.MX6 */
+ uint32_t FingerPrint;
+ uint32_t Version;
+ uint32_t numberBB; /* reserved on i.MX6 */
+ uint32_t DBBTNumOfPages;
+};
+
+struct fcb_block {
+ uint32_t Checksum; /* First fingerprint in first byte */
+ uint32_t FingerPrint; /* 2nd fingerprint at byte 4 */
+ uint32_t Version; /* 3rd fingerprint at byte 8 */
+ uint8_t DataSetup;
+ uint8_t DataHold;
+ uint8_t AddressSetup;
+ uint8_t DSAMPLE_TIME;
+ /* These are for application use only and not for ROM. */
+ uint8_t NandTimingState;
+ uint8_t REA;
+ uint8_t RLOH;
+ uint8_t RHOH;
+ uint32_t PageDataSize; /* 2048 for 2K pages, 4096 for 4K pages */
+ uint32_t TotalPageSize; /* 2112 for 2K pages, 4314 for 4K pages */
+ uint32_t SectorsPerBlock; /* Number of 2K sections per block */
+ uint32_t NumberOfNANDs; /* Total Number of NANDs - not used by ROM */
+ uint32_t TotalInternalDie; /* Number of separate chips in this NAND */
+ uint32_t CellType; /* MLC or SLC */
+ uint32_t EccBlockNEccType; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0Size; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSize; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccType; /* Ecc level for Block 0 - BCH */
+ uint32_t MetadataBytes; /* Metadata size - BCH */
+ uint32_t NumEccBlocksPerPage; /* Number of blocks per page for ROM use - BCH */
+ uint32_t EccBlockNEccLevelSDK; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0SizeSDK; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSizeSDK; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccLevelSDK; /* Ecc level for Block 0 - BCH */
+ uint32_t NumEccBlocksPerPageSDK;/* Number of blocks per page for SDK use - BCH */
+ uint32_t MetadataBytesSDK; /* Metadata size - BCH */
+ uint32_t EraseThreshold; /* To set into BCH_MODE register */
+ uint32_t BootPatch; /* 0 for normal boot and 1 to load patch starting next to FCB */
+ uint32_t PatchSectors; /* Size of patch in sectors */
+ uint32_t Firmware1_startingPage;/* Firmware image starts on this sector */
+ uint32_t Firmware2_startingPage;/* Secondary FW Image starting Sector */
+ uint32_t PagesInFirmware1; /* Number of sectors in firmware image */
+ uint32_t PagesInFirmware2; /* Number of sector in secondary FW image */
+ uint32_t DBBTSearchAreaStartAddress; /* Page address where dbbt search area begins */
+ uint32_t BadBlockMarkerByte; /* Byte in page data that have manufacturer marked bad block marker, */
+ /* this will be swapped with metadata[0] to complete page data. */
+ uint32_t BadBlockMarkerStartBit;/* For BCH ECC sizes other than 8 and 16 the bad block marker does not */
+ /* start at 0th bit of BadBlockMarkerByte. This field is used to get to */
+ /* the start bit of bad block marker byte with in BadBlockMarkerByte */
+ uint32_t BBMarkerPhysicalOffset;/* FCB value that gives byte offset for bad block marker on physical NAND page */
+ uint32_t BCHType;
+
+ uint32_t TMTiming2_ReadLatency;
+ uint32_t TMTiming2_PreambleDelay;
+ uint32_t TMTiming2_CEDelay;
+ uint32_t TMTiming2_PostambleDelay;
+ uint32_t TMTiming2_CmdAddPause;
+ uint32_t TMTiming2_DataPause;
+ uint32_t TMSpeed;
+ uint32_t TMTiming1_BusyTimeout;
+
+ uint32_t DISBBM; /* the flag to enable (1)/disable(0) bi swap */
+ uint32_t BBMarkerPhysicalOffsetInSpareData; /* The swap position of main area in spare area */
+};
+
+struct fw_write_data {
+ int fw1_blk;
+ int fw2_blk;
+ int part_blks;
+ void *buf;
+ size_t len;
+};
+
+#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET)
+#define GETBIT(v,n) (((v) >> (n)) & 0x1)
+
+
+static uint8_t calculate_parity_13_8(uint8_t d)
+{
+ uint8_t p = 0;
+
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^ GETBIT(d, 1)) << 1;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 2;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
+ return p;
+}
+
+static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
+{
+ int i;
+ uint8_t *src = _src;
+ uint8_t *ecc = _ecc;
+
+ for (i = 0; i < size; i++)
+ ecc[i] = calculate_parity_13_8(src[i]);
+}
+
+static uint32_t calc_chksum(void *buf, size_t size)
+{
+ u32 chksum = 0;
+ u8 *bp = buf;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ chksum += bp[i];
+
+ return ~chksum;
+}
+
+
+static ssize_t raw_write_page(struct mtd_info *mtd, void *buf, loff_t offset)
+{
+ struct mtd_oob_ops ops;
+ ssize_t ret;
+
+ ops.mode = MTD_OPS_RAW;
+ ops.ooboffs = 0;
+ ops.datbuf = buf;
+ ops.len = mtd->writesize;
+ ops.oobbuf = buf + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ret = mtd_write_oob(mtd, offset, &ops);
+
+ return ret;
+}
+
+static int fcb_create(struct fcb_block *fcb, struct mtd_info *mtd)
+{
+ fcb->FingerPrint = 0x20424346;
+ fcb->Version = 0x01000000;
+ fcb->PageDataSize = mtd->writesize;
+ fcb->TotalPageSize = mtd->writesize + mtd->oobsize;
+ fcb->SectorsPerBlock = mtd->erasesize / mtd->writesize;
+
+ /* Divide ECC strength by two and save the value into FCB structure. */
+ fcb->EccBlock0EccType =
+ mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1;
+
+ fcb->EccBlockNEccType = fcb->EccBlock0EccType;
+
+ /* Also hardcoded in kobs-ng */
+ fcb->EccBlock0Size = 0x00000200;
+ fcb->EccBlockNSize = 0x00000200;
+ fcb->DataSetup = 80;
+ fcb->DataHold = 60;
+ fcb->AddressSetup = 25;
+ fcb->DSAMPLE_TIME = 6;
+ fcb->MetadataBytes = 10;
+
+ /* DBBT search area starts at second page on first block */
+ fcb->DBBTSearchAreaStartAddress = 1;
+
+ fcb->BadBlockMarkerByte = mxs_nand_mark_byte_offset(mtd);
+ fcb->BadBlockMarkerStartBit = mxs_nand_mark_bit_offset(mtd);
+
+ fcb->BBMarkerPhysicalOffset = mtd->writesize;
+
+ fcb->NumEccBlocksPerPage = mtd->writesize / fcb->EccBlock0Size - 1;
+
+ fcb->Checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
+
+ return 0;
+}
+
+/* Erase entire U-Boot partition */
+static int mxs_nand_uboot_erase(struct mtd_info *mtd, struct part_info *part)
+{
+ uint64_t offset = 0;
+ struct erase_info erase;
+ int len = part->size;
+ int ret;
+
+ while (len > 0) {
+ pr_debug("erasing at 0x%08llx\n", offset);
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("erase skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ continue;
+ }
+
+ memset(&erase, 0, sizeof(erase));
+ erase.addr = offset;
+ erase.len = mtd->erasesize;
+
+ ret = mtd_erase(mtd, &erase);
+ if (ret)
+ return ret;
+
+ offset += mtd->erasesize;
+ len -= mtd->erasesize;
+ }
+
+ return 0;
+}
+
+/* Write the U-Boot proper (2 copies) to where it belongs. */
+/* This is U-Boot binary image itself, no FCB/DBBT here yet. */
+static int mxs_nand_uboot_write_fw(struct mtd_info *mtd, struct fw_write_data *fw)
+{
+ uint64_t offset;
+ int ret;
+ int blk;
+ size_t dummy;
+ size_t bytes_left;
+ int chunk;
+ void *p;
+
+ bytes_left = fw->len;
+ p = fw->buf;
+ blk = fw->fw1_blk;
+ offset = blk * mtd->erasesize;
+
+ while (bytes_left > 0) {
+ chunk = min(bytes_left, mtd->erasesize);
+
+ pr_debug("fw1: writing %p at 0x%08llx, left 0x%08x\n",
+ p, offset, bytes_left);
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("fw1: write skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ blk++;
+ continue;
+ }
+
+ if (blk >= fw->fw2_blk)
+ return -ENOSPC;
+
+ ret = mtd_write(mtd, offset, chunk, &dummy, p);
+ if (ret)
+ return ret;
+
+ offset += chunk;
+ bytes_left -= chunk;
+ p += chunk;
+ blk++;
+ }
+
+ bytes_left = fw->len;
+ p = fw->buf;
+ blk = fw->fw2_blk;
+ offset = blk * mtd->erasesize;
+
+ while (bytes_left > 0) {
+ chunk = min(bytes_left, mtd->erasesize);
+
+ pr_debug("fw2: writing %p at 0x%08llx, left 0x%08x\n",
+ p, offset, bytes_left);
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("fw2: write skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ blk++;
+ continue;
+ }
+
+ if (blk >= fw->part_blks)
+ return -ENOSPC;
+
+ ret = mtd_write(mtd, offset, chunk, &dummy, p);
+ if (ret)
+ return ret;
+
+ offset += chunk;
+ bytes_left -= chunk;
+ p += chunk;
+ blk++;
+ }
+
+ return 0;
+}
+
+static int dbbt_data_create(struct mtd_info *mtd, void *buf, int num_blocks)
+{
+ int n;
+ int n_bad_blocks = 0;
+ uint32_t *bb = buf + 0x8;
+ uint32_t *n_bad_blocksp = buf + 0x4;
+
+ for (n = 0; n < num_blocks; n++) {
+ loff_t offset = n * mtd->erasesize;
+ if (mtd_block_isbad(mtd, offset)) {
+ n_bad_blocks++;
+ *bb = n;
+ bb++;
+ }
+ }
+
+ *n_bad_blocksp = n_bad_blocks;
+
+ return n_bad_blocks;
+}
+
+/********************************************************************/
+/* This is where it is all done. Takes pointer to a U-Boot image in */
+/* RAM and image size, creates FCB/DBBT and writes everything where */
+/* it belongs into NAND. Image must be an IMX image built for NAND. */
+/********************************************************************/
+static int mxs_nand_uboot_update(const void *img, size_t len)
+{
+ int i, ret;
+
+ size_t dummy;
+ loff_t offset = 0;
+
+ void *fcb_raw_page;
+ void *dbbt_page;
+ void *dbbt_data_page;
+ void *ecc;
+
+ uint32_t num_blocks_fcb_dbbt;
+ uint32_t num_blocks_fw;
+
+ struct mtd_info *mtd;
+ struct fcb_block *fcb;
+ struct dbbt_block *dbbt;
+
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 pnum;
+
+ struct fw_write_data fw;
+
+ if ((mtdparts_init() == 0) &&
+ (find_dev_and_part(uboot_tgt, &dev, &pnum, &part) == 0)) {
+ if (dev->id->type != MTD_DEV_TYPE_NAND) {
+ puts("Not a NAND device\n");
+ return -ENODEV;
+ }
+ }
+
+ nand_curr_device = dev->id->num;
+
+#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
+ board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
+#endif
+
+ /* Get a pointer to mtd_info for selected device */
+
+ mtd = get_mtd_device_nm("nand0"); /* We always boot off of nand0 */
+
+ if (IS_ERR(mtd)) {
+ /* Should not happen */
+ puts("No nand0 device...\n");
+ return -ENODEV;
+ }
+
+ put_mtd_device(mtd);
+
+ /* Quick and dirty check if we have 2Mbytes of good blocks in nand0,0 */
+ /* Not sure if it is needed at all but won't hurt so here it is... */
+
+ i = 0;
+ offset = 0; /* It is the first partition so it starts at block 0 */
+
+ while (offset < part->size) {
+ if (!mtd_block_isbad(mtd, offset)) {
+ i += mtd->erasesize;
+ }
+ offset += mtd->erasesize;
+ }
+
+ if (i < SZ_2M) {
+ puts("Partition too small for U-Boot!\n");
+ return -EINVAL;
+ }
+
+ /* We will use 4 first blocks for FCB/DBBT copies. */
+ /* The rest of partition is split in half and used */
+ /* for two U-Boot copies. We don't care if those */
+ /* start on good or bad block - bad blocks will be */
+ /* skipped on write, ROM boot code will also skip */
+ /* bad blocks on bootup when loading U-Boot image. */
+
+ fw.part_blks = part->size / mtd->erasesize;
+ num_blocks_fcb_dbbt = 4;
+ num_blocks_fw = (fw.part_blks - num_blocks_fcb_dbbt) / 2;
+ fw.fw1_blk = num_blocks_fcb_dbbt;
+ fw.fw2_blk = fw.fw1_blk + num_blocks_fw;
+
+ /* OK, now create FCB structure for bootROM NAND boot */
+
+ fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+
+ fcb = fcb_raw_page + 12;
+ ecc = fcb_raw_page + 512 + 12;
+
+ dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ dbbt = dbbt_page;
+
+ /* Write one additional page to make the ROM happy. */
+ /* Maybe the PagesInFirmwarex fields are really the */
+ /* number of pages - 1. kobs-ng does the same. */
+
+ fw.len = ALIGN(len + FLASH_OFFSET_STANDARD + mtd->writesize, mtd->writesize);
+ fw.buf = kzalloc(fw.len, GFP_KERNEL);
+ memcpy(fw.buf + FLASH_OFFSET_STANDARD, img, len);
+
+ /* Erase entire partition */
+ ret = mxs_nand_uboot_erase(mtd, part);
+ if (ret)
+ goto out;
+
+ /* Now write 2 copies of the U-Boot proper to where they belong. */
+ /* Headers (FCB, DBBT) will be generated and written after that. */
+ ret = mxs_nand_uboot_write_fw(mtd, &fw);
+ if (ret < 0)
+ goto out;
+
+ /* Create FCB, calculate ECC (we don't/can't use hardware ECC */
+ /* here so we do it ourselves and then write _RAW_ pages. */
+
+ fcb->Firmware1_startingPage = fw.fw1_blk * mtd->erasesize / mtd->writesize;
+ fcb->Firmware2_startingPage = fw.fw2_blk * mtd->erasesize / mtd->writesize;
+ fcb->PagesInFirmware1 =
+ ALIGN(len + FLASH_OFFSET_STANDARD, mtd->writesize) / mtd->writesize;
+ fcb->PagesInFirmware2 = fcb->PagesInFirmware1;
+
+ fcb_create(fcb, mtd);
+ encode_hamming_13_8(fcb, ecc, 512);
+
+ /*
+ * Set the first and second byte of OOB data to 0xFF, not 0x00. These
+ * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since
+ * the FCB is mostly written to the first page in a block, a scan for
+ * factory bad blocks will detect these blocks as bad, e.g. when
+ * function nand_scan_bbt() is executed to build a new bad block table.
+ * We will _NOT_ mark a bad block as good -- we skip the bad blocks.
+ */
+ memset(fcb_raw_page + mtd->writesize, 0xff, 2);
+
+ /* Now create DBBT */
+ dbbt->Checksum = 0;
+ dbbt->FingerPrint = 0x54424244;
+ dbbt->Version = 0x01000000;
+
+ if ((ret = dbbt_data_create(mtd, dbbt_data_page, fw.part_blks)) < 0)
+ goto out;
+
+ if (ret > 0)
+ dbbt->DBBTNumOfPages = 1;
+
+ offset = 0;
+
+ if (mtd_block_isbad(mtd, offset)) {
+ puts("Block 0 is bad, NAND unusable\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Write FCB/DBBT to first 4 blocks. Skip bad blocks if any. */
+ /* Less than 4 copies will be written if there were BBs !!! */
+ for (i = 0; i < 4; i++) {
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_err("Block %d is bad, skipped\n", i);
+ continue;
+ }
+
+
+ ret = raw_write_page(mtd, fcb_raw_page, mtd->erasesize * i);
+ if (ret)
+ goto out;
+
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
+ mtd->writesize, &dummy, dbbt_page);
+ if (ret)
+ goto out;
+
+ /* DBBTNumOfPages == 0 if no bad blocks */
+ if (dbbt->DBBTNumOfPages > 0) {
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize * 5,
+ mtd->writesize, &dummy, dbbt_data_page);
+ if (ret)
+ goto out;
+ }
+ }
+
+out:
+ kfree(dbbt_page);
+ kfree(dbbt_data_page);
+ kfree(fcb_raw_page);
+ kfree(fw.buf);
+
+ return ret;
+}
+
+
+int mxs_do_nand_bootupdate(ulong addr, size_t len)
+{
+ /* KSI: Unlock NAND first if it is locked... */
+
+ return mxs_nand_uboot_update((const void *)addr, len);
+}
--
2.5.5
---
******************************************************************
* KSI@home KOI8 Net < > The impossible we do immediately. *
* Las Vegas NV, USA < > Miracles require 24-hour notice. *
******************************************************************
2
5
Support driver model for ehci mx6 driver.
Consolidate code to be shared between DM and non-DM, such as
introducing ehci_mx6_common_init.
For simplicity, some old fasion code are keeped for DM usage,
such as board_ehci_power and board_usb_phy_mode. And 'dr-mode',
usbphy and vbus handling code for DM is not added now.
These will be added in future patches.
Signed-off-by: Peng Fan <peng.fan(a)nxp.com>
Cc: Marek Vasut <marex(a)denx.de>
Cc: Mateusz Kulikowski <mateusz.kulikowski(a)gmail.com>
Cc: Stefan Agner <stefan(a)agner.ch>
Cc: Simon Glass <sjg(a)chromium.org>
---
drivers/usb/host/ehci-mx6.c | 144 +++++++++++++++++++++++++++++++++++++++-----
1 file changed, 130 insertions(+), 14 deletions(-)
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c
index 922ef29..83364e1 100644
--- a/drivers/usb/host/ehci-mx6.c
+++ b/drivers/usb/host/ehci-mx6.c
@@ -15,6 +15,7 @@
#include <asm/arch/imx-regs.h>
#include <asm/arch/clock.h>
#include <asm/imx-common/iomux-v3.h>
+#include <dm.h>
#include "ehci.h"
@@ -303,6 +304,30 @@ int __weak board_ehci_power(int port, int on)
return 0;
}
+int ehci_mx6_common_init(struct usb_ehci *ehci, int index)
+{
+ int ret;
+
+ enable_usboh3_clk(1);
+ mdelay(1);
+
+ /* Do board specific initialization */
+ ret = board_ehci_hcd_init(index);
+ if (ret)
+ return ret;
+
+ usb_power_config(index);
+ usb_oc_config(index);
+
+#if defined(CONFIG_MX6)
+ usb_internal_phy_clock_gate(index, 1);
+ usb_phy_enable(index, ehci);
+#endif
+
+ return 0;
+}
+
+#ifndef CONFIG_DM_USB
int ehci_hcd_init(int index, enum usb_init_type init,
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
{
@@ -318,26 +343,18 @@ int ehci_hcd_init(int index, enum usb_init_type init,
if (index > 3)
return -EINVAL;
- enable_usboh3_clk(1);
- mdelay(1);
- /* Do board specific initialization */
- ret = board_ehci_hcd_init(index);
+ ret = ehci_mx6_common_init(ehci, index);
if (ret)
return ret;
- usb_power_config(index);
- usb_oc_config(index);
-
-#if defined(CONFIG_MX6)
- usb_internal_phy_clock_gate(index, 1);
- usb_phy_enable(index, ehci);
-#endif
type = board_usb_phy_mode(index);
- *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
- *hcor = (struct ehci_hcor *)((uint32_t)*hccr +
- HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
+ if (hccr && hcor) {
+ *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
+ *hcor = (struct ehci_hcor *)((uint32_t)*hccr +
+ HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
+ }
if ((type == init) || (type == USB_INIT_DEVICE))
board_ehci_power(index, (type == USB_INIT_DEVICE) ? 0 : 1);
@@ -359,3 +376,102 @@ int ehci_hcd_stop(int index)
{
return 0;
}
+#else
+struct ehci_mx6_priv_data {
+ struct ehci_ctrl ctrl;
+ struct usb_ehci *ehci;
+ enum usb_init_type init_type;
+ int portnr;
+};
+
+static int mx6_init_after_reset(struct ehci_ctrl *dev)
+{
+ struct ehci_mx6_priv_data *priv = dev->priv;
+ enum usb_init_type type = priv->init_type;
+ struct usb_ehci *ehci = priv->ehci;
+ int ret;
+
+ ret = ehci_mx6_common_init(priv->ehci, priv->portnr);
+ if (ret)
+ return ret;
+
+ board_ehci_power(priv->portnr, (type == USB_INIT_DEVICE) ? 0 : 1);
+
+ if (type == USB_INIT_DEVICE)
+ return 0;
+
+ setbits_le32(&ehci->usbmode, CM_HOST);
+ writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
+ setbits_le32(&ehci->portsc, USB_EN);
+
+ mdelay(10);
+
+ return 0;
+}
+
+static const struct ehci_ops mx6_ehci_ops = {
+ .init_after_reset = mx6_init_after_reset
+};
+
+static int ehci_usb_probe(struct udevice *dev)
+{
+ struct usb_platdata *plat = dev_get_platdata(dev);
+ struct usb_ehci *ehci = (struct usb_ehci *)dev_get_addr(dev);
+ struct ehci_mx6_priv_data *priv = dev_get_priv(dev);
+ struct ehci_hccr *hccr;
+ struct ehci_hcor *hcor;
+ int ret;
+
+ priv->ehci = ehci;
+ priv->portnr = dev->seq;
+ priv->init_type = plat->init_type;
+
+ ret = ehci_mx6_common_init(ehci, priv->portnr);
+ if (ret)
+ return ret;
+
+ board_ehci_power(priv->portnr, (priv->init_type == USB_INIT_DEVICE) ? 0 : 1);
+
+ if (priv->init_type == USB_INIT_HOST) {
+ setbits_le32(&ehci->usbmode, CM_HOST);
+ writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
+ setbits_le32(&ehci->portsc, USB_EN);
+ }
+
+ mdelay(10);
+
+ hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
+ hcor = (struct ehci_hcor *)((uint32_t)hccr +
+ HC_LENGTH(ehci_readl(&(hccr)->cr_capbase)));
+
+ return ehci_register(dev, hccr, hcor, &mx6_ehci_ops, 0, priv->init_type);
+}
+
+static int ehci_usb_remove(struct udevice *dev)
+{
+ int ret;
+
+ ret = ehci_deregister(dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct udevice_id mx6_usb_ids[] = {
+ { .compatible = "fsl,imx27-usb" },
+ { }
+};
+
+U_BOOT_DRIVER(usb_mx6) = {
+ .name = "ehci_mx6",
+ .id = UCLASS_USB,
+ .of_match = mx6_usb_ids,
+ .probe = ehci_usb_probe,
+ .remove = ehci_usb_remove,
+ .ops = &ehci_usb_ops,
+ .platdata_auto_alloc_size = sizeof(struct usb_platdata),
+ .priv_auto_alloc_size = sizeof(struct ehci_mx6_priv_data),
+ .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
+#endif
--
2.6.2
4
5
Add plugin support for imximage.
Define CONFIG_USE_IMXIMG_PLUGIN in defconfig to enable using plugin.
Signed-off-by: Peng Fan <peng.fan(a)nxp.com>
Cc: Stefano Babic <sbabic(a)denx.de>
Cc: Eric Nelson <eric(a)nelint.com>
Cc: Troy Kisky <troy.kisky(a)boundarydevices.com>
Cc: Ye Li <ye.li(a)nxp.com>
---
V2:
Drop the CONFIG_USE_PLUGIN, make plugin always support in imximage.
tools/imximage.c | 282 +++++++++++++++++++++++++++++++++++++++++++------------
tools/imximage.h | 8 +-
2 files changed, 230 insertions(+), 60 deletions(-)
diff --git a/tools/imximage.c b/tools/imximage.c
index 092d550..fefc129 100644
--- a/tools/imximage.c
+++ b/tools/imximage.c
@@ -27,6 +27,7 @@ static table_entry_t imximage_cmds[] = {
{CMD_CHECK_BITS_CLR, "CHECK_BITS_CLR", "Reg Check bits clr", },
{CMD_CSF, "CSF", "Command Sequence File", },
{CMD_IMAGE_VERSION, "IMAGE_VERSION", "image version", },
+ {CMD_PLUGIN, "PLUGIN", "file plugin_addr", },
{-1, "", "", },
};
@@ -80,6 +81,9 @@ static uint32_t imximage_ivt_offset = UNDEFINED;
static uint32_t imximage_csf_size = UNDEFINED;
/* Initial Load Region Size */
static uint32_t imximage_init_loadsize;
+static uint32_t imximage_iram_free_start;
+static uint32_t imximage_plugin_size;
+static uint32_t plugin_image;
static set_dcd_val_t set_dcd_val;
static set_dcd_param_t set_dcd_param;
@@ -118,7 +122,11 @@ static uint32_t detect_imximage_version(struct imx_header *imx_hdr)
/* Try to detect V2 */
if ((fhdr_v2->header.tag == IVT_HEADER_TAG) &&
- (hdr_v2->dcd_table.header.tag == DCD_HEADER_TAG))
+ (hdr_v2->data.dcd_table.header.tag == DCD_HEADER_TAG))
+ return IMXIMAGE_V2;
+
+ if ((fhdr_v2->header.tag == IVT_HEADER_TAG) &&
+ hdr_v2->boot_data.plugin)
return IMXIMAGE_V2;
return IMXIMAGE_VER_INVALID;
@@ -165,7 +173,7 @@ static struct dcd_v2_cmd *gd_last_cmd;
static void set_dcd_param_v2(struct imx_header *imxhdr, uint32_t dcd_len,
int32_t cmd)
{
- dcd_v2_t *dcd_v2 = &imxhdr->header.hdr_v2.dcd_table;
+ dcd_v2_t *dcd_v2 = &imxhdr->header.hdr_v2.data.dcd_table;
struct dcd_v2_cmd *d = gd_last_cmd;
struct dcd_v2_cmd *d2;
int len;
@@ -261,21 +269,23 @@ static void set_dcd_rst_v1(struct imx_header *imxhdr, uint32_t dcd_len,
static void set_dcd_rst_v2(struct imx_header *imxhdr, uint32_t dcd_len,
char *name, int lineno)
{
- dcd_v2_t *dcd_v2 = &imxhdr->header.hdr_v2.dcd_table;
- struct dcd_v2_cmd *d = gd_last_cmd;
- int len;
-
- if (!d)
- d = &dcd_v2->dcd_cmd;
- len = be16_to_cpu(d->write_dcd_command.length);
- if (len > 4)
- d = (struct dcd_v2_cmd *)(((char *)d) + len);
-
- len = (char *)d - (char *)&dcd_v2->header;
-
- dcd_v2->header.tag = DCD_HEADER_TAG;
- dcd_v2->header.length = cpu_to_be16(len);
- dcd_v2->header.version = DCD_VERSION;
+ if (!imxhdr->header.hdr_v2.boot_data.plugin) {
+ dcd_v2_t *dcd_v2 = &imxhdr->header.hdr_v2.data.dcd_table;
+ struct dcd_v2_cmd *d = gd_last_cmd;
+ int len;
+
+ if (!d)
+ d = &dcd_v2->dcd_cmd;
+ len = be16_to_cpu(d->write_dcd_command.length);
+ if (len > 4)
+ d = (struct dcd_v2_cmd *)(((char *)d) + len);
+
+ len = (char *)d - (char *)&dcd_v2->header;
+
+ dcd_v2->header.tag = DCD_HEADER_TAG;
+ dcd_v2->header.length = cpu_to_be16(len);
+ dcd_v2->header.version = DCD_VERSION;
+ }
}
static void set_imx_hdr_v1(struct imx_header *imxhdr, uint32_t dcd_len,
@@ -317,24 +327,93 @@ static void set_imx_hdr_v2(struct imx_header *imxhdr, uint32_t dcd_len,
fhdr_v2->header.length = cpu_to_be16(sizeof(flash_header_v2_t));
fhdr_v2->header.version = IVT_VERSION; /* 0x40 */
- fhdr_v2->entry = entry_point;
- fhdr_v2->reserved1 = fhdr_v2->reserved2 = 0;
- hdr_base = entry_point - imximage_init_loadsize +
- flash_offset;
- fhdr_v2->self = hdr_base;
- if (dcd_len > 0)
- fhdr_v2->dcd_ptr = hdr_base
- + offsetof(imx_header_v2_t, dcd_table);
- else
+ if (!hdr_v2->boot_data.plugin) {
+ fhdr_v2->entry = entry_point;
+ fhdr_v2->reserved1 = 0;
+ fhdr_v2->reserved1 = 0;
+ hdr_base = entry_point - imximage_init_loadsize +
+ flash_offset;
+ fhdr_v2->self = hdr_base;
+ if (dcd_len > 0)
+ fhdr_v2->dcd_ptr = hdr_base +
+ offsetof(imx_header_v2_t, data);
+ else
+ fhdr_v2->dcd_ptr = 0;
+ fhdr_v2->boot_data_ptr = hdr_base
+ + offsetof(imx_header_v2_t, boot_data);
+ hdr_v2->boot_data.start = entry_point - imximage_init_loadsize;
+
+ fhdr_v2->csf = 0;
+
+ header_size_ptr = &hdr_v2->boot_data.size;
+ csf_ptr = &fhdr_v2->csf;
+ } else {
+ imx_header_v2_t *next_hdr_v2;
+ flash_header_v2_t *next_fhdr_v2;
+
+ if (imximage_csf_size != 0) {
+ fprintf(stderr, "Error: Header v2: SECURE_BOOT is only supported in DCD mode!");
+ exit(EXIT_FAILURE);
+ }
+
+ fhdr_v2->entry = imximage_iram_free_start +
+ flash_offset + sizeof(flash_header_v2_t) +
+ sizeof(boot_data_t);
+
+ fhdr_v2->reserved1 = 0;
+ fhdr_v2->reserved2 = 0;
+ fhdr_v2->self = imximage_iram_free_start + flash_offset;
+
fhdr_v2->dcd_ptr = 0;
- fhdr_v2->boot_data_ptr = hdr_base
- + offsetof(imx_header_v2_t, boot_data);
- hdr_v2->boot_data.start = entry_point - imximage_init_loadsize;
- fhdr_v2->csf = 0;
+ fhdr_v2->boot_data_ptr = fhdr_v2->self +
+ offsetof(imx_header_v2_t, boot_data);
+
+ hdr_v2->boot_data.start = imximage_iram_free_start;
+ /*
+ * The actural size of plugin image is "imximage_plugin_size +
+ * sizeof(flash_header_v2_t) + sizeof(boot_data_t)", plus the
+ * flash_offset space.The ROM code only need to copy this size
+ * to run the plugin code. However, later when copy the whole
+ * U-Boot image to DDR, the ROM code use memcpy to copy the
+ * first part of the image, and use the storage read function
+ * to get the remaining part. This requires the dividing point
+ * must be multiple of storage sector size. Here we set the
+ * first section to be 16KB for this purpose.
+ */
+ hdr_v2->boot_data.size = MAX_PLUGIN_CODE_SIZE;
+
+ /* Security feature are not supported */
+ fhdr_v2->csf = 0;
+
+ next_hdr_v2 = (imx_header_v2_t *)((char *)hdr_v2 +
+ imximage_plugin_size);
+
+ next_fhdr_v2 = &next_hdr_v2->fhdr;
+
+ next_fhdr_v2->header.tag = IVT_HEADER_TAG; /* 0xD1 */
+ next_fhdr_v2->header.length =
+ cpu_to_be16(sizeof(flash_header_v2_t));
+ next_fhdr_v2->header.version = IVT_VERSION; /* 0x40 */
+
+ next_fhdr_v2->entry = entry_point;
+ hdr_base = entry_point - sizeof(struct imx_header);
+ next_fhdr_v2->reserved1 = 0;
+ next_fhdr_v2->reserved2 = 0;
+ next_fhdr_v2->self = hdr_base + imximage_plugin_size;
+
+ next_fhdr_v2->dcd_ptr = 0;
+ next_fhdr_v2->boot_data_ptr = next_fhdr_v2->self +
+ offsetof(imx_header_v2_t, boot_data);
+
+ next_hdr_v2->boot_data.start = hdr_base - flash_offset;
+
+ header_size_ptr = &next_hdr_v2->boot_data.size;
- header_size_ptr = &hdr_v2->boot_data.size;
- csf_ptr = &fhdr_v2->csf;
+ next_hdr_v2->boot_data.plugin = 0;
+
+ next_fhdr_v2->csf = 0;
+ }
}
static void set_hdr_func(void)
@@ -393,16 +472,19 @@ static void print_hdr_v2(struct imx_header *imx_hdr)
{
imx_header_v2_t *hdr_v2 = &imx_hdr->header.hdr_v2;
flash_header_v2_t *fhdr_v2 = &hdr_v2->fhdr;
- dcd_v2_t *dcd_v2 = &hdr_v2->dcd_table;
- uint32_t size, version;
+ dcd_v2_t *dcd_v2 = &hdr_v2->data.dcd_table;
+ uint32_t size, version, plugin;
- size = be16_to_cpu(dcd_v2->header.length);
- if (size > (MAX_HW_CFG_SIZE_V2 * sizeof(dcd_addr_data_t)) + 8) {
- fprintf(stderr,
- "Error: Image corrupt DCD size %d exceed maximum %d\n",
- (uint32_t)(size / sizeof(dcd_addr_data_t)),
- MAX_HW_CFG_SIZE_V2);
- exit(EXIT_FAILURE);
+ plugin = hdr_v2->boot_data.plugin;
+ if (!plugin) {
+ size = be16_to_cpu(dcd_v2->header.length) - 8;
+ if (size > (MAX_HW_CFG_SIZE_V2 * sizeof(dcd_addr_data_t))) {
+ fprintf(stderr,
+ "Error: Image corrupt DCD size %d exceed maximum %d\n",
+ (uint32_t)(size / sizeof(dcd_addr_data_t)),
+ MAX_HW_CFG_SIZE_V2);
+ exit(EXIT_FAILURE);
+ }
}
version = detect_imximage_version(imx_hdr);
@@ -410,19 +492,81 @@ static void print_hdr_v2(struct imx_header *imx_hdr)
printf("Image Type: Freescale IMX Boot Image\n");
printf("Image Ver: %x", version);
printf("%s\n", get_table_entry_name(imximage_versions, NULL, version));
- printf("Data Size: ");
- genimg_print_size(hdr_v2->boot_data.size);
- printf("Load Address: %08x\n", (uint32_t)fhdr_v2->boot_data_ptr);
- printf("Entry Point: %08x\n", (uint32_t)fhdr_v2->entry);
- if (fhdr_v2->csf && (imximage_ivt_offset != UNDEFINED) &&
- (imximage_csf_size != UNDEFINED)) {
- printf("HAB Blocks: %08x %08x %08x\n",
- (uint32_t)fhdr_v2->self, 0,
- hdr_v2->boot_data.size - imximage_ivt_offset -
- imximage_csf_size);
+ printf("Mode: %s\n", plugin ? "PLUGIN" : "DCD");
+ if (!plugin) {
+ printf("Data Size: ");
+ genimg_print_size(hdr_v2->boot_data.size);
+ printf("Load Address: %08x\n", (uint32_t)fhdr_v2->boot_data_ptr);
+ printf("Entry Point: %08x\n", (uint32_t)fhdr_v2->entry);
+ if (fhdr_v2->csf && (imximage_ivt_offset != UNDEFINED) &&
+ (imximage_csf_size != UNDEFINED)) {
+ printf("HAB Blocks: %08x %08x %08x\n",
+ (uint32_t)fhdr_v2->self, 0,
+ hdr_v2->boot_data.size - imximage_ivt_offset -
+ imximage_csf_size);
+ }
+ } else {
+ imx_header_v2_t *next_hdr_v2;
+ flash_header_v2_t *next_fhdr_v2;
+
+ /*First Header*/
+ printf("Plugin Data Size: ");
+ genimg_print_size(hdr_v2->boot_data.size);
+ printf("Plugin Code Size: ");
+ genimg_print_size(imximage_plugin_size);
+ printf("Plugin Load Address: %08x\n", hdr_v2->boot_data.start);
+ printf("Plugin Entry Point: %08x\n", (uint32_t)fhdr_v2->entry);
+
+ /*Second Header*/
+ next_hdr_v2 = (imx_header_v2_t *)((char *)hdr_v2 +
+ imximage_plugin_size);
+ next_fhdr_v2 = &next_hdr_v2->fhdr;
+ printf("U-Boot Data Size: ");
+ genimg_print_size(next_hdr_v2->boot_data.size);
+ printf("U-Boot Load Address: %08x\n",
+ next_hdr_v2->boot_data.start);
+ printf("U-Boot Entry Point: %08x\n",
+ (uint32_t)next_fhdr_v2->entry);
}
}
+static void copy_plugin_code(struct imx_header *imxhdr, char *plugin_file)
+{
+ int ifd = -1;
+ struct stat sbuf;
+ char *plugin_buf = imxhdr->header.hdr_v2.data.plugin_code;
+ char *ptr;
+
+ ifd = open(plugin_file, O_RDONLY|O_BINARY);
+ if (fstat(ifd, &sbuf) < 0) {
+ fprintf(stderr, "Can't stat %s: %s\n",
+ plugin_file,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, ifd, 0);
+ if (ptr == MAP_FAILED) {
+ fprintf(stderr, "Can't read %s: %s\n",
+ plugin_file,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (sbuf.st_size > MAX_PLUGIN_CODE_SIZE) {
+ printf("plugin binary size too large\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memcpy(plugin_buf, ptr, sbuf.st_size);
+ imximage_plugin_size = sbuf.st_size;
+
+ (void) munmap((void *)ptr, sbuf.st_size);
+ (void) close(ifd);
+
+ imxhdr->header.hdr_v2.boot_data.plugin = 1;
+}
+
static void parse_cfg_cmd(struct imx_header *imxhdr, int32_t cmd, char *token,
char *name, int lineno, int fld, int dcd_len)
{
@@ -497,6 +641,10 @@ static void parse_cfg_cmd(struct imx_header *imxhdr, int32_t cmd, char *token,
if (unlikely(cmd_ver_first != 1))
cmd_ver_first = 0;
break;
+ case CMD_PLUGIN:
+ plugin_image = 1;
+ copy_plugin_code(imxhdr, token);
+ break;
}
}
@@ -542,6 +690,10 @@ static void parse_cfg_fld(struct imx_header *imxhdr, int32_t *cmd,
}
}
break;
+ case CMD_PLUGIN:
+ value = get_cfg_value(token, name, lineno);
+ imximage_iram_free_start = value;
+ break;
default:
break;
}
@@ -649,6 +801,7 @@ static void imximage_set_header(void *ptr, struct stat *sbuf, int ifd,
{
struct imx_header *imxhdr = (struct imx_header *)ptr;
uint32_t dcd_len;
+ uint32_t header_size;
/*
* In order to not change the old imx cfg file
@@ -665,10 +818,15 @@ static void imximage_set_header(void *ptr, struct stat *sbuf, int ifd,
dcd_len = parse_cfg_file(imxhdr, params->imagename);
if (imximage_version == IMXIMAGE_V2) {
- if (imximage_init_loadsize < imximage_ivt_offset +
- sizeof(imx_header_v2_t))
+ header_size = sizeof(flash_header_v2_t) + sizeof(boot_data_t);
+ if (!plugin_image)
+ header_size += sizeof(dcd_v2_t);
+ else
+ header_size += MAX_PLUGIN_CODE_SIZE;
+
+ if (imximage_init_loadsize < imximage_ivt_offset + header_size)
imximage_init_loadsize = imximage_ivt_offset +
- sizeof(imx_header_v2_t);
+ header_size;
}
/* Set the imx header */
@@ -721,7 +879,7 @@ static int imximage_generate(struct image_tool_params *params,
size_t alloc_len;
struct stat sbuf;
char *datafile = params->datafile;
- uint32_t pad_len;
+ uint32_t pad_len, header_size;
memset(&imximage_header, 0, sizeof(imximage_header));
@@ -742,15 +900,21 @@ static int imximage_generate(struct image_tool_params *params,
/* TODO: check i.MX image V1 handling, for now use 'old' style */
if (imximage_version == IMXIMAGE_V1) {
alloc_len = 4096;
+ header_size = 4096;
} else {
- if (imximage_init_loadsize < imximage_ivt_offset +
- sizeof(imx_header_v2_t))
+ header_size = sizeof(flash_header_v2_t) + sizeof(boot_data_t);
+ if (!plugin_image)
+ header_size += sizeof(dcd_v2_t);
+ else
+ header_size += MAX_PLUGIN_CODE_SIZE;
+
+ if (imximage_init_loadsize < imximage_ivt_offset + header_size)
imximage_init_loadsize = imximage_ivt_offset +
- sizeof(imx_header_v2_t);
+ header_size;
alloc_len = imximage_init_loadsize - imximage_ivt_offset;
}
- if (alloc_len < sizeof(struct imx_header)) {
+ if (alloc_len < header_size) {
fprintf(stderr, "%s: header error\n",
params->cmdname);
exit(EXIT_FAILURE);
diff --git a/tools/imximage.h b/tools/imximage.h
index c7b9b5c..c636d9d 100644
--- a/tools/imximage.h
+++ b/tools/imximage.h
@@ -8,7 +8,9 @@
#ifndef _IMXIMAGE_H_
#define _IMXIMAGE_H_
+#include <config.h>
#define MAX_HW_CFG_SIZE_V2 220 /* Max number of registers imx can set for v2 */
+#define MAX_PLUGIN_CODE_SIZE (16*1024)
#define MAX_HW_CFG_SIZE_V1 60 /* Max number of registers imx can set for v1 */
#define APP_CODE_BARKER 0xB1
#define DCD_BARKER 0xB17219E9
@@ -64,6 +66,7 @@ enum imximage_cmd {
CMD_CHECK_BITS_SET,
CMD_CHECK_BITS_CLR,
CMD_CSF,
+ CMD_PLUGIN,
};
enum imximage_fld_types {
@@ -164,7 +167,10 @@ typedef struct {
typedef struct {
flash_header_v2_t fhdr;
boot_data_t boot_data;
- dcd_v2_t dcd_table;
+ union {
+ dcd_v2_t dcd_table;
+ char plugin_code[MAX_PLUGIN_CODE_SIZE];
+ } data;
} imx_header_v2_t;
/* The header must be aligned to 4k on MX53 for NAND boot */
--
2.6.2
5
19
Hello,
I just tested v2016.11-rc1 on STM32F7 Discovery and it hangs:
(gdb) c
Continuing.
^C
Program received signal SIGTRAP, Trace/breakpoint trap.
0x08000d56 in dram_init () at board/st/stm32f746-disco/stm32f746-disco.c:211
211 FMC_BUSY_WAIT();
(gdb) bt
#0 0x08000d56 in dram_init () at board/st/stm32f746-disco/stm32f746-disco.c:211
#1 0x0800d75a in initcall_run_list
(init_sequence=init_sequence@entry=0x801b434 <init_sequence_f>) at
lib/initcall.c:31
#2 0x0800614e in board_init_f (boot_flags=<optimized out>) at
common/board_f.c:1081
#3 0x0800046a in _main () at arch/arm/lib/crt0.S:93
#4 0x0800046a in _main () at arch/arm/lib/crt0.S:93
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
It prints this:
U-Boot 2016.11-rc1 (Oct 04 2016 - 19:53:31 +0300)
DRAM: 8 MiB
or this:
U-Boot 2016.11-rc1 (Oct 04 2016 - 19:53:31 +0300)
DRAM:
Reverting 25c1b1353ce4b8188de6058f9f3b0d5d2dad8230 (not desirable)
makes it working again but I can't seem to write anything to the
console.
Offtopic: How are you developing for this board?
I'm using gcc 5_4-2016q3 from [0], st-util [1] which exports a
/dev/ttyACM0 UART port I'm using at 115200n8 and gdb connected to
st-util gdbserver.
Best regards,
Nicolae Rosia
[0] https://github.com/texane/stlink
[1] https://launchpad.net/gcc-arm-embedded
2
1

08 Oct '16
This one adds nand_bootupdate command for i.MX6 and similar MCUs. It
generates proper NAND boot structures (FCB, DBBT, etc) and writes those
along with U-Boot mx image to where they belong so system would be able
to boot off of raw NAND.
As of now the only way to do it is by using user-space kobs-ng utility
that has lots of unnecessary bells and whistles and only runs from
Linux so it is impossible to update raw NAND U-Boot from U-Boot itself.
It does not give any choice for the actual data placement in NAND but
that is done deliberately -- there is no reason to put it anywhere but
the only location i.MX6 Boot ROM expects it to be.
This is my THIRD attempt to get it into the main U-Boot tree. I do
really hope it would get applied before I retire.
Signed-off-by: Sergey Kubushyn <ksi(a)koi8.net>
---
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -571,6 +571,16 @@ config CMD_TIME
help
Run commands and summarize execution time.
+config CMD_NAND_BOOTUPDATE
+ bool "Update NAND bootloader on iMX6 and its brethen"
+ depends on ARCH_MX6 && NAND_BOOT && CMD_NAND
+ help
+ Having iMX6 NAND U-Boot image in memory creates all necessary
+ structures and writes those where they belong along with that
+ U-Boot image so system is able to boot off of raw NAND. Kinda
+ like kobs-ng utility but simpler.
+
+
# TODO: rename to CMD_SLEEP
config CMD_MISC
bool "sleep"
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o
obj-$(CONFIG_NAND_PLAT) += nand_plat.o
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
+obj-$(CONFIG_CMD_NAND_BOOTUPDATE) += mxs_nand_bootupdate.o
else # minimal SPL drivers
--- a/drivers/mtd/nand/mxs_nand.c
+++ b/drivers/mtd/nand/mxs_nand.c
@@ -26,6 +26,7 @@
#include <asm/imx-common/regs-gpmi.h>
#include <asm/arch/sys_proto.h>
#include <asm/imx-common/dma.h>
+#include <asm/imx-common/mxs_nand.h>
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
@@ -150,7 +151,7 @@ static uint32_t mxs_nand_aux_status_offset(void)
return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3;
}
-static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
uint32_t page_oob_size)
{
int ecc_strength;
@@ -226,14 +227,14 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size,
return block_mark_bit_offset;
}
-static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3;
}
-static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
--- /dev/null
+++ b/arch/arm/include/asm/imx-common/mxs_nand.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 PHYTEC Messtechnik GmbH,
+ * Author: Stefan Christ <s.christ at phytec.de>
+ *
+ * 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.
+ */
+
+#ifndef __NAND_MXS_H
+#define __NAND_MXS_H
+
+/*
+ * Functions are definied in drivers/mtd/nand/nand_mxs.c. They are used to
+ * calculate the ECC Strength, BadBlockMarkerByte and BadBlockMarkerStartBit
+ * which are placed into the FCB structure. The i.MX6 ROM needs these
+ * parameters to read the firmware from NAND.
+ *
+ * The parameters depends on the pagesize and oobsize of NAND chips and are
+ * different for each combination. To avoid placing hardcoded values in the bbu
+ * update handler code, the generic calculation from the driver code is used.
+ */
+
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+ uint32_t page_oob_size);
+
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd);
+
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd);
+
+#endif /* __NAND_MXS_H */
--- /dev/null
+++ b/drivers/mtd/nand/mxs_nand_bootupdate.c
@@ -0,0 +1,556 @@
+/*
+ * mxs_nand_bootupdate.c - write U-Boot to MXS NAND to make it bootable
+ *
+ * Copyright (c) 2016 Sergey Kubushyn <ksi(a)koi8.net>
+ *
+ * Most of the source shamelesly stolen from barebox.
+ *
+ * Here is the original copyright:
+ *
+ *=== Cut ===
+ * Copyright (C) 2014 Sascha Hauer, Pengutronix
+ *
+ * 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.
+ *=== Cut ===
+ */
+
+
+#include <common.h>
+#include <linux/sizes.h>
+#include <linux/mtd/mtd.h>
+#include <linux/compat.h>
+#include <command.h>
+#include <console.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <jffs2/jffs2.h>
+#include <nand.h>
+#include <errno.h>
+#include <asm/imx-common/mxs_nand.h>
+#include <asm/imx-common/imximage.cfg>
+
+
+#ifndef CONFIG_CMD_MTDPARTS
+#error "CONFIG_CMD_MTDPARTS is not set, mtd partition support missing"
+#endif
+
+static const char *uboot_tgt = "nand0,0";
+
+/* partition handling routines */
+int mtdparts_init(void);
+int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+ u8 *part_num, struct part_info **part);
+
+struct dbbt_block {
+ uint32_t Checksum; /* reserved on i.MX6 */
+ uint32_t FingerPrint;
+ uint32_t Version;
+ uint32_t numberBB; /* reserved on i.MX6 */
+ uint32_t DBBTNumOfPages;
+};
+
+struct fcb_block {
+ uint32_t Checksum; /* First fingerprint in first byte */
+ uint32_t FingerPrint; /* 2nd fingerprint at byte 4 */
+ uint32_t Version; /* 3rd fingerprint at byte 8 */
+ uint8_t DataSetup;
+ uint8_t DataHold;
+ uint8_t AddressSetup;
+ uint8_t DSAMPLE_TIME;
+ /* These are for application use only and not for ROM. */
+ uint8_t NandTimingState;
+ uint8_t REA;
+ uint8_t RLOH;
+ uint8_t RHOH;
+ uint32_t PageDataSize; /* 2048 for 2K pages, 4096 for 4K pages */
+ uint32_t TotalPageSize; /* 2112 for 2K pages, 4314 for 4K pages */
+ uint32_t SectorsPerBlock; /* Number of 2K sections per block */
+ uint32_t NumberOfNANDs; /* Total Number of NANDs - not used by ROM */
+ uint32_t TotalInternalDie; /* Number of separate chips in this NAND */
+ uint32_t CellType; /* MLC or SLC */
+ uint32_t EccBlockNEccType; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0Size; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSize; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccType; /* Ecc level for Block 0 - BCH */
+ uint32_t MetadataBytes; /* Metadata size - BCH */
+ uint32_t NumEccBlocksPerPage; /* Number of blocks per page for ROM use - BCH */
+ uint32_t EccBlockNEccLevelSDK; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0SizeSDK; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSizeSDK; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccLevelSDK; /* Ecc level for Block 0 - BCH */
+ uint32_t NumEccBlocksPerPageSDK;/* Number of blocks per page for SDK use - BCH */
+ uint32_t MetadataBytesSDK; /* Metadata size - BCH */
+ uint32_t EraseThreshold; /* To set into BCH_MODE register */
+ uint32_t BootPatch; /* 0 for normal boot and 1 to load patch starting next to FCB */
+ uint32_t PatchSectors; /* Size of patch in sectors */
+ uint32_t Firmware1_startingPage;/* Firmware image starts on this sector */
+ uint32_t Firmware2_startingPage;/* Secondary FW Image starting Sector */
+ uint32_t PagesInFirmware1; /* Number of sectors in firmware image */
+ uint32_t PagesInFirmware2; /* Number of sector in secondary FW image */
+ uint32_t DBBTSearchAreaStartAddress; /* Page address where dbbt search area begins */
+ uint32_t BadBlockMarkerByte; /* Byte in page data that have manufacturer marked bad block marker, */
+ /* this will be swapped with metadata[0] to complete page data. */
+ uint32_t BadBlockMarkerStartBit;/* For BCH ECC sizes other than 8 and 16 the bad block marker does not */
+ /* start at 0th bit of BadBlockMarkerByte. This field is used to get to */
+ /* the start bit of bad block marker byte with in BadBlockMarkerByte */
+ uint32_t BBMarkerPhysicalOffset;/* FCB value that gives byte offset for bad block marker on physical NAND page */
+ uint32_t BCHType;
+
+ uint32_t TMTiming2_ReadLatency;
+ uint32_t TMTiming2_PreambleDelay;
+ uint32_t TMTiming2_CEDelay;
+ uint32_t TMTiming2_PostambleDelay;
+ uint32_t TMTiming2_CmdAddPause;
+ uint32_t TMTiming2_DataPause;
+ uint32_t TMSpeed;
+ uint32_t TMTiming1_BusyTimeout;
+
+ uint32_t DISBBM; /* the flag to enable (1)/disable(0) bi swap */
+ uint32_t BBMarkerPhysicalOffsetInSpareData; /* The swap position of main area in spare area */
+};
+
+struct fw_write_data {
+ int fw1_blk;
+ int fw2_blk;
+ int part_blks;
+ void *buf;
+ size_t len;
+};
+
+#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET)
+#define GETBIT(v,n) (((v) >> (n)) & 0x1)
+
+
+static uint8_t calculate_parity_13_8(uint8_t d)
+{
+ uint8_t p = 0;
+
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^ GETBIT(d, 1)) << 1;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 2;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
+ return p;
+}
+
+static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
+{
+ int i;
+ uint8_t *src = _src;
+ uint8_t *ecc = _ecc;
+
+ for (i = 0; i < size; i++)
+ ecc[i] = calculate_parity_13_8(src[i]);
+}
+
+static uint32_t calc_chksum(void *buf, size_t size)
+{
+ u32 chksum = 0;
+ u8 *bp = buf;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ chksum += bp[i];
+
+ return ~chksum;
+}
+
+
+static ssize_t raw_write_page(struct mtd_info *mtd, void *buf, loff_t offset)
+{
+ struct mtd_oob_ops ops;
+ ssize_t ret;
+
+ ops.mode = MTD_OPS_RAW;
+ ops.ooboffs = 0;
+ ops.datbuf = buf;
+ ops.len = mtd->writesize;
+ ops.oobbuf = buf + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ret = mtd_write_oob(mtd, offset, &ops);
+
+ return ret;
+}
+
+static int fcb_create(struct fcb_block *fcb, struct mtd_info *mtd)
+{
+ fcb->FingerPrint = 0x20424346;
+ fcb->Version = 0x01000000;
+ fcb->PageDataSize = mtd->writesize;
+ fcb->TotalPageSize = mtd->writesize + mtd->oobsize;
+ fcb->SectorsPerBlock = mtd->erasesize / mtd->writesize;
+
+ /* Divide ECC strength by two and save the value into FCB structure. */
+ fcb->EccBlock0EccType =
+ mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1;
+
+ fcb->EccBlockNEccType = fcb->EccBlock0EccType;
+
+ /* Also hardcoded in kobs-ng */
+ fcb->EccBlock0Size = 0x00000200;
+ fcb->EccBlockNSize = 0x00000200;
+ fcb->DataSetup = 80;
+ fcb->DataHold = 60;
+ fcb->AddressSetup = 25;
+ fcb->DSAMPLE_TIME = 6;
+ fcb->MetadataBytes = 10;
+
+ /* DBBT search area starts at second page on first block */
+ fcb->DBBTSearchAreaStartAddress = 1;
+
+ fcb->BadBlockMarkerByte = mxs_nand_mark_byte_offset(mtd);
+ fcb->BadBlockMarkerStartBit = mxs_nand_mark_bit_offset(mtd);
+
+ fcb->BBMarkerPhysicalOffset = mtd->writesize;
+
+ fcb->NumEccBlocksPerPage = mtd->writesize / fcb->EccBlock0Size - 1;
+
+ fcb->Checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
+
+ return 0;
+}
+
+/* Erase entire U-Boot partition */
+static int mxs_nand_uboot_erase(struct mtd_info *mtd, struct part_info *part)
+{
+ uint64_t offset = 0;
+ struct erase_info erase;
+ int len = part->size;
+ int ret;
+
+ while (len > 0) {
+ pr_debug("erasing at 0x%08llx\n", offset);
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("erase skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ continue;
+ }
+
+ memset(&erase, 0, sizeof(erase));
+ erase.addr = offset;
+ erase.len = mtd->erasesize;
+
+ ret = mtd_erase(mtd, &erase);
+ if (ret)
+ return ret;
+
+ offset += mtd->erasesize;
+ len -= mtd->erasesize;
+ }
+
+ return 0;
+}
+
+/* Write the U-Boot proper (2 copies) to where it belongs. */
+/* This is U-Boot binary image itself, no FCB/DBBT here yet. */
+static int mxs_nand_uboot_write_fw(struct mtd_info *mtd, struct fw_write_data *fw)
+{
+ uint64_t offset;
+ int ret;
+ int blk;
+ size_t dummy;
+ size_t bytes_left;
+ int chunk;
+ void *p;
+
+ bytes_left = fw->len;
+ p = fw->buf;
+ blk = fw->fw1_blk;
+ offset = blk * mtd->erasesize;
+
+ while (bytes_left > 0) {
+ chunk = min(bytes_left, mtd->erasesize);
+
+ pr_debug("fw1: writing %p at 0x%08llx, left 0x%08x\n",
+ p, offset, bytes_left);
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("fw1: write skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ blk++;
+ continue;
+ }
+
+ if (blk >= fw->fw2_blk)
+ return -ENOSPC;
+
+ ret = mtd_write(mtd, offset, chunk, &dummy, p);
+ if (ret)
+ return ret;
+
+ offset += chunk;
+ bytes_left -= chunk;
+ p += chunk;
+ blk++;
+ }
+
+ bytes_left = fw->len;
+ p = fw->buf;
+ blk = fw->fw2_blk;
+ offset = blk * mtd->erasesize;
+
+ while (bytes_left > 0) {
+ chunk = min(bytes_left, mtd->erasesize);
+
+ pr_debug("fw2: writing %p at 0x%08llx, left 0x%08x\n",
+ p, offset, bytes_left);
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("fw2: write skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ blk++;
+ continue;
+ }
+
+ if (blk >= fw->part_blks)
+ return -ENOSPC;
+
+ ret = mtd_write(mtd, offset, chunk, &dummy, p);
+ if (ret)
+ return ret;
+
+ offset += chunk;
+ bytes_left -= chunk;
+ p += chunk;
+ blk++;
+ }
+
+ return 0;
+}
+
+static int dbbt_data_create(struct mtd_info *mtd, void *buf, int num_blocks)
+{
+ int n;
+ int n_bad_blocks = 0;
+ uint32_t *bb = buf + 0x8;
+ uint32_t *n_bad_blocksp = buf + 0x4;
+
+ for (n = 0; n < num_blocks; n++) {
+ loff_t offset = n * mtd->erasesize;
+ if (mtd_block_isbad(mtd, offset)) {
+ n_bad_blocks++;
+ *bb = n;
+ bb++;
+ }
+ }
+
+ *n_bad_blocksp = n_bad_blocks;
+
+ return n_bad_blocks;
+}
+
+/********************************************************************/
+/* This is where it is all done. Takes pointer to a U-Boot image in */
+/* RAM and image size, creates FCB/DBBT and writes everything where */
+/* it belongs into NAND. Image must be an IMX image built for NAND. */
+/********************************************************************/
+static int mxs_nand_uboot_update(const void *img, size_t len)
+{
+ int i, ret;
+
+ size_t dummy;
+ loff_t offset = 0;
+
+ void *fcb_raw_page;
+ void *dbbt_page;
+ void *dbbt_data_page;
+ void *ecc;
+
+ uint32_t num_blocks_fcb_dbbt;
+ uint32_t num_blocks_fw;
+
+ struct mtd_info *mtd;
+ struct fcb_block *fcb;
+ struct dbbt_block *dbbt;
+
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 pnum;
+
+ struct fw_write_data fw;
+
+ if ((mtdparts_init() == 0) &&
+ (find_dev_and_part(uboot_tgt, &dev, &pnum, &part) == 0)) {
+ if (dev->id->type != MTD_DEV_TYPE_NAND) {
+ puts("Not a NAND device\n");
+ return -ENODEV;
+ }
+ }
+
+ nand_curr_device = dev->id->num;
+
+#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
+ board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
+#endif
+
+ /* Get a pointer to mtd_info for selected device */
+
+ mtd = get_mtd_device_nm("nand0"); /* We always boot off of nand0 */
+
+ if (IS_ERR(mtd)) {
+ /* Should not happen */
+ puts("No nand0 device...\n");
+ return -ENODEV;
+ }
+
+ put_mtd_device(mtd);
+
+ /* Quick and dirty check if we have 2Mbytes of good blocks in nand0,0 */
+ /* Not sure if it is needed at all but won't hurt so here it is... */
+
+ i = 0;
+ offset = 0; /* It is the first partition so it starts at block 0 */
+
+ while (offset < part->size) {
+ if (!mtd_block_isbad(mtd, offset)) {
+ i += mtd->erasesize;
+ }
+ offset += mtd->erasesize;
+ }
+
+ if (i < SZ_2M) {
+ puts("Partition too small for U-Boot!\n");
+ return -EINVAL;
+ }
+
+ /* We will use 4 first blocks for FCB/DBBT copies. */
+ /* The rest of partition is split in half and used */
+ /* for two U-Boot copies. We don't care if those */
+ /* start on good or bad block - bad blocks will be */
+ /* skipped on write, ROM boot code will also skip */
+ /* bad blocks on bootup when loading U-Boot image. */
+
+ fw.part_blks = part->size / mtd->erasesize;
+ num_blocks_fcb_dbbt = 4;
+ num_blocks_fw = (fw.part_blks - num_blocks_fcb_dbbt) / 2;
+ fw.fw1_blk = num_blocks_fcb_dbbt;
+ fw.fw2_blk = fw.fw1_blk + num_blocks_fw;
+
+ /* OK, now create FCB structure for bootROM NAND boot */
+
+ fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+
+ fcb = fcb_raw_page + 12;
+ ecc = fcb_raw_page + 512 + 12;
+
+ dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ dbbt = dbbt_page;
+
+ /* Write one additional page to make the ROM happy. */
+ /* Maybe the PagesInFirmwarex fields are really the */
+ /* number of pages - 1. kobs-ng does the same. */
+
+ fw.len = ALIGN(len + FLASH_OFFSET_STANDARD + mtd->writesize, mtd->writesize);
+ fw.buf = kzalloc(fw.len, GFP_KERNEL);
+ memcpy(fw.buf + FLASH_OFFSET_STANDARD, img, len);
+
+ /* Erase entire partition */
+ ret = mxs_nand_uboot_erase(mtd, part);
+ if (ret)
+ goto out;
+
+ /* Now write 2 copies of the U-Boot proper to where they belong. */
+ /* Headers (FCB, DBBT) will be generated and written after that. */
+ ret = mxs_nand_uboot_write_fw(mtd, &fw);
+ if (ret < 0)
+ goto out;
+
+ /* Create FCB, calculate ECC (we don't/can't use hardware ECC */
+ /* here so we do it ourselves and then write _RAW_ pages. */
+
+ fcb->Firmware1_startingPage = fw.fw1_blk * mtd->erasesize / mtd->writesize;
+ fcb->Firmware2_startingPage = fw.fw2_blk * mtd->erasesize / mtd->writesize;
+ fcb->PagesInFirmware1 =
+ ALIGN(len + FLASH_OFFSET_STANDARD, mtd->writesize) / mtd->writesize;
+ fcb->PagesInFirmware2 = fcb->PagesInFirmware1;
+
+ fcb_create(fcb, mtd);
+ encode_hamming_13_8(fcb, ecc, 512);
+
+ /*
+ * Set the first and second byte of OOB data to 0xFF, not 0x00. These
+ * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since
+ * the FCB is mostly written to the first page in a block, a scan for
+ * factory bad blocks will detect these blocks as bad, e.g. when
+ * function nand_scan_bbt() is executed to build a new bad block table.
+ * We will _NOT_ mark a bad block as good -- we skip the bad blocks.
+ */
+ memset(fcb_raw_page + mtd->writesize, 0xff, 2);
+
+ /* Now create DBBT */
+ dbbt->Checksum = 0;
+ dbbt->FingerPrint = 0x54424244;
+ dbbt->Version = 0x01000000;
+
+ if ((ret = dbbt_data_create(mtd, dbbt_data_page, fw.part_blks)) < 0)
+ goto out;
+
+ if (ret > 0)
+ dbbt->DBBTNumOfPages = 1;
+
+ offset = 0;
+
+ if (mtd_block_isbad(mtd, offset)) {
+ puts("Block 0 is bad, NAND unusable\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Write FCB/DBBT to first 4 blocks. Skip bad blocks if any. */
+ /* Less than 4 copies will be written if there were BBs !!! */
+ for (i = 0; i < 4; i++) {
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_err("Block %d is bad, skipped\n", i);
+ continue;
+ }
+
+
+ ret = raw_write_page(mtd, fcb_raw_page, mtd->erasesize * i);
+ if (ret)
+ goto out;
+
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
+ mtd->writesize, &dummy, dbbt_page);
+ if (ret)
+ goto out;
+
+ /* DBBTNumOfPages == 0 if no bad blocks */
+ if (dbbt->DBBTNumOfPages > 0) {
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize * 5,
+ mtd->writesize, &dummy, dbbt_data_page);
+ if (ret)
+ goto out;
+ }
+ }
+
+out:
+ kfree(dbbt_page);
+ kfree(dbbt_data_page);
+ kfree(fcb_raw_page);
+ kfree(fw.buf);
+
+ return ret;
+}
+
+
+int mxs_do_nand_bootupdate(ulong addr, size_t len)
+{
+ /* KSI: Unlock NAND first if it is locked... */
+
+ return mxs_nand_uboot_update((const void *)addr, len);
+}
--- a/cmd/nand.c
+++ b/cmd/nand.c
@@ -38,6 +38,11 @@ int find_dev_and_part(const char *id, struct mtd_device **dev,
u8 *part_num, struct part_info **part);
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+/* This comes from a separate file in drivers/mtd/nand */
+int mxs_do_nand_bootupdate(ulong addr, size_t len);
+#endif
+
static int nand_dump(struct mtd_info *mtd, ulong off, int only_oob,
int repeat)
{
@@ -372,6 +377,9 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
loff_t off, size, maxsize;
char *cmd, *s;
struct mtd_info *mtd;
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ size_t cnt;
+#endif
#ifdef CONFIG_SYS_NAND_QUIET
int quiet = CONFIG_SYS_NAND_QUIET;
#else
@@ -777,6 +785,48 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ if (strncmp(cmd, "bootupdate", 10) == 0) {
+
+ if (argc < 3) {
+ /* All default values */
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+ if (argc == 3) {
+ /* 'addr' only, file size from environment */
+ if ((addr = simple_strtoul(argv[2], NULL, 16)) == 0UL)
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+ if (argc > 3) {
+ /* 'addr', 'size', and possibly more */
+ if ((addr = simple_strtoul(argv[2], NULL, 16)) == 0UL)
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+
+ if ((cnt = simple_strtoul(argv[3], NULL, 16)) == 0UL)
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+
+ if (addr == 0 || cnt == 0) {
+ puts("Invalid arguments to nand bootupdate!\n");
+ return 1;
+ }
+
+ if (mxs_do_nand_bootupdate(addr, cnt)) {
+ puts("NAND bootupdate failed!\n");
+ return 1;
+ }
+
+ puts("NAND bootupdate successful\n");
+ return 0;
+ }
+#endif
+
usage:
return CMD_RET_USAGE;
}
@@ -798,6 +848,17 @@ static char nand_help_text[] =
" 'addr', skipping bad blocks and dropping any pages at the end\n"
" of eraseblocks that contain only 0xFF\n"
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ "nand bootupdate - [addr] [size]\n"
+ " write U-Boot into NAND in board/SoC specific manner creating all\n"
+ " required headers and other bits and pieces as required for the\n"
+ " system to be able to boot off of NAND. 'addr' is the address\n"
+ " where U-Boot image has been loaded at, 'size' is its size.\n"
+ " If any of 'addr'/'size' is missing it is taken from environment\n"
+ " for the last file loaded. U-Boot image must be of a proper type\n"
+ " for the target platform (only IMX image supported at the moment)\n"
+ " binary without U-Boot image headers (e.g. u-boot.imx file.)\n"
+#endif
"nand erase[.spread] [clean] off size - erase 'size' bytes "
"from offset 'off'\n"
" With '.spread', erase enough for given file size, otherwise,\n"
---
******************************************************************
* KSI@home KOI8 Net < > The impossible we do immediately. *
* Las Vegas NV, USA < > Miracles require 24-hour notice. *
******************************************************************
5
8
The README mentions various options that are not used in U-Boot. This
series tidies these up.
There are also some other README files that are out of date, or reference
old options. This series tidies up/removes some of these also.
Simon Glass (16):
README: Drop old Intel Monahans comment
README: Drop CONFIG_COGENT and related options
README: Correct CONFIG_ENV_OFFSET_RENDUND typo
README: Drop unused JFFS2 options
README: Drop unused CONFIG_SYS_LS_MC_FW_... options
README: Drop CONFIG_OF_BOOT_CPU
README: Drop CONFIG_LAN91C96_BASE
README: Drop CONFIG_SYS_USB_BRG_CLK
README: sh: Drop CONFIG_SYS_I2C_SH_BASE5
README: i2c: Drop unused i2c CONFIG options
README: Drop CONFIG_SYS_INIT_DATA_SIZE
README: Drop CONFIG_SYS_USE_OSCCLK
atmel: Drop README.at91-soc
README: Drop README.imx31
README: Drop CONFIG_MPC8349ADS
README: Fix CONFIG_SYS_NAND_MAX_DEVICE typo
README | 104 +++--------------------------------------
doc/README.JFFS2 | 43 ++---------------
doc/README.JFFS2_NAND | 20 +-------
doc/README.at91-soc | 48 -------------------
doc/README.imx31 | 29 ------------
doc/README.mpc83xxads | 1 -
doc/README.nand | 2 +-
include/configs/ls2080a_simu.h | 3 --
8 files changed, 12 insertions(+), 238 deletions(-)
delete mode 100644 doc/README.at91-soc
delete mode 100644 doc/README.imx31
--
2.8.0.rc3.226.g39d4020
7
54
Here it is agains your latest u-boot-imx master.
Won't post that description again to lower the noise level, just a diff
itself.
Signed-off-by: Sergey Kubushyn <ksi(a)koi8.net>
---
arch/arm/include/asm/imx-common/mxs_nand.h | 37 ++
cmd/Kconfig | 10 +
cmd/nand.c | 61 +++
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/mxs_nand.c | 7 +-
drivers/mtd/nand/mxs_nand_bootupdate.c | 556 +++++++++++++++++++++
6 files changed, 669 insertions(+), 3 deletions(-)
diff --git a/arch/arm/include/asm/imx-common/mxs_nand.h b/arch/arm/include/asm/imx-common/mxs_nand.h
new file mode 100644
index 0000000..7826a9f
--- /dev/null
+++ b/arch/arm/include/asm/imx-common/mxs_nand.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 PHYTEC Messtechnik GmbH,
+ * Author: Stefan Christ <s.christ at phytec.de>
+ *
+ * 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.
+ */
+
+#ifndef __NAND_MXS_H
+#define __NAND_MXS_H
+
+/*
+ * Functions are definied in drivers/mtd/nand/nand_mxs.c. They are used to
+ * calculate the ECC Strength, BadBlockMarkerByte and BadBlockMarkerStartBit
+ * which are placed into the FCB structure. The i.MX6 ROM needs these
+ * parameters to read the firmware from NAND.
+ *
+ * The parameters depends on the pagesize and oobsize of NAND chips and are
+ * different for each combination. To avoid placing hardcoded values in the bbu
+ * update handler code, the generic calculation from the driver code is used.
+ */
+
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+ uint32_t page_oob_size);
+
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd);
+
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd);
+
+#endif /* __NAND_MXS_H */
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 86554ea..6ef7253 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -571,6 +571,16 @@ config CMD_TIME
help
Run commands and summarize execution time.
+config CMD_NAND_BOOTUPDATE
+ bool "Update NAND bootloader on iMX6 and its brethen"
+ depends on ARCH_MX6 && NAND_BOOT && CMD_NAND
+ help
+ Having iMX6 NAND U-Boot image in memory creates all necessary
+ structures and writes those where they belong along with that
+ U-Boot image so system is able to boot off of raw NAND. Kinda
+ like kobs-ng utility but simpler.
+
+
# TODO: rename to CMD_SLEEP
config CMD_MISC
bool "sleep"
diff --git a/cmd/nand.c b/cmd/nand.c
index c16ec77..c25f1ef 100644
--- a/cmd/nand.c
+++ b/cmd/nand.c
@@ -38,6 +38,11 @@ int find_dev_and_part(const char *id, struct mtd_device **dev,
u8 *part_num, struct part_info **part);
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+/* This comes from a separate file in drivers/mtd/nand */
+int mxs_do_nand_bootupdate(ulong addr, size_t len);
+#endif
+
static int nand_dump(struct mtd_info *mtd, ulong off, int only_oob,
int repeat)
{
@@ -372,6 +377,9 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
loff_t off, size, maxsize;
char *cmd, *s;
struct mtd_info *mtd;
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ size_t cnt;
+#endif
#ifdef CONFIG_SYS_NAND_QUIET
int quiet = CONFIG_SYS_NAND_QUIET;
#else
@@ -777,6 +785,48 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ if (strncmp(cmd, "bootupdate", 10) == 0) {
+
+ if (argc < 3) {
+ /* All default values */
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+ if (argc == 3) {
+ /* 'addr' only, file size from environment */
+ if ((addr = simple_strtoul(argv[2], NULL, 16)) == 0UL)
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+ if (argc > 3) {
+ /* 'addr', 'size', and possibly more */
+ if ((addr = simple_strtoul(argv[2], NULL, 16)) == 0UL)
+ addr = getenv_ulong("fileaddr", 16, 0UL);
+
+ if ((cnt = simple_strtoul(argv[3], NULL, 16)) == 0UL)
+ cnt = getenv_ulong("filesize", 16, 0UL);
+ }
+
+
+ if (addr == 0 || cnt == 0) {
+ puts("Invalid arguments to nand bootupdate!\n");
+ return 1;
+ }
+
+ if (mxs_do_nand_bootupdate(addr, cnt)) {
+ puts("NAND bootupdate failed!\n");
+ return 1;
+ }
+
+ puts("NAND bootupdate successful\n");
+ return 0;
+ }
+#endif
+
usage:
return CMD_RET_USAGE;
}
@@ -798,6 +848,17 @@ static char nand_help_text[] =
" 'addr', skipping bad blocks and dropping any pages at the end\n"
" of eraseblocks that contain only 0xFF\n"
#endif
+#ifdef CONFIG_CMD_NAND_BOOTUPDATE
+ "nand bootupdate - [addr] [size]\n"
+ " write U-Boot into NAND in board/SoC specific manner creating all\n"
+ " required headers and other bits and pieces as required for the\n"
+ " system to be able to boot off of NAND. 'addr' is the address\n"
+ " where U-Boot image has been loaded at, 'size' is its size.\n"
+ " If any of 'addr'/'size' is missing it is taken from environment\n"
+ " for the last file loaded. U-Boot image must be of a proper type\n"
+ " for the target platform (only IMX image supported at the moment)\n"
+ " binary without U-Boot image headers (e.g. u-boot.imx file.)\n"
+#endif
"nand erase[.spread] [clean] off size - erase 'size' bytes "
"from offset 'off'\n"
" With '.spread', erase enough for given file size, otherwise,\n"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1df9273..b1b98dc 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o
obj-$(CONFIG_NAND_PLAT) += nand_plat.o
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
+obj-$(CONFIG_CMD_NAND_BOOTUPDATE) += mxs_nand_bootupdate.o
else # minimal SPL drivers
diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c
index 9200544..333ee09 100644
--- a/drivers/mtd/nand/mxs_nand.c
+++ b/drivers/mtd/nand/mxs_nand.c
@@ -26,6 +26,7 @@
#include <asm/imx-common/regs-gpmi.h>
#include <asm/arch/sys_proto.h>
#include <asm/imx-common/dma.h>
+#include <asm/imx-common/mxs_nand.h>
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
@@ -150,7 +151,7 @@ static uint32_t mxs_nand_aux_status_offset(void)
return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3;
}
-static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
uint32_t page_oob_size)
{
int ecc_strength;
@@ -226,14 +227,14 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size,
return block_mark_bit_offset;
}
-static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3;
}
-static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
diff --git a/drivers/mtd/nand/mxs_nand_bootupdate.c b/drivers/mtd/nand/mxs_nand_bootupdate.c
new file mode 100644
index 0000000..9442553
--- /dev/null
+++ b/drivers/mtd/nand/mxs_nand_bootupdate.c
@@ -0,0 +1,556 @@
+/*
+ * mxs_nand_bootupdate.c - write U-Boot to MXS NAND to make it bootable
+ *
+ * Copyright (c) 2016 Sergey Kubushyn <ksi(a)koi8.net>
+ *
+ * Most of the source shamelesly stolen from barebox.
+ *
+ * Here is the original copyright:
+ *
+ *=== Cut ===
+ * Copyright (C) 2014 Sascha Hauer, Pengutronix
+ *
+ * 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.
+ *=== Cut ===
+ */
+
+
+#include <common.h>
+#include <linux/sizes.h>
+#include <linux/mtd/mtd.h>
+#include <linux/compat.h>
+#include <command.h>
+#include <console.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <jffs2/jffs2.h>
+#include <nand.h>
+#include <errno.h>
+#include <asm/imx-common/mxs_nand.h>
+#include <asm/imx-common/imximage.cfg>
+
+
+#ifndef CONFIG_CMD_MTDPARTS
+#error "CONFIG_CMD_MTDPARTS is not set, mtd partition support missing"
+#endif
+
+static const char *uboot_tgt = "nand0,0";
+
+/* partition handling routines */
+int mtdparts_init(void);
+int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+ u8 *part_num, struct part_info **part);
+
+struct dbbt_block {
+ uint32_t Checksum; /* reserved on i.MX6 */
+ uint32_t FingerPrint;
+ uint32_t Version;
+ uint32_t numberBB; /* reserved on i.MX6 */
+ uint32_t DBBTNumOfPages;
+};
+
+struct fcb_block {
+ uint32_t Checksum; /* First fingerprint in first byte */
+ uint32_t FingerPrint; /* 2nd fingerprint at byte 4 */
+ uint32_t Version; /* 3rd fingerprint at byte 8 */
+ uint8_t DataSetup;
+ uint8_t DataHold;
+ uint8_t AddressSetup;
+ uint8_t DSAMPLE_TIME;
+ /* These are for application use only and not for ROM. */
+ uint8_t NandTimingState;
+ uint8_t REA;
+ uint8_t RLOH;
+ uint8_t RHOH;
+ uint32_t PageDataSize; /* 2048 for 2K pages, 4096 for 4K pages */
+ uint32_t TotalPageSize; /* 2112 for 2K pages, 4314 for 4K pages */
+ uint32_t SectorsPerBlock; /* Number of 2K sections per block */
+ uint32_t NumberOfNANDs; /* Total Number of NANDs - not used by ROM */
+ uint32_t TotalInternalDie; /* Number of separate chips in this NAND */
+ uint32_t CellType; /* MLC or SLC */
+ uint32_t EccBlockNEccType; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0Size; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSize; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccType; /* Ecc level for Block 0 - BCH */
+ uint32_t MetadataBytes; /* Metadata size - BCH */
+ uint32_t NumEccBlocksPerPage; /* Number of blocks per page for ROM use - BCH */
+ uint32_t EccBlockNEccLevelSDK; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0SizeSDK; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSizeSDK; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccLevelSDK; /* Ecc level for Block 0 - BCH */
+ uint32_t NumEccBlocksPerPageSDK;/* Number of blocks per page for SDK use - BCH */
+ uint32_t MetadataBytesSDK; /* Metadata size - BCH */
+ uint32_t EraseThreshold; /* To set into BCH_MODE register */
+ uint32_t BootPatch; /* 0 for normal boot and 1 to load patch starting next to FCB */
+ uint32_t PatchSectors; /* Size of patch in sectors */
+ uint32_t Firmware1_startingPage;/* Firmware image starts on this sector */
+ uint32_t Firmware2_startingPage;/* Secondary FW Image starting Sector */
+ uint32_t PagesInFirmware1; /* Number of sectors in firmware image */
+ uint32_t PagesInFirmware2; /* Number of sector in secondary FW image */
+ uint32_t DBBTSearchAreaStartAddress; /* Page address where dbbt search area begins */
+ uint32_t BadBlockMarkerByte; /* Byte in page data that have manufacturer marked bad block marker, */
+ /* this will be swapped with metadata[0] to complete page data. */
+ uint32_t BadBlockMarkerStartBit;/* For BCH ECC sizes other than 8 and 16 the bad block marker does not */
+ /* start at 0th bit of BadBlockMarkerByte. This field is used to get to */
+ /* the start bit of bad block marker byte with in BadBlockMarkerByte */
+ uint32_t BBMarkerPhysicalOffset;/* FCB value that gives byte offset for bad block marker on physical NAND page */
+ uint32_t BCHType;
+
+ uint32_t TMTiming2_ReadLatency;
+ uint32_t TMTiming2_PreambleDelay;
+ uint32_t TMTiming2_CEDelay;
+ uint32_t TMTiming2_PostambleDelay;
+ uint32_t TMTiming2_CmdAddPause;
+ uint32_t TMTiming2_DataPause;
+ uint32_t TMSpeed;
+ uint32_t TMTiming1_BusyTimeout;
+
+ uint32_t DISBBM; /* the flag to enable (1)/disable(0) bi swap */
+ uint32_t BBMarkerPhysicalOffsetInSpareData; /* The swap position of main area in spare area */
+};
+
+struct fw_write_data {
+ int fw1_blk;
+ int fw2_blk;
+ int part_blks;
+ void *buf;
+ size_t len;
+};
+
+#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET)
+#define GETBIT(v,n) (((v) >> (n)) & 0x1)
+
+
+static uint8_t calculate_parity_13_8(uint8_t d)
+{
+ uint8_t p = 0;
+
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^ GETBIT(d, 1)) << 1;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 2;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
+ return p;
+}
+
+static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
+{
+ int i;
+ uint8_t *src = _src;
+ uint8_t *ecc = _ecc;
+
+ for (i = 0; i < size; i++)
+ ecc[i] = calculate_parity_13_8(src[i]);
+}
+
+static uint32_t calc_chksum(void *buf, size_t size)
+{
+ u32 chksum = 0;
+ u8 *bp = buf;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ chksum += bp[i];
+
+ return ~chksum;
+}
+
+
+static ssize_t raw_write_page(struct mtd_info *mtd, void *buf, loff_t offset)
+{
+ struct mtd_oob_ops ops;
+ ssize_t ret;
+
+ ops.mode = MTD_OPS_RAW;
+ ops.ooboffs = 0;
+ ops.datbuf = buf;
+ ops.len = mtd->writesize;
+ ops.oobbuf = buf + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ret = mtd_write_oob(mtd, offset, &ops);
+
+ return ret;
+}
+
+static int fcb_create(struct fcb_block *fcb, struct mtd_info *mtd)
+{
+ fcb->FingerPrint = 0x20424346;
+ fcb->Version = 0x01000000;
+ fcb->PageDataSize = mtd->writesize;
+ fcb->TotalPageSize = mtd->writesize + mtd->oobsize;
+ fcb->SectorsPerBlock = mtd->erasesize / mtd->writesize;
+
+ /* Divide ECC strength by two and save the value into FCB structure. */
+ fcb->EccBlock0EccType =
+ mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1;
+
+ fcb->EccBlockNEccType = fcb->EccBlock0EccType;
+
+ /* Also hardcoded in kobs-ng */
+ fcb->EccBlock0Size = 0x00000200;
+ fcb->EccBlockNSize = 0x00000200;
+ fcb->DataSetup = 80;
+ fcb->DataHold = 60;
+ fcb->AddressSetup = 25;
+ fcb->DSAMPLE_TIME = 6;
+ fcb->MetadataBytes = 10;
+
+ /* DBBT search area starts at second page on first block */
+ fcb->DBBTSearchAreaStartAddress = 1;
+
+ fcb->BadBlockMarkerByte = mxs_nand_mark_byte_offset(mtd);
+ fcb->BadBlockMarkerStartBit = mxs_nand_mark_bit_offset(mtd);
+
+ fcb->BBMarkerPhysicalOffset = mtd->writesize;
+
+ fcb->NumEccBlocksPerPage = mtd->writesize / fcb->EccBlock0Size - 1;
+
+ fcb->Checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
+
+ return 0;
+}
+
+/* Erase entire U-Boot partition */
+static int mxs_nand_uboot_erase(struct mtd_info *mtd, struct part_info *part)
+{
+ uint64_t offset = 0;
+ struct erase_info erase;
+ int len = part->size;
+ int ret;
+
+ while (len > 0) {
+ pr_debug("erasing at 0x%08llx\n", offset);
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("erase skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ continue;
+ }
+
+ memset(&erase, 0, sizeof(erase));
+ erase.addr = offset;
+ erase.len = mtd->erasesize;
+
+ ret = mtd_erase(mtd, &erase);
+ if (ret)
+ return ret;
+
+ offset += mtd->erasesize;
+ len -= mtd->erasesize;
+ }
+
+ return 0;
+}
+
+/* Write the U-Boot proper (2 copies) to where it belongs. */
+/* This is U-Boot binary image itself, no FCB/DBBT here yet. */
+static int mxs_nand_uboot_write_fw(struct mtd_info *mtd, struct fw_write_data *fw)
+{
+ uint64_t offset;
+ int ret;
+ int blk;
+ size_t dummy;
+ size_t bytes_left;
+ int chunk;
+ void *p;
+
+ bytes_left = fw->len;
+ p = fw->buf;
+ blk = fw->fw1_blk;
+ offset = blk * mtd->erasesize;
+
+ while (bytes_left > 0) {
+ chunk = min(bytes_left, mtd->erasesize);
+
+ pr_debug("fw1: writing %p at 0x%08llx, left 0x%08x\n",
+ p, offset, bytes_left);
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("fw1: write skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ blk++;
+ continue;
+ }
+
+ if (blk >= fw->fw2_blk)
+ return -ENOSPC;
+
+ ret = mtd_write(mtd, offset, chunk, &dummy, p);
+ if (ret)
+ return ret;
+
+ offset += chunk;
+ bytes_left -= chunk;
+ p += chunk;
+ blk++;
+ }
+
+ bytes_left = fw->len;
+ p = fw->buf;
+ blk = fw->fw2_blk;
+ offset = blk * mtd->erasesize;
+
+ while (bytes_left > 0) {
+ chunk = min(bytes_left, mtd->erasesize);
+
+ pr_debug("fw2: writing %p at 0x%08llx, left 0x%08x\n",
+ p, offset, bytes_left);
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_debug("fw2: write skip block @ 0x%08llx\n", offset);
+ offset += mtd->erasesize;
+ blk++;
+ continue;
+ }
+
+ if (blk >= fw->part_blks)
+ return -ENOSPC;
+
+ ret = mtd_write(mtd, offset, chunk, &dummy, p);
+ if (ret)
+ return ret;
+
+ offset += chunk;
+ bytes_left -= chunk;
+ p += chunk;
+ blk++;
+ }
+
+ return 0;
+}
+
+static int dbbt_data_create(struct mtd_info *mtd, void *buf, int num_blocks)
+{
+ int n;
+ int n_bad_blocks = 0;
+ uint32_t *bb = buf + 0x8;
+ uint32_t *n_bad_blocksp = buf + 0x4;
+
+ for (n = 0; n < num_blocks; n++) {
+ loff_t offset = n * mtd->erasesize;
+ if (mtd_block_isbad(mtd, offset)) {
+ n_bad_blocks++;
+ *bb = n;
+ bb++;
+ }
+ }
+
+ *n_bad_blocksp = n_bad_blocks;
+
+ return n_bad_blocks;
+}
+
+/********************************************************************/
+/* This is where it is all done. Takes pointer to a U-Boot image in */
+/* RAM and image size, creates FCB/DBBT and writes everything where */
+/* it belongs into NAND. Image must be an IMX image built for NAND. */
+/********************************************************************/
+static int mxs_nand_uboot_update(const void *img, size_t len)
+{
+ int i, ret;
+
+ size_t dummy;
+ loff_t offset = 0;
+
+ void *fcb_raw_page;
+ void *dbbt_page;
+ void *dbbt_data_page;
+ void *ecc;
+
+ uint32_t num_blocks_fcb_dbbt;
+ uint32_t num_blocks_fw;
+
+ struct mtd_info *mtd;
+ struct fcb_block *fcb;
+ struct dbbt_block *dbbt;
+
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 pnum;
+
+ struct fw_write_data fw;
+
+ if ((mtdparts_init() == 0) &&
+ (find_dev_and_part(uboot_tgt, &dev, &pnum, &part) == 0)) {
+ if (dev->id->type != MTD_DEV_TYPE_NAND) {
+ puts("Not a NAND device\n");
+ return -ENODEV;
+ }
+ }
+
+ nand_curr_device = dev->id->num;
+
+#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
+ board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
+#endif
+
+ /* Get a pointer to mtd_info for selected device */
+
+ mtd = get_mtd_device_nm("nand0"); /* We always boot off of nand0 */
+
+ if (IS_ERR(mtd)) {
+ /* Should not happen */
+ puts("No nand0 device...\n");
+ return -ENODEV;
+ }
+
+ put_mtd_device(mtd);
+
+ /* Quick and dirty check if we have 2Mbytes of good blocks in nand0,0 */
+ /* Not sure if it is needed at all but won't hurt so here it is... */
+
+ i = 0;
+ offset = 0; /* It is the first partition so it starts at block 0 */
+
+ while (offset < part->size) {
+ if (!mtd_block_isbad(mtd, offset)) {
+ i += mtd->erasesize;
+ }
+ offset += mtd->erasesize;
+ }
+
+ if (i < SZ_2M) {
+ puts("Partition too small for U-Boot!\n");
+ return -EINVAL;
+ }
+
+ /* We will use 4 first blocks for FCB/DBBT copies. */
+ /* The rest of partition is split in half and used */
+ /* for two U-Boot copies. We don't care if those */
+ /* start on good or bad block - bad blocks will be */
+ /* skipped on write, ROM boot code will also skip */
+ /* bad blocks on bootup when loading U-Boot image. */
+
+ fw.part_blks = part->size / mtd->erasesize;
+ num_blocks_fcb_dbbt = 4;
+ num_blocks_fw = (fw.part_blks - num_blocks_fcb_dbbt) / 2;
+ fw.fw1_blk = num_blocks_fcb_dbbt;
+ fw.fw2_blk = fw.fw1_blk + num_blocks_fw;
+
+ /* OK, now create FCB structure for bootROM NAND boot */
+
+ fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+
+ fcb = fcb_raw_page + 12;
+ ecc = fcb_raw_page + 512 + 12;
+
+ dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ dbbt = dbbt_page;
+
+ /* Write one additional page to make the ROM happy. */
+ /* Maybe the PagesInFirmwarex fields are really the */
+ /* number of pages - 1. kobs-ng does the same. */
+
+ fw.len = ALIGN(len + FLASH_OFFSET_STANDARD + mtd->writesize, mtd->writesize);
+ fw.buf = kzalloc(fw.len, GFP_KERNEL);
+ memcpy(fw.buf + FLASH_OFFSET_STANDARD, img, len);
+
+ /* Erase entire partition */
+ ret = mxs_nand_uboot_erase(mtd, part);
+ if (ret)
+ goto out;
+
+ /* Now write 2 copies of the U-Boot proper to where they belong. */
+ /* Headers (FCB, DBBT) will be generated and written after that. */
+ ret = mxs_nand_uboot_write_fw(mtd, &fw);
+ if (ret < 0)
+ goto out;
+
+ /* Create FCB, calculate ECC (we don't/can't use hardware ECC */
+ /* here so we do it ourselves and then write _RAW_ pages. */
+
+ fcb->Firmware1_startingPage = fw.fw1_blk * mtd->erasesize / mtd->writesize;
+ fcb->Firmware2_startingPage = fw.fw2_blk * mtd->erasesize / mtd->writesize;
+ fcb->PagesInFirmware1 =
+ ALIGN(len + FLASH_OFFSET_STANDARD, mtd->writesize) / mtd->writesize;
+ fcb->PagesInFirmware2 = fcb->PagesInFirmware1;
+
+ fcb_create(fcb, mtd);
+ encode_hamming_13_8(fcb, ecc, 512);
+
+ /*
+ * Set the first and second byte of OOB data to 0xFF, not 0x00. These
+ * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since
+ * the FCB is mostly written to the first page in a block, a scan for
+ * factory bad blocks will detect these blocks as bad, e.g. when
+ * function nand_scan_bbt() is executed to build a new bad block table.
+ * We will _NOT_ mark a bad block as good -- we skip the bad blocks.
+ */
+ memset(fcb_raw_page + mtd->writesize, 0xff, 2);
+
+ /* Now create DBBT */
+ dbbt->Checksum = 0;
+ dbbt->FingerPrint = 0x54424244;
+ dbbt->Version = 0x01000000;
+
+ if ((ret = dbbt_data_create(mtd, dbbt_data_page, fw.part_blks)) < 0)
+ goto out;
+
+ if (ret > 0)
+ dbbt->DBBTNumOfPages = 1;
+
+ offset = 0;
+
+ if (mtd_block_isbad(mtd, offset)) {
+ puts("Block 0 is bad, NAND unusable\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Write FCB/DBBT to first 4 blocks. Skip bad blocks if any. */
+ /* Less than 4 copies will be written if there were BBs !!! */
+ for (i = 0; i < 4; i++) {
+
+ if (mtd_block_isbad(mtd, offset)) {
+ pr_err("Block %d is bad, skipped\n", i);
+ continue;
+ }
+
+
+ ret = raw_write_page(mtd, fcb_raw_page, mtd->erasesize * i);
+ if (ret)
+ goto out;
+
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
+ mtd->writesize, &dummy, dbbt_page);
+ if (ret)
+ goto out;
+
+ /* DBBTNumOfPages == 0 if no bad blocks */
+ if (dbbt->DBBTNumOfPages > 0) {
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize * 5,
+ mtd->writesize, &dummy, dbbt_data_page);
+ if (ret)
+ goto out;
+ }
+ }
+
+out:
+ kfree(dbbt_page);
+ kfree(dbbt_data_page);
+ kfree(fcb_raw_page);
+ kfree(fw.buf);
+
+ return ret;
+}
+
+
+int mxs_do_nand_bootupdate(ulong addr, size_t len)
+{
+ /* KSI: Unlock NAND first if it is locked... */
+
+ return mxs_nand_uboot_update((const void *)addr, len);
+}
---
******************************************************************
* KSI@home KOI8 Net < > The impossible we do immediately. *
* Las Vegas NV, USA < > Miracles require 24-hour notice. *
******************************************************************
2
1