Hi Sergey, On 05/10/2016 21:57, Sergey Kubushyn wrote: > 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. >
Do not think twice, I would *really* like to have this feature merged. But the patch should at least be applied clean. I cannot apply it becuase there are conflicts in cmd/nand.c and mxs_nand.c (the other conflicts are marginals). Can you please check this (before you retire, of course !) ? Best regards, Stefano Babic > Signed-off-by: Sergey Kubushyn <k...@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 <k...@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. * > ****************************************************************** > _______________________________________________ > U-Boot mailing list > U-Boot@lists.denx.de > http://lists.denx.de/mailman/listinfo/u-boot -- ===================================================================== DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sba...@denx.de ===================================================================== _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot