diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-11 10:18:18 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-11 10:18:18 -0700 |
commit | 200d481f28be4522464bb849dd0eb5f8cb6be781 (patch) | |
tree | 8cd00ead1b202dfd377cf34000a5193959aa2e8b /drivers/mtd/nand/nand_bbt.c | |
parent | f43a64c5e1a65d12b9b53a35ed2d5db441fcb64c (diff) | |
parent | 97f927a4d7dbccde0a854a62c3ea54d90bae8679 (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/tglx/mtd-2.6
Diffstat (limited to 'drivers/mtd/nand/nand_bbt.c')
-rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 114 |
1 files changed, 86 insertions, 28 deletions
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 9a1949751c1..5ac2d296222 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -6,7 +6,7 @@ * * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $ + * $Id: nand_bbt.c,v 1.33 2005/06/14 15:47:56 gleixner Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -77,7 +77,7 @@ */ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) { - int i, end; + int i, end = 0; uint8_t *p = buf; end = paglen + td->offs; @@ -95,9 +95,9 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des return -1; } - p += td->len; - end += td->len; if (td->options & NAND_BBT_SCANEMPTY) { + p += td->len; + end += td->len; for (i = end; i < len; i++) { if (*p++ != 0xff) return -1; @@ -106,6 +106,32 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des return 0; } +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. Same as check_pattern, but + * no optional empty check and the pattern is expected to start + * at offset 0. + * +*/ +static int check_short_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +{ + int i; + uint8_t *p = buf; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) + return -1; + } + return 0; +} + /** * read_bbt - [GENERIC] Read the bad block table starting from page * @mtd: MTD device structure @@ -252,7 +278,7 @@ static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_de * Create a bad block table by scanning the device * for the given good/bad block identify pattern */ -static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) { struct nand_chip *this = mtd->priv; int i, j, numblocks, len, scanlen; @@ -270,9 +296,17 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc else len = 1; } - scanlen = mtd->oobblock + mtd->oobsize; - readlen = len * mtd->oobblock; - ooblen = len * mtd->oobsize; + + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + /* We need only read few bytes from the OOB area */ + scanlen = ooblen = 0; + readlen = bd->len; + } else { + /* Full page content should be read */ + scanlen = mtd->oobblock + mtd->oobsize; + readlen = len * mtd->oobblock; + ooblen = len * mtd->oobsize; + } if (chip == -1) { /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it @@ -284,7 +318,7 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc if (chip >= this->numchips) { printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", chip + 1, this->numchips); - return; + return -EINVAL; } numblocks = this->chipsize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; @@ -293,18 +327,41 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc } for (i = startblock; i < numblocks;) { - nand_read_raw (mtd, buf, from, readlen, ooblen); + int ret; + + if (bd->options & NAND_BBT_SCANEMPTY) + if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen))) + return ret; + for (j = 0; j < len; j++) { - if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { - this->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int) from); - break; + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + size_t retlen; + + /* No need to read pages fully, just read required OOB bytes */ + ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs, + readlen, &retlen, &buf[0]); + if (ret) + return ret; + + if (check_short_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int) from); + break; + } + } else { + if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int) from); + break; + } } } i += 2; from += (1 << this->bbt_erase_shift); } + return 0; } /** @@ -589,14 +646,12 @@ write: * The function creates a memory based bbt by scanning the device * for manufacturer / software marked good / bad blocks */ -static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; - /* Ensure that we only scan for the pattern and nothing else */ - bd->options = 0; - create_bbt (mtd, this->data_buf, bd, -1); - return 0; + bd->options &= ~NAND_BBT_SCANEMPTY; + return create_bbt (mtd, this->data_buf, bd, -1); } /** @@ -808,8 +863,14 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) /* If no primary table decriptor is given, scan the device * to build a memory based bad block table */ - if (!td) - return nand_memory_bbt(mtd, bd); + if (!td) { + if ((res = nand_memory_bbt(mtd, bd))) { + printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); + kfree (this->bbt); + this->bbt = NULL; + } + return res; + } /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); @@ -904,14 +965,11 @@ out: } /* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks - * - * The memory based patterns just - */ + * while scanning a device for factory marked good / bad blocks. */ static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; static struct nand_bbt_descr smallpage_memorybased = { - .options = 0, + .options = NAND_BBT_SCAN2NDPAGE, .offs = 5, .len = 1, .pattern = scan_ff_pattern @@ -1042,7 +1100,7 @@ int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt) res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, res, block >> 1); + (unsigned int)offs, block >> 1, res); switch ((int)res) { case 0x00: return 0; |