diff options
Diffstat (limited to 'drivers/mtd/nand/nand_bbt.c')
-rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 188 |
1 files changed, 139 insertions, 49 deletions
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 40f99304df76..480c3cbf9bf9 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c | |||
@@ -230,6 +230,42 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc | |||
230 | return 0; | 230 | return 0; |
231 | } | 231 | } |
232 | 232 | ||
233 | /* | ||
234 | * Scan read raw data from flash | ||
235 | */ | ||
236 | static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, | ||
237 | size_t len) | ||
238 | { | ||
239 | struct mtd_oob_ops ops; | ||
240 | |||
241 | ops.mode = MTD_OOB_RAW; | ||
242 | ops.ooboffs = 0; | ||
243 | ops.ooblen = mtd->oobsize; | ||
244 | ops.oobbuf = buf; | ||
245 | ops.datbuf = buf; | ||
246 | ops.len = len; | ||
247 | |||
248 | return mtd->read_oob(mtd, offs, &ops); | ||
249 | } | ||
250 | |||
251 | /* | ||
252 | * Scan write data with oob to flash | ||
253 | */ | ||
254 | static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, | ||
255 | uint8_t *buf, uint8_t *oob) | ||
256 | { | ||
257 | struct mtd_oob_ops ops; | ||
258 | |||
259 | ops.mode = MTD_OOB_PLACE; | ||
260 | ops.ooboffs = 0; | ||
261 | ops.ooblen = mtd->oobsize; | ||
262 | ops.datbuf = buf; | ||
263 | ops.oobbuf = oob; | ||
264 | ops.len = len; | ||
265 | |||
266 | return mtd->write_oob(mtd, offs, &ops); | ||
267 | } | ||
268 | |||
233 | /** | 269 | /** |
234 | * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page | 270 | * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page |
235 | * @mtd: MTD device structure | 271 | * @mtd: MTD device structure |
@@ -241,27 +277,85 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc | |||
241 | * We assume that the bbt bits are in consecutive order. | 277 | * We assume that the bbt bits are in consecutive order. |
242 | * | 278 | * |
243 | */ | 279 | */ |
244 | static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) | 280 | static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, |
281 | struct nand_bbt_descr *td, struct nand_bbt_descr *md) | ||
245 | { | 282 | { |
246 | struct nand_chip *this = mtd->priv; | 283 | struct nand_chip *this = mtd->priv; |
247 | 284 | ||
248 | /* Read the primary version, if available */ | 285 | /* Read the primary version, if available */ |
249 | if (td->options & NAND_BBT_VERSION) { | 286 | if (td->options & NAND_BBT_VERSION) { |
250 | nand_read_raw(mtd, buf, td->pages[0] << this->page_shift, mtd->writesize, mtd->oobsize); | 287 | scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, |
288 | mtd->writesize); | ||
251 | td->version[0] = buf[mtd->writesize + td->veroffs]; | 289 | td->version[0] = buf[mtd->writesize + td->veroffs]; |
252 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); | 290 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", |
291 | td->pages[0], td->version[0]); | ||
253 | } | 292 | } |
254 | 293 | ||
255 | /* Read the mirror version, if available */ | 294 | /* Read the mirror version, if available */ |
256 | if (md && (md->options & NAND_BBT_VERSION)) { | 295 | if (md && (md->options & NAND_BBT_VERSION)) { |
257 | nand_read_raw(mtd, buf, md->pages[0] << this->page_shift, mtd->writesize, mtd->oobsize); | 296 | scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, |
297 | mtd->writesize); | ||
258 | md->version[0] = buf[mtd->writesize + md->veroffs]; | 298 | md->version[0] = buf[mtd->writesize + md->veroffs]; |
259 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); | 299 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", |
300 | md->pages[0], md->version[0]); | ||
260 | } | 301 | } |
261 | |||
262 | return 1; | 302 | return 1; |
263 | } | 303 | } |
264 | 304 | ||
305 | /* | ||
306 | * Scan a given block full | ||
307 | */ | ||
308 | static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, | ||
309 | loff_t offs, uint8_t *buf, size_t readlen, | ||
310 | int scanlen, int len) | ||
311 | { | ||
312 | int ret, j; | ||
313 | |||
314 | ret = scan_read_raw(mtd, buf, offs, readlen); | ||
315 | if (ret) | ||
316 | return ret; | ||
317 | |||
318 | for (j = 0; j < len; j++, buf += scanlen) { | ||
319 | if (check_pattern(buf, scanlen, mtd->writesize, bd)) | ||
320 | return 1; | ||
321 | } | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | /* | ||
326 | * Scan a given block partially | ||
327 | */ | ||
328 | static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, | ||
329 | loff_t offs, uint8_t *buf, int len) | ||
330 | { | ||
331 | struct mtd_oob_ops ops; | ||
332 | int j, ret; | ||
333 | |||
334 | ops.len = mtd->oobsize; | ||
335 | ops.ooblen = mtd->oobsize; | ||
336 | ops.oobbuf = buf; | ||
337 | ops.ooboffs = 0; | ||
338 | ops.datbuf = NULL; | ||
339 | ops.mode = MTD_OOB_PLACE; | ||
340 | |||
341 | for (j = 0; j < len; j++) { | ||
342 | /* | ||
343 | * Read the full oob until read_oob is fixed to | ||
344 | * handle single byte reads for 16 bit | ||
345 | * buswidth | ||
346 | */ | ||
347 | ret = mtd->read_oob(mtd, offs, &ops); | ||
348 | if (ret) | ||
349 | return ret; | ||
350 | |||
351 | if (check_short_pattern(buf, bd)) | ||
352 | return 1; | ||
353 | |||
354 | offs += mtd->writesize; | ||
355 | } | ||
356 | return 0; | ||
357 | } | ||
358 | |||
265 | /** | 359 | /** |
266 | * create_bbt - [GENERIC] Create a bad block table by scanning the device | 360 | * create_bbt - [GENERIC] Create a bad block table by scanning the device |
267 | * @mtd: MTD device structure | 361 | * @mtd: MTD device structure |
@@ -273,13 +367,14 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des | |||
273 | * Create a bad block table by scanning the device | 367 | * Create a bad block table by scanning the device |
274 | * for the given good/bad block identify pattern | 368 | * for the given good/bad block identify pattern |
275 | */ | 369 | */ |
276 | static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) | 370 | static int create_bbt(struct mtd_info *mtd, uint8_t *buf, |
371 | struct nand_bbt_descr *bd, int chip) | ||
277 | { | 372 | { |
278 | struct nand_chip *this = mtd->priv; | 373 | struct nand_chip *this = mtd->priv; |
279 | int i, j, numblocks, len, scanlen; | 374 | int i, numblocks, len, scanlen; |
280 | int startblock; | 375 | int startblock; |
281 | loff_t from; | 376 | loff_t from; |
282 | size_t readlen, ooblen; | 377 | size_t readlen; |
283 | 378 | ||
284 | printk(KERN_INFO "Scanning device for bad blocks\n"); | 379 | printk(KERN_INFO "Scanning device for bad blocks\n"); |
285 | 380 | ||
@@ -294,18 +389,17 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
294 | 389 | ||
295 | if (!(bd->options & NAND_BBT_SCANEMPTY)) { | 390 | if (!(bd->options & NAND_BBT_SCANEMPTY)) { |
296 | /* We need only read few bytes from the OOB area */ | 391 | /* We need only read few bytes from the OOB area */ |
297 | scanlen = ooblen = 0; | 392 | scanlen = 0; |
298 | readlen = bd->len; | 393 | readlen = bd->len; |
299 | } else { | 394 | } else { |
300 | /* Full page content should be read */ | 395 | /* Full page content should be read */ |
301 | scanlen = mtd->writesize + mtd->oobsize; | 396 | scanlen = mtd->writesize + mtd->oobsize; |
302 | readlen = len * mtd->writesize; | 397 | readlen = len * mtd->writesize; |
303 | ooblen = len * mtd->oobsize; | ||
304 | } | 398 | } |
305 | 399 | ||
306 | if (chip == -1) { | 400 | if (chip == -1) { |
307 | /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it | 401 | /* Note that numblocks is 2 * (real numblocks) here, see i+=2 |
308 | * makes shifting and masking less painful */ | 402 | * below as it makes shifting and masking less painful */ |
309 | numblocks = mtd->size >> (this->bbt_erase_shift - 1); | 403 | numblocks = mtd->size >> (this->bbt_erase_shift - 1); |
310 | startblock = 0; | 404 | startblock = 0; |
311 | from = 0; | 405 | from = 0; |
@@ -324,35 +418,21 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
324 | for (i = startblock; i < numblocks;) { | 418 | for (i = startblock; i < numblocks;) { |
325 | int ret; | 419 | int ret; |
326 | 420 | ||
327 | if (bd->options & NAND_BBT_SCANEMPTY) | 421 | if (bd->options & NAND_BBT_SCANALLPAGES) |
328 | if ((ret = nand_read_raw(mtd, buf, from, readlen, ooblen))) | 422 | ret = scan_block_full(mtd, bd, from, buf, readlen, |
329 | return ret; | 423 | scanlen, len); |
330 | 424 | else | |
331 | for (j = 0; j < len; j++) { | 425 | ret = scan_block_fast(mtd, bd, from, buf, len); |
332 | if (!(bd->options & NAND_BBT_SCANEMPTY)) { | 426 | |
333 | size_t retlen; | 427 | if (ret < 0) |
334 | 428 | return ret; | |
335 | /* Read the full oob until read_oob is fixed to | 429 | |
336 | * handle single byte reads for 16 bit buswidth */ | 430 | if (ret) { |
337 | ret = mtd->read_oob(mtd, from + j * mtd->writesize, mtd->oobsize, &retlen, buf); | 431 | this->bbt[i >> 3] |= 0x03 << (i & 0x6); |
338 | if (ret) | 432 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", |
339 | return ret; | 433 | i >> 1, (unsigned int)from); |
340 | |||
341 | if (check_short_pattern(buf, bd)) { | ||
342 | this->bbt[i >> 3] |= 0x03 << (i & 0x6); | ||
343 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | ||
344 | i >> 1, (unsigned int)from); | ||
345 | break; | ||
346 | } | ||
347 | } else { | ||
348 | if (check_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { | ||
349 | this->bbt[i >> 3] |= 0x03 << (i & 0x6); | ||
350 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | ||
351 | i >> 1, (unsigned int)from); | ||
352 | break; | ||
353 | } | ||
354 | } | ||
355 | } | 434 | } |
435 | |||
356 | i += 2; | 436 | i += 2; |
357 | from += (1 << this->bbt_erase_shift); | 437 | from += (1 << this->bbt_erase_shift); |
358 | } | 438 | } |
@@ -383,6 +463,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
383 | int bits, startblock, block, dir; | 463 | int bits, startblock, block, dir; |
384 | int scanlen = mtd->writesize + mtd->oobsize; | 464 | int scanlen = mtd->writesize + mtd->oobsize; |
385 | int bbtblocks; | 465 | int bbtblocks; |
466 | int blocktopage = this->bbt_erase_shift - this->page_shift; | ||
386 | 467 | ||
387 | /* Search direction top -> down ? */ | 468 | /* Search direction top -> down ? */ |
388 | if (td->options & NAND_BBT_LASTBLOCK) { | 469 | if (td->options & NAND_BBT_LASTBLOCK) { |
@@ -412,11 +493,14 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
412 | td->pages[i] = -1; | 493 | td->pages[i] = -1; |
413 | /* Scan the maximum number of blocks */ | 494 | /* Scan the maximum number of blocks */ |
414 | for (block = 0; block < td->maxblocks; block++) { | 495 | for (block = 0; block < td->maxblocks; block++) { |
496 | |||
415 | int actblock = startblock + dir * block; | 497 | int actblock = startblock + dir * block; |
498 | loff_t offs = actblock << this->bbt_erase_shift; | ||
499 | |||
416 | /* Read first page */ | 500 | /* Read first page */ |
417 | nand_read_raw(mtd, buf, actblock << this->bbt_erase_shift, mtd->writesize, mtd->oobsize); | 501 | scan_read_raw(mtd, buf, offs, mtd->writesize); |
418 | if (!check_pattern(buf, scanlen, mtd->writesize, td)) { | 502 | if (!check_pattern(buf, scanlen, mtd->writesize, td)) { |
419 | td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); | 503 | td->pages[i] = actblock << blocktopage; |
420 | if (td->options & NAND_BBT_VERSION) { | 504 | if (td->options & NAND_BBT_VERSION) { |
421 | td->version[i] = buf[mtd->writesize + td->veroffs]; | 505 | td->version[i] = buf[mtd->writesize + td->veroffs]; |
422 | } | 506 | } |
@@ -481,8 +565,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, | |||
481 | int nrchips, bbtoffs, pageoffs, ooboffs; | 565 | int nrchips, bbtoffs, pageoffs, ooboffs; |
482 | uint8_t msk[4]; | 566 | uint8_t msk[4]; |
483 | uint8_t rcode = td->reserved_block_code; | 567 | uint8_t rcode = td->reserved_block_code; |
484 | size_t retlen, len = 0, ooblen; | 568 | size_t retlen, len = 0; |
485 | loff_t to; | 569 | loff_t to; |
570 | struct mtd_oob_ops ops; | ||
571 | |||
572 | ops.ooblen = mtd->oobsize; | ||
573 | ops.ooboffs = 0; | ||
574 | ops.datbuf = NULL; | ||
575 | ops.mode = MTD_OOB_PLACE; | ||
486 | 576 | ||
487 | if (!rcode) | 577 | if (!rcode) |
488 | rcode = 0xff; | 578 | rcode = 0xff; |
@@ -583,10 +673,10 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, | |||
583 | "bad block table\n"); | 673 | "bad block table\n"); |
584 | } | 674 | } |
585 | /* Read oob data */ | 675 | /* Read oob data */ |
586 | ooblen = (len >> this->page_shift) * mtd->oobsize; | 676 | ops.len = (len >> this->page_shift) * mtd->oobsize; |
587 | res = mtd->read_oob(mtd, to + mtd->writesize, ooblen, | 677 | ops.oobbuf = &buf[len]; |
588 | &retlen, &buf[len]); | 678 | res = mtd->read_oob(mtd, to + mtd->writesize, &ops); |
589 | if (res < 0 || retlen != ooblen) | 679 | if (res < 0 || ops.retlen != ops.len) |
590 | goto outerr; | 680 | goto outerr; |
591 | 681 | ||
592 | /* Calc the byte offset in the buffer */ | 682 | /* Calc the byte offset in the buffer */ |
@@ -635,7 +725,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, | |||
635 | if (res < 0) | 725 | if (res < 0) |
636 | goto outerr; | 726 | goto outerr; |
637 | 727 | ||
638 | res = nand_write_raw(mtd, to, len, &retlen, buf, &buf[len]); | 728 | res = scan_write_bbt(mtd, to, len, buf, &buf[len]); |
639 | if (res < 0) | 729 | if (res < 0) |
640 | goto outerr; | 730 | goto outerr; |
641 | 731 | ||