[U-Boot] [PATCH 1/1]: add nand_bootupdate for i.MX6 and likes

Sergey Kubushyn ksi at koi8.net
Fri Oct 7 17:04:55 CEST 2016


On Fri, 7 Oct 2016, Stefano Babic wrote:

> 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 !) ?

What should I use a base? I did my patches against whatever is the main
U-Boot git tree (doing git pull befofe generating patches) but it is a
moving target so there is absolutely no guarantee it will work if I pull
again and redo the patches against whatever it is today.

Any particular version/repo/commit/whatever I should use?

>
> 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
> =====================================================================
>

---
******************************************************************
*  KSI at home    KOI8 Net  < >  The impossible we do immediately.  *
*  Las Vegas   NV, USA   < >  Miracles require 24-hour notice.   *
******************************************************************


More information about the U-Boot mailing list