On 06/28/2012 03:47 PM, Rafael Beims wrote:
> Freescale FCM controller has a 2K size limitation of buffer RAM. In order
> to support the Nand flash chip with pagesize larger than 2K bytes,
> we read/write 2k data repeatedly by issuing FIR_OP_RB/FIR_OP_WB and save
> them to a large buffer.
> Because of this, the in flash layout of the oob is different from the
> default for 4096kiB page sizes. Therefore, we need to migrate the
> factory bad block markers from the original position to the new layout.
> 
> Signed-off-by: Shengzhou Liu <shengzhou....@freescale.com>
> Signed-off-by: Liu Shuo <b35...@freescale.com>
> Signed-off-by: Rafael Beims <rafael.be...@datacom.ind.br>
> ---
> Changes in v2:
>  - Added check to disallow the migration code to run in devices with
>  page size <= 2048
> 
>  drivers/mtd/nand/fsl_elbc_nand.c |  447 
> +++++++++++++++++++++++++++++++++++---
>  1 files changed, 419 insertions(+), 28 deletions(-)

Thanks for working on this!  I've been meaning to for a while, but have
been busy with other things.

> diff --git a/drivers/mtd/nand/fsl_elbc_nand.c 
> b/drivers/mtd/nand/fsl_elbc_nand.c
> index 9076ad4..3b6bb1d 100644
> --- a/drivers/mtd/nand/fsl_elbc_nand.c
> +++ b/drivers/mtd/nand/fsl_elbc_nand.c
> @@ -76,15 +76,17 @@ struct fsl_elbc_ctrl {
> 
>       /* device info */
>       fsl_lbc_t *regs;
> -     u8 __iomem *addr;        /* Address of assigned FCM buffer        */
> -     unsigned int page;       /* Last page written to / read from      */
> -     unsigned int read_bytes; /* Number of bytes read during command   */
> -     unsigned int column;     /* Saved column from SEQIN               */
> -     unsigned int index;      /* Pointer to next byte to 'read'        */
> -     unsigned int status;     /* status read from LTESR after last op  */
> -     unsigned int mdr;        /* UPM/FCM Data Register value           */
> -     unsigned int use_mdr;    /* Non zero if the MDR is to be set      */
> -     unsigned int oob;        /* Non zero if operating on OOB data     */
> +     u8 __iomem *addr;           /* Address of assigned FCM buffer       */
> +     unsigned int page;          /* Last page written to / read from     */
> +     unsigned int read_bytes;    /* Number of bytes read during command  */
> +     unsigned int column;        /* Saved column from SEQIN              */
> +     unsigned int index;         /* Pointer to next byte to 'read'       */
> +     unsigned int status;        /* status read from LTESR after last op */
> +     unsigned int mdr;           /* UPM/FCM Data Register value          */
> +     unsigned int use_mdr;       /* Non zero if the MDR is to be set     */
> +     unsigned int oob;           /* Non zero if operating on OOB data    */
> +     char *buffer;               /* Just used when pagesize is greater   */
> +                                 /* than FCM RAM 2K limitation           */
>  };
> 
>  /* These map to the positions used by the FCM hardware ECC generator */
> @@ -131,6 +133,15 @@ static struct nand_bbt_descr largepage_memorybased = {
>       .pattern = scan_ff_pattern,
>  };
> 
> +static u8 migrated_pattern[] = { 0xde, 0xad, 0xde, 0xad };

Let's generate a proper random number here -- or at least a meaningful
ASCII string.   Things like the above are too common and invite magic
number collision.

> +static int fsl_elbc_migrate_badblocks(struct mtd_info *mtd,
> +                                   struct nand_bbt_descr *bd)
> +{
> +     struct nand_chip *this = mtd->priv;
> +     int len, numblocks, i;
> +     int startblock;
> +     loff_t from;
> +     uint8_t *newoob, *buf;
> +
> +     struct fsl_elbc_mtd *priv = this->priv;
> +     struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +
> +     int num_subpages = mtd->writesize / 2048-1;
> +     len = mtd->writesize + mtd->oobsize;
> +     numblocks = this->chipsize >> (this->phys_erase_shift - 1);
> +     startblock = 0;
> +     from = 0;
> +
> +     newoob = vmalloc(mtd->oobsize);

Even if this were Linux, oobsize should never be big enough to need vmalloc.

> +     memset(newoob, 0xff, mtd->writesize+mtd->oobsize);

You're writing beyond the end of that buffer.

> +     for (i = startblock; i < numblocks; i += 2) {
> +             int page = (from >> this->page_shift) & this->pagemask;
> +             fsl_elbc_cmdfunc(mtd, NAND_CMD_READ0, 0, page);
> +
> +             /* As we are already using the hack to read the bytes
> +              * from NAND, the original badblock marker is offset
> +              * from its original location in the internal buffer.
> +              * This is because the hack reads 2048 + 64 and already
> +              * positions the spare in the correct region
> +              * (after the 4096 offset)
> +              */
> +             uint8_t *badblock_pattern = (ctrl->buffer+
> +                             mtd->writesize-(num_subpages*64))+bd->offs;

Spaces around operators.

I think that should be (num_subpages - 1) * 64.

> +             if (fsl_elbc_check_pattern(mtd, badblock_pattern, bd)) {
> +                     printf("Badblock found at %08x, migrating...\n", page);
> +                     memcpy(newoob, badblock_pattern , bd->len);

No space before ,

> +static int fsl_elbc_write_migration_marker(struct mtd_info *mtd,
> +                     uint8_t *buf, int len, struct nand_bbt_descr *bd)
> +{
> +     struct nand_chip *this = mtd->priv;
> +     struct nand_bbt_descr *td = this->bbt_td;
> +     int startblock;
> +     int dir, i;
> +     int blocks_to_write = 2;
> +
> +     /* Start below maximum bbt */
> +     startblock = (mtd->size >> this->phys_erase_shift) - td->maxblocks;
> +     dir = -1;

If you want startblock below the bbt area, I think you're off by one.

> +     for (i = 0; i < bd->maxblocks && blocks_to_write != 0; i++) {
> +             int actblock = startblock + dir*i;
> +             loff_t offs = (loff_t)actblock << this->phys_erase_shift;
> +             int page = (offs >> this->page_shift) & this->pagemask;
> +
> +             /* Avoid badblocks writing the marker... */
> +             if (!fsl_elbc_scan_read_raw_oob(mtd, buf, page, mtd->writesize,
> +                                     &largepage_memorybased)) {
> +
> +                     /* We are reusing this buffer, reset it */
> +                     memset(buf, 0xff, len);
> +                     memcpy(buf+bd->offs, bd->pattern, bd->len);
> +
> +                     /*  Mark the block as bad the same way that
> +                      *  it's done for the bbt. This should avoid
> +                      *  this block being overwritten
> +                      */
> +                     memset(buf+this->badblockpos, 0x02, 1);
> +
> +                     fsl_elbc_cmdfunc(mtd, NAND_CMD_SEQIN,
> +                                             mtd->writesize, page);
> +                     fsl_elbc_write_buf(mtd, buf, mtd->oobsize);
> +                     fsl_elbc_cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
> +                     blocks_to_write--;
> +                     printf("Wrote migration marker to offset: %x\n", page);
> +             }

Why are we writing the marker twice?

> +static int fsl_elbc_scan_bbt(struct mtd_info *mtd)
> +{
> +     struct nand_chip *this = mtd->priv;
> +     struct nand_bbt_descr *bd = &largepage_migrated;
> +     struct nand_bbt_descr *td = this->bbt_td;
> +     int len;
> +     int startblock, block, dir;
> +     uint8_t *buf;
> +     int migrate = 0;
> +
> +     if (mtd->writesize > 2048) {
> +             /* Start below maximum bbt */
> +             startblock = (mtd->size >> this->phys_erase_shift) - 
> td->maxblocks;

Again, off by one.

> +             dir = -1;

dir never seems to get set to anything else, so why a variable?

I think we should start with the last block, and search backwards until
we find it, or until we exceed some reasonable limit, such as
td->maxblocks * 2 (Is that really enough?  How many contiguous bad
blocks can we see?).  Actually writing a joint bbt/migration marker
(which was requested in earlier discussion to avoid wasting an erase
block, but I don't want it to be mandatory) can come later, but we want
to recognize it now.

> +             /* Allocate a temporary buffer for one eraseblock incl. oob */
> +             len = (1 << this->phys_erase_shift);
> +             len += (len >> this->page_shift) * mtd->oobsize;
> +             buf = vmalloc(len);

This code isn't going to be shared with Linux, so just malloc().
Likewise printf(...) instead of printk(KERN_ERR ...).

BTW, should mention at least in the changelog that this is partially
derived from code in nand_bbt.c.  Also, using this code means we need to
remove the "or later" from fsl_elbc_nand.c, because nand_bbt.c is GPL v2
only.  Wolfgang probably won't be pleased.  It might be better to just
write it from scratch -- I'm not sure how much it really helps to be
mimicking the bbt code here (without actually being able to share it),
versus straightforwardly coding this specific task.

> +             if (!buf) {
> +                     printk(KERN_ERR "fsl_elbc_nand: Out of memory\n");
> +                     kfree(this->bbt);
> +                     this->bbt = NULL;
> +                     return -ENOMEM;
> +             }

Why are we freeing the bbt here?  When did we allocate it?

> +             for (block = 0; block < td->maxblocks; block++) {
> +                     int actblock = startblock + dir * block;
> +                     loff_t offs = (loff_t)actblock << 
> this->phys_erase_shift;
> +                     int page = (offs >> this->page_shift) & this->pagemask;
> +
> +                     migrate = fsl_elbc_scan_read_raw_oob(mtd, buf, page,
> +                                     mtd->writesize, &largepage_migrated);
> +
> +                     /* We found the migration marker, get out of here */
> +                     if (migrate == 0)
> +                             break;
> +             }
> +
> +             if (migrate) {
> +                     printf("Moving factory marked badblocks to new oob\n");
> +                     fsl_elbc_migrate_badblocks(mtd, &largepage_memorybased);
> +                     fsl_elbc_write_migration_marker(mtd, buf, len,
> +                                                     &largepage_migrated);
> +             }
> +
> +             vfree(buf);
> +     }
> +     /* Now that we checked and possibly migrated badblock
> +      * markers, continue with default bbt scanning */

/*
 * U-Boot multiline comment style
 * is like this.
 */

Again, thanks for working on this.  To properly test it I need to get
raw reads/writes working properly with eLBC, though.  Once I do that,
I'll fix this patch up (unless you want to do a v3 before then).

-Scott

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to