diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-11 13:18:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-11 13:18:18 -0400 |
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 9a1949751c1f..5ac2d2962220 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c | |||
@@ -6,7 +6,7 @@ | |||
6 | * | 6 | * |
7 | * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) | 7 | * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) |
8 | * | 8 | * |
9 | * $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $ | 9 | * $Id: nand_bbt.c,v 1.33 2005/06/14 15:47:56 gleixner Exp $ |
10 | * | 10 | * |
11 | * This program is free software; you can redistribute it and/or modify | 11 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License version 2 as | 12 | * it under the terms of the GNU General Public License version 2 as |
@@ -77,7 +77,7 @@ | |||
77 | */ | 77 | */ |
78 | static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) | 78 | static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) |
79 | { | 79 | { |
80 | int i, end; | 80 | int i, end = 0; |
81 | uint8_t *p = buf; | 81 | uint8_t *p = buf; |
82 | 82 | ||
83 | end = paglen + td->offs; | 83 | end = paglen + td->offs; |
@@ -95,9 +95,9 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des | |||
95 | return -1; | 95 | return -1; |
96 | } | 96 | } |
97 | 97 | ||
98 | p += td->len; | ||
99 | end += td->len; | ||
100 | if (td->options & NAND_BBT_SCANEMPTY) { | 98 | if (td->options & NAND_BBT_SCANEMPTY) { |
99 | p += td->len; | ||
100 | end += td->len; | ||
101 | for (i = end; i < len; i++) { | 101 | for (i = end; i < len; i++) { |
102 | if (*p++ != 0xff) | 102 | if (*p++ != 0xff) |
103 | return -1; | 103 | return -1; |
@@ -106,6 +106,32 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des | |||
106 | return 0; | 106 | return 0; |
107 | } | 107 | } |
108 | 108 | ||
109 | /** | ||
110 | * check_short_pattern - [GENERIC] check if a pattern is in the buffer | ||
111 | * @buf: the buffer to search | ||
112 | * @len: the length of buffer to search | ||
113 | * @paglen: the pagelength | ||
114 | * @td: search pattern descriptor | ||
115 | * | ||
116 | * Check for a pattern at the given place. Used to search bad block | ||
117 | * tables and good / bad block identifiers. Same as check_pattern, but | ||
118 | * no optional empty check and the pattern is expected to start | ||
119 | * at offset 0. | ||
120 | * | ||
121 | */ | ||
122 | static int check_short_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) | ||
123 | { | ||
124 | int i; | ||
125 | uint8_t *p = buf; | ||
126 | |||
127 | /* Compare the pattern */ | ||
128 | for (i = 0; i < td->len; i++) { | ||
129 | if (p[i] != td->pattern[i]) | ||
130 | return -1; | ||
131 | } | ||
132 | return 0; | ||
133 | } | ||
134 | |||
109 | /** | 135 | /** |
110 | * read_bbt - [GENERIC] Read the bad block table starting from page | 136 | * read_bbt - [GENERIC] Read the bad block table starting from page |
111 | * @mtd: MTD device structure | 137 | * @mtd: MTD device structure |
@@ -252,7 +278,7 @@ static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_de | |||
252 | * Create a bad block table by scanning the device | 278 | * Create a bad block table by scanning the device |
253 | * for the given good/bad block identify pattern | 279 | * for the given good/bad block identify pattern |
254 | */ | 280 | */ |
255 | static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) | 281 | static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) |
256 | { | 282 | { |
257 | struct nand_chip *this = mtd->priv; | 283 | struct nand_chip *this = mtd->priv; |
258 | int i, j, numblocks, len, scanlen; | 284 | 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 | |||
270 | else | 296 | else |
271 | len = 1; | 297 | len = 1; |
272 | } | 298 | } |
273 | scanlen = mtd->oobblock + mtd->oobsize; | 299 | |
274 | readlen = len * mtd->oobblock; | 300 | if (!(bd->options & NAND_BBT_SCANEMPTY)) { |
275 | ooblen = len * mtd->oobsize; | 301 | /* We need only read few bytes from the OOB area */ |
302 | scanlen = ooblen = 0; | ||
303 | readlen = bd->len; | ||
304 | } else { | ||
305 | /* Full page content should be read */ | ||
306 | scanlen = mtd->oobblock + mtd->oobsize; | ||
307 | readlen = len * mtd->oobblock; | ||
308 | ooblen = len * mtd->oobsize; | ||
309 | } | ||
276 | 310 | ||
277 | if (chip == -1) { | 311 | if (chip == -1) { |
278 | /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it | 312 | /* 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 | |||
284 | if (chip >= this->numchips) { | 318 | if (chip >= this->numchips) { |
285 | printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", | 319 | printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", |
286 | chip + 1, this->numchips); | 320 | chip + 1, this->numchips); |
287 | return; | 321 | return -EINVAL; |
288 | } | 322 | } |
289 | numblocks = this->chipsize >> (this->bbt_erase_shift - 1); | 323 | numblocks = this->chipsize >> (this->bbt_erase_shift - 1); |
290 | startblock = chip * numblocks; | 324 | startblock = chip * numblocks; |
@@ -293,18 +327,41 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc | |||
293 | } | 327 | } |
294 | 328 | ||
295 | for (i = startblock; i < numblocks;) { | 329 | for (i = startblock; i < numblocks;) { |
296 | nand_read_raw (mtd, buf, from, readlen, ooblen); | 330 | int ret; |
331 | |||
332 | if (bd->options & NAND_BBT_SCANEMPTY) | ||
333 | if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen))) | ||
334 | return ret; | ||
335 | |||
297 | for (j = 0; j < len; j++) { | 336 | for (j = 0; j < len; j++) { |
298 | if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { | 337 | if (!(bd->options & NAND_BBT_SCANEMPTY)) { |
299 | this->bbt[i >> 3] |= 0x03 << (i & 0x6); | 338 | size_t retlen; |
300 | printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | 339 | |
301 | i >> 1, (unsigned int) from); | 340 | /* No need to read pages fully, just read required OOB bytes */ |
302 | break; | 341 | ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs, |
342 | readlen, &retlen, &buf[0]); | ||
343 | if (ret) | ||
344 | return ret; | ||
345 | |||
346 | if (check_short_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { | ||
347 | this->bbt[i >> 3] |= 0x03 << (i & 0x6); | ||
348 | printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | ||
349 | i >> 1, (unsigned int) from); | ||
350 | break; | ||
351 | } | ||
352 | } else { | ||
353 | if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { | ||
354 | this->bbt[i >> 3] |= 0x03 << (i & 0x6); | ||
355 | printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | ||
356 | i >> 1, (unsigned int) from); | ||
357 | break; | ||
358 | } | ||
303 | } | 359 | } |
304 | } | 360 | } |
305 | i += 2; | 361 | i += 2; |
306 | from += (1 << this->bbt_erase_shift); | 362 | from += (1 << this->bbt_erase_shift); |
307 | } | 363 | } |
364 | return 0; | ||
308 | } | 365 | } |
309 | 366 | ||
310 | /** | 367 | /** |
@@ -589,14 +646,12 @@ write: | |||
589 | * The function creates a memory based bbt by scanning the device | 646 | * The function creates a memory based bbt by scanning the device |
590 | * for manufacturer / software marked good / bad blocks | 647 | * for manufacturer / software marked good / bad blocks |
591 | */ | 648 | */ |
592 | static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) | 649 | static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) |
593 | { | 650 | { |
594 | struct nand_chip *this = mtd->priv; | 651 | struct nand_chip *this = mtd->priv; |
595 | 652 | ||
596 | /* Ensure that we only scan for the pattern and nothing else */ | 653 | bd->options &= ~NAND_BBT_SCANEMPTY; |
597 | bd->options = 0; | 654 | return create_bbt (mtd, this->data_buf, bd, -1); |
598 | create_bbt (mtd, this->data_buf, bd, -1); | ||
599 | return 0; | ||
600 | } | 655 | } |
601 | 656 | ||
602 | /** | 657 | /** |
@@ -808,8 +863,14 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) | |||
808 | /* If no primary table decriptor is given, scan the device | 863 | /* If no primary table decriptor is given, scan the device |
809 | * to build a memory based bad block table | 864 | * to build a memory based bad block table |
810 | */ | 865 | */ |
811 | if (!td) | 866 | if (!td) { |
812 | return nand_memory_bbt(mtd, bd); | 867 | if ((res = nand_memory_bbt(mtd, bd))) { |
868 | printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); | ||
869 | kfree (this->bbt); | ||
870 | this->bbt = NULL; | ||
871 | } | ||
872 | return res; | ||
873 | } | ||
813 | 874 | ||
814 | /* Allocate a temporary buffer for one eraseblock incl. oob */ | 875 | /* Allocate a temporary buffer for one eraseblock incl. oob */ |
815 | len = (1 << this->bbt_erase_shift); | 876 | len = (1 << this->bbt_erase_shift); |
@@ -904,14 +965,11 @@ out: | |||
904 | } | 965 | } |
905 | 966 | ||
906 | /* Define some generic bad / good block scan pattern which are used | 967 | /* Define some generic bad / good block scan pattern which are used |
907 | * while scanning a device for factory marked good / bad blocks | 968 | * while scanning a device for factory marked good / bad blocks. */ |
908 | * | ||
909 | * The memory based patterns just | ||
910 | */ | ||
911 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; | 969 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; |
912 | 970 | ||
913 | static struct nand_bbt_descr smallpage_memorybased = { | 971 | static struct nand_bbt_descr smallpage_memorybased = { |
914 | .options = 0, | 972 | .options = NAND_BBT_SCAN2NDPAGE, |
915 | .offs = 5, | 973 | .offs = 5, |
916 | .len = 1, | 974 | .len = 1, |
917 | .pattern = scan_ff_pattern | 975 | .pattern = scan_ff_pattern |
@@ -1042,7 +1100,7 @@ int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt) | |||
1042 | res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; | 1100 | res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; |
1043 | 1101 | ||
1044 | DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", | 1102 | DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", |
1045 | (unsigned int)offs, res, block >> 1); | 1103 | (unsigned int)offs, block >> 1, res); |
1046 | 1104 | ||
1047 | switch ((int)res) { | 1105 | switch ((int)res) { |
1048 | case 0x00: return 0; | 1106 | case 0x00: return 0; |