aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/nand_bbt.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-28 21:26:58 -0400
committerThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-29 09:06:51 -0400
commit8593fbc68b0df1168995de76d1af38eb62fd6b62 (patch)
treedd244def53d2be4f1fbff9f74eac404fab8e240f /drivers/mtd/nand/nand_bbt.c
parentf4a43cfcecfcaeeaa40a9dbc1d1378298c22446e (diff)
[MTD] Rework the out of band handling completely
Hopefully the last iteration on this! The handling of out of band data on NAND was accompanied by tons of fruitless discussions and halfarsed patches to make it work for a particular problem. Sufficiently annoyed by I all those "I know it better" mails and the resonable amount of discarded "it solves my problem" patches, I finally decided to go for the big rework. After removing the _ecc variants of mtd read/write functions the solution to satisfy the various requirements was to refactor the read/write _oob functions in mtd. The major change is that read/write_oob now takes a pointer to an operation descriptor structure "struct mtd_oob_ops".instead of having a function with at least seven arguments. read/write_oob which should probably renamed to a more descriptive name, can do the following tasks: - read/write out of band data - read/write data content and out of band data - read/write raw data content and out of band data (ecc disabled) struct mtd_oob_ops has a mode field, which determines the oob handling mode. Aside of the MTD_OOB_RAW mode, which is intended to be especially for diagnostic purposes and some internal functions e.g. bad block table creation, the other two modes are for mtd clients: MTD_OOB_PLACE puts/gets the given oob data exactly to/from the place which is described by the ooboffs and ooblen fields of the mtd_oob_ops strcuture. It's up to the caller to make sure that the byte positions are not used by the ECC placement algorithms. MTD_OOB_AUTO puts/gets the given oob data automaticaly to/from the places in the out of band area which are described by the oobfree tuples in the ecclayout data structre which is associated to the devicee. The decision whether data plus oob or oob only handling is done depends on the setting of the datbuf member of the data structure. When datbuf == NULL then the internal read/write_oob functions are selected, otherwise the read/write data routines are invoked. Tested on a few platforms with all variants. Please be aware of possible regressions for your particular device / application scenario Disclaimer: Any whining will be ignored from those who just contributed "hot air blurb" and never sat down to tackle the underlying problem of the mess in the NAND driver grown over time and the big chunk of work to fix up the existing users. The problem was not the holiness of the existing MTD interfaces. The problems was the lack of time to go for the big overhaul. It's easy to add more mess to the existing one, but it takes alot of effort to go for a real solution. Improvements and bugfixes are welcome! Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers/mtd/nand/nand_bbt.c')
-rw-r--r--drivers/mtd/nand/nand_bbt.c188
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 */
236static 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 */
254static 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*/
244static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) 280static 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 */
308static 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 */
328static 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 */
276static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) 370static 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