[U-Boot] [PATCH 1/1]: add nand_bootupdate for i.MX6 and likes
Stefano Babic
sbabic at denx.de
Fri Oct 7 16:30:39 CEST 2016
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 <ksi at 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 at 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 at home KOI8 Net < > The impossible we do immediately. *
> * Las Vegas NV, USA < > Miracles require 24-hour notice. *
> ******************************************************************
> _______________________________________________
> U-Boot mailing list
> U-Boot at 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: sbabic at denx.de
=====================================================================
More information about the U-Boot
mailing list