1 /* Linux driver for NAND Flash Translation Layer */ 2 /* (c) 1999 Machine Vision Holdings, Inc. */ 3 /* Author: David Woodhouse <dwmw2@infradead.org> */ 4 /* $Id: nftlcore.c,v 1.98 2005/11/07 11:14:21 gleixner Exp $ */ 5 6 /* 7 The contents of this file are distributed under the GNU General 8 Public License version 2. The author places no additional 9 restrictions of any kind on it. 10 */ 11 12 #define PRERELEASE 13 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <asm/errno.h> 17 #include <asm/io.h> 18 #include <asm/uaccess.h> 19 #include <linux/miscdevice.h> 20 #include <linux/pci.h> 21 #include <linux/delay.h> 22 #include <linux/slab.h> 23 #include <linux/sched.h> 24 #include <linux/init.h> 25 #include <linux/hdreg.h> 26 27 #include <linux/kmod.h> 28 #include <linux/mtd/mtd.h> 29 #include <linux/mtd/nand.h> 30 #include <linux/mtd/nftl.h> 31 #include <linux/mtd/blktrans.h> 32 33 /* maximum number of loops while examining next block, to have a 34 chance to detect consistency problems (they should never happen 35 because of the checks done in the mounting */ 36 37 #define MAX_LOOPS 10000 38 39 40 static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) 41 { 42 struct NFTLrecord *nftl; 43 unsigned long temp; 44 45 if (mtd->type != MTD_NANDFLASH) 46 return; 47 /* OK, this is moderately ugly. But probably safe. Alternatives? */ 48 if (memcmp(mtd->name, "DiskOnChip", 10)) 49 return; 50 51 if (!mtd->block_isbad) { 52 printk(KERN_ERR 53 "NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n" 54 "Please use the new diskonchip driver under the NAND subsystem.\n"); 55 return; 56 } 57 58 DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name); 59 60 nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); 61 62 if (!nftl) { 63 printk(KERN_WARNING "NFTL: out of memory for data structures\n"); 64 return; 65 } 66 memset(nftl, 0, sizeof(*nftl)); 67 68 nftl->mbd.mtd = mtd; 69 nftl->mbd.devnum = -1; 70 71 nftl->mbd.tr = tr; 72 73 if (NFTL_mount(nftl) < 0) { 74 printk(KERN_WARNING "NFTL: could not mount device\n"); 75 kfree(nftl); 76 return; 77 } 78 79 /* OK, it's a new one. Set up all the data structures. */ 80 81 /* Calculate geometry */ 82 nftl->cylinders = 1024; 83 nftl->heads = 16; 84 85 temp = nftl->cylinders * nftl->heads; 86 nftl->sectors = nftl->mbd.size / temp; 87 if (nftl->mbd.size % temp) { 88 nftl->sectors++; 89 temp = nftl->cylinders * nftl->sectors; 90 nftl->heads = nftl->mbd.size / temp; 91 92 if (nftl->mbd.size % temp) { 93 nftl->heads++; 94 temp = nftl->heads * nftl->sectors; 95 nftl->cylinders = nftl->mbd.size / temp; 96 } 97 } 98 99 if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) { 100 /* 101 Oh no we don't have 102 mbd.size == heads * cylinders * sectors 103 */ 104 printk(KERN_WARNING "NFTL: cannot calculate a geometry to " 105 "match size of 0x%lx.\n", nftl->mbd.size); 106 printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d " 107 "(== 0x%lx sects)\n", 108 nftl->cylinders, nftl->heads , nftl->sectors, 109 (long)nftl->cylinders * (long)nftl->heads * 110 (long)nftl->sectors ); 111 } 112 113 if (add_mtd_blktrans_dev(&nftl->mbd)) { 114 kfree(nftl->ReplUnitTable); 115 kfree(nftl->EUNtable); 116 kfree(nftl); 117 return; 118 } 119 #ifdef PSYCHO_DEBUG 120 printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); 121 #endif 122 } 123 124 static void nftl_remove_dev(struct mtd_blktrans_dev *dev) 125 { 126 struct NFTLrecord *nftl = (void *)dev; 127 128 DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum); 129 130 del_mtd_blktrans_dev(dev); 131 kfree(nftl->ReplUnitTable); 132 kfree(nftl->EUNtable); 133 kfree(nftl); 134 } 135 136 /* 137 * Read oob data from flash 138 */ 139 int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, 140 size_t *retlen, uint8_t *buf) 141 { 142 struct mtd_oob_ops ops; 143 int res; 144 145 ops.mode = MTD_OOB_PLACE; 146 ops.ooboffs = offs & (mtd->writesize - 1); 147 ops.ooblen = len; 148 ops.oobbuf = buf; 149 ops.datbuf = NULL; 150 151 res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); 152 *retlen = ops.oobretlen; 153 return res; 154 } 155 156 /* 157 * Write oob data to flash 158 */ 159 int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, 160 size_t *retlen, uint8_t *buf) 161 { 162 struct mtd_oob_ops ops; 163 int res; 164 165 ops.mode = MTD_OOB_PLACE; 166 ops.ooboffs = offs & (mtd->writesize - 1); 167 ops.ooblen = len; 168 ops.oobbuf = buf; 169 ops.datbuf = NULL; 170 171 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); 172 *retlen = ops.oobretlen; 173 return res; 174 } 175 176 #ifdef CONFIG_NFTL_RW 177 178 /* 179 * Write data and oob to flash 180 */ 181 static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len, 182 size_t *retlen, uint8_t *buf, uint8_t *oob) 183 { 184 struct mtd_oob_ops ops; 185 int res; 186 187 ops.mode = MTD_OOB_PLACE; 188 ops.ooboffs = offs; 189 ops.ooblen = mtd->oobsize; 190 ops.oobbuf = oob; 191 ops.datbuf = buf; 192 ops.len = len; 193 194 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); 195 *retlen = ops.retlen; 196 return res; 197 } 198 199 /* Actual NFTL access routines */ 200 /* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used 201 * when the give Virtual Unit Chain 202 */ 203 static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate ) 204 { 205 /* For a given Virtual Unit Chain: find or create a free block and 206 add it to the chain */ 207 /* We're passed the number of the last EUN in the chain, to save us from 208 having to look it up again */ 209 u16 pot = nftl->LastFreeEUN; 210 int silly = nftl->nb_blocks; 211 212 /* Normally, we force a fold to happen before we run out of free blocks completely */ 213 if (!desperate && nftl->numfreeEUNs < 2) { 214 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n"); 215 return 0xffff; 216 } 217 218 /* Scan for a free block */ 219 do { 220 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) { 221 nftl->LastFreeEUN = pot; 222 nftl->numfreeEUNs--; 223 return pot; 224 } 225 226 /* This will probably point to the MediaHdr unit itself, 227 right at the beginning of the partition. But that unit 228 (and the backup unit too) should have the UCI set 229 up so that it's not selected for overwriting */ 230 if (++pot > nftl->lastEUN) 231 pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN); 232 233 if (!silly--) { 234 printk("Argh! No free blocks found! LastFreeEUN = %d, " 235 "FirstEUN = %d\n", nftl->LastFreeEUN, 236 le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN)); 237 return 0xffff; 238 } 239 } while (pot != nftl->LastFreeEUN); 240 241 return 0xffff; 242 } 243 244 static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock ) 245 { 246 struct mtd_info *mtd = nftl->mbd.mtd; 247 u16 BlockMap[MAX_SECTORS_PER_UNIT]; 248 unsigned char BlockLastState[MAX_SECTORS_PER_UNIT]; 249 unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT]; 250 unsigned int thisEUN; 251 int block; 252 int silly; 253 unsigned int targetEUN; 254 struct nftl_oob oob; 255 int inplace = 1; 256 size_t retlen; 257 258 memset(BlockMap, 0xff, sizeof(BlockMap)); 259 memset(BlockFreeFound, 0, sizeof(BlockFreeFound)); 260 261 thisEUN = nftl->EUNtable[thisVUC]; 262 263 if (thisEUN == BLOCK_NIL) { 264 printk(KERN_WARNING "Trying to fold non-existent " 265 "Virtual Unit Chain %d!\n", thisVUC); 266 return BLOCK_NIL; 267 } 268 269 /* Scan to find the Erase Unit which holds the actual data for each 270 512-byte block within the Chain. 271 */ 272 silly = MAX_LOOPS; 273 targetEUN = BLOCK_NIL; 274 while (thisEUN <= nftl->lastEUN ) { 275 unsigned int status, foldmark; 276 277 targetEUN = thisEUN; 278 for (block = 0; block < nftl->EraseSize / 512; block ++) { 279 nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + 280 (block * 512), 16 , &retlen, 281 (char *)&oob); 282 if (block == 2) { 283 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1; 284 if (foldmark == FOLD_MARK_IN_PROGRESS) { 285 DEBUG(MTD_DEBUG_LEVEL1, 286 "Write Inhibited on EUN %d\n", thisEUN); 287 inplace = 0; 288 } else { 289 /* There's no other reason not to do inplace, 290 except ones that come later. So we don't need 291 to preserve inplace */ 292 inplace = 1; 293 } 294 } 295 status = oob.b.Status | oob.b.Status1; 296 BlockLastState[block] = status; 297 298 switch(status) { 299 case SECTOR_FREE: 300 BlockFreeFound[block] = 1; 301 break; 302 303 case SECTOR_USED: 304 if (!BlockFreeFound[block]) 305 BlockMap[block] = thisEUN; 306 else 307 printk(KERN_WARNING 308 "SECTOR_USED found after SECTOR_FREE " 309 "in Virtual Unit Chain %d for block %d\n", 310 thisVUC, block); 311 break; 312 case SECTOR_DELETED: 313 if (!BlockFreeFound[block]) 314 BlockMap[block] = BLOCK_NIL; 315 else 316 printk(KERN_WARNING 317 "SECTOR_DELETED found after SECTOR_FREE " 318 "in Virtual Unit Chain %d for block %d\n", 319 thisVUC, block); 320 break; 321 322 case SECTOR_IGNORE: 323 break; 324 default: 325 printk("Unknown status for block %d in EUN %d: %x\n", 326 block, thisEUN, status); 327 } 328 } 329 330 if (!silly--) { 331 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", 332 thisVUC); 333 return BLOCK_NIL; 334 } 335 336 thisEUN = nftl->ReplUnitTable[thisEUN]; 337 } 338 339 if (inplace) { 340 /* We're being asked to be a fold-in-place. Check 341 that all blocks which actually have data associated 342 with them (i.e. BlockMap[block] != BLOCK_NIL) are 343 either already present or SECTOR_FREE in the target 344 block. If not, we're going to have to fold out-of-place 345 anyway. 346 */ 347 for (block = 0; block < nftl->EraseSize / 512 ; block++) { 348 if (BlockLastState[block] != SECTOR_FREE && 349 BlockMap[block] != BLOCK_NIL && 350 BlockMap[block] != targetEUN) { 351 DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, " 352 "block %d was %x lastEUN, " 353 "and is in EUN %d (%s) %d\n", 354 thisVUC, block, BlockLastState[block], 355 BlockMap[block], 356 BlockMap[block]== targetEUN ? "==" : "!=", 357 targetEUN); 358 inplace = 0; 359 break; 360 } 361 } 362 363 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) && 364 pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) && 365 BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] != 366 SECTOR_FREE) { 367 DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. " 368 "Folding out of place.\n", targetEUN); 369 inplace = 0; 370 } 371 } 372 373 if (!inplace) { 374 DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. " 375 "Trying out-of-place\n", thisVUC); 376 /* We need to find a targetEUN to fold into. */ 377 targetEUN = NFTL_findfreeblock(nftl, 1); 378 if (targetEUN == BLOCK_NIL) { 379 /* Ouch. Now we're screwed. We need to do a 380 fold-in-place of another chain to make room 381 for this one. We need a better way of selecting 382 which chain to fold, because makefreeblock will 383 only ask us to fold the same one again. 384 */ 385 printk(KERN_WARNING 386 "NFTL_findfreeblock(desperate) returns 0xffff.\n"); 387 return BLOCK_NIL; 388 } 389 } else { 390 /* We put a fold mark in the chain we are folding only if we 391 fold in place to help the mount check code. If we do not fold in 392 place, it is possible to find the valid chain by selecting the 393 longer one */ 394 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); 395 oob.u.c.unused = 0xffffffff; 396 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, 397 8, &retlen, (char *)&oob.u); 398 } 399 400 /* OK. We now know the location of every block in the Virtual Unit Chain, 401 and the Erase Unit into which we are supposed to be copying. 402 Go for it. 403 */ 404 DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN); 405 for (block = 0; block < nftl->EraseSize / 512 ; block++) { 406 unsigned char movebuf[512]; 407 int ret; 408 409 /* If it's in the target EUN already, or if it's pending write, do nothing */ 410 if (BlockMap[block] == targetEUN || 411 (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) { 412 continue; 413 } 414 415 /* copy only in non free block (free blocks can only 416 happen in case of media errors or deleted blocks) */ 417 if (BlockMap[block] == BLOCK_NIL) 418 continue; 419 420 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), 421 512, &retlen, movebuf); 422 if (ret < 0 && ret != -EUCLEAN) { 423 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) 424 + (block * 512), 512, &retlen, 425 movebuf); 426 if (ret != -EIO) 427 printk("Error went away on retry.\n"); 428 } 429 memset(&oob, 0xff, sizeof(struct nftl_oob)); 430 oob.b.Status = oob.b.Status1 = SECTOR_USED; 431 432 nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 433 (block * 512), 512, &retlen, movebuf, (char *)&oob); 434 } 435 436 /* add the header so that it is now a valid chain */ 437 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); 438 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; 439 440 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8, 441 8, &retlen, (char *)&oob.u); 442 443 /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ 444 445 /* At this point, we have two different chains for this Virtual Unit, and no way to tell 446 them apart. If we crash now, we get confused. However, both contain the same data, so we 447 shouldn't actually lose data in this case. It's just that when we load up on a medium which 448 has duplicate chains, we need to free one of the chains because it's not necessary any more. 449 */ 450 thisEUN = nftl->EUNtable[thisVUC]; 451 DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n"); 452 453 /* For each block in the old chain (except the targetEUN of course), 454 free it and make it available for future use */ 455 while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) { 456 unsigned int EUNtmp; 457 458 EUNtmp = nftl->ReplUnitTable[thisEUN]; 459 460 if (NFTL_formatblock(nftl, thisEUN) < 0) { 461 /* could not erase : mark block as reserved 462 */ 463 nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED; 464 } else { 465 /* correctly erased : mark it as free */ 466 nftl->ReplUnitTable[thisEUN] = BLOCK_FREE; 467 nftl->numfreeEUNs++; 468 } 469 thisEUN = EUNtmp; 470 } 471 472 /* Make this the new start of chain for thisVUC */ 473 nftl->ReplUnitTable[targetEUN] = BLOCK_NIL; 474 nftl->EUNtable[thisVUC] = targetEUN; 475 476 return targetEUN; 477 } 478 479 static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock) 480 { 481 /* This is the part that needs some cleverness applied. 482 For now, I'm doing the minimum applicable to actually 483 get the thing to work. 484 Wear-levelling and other clever stuff needs to be implemented 485 and we also need to do some assessment of the results when 486 the system loses power half-way through the routine. 487 */ 488 u16 LongestChain = 0; 489 u16 ChainLength = 0, thislen; 490 u16 chain, EUN; 491 492 for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) { 493 EUN = nftl->EUNtable[chain]; 494 thislen = 0; 495 496 while (EUN <= nftl->lastEUN) { 497 thislen++; 498 //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN); 499 EUN = nftl->ReplUnitTable[EUN] & 0x7fff; 500 if (thislen > 0xff00) { 501 printk("Endless loop in Virtual Chain %d: Unit %x\n", 502 chain, EUN); 503 } 504 if (thislen > 0xff10) { 505 /* Actually, don't return failure. Just ignore this chain and 506 get on with it. */ 507 thislen = 0; 508 break; 509 } 510 } 511 512 if (thislen > ChainLength) { 513 //printk("New longest chain is %d with length %d\n", chain, thislen); 514 ChainLength = thislen; 515 LongestChain = chain; 516 } 517 } 518 519 if (ChainLength < 2) { 520 printk(KERN_WARNING "No Virtual Unit Chains available for folding. " 521 "Failing request\n"); 522 return 0xffff; 523 } 524 525 return NFTL_foldchain (nftl, LongestChain, pendingblock); 526 } 527 528 /* NFTL_findwriteunit: Return the unit number into which we can write 529 for this block. Make it available if it isn't already 530 */ 531 static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) 532 { 533 u16 lastEUN; 534 u16 thisVUC = block / (nftl->EraseSize / 512); 535 struct mtd_info *mtd = nftl->mbd.mtd; 536 unsigned int writeEUN; 537 unsigned long blockofs = (block * 512) & (nftl->EraseSize -1); 538 size_t retlen; 539 int silly, silly2 = 3; 540 struct nftl_oob oob; 541 542 do { 543 /* Scan the media to find a unit in the VUC which has 544 a free space for the block in question. 545 */ 546 547 /* This condition catches the 0x[7f]fff cases, as well as 548 being a sanity check for past-end-of-media access 549 */ 550 lastEUN = BLOCK_NIL; 551 writeEUN = nftl->EUNtable[thisVUC]; 552 silly = MAX_LOOPS; 553 while (writeEUN <= nftl->lastEUN) { 554 struct nftl_bci bci; 555 size_t retlen; 556 unsigned int status; 557 558 lastEUN = writeEUN; 559 560 nftl_read_oob(mtd, 561 (writeEUN * nftl->EraseSize) + blockofs, 562 8, &retlen, (char *)&bci); 563 564 DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n", 565 block , writeEUN, le16_to_cpu(bci.Status)); 566 567 status = bci.Status | bci.Status1; 568 switch(status) { 569 case SECTOR_FREE: 570 return writeEUN; 571 572 case SECTOR_DELETED: 573 case SECTOR_USED: 574 case SECTOR_IGNORE: 575 break; 576 default: 577 // Invalid block. Don't use it any more. Must implement. 578 break; 579 } 580 581 if (!silly--) { 582 printk(KERN_WARNING 583 "Infinite loop in Virtual Unit Chain 0x%x\n", 584 thisVUC); 585 return 0xffff; 586 } 587 588 /* Skip to next block in chain */ 589 writeEUN = nftl->ReplUnitTable[writeEUN]; 590 } 591 592 /* OK. We didn't find one in the existing chain, or there 593 is no existing chain. */ 594 595 /* Try to find an already-free block */ 596 writeEUN = NFTL_findfreeblock(nftl, 0); 597 598 if (writeEUN == BLOCK_NIL) { 599 /* That didn't work - there were no free blocks just 600 waiting to be picked up. We're going to have to fold 601 a chain to make room. 602 */ 603 604 /* First remember the start of this chain */ 605 //u16 startEUN = nftl->EUNtable[thisVUC]; 606 607 //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC); 608 writeEUN = NFTL_makefreeblock(nftl, 0xffff); 609 610 if (writeEUN == BLOCK_NIL) { 611 /* OK, we accept that the above comment is 612 lying - there may have been free blocks 613 last time we called NFTL_findfreeblock(), 614 but they are reserved for when we're 615 desperate. Well, now we're desperate. 616 */ 617 DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC); 618 writeEUN = NFTL_findfreeblock(nftl, 1); 619 } 620 if (writeEUN == BLOCK_NIL) { 621 /* Ouch. This should never happen - we should 622 always be able to make some room somehow. 623 If we get here, we've allocated more storage 624 space than actual media, or our makefreeblock 625 routine is missing something. 626 */ 627 printk(KERN_WARNING "Cannot make free space.\n"); 628 return BLOCK_NIL; 629 } 630 //printk("Restarting scan\n"); 631 lastEUN = BLOCK_NIL; 632 continue; 633 } 634 635 /* We've found a free block. Insert it into the chain. */ 636 637 if (lastEUN != BLOCK_NIL) { 638 thisVUC |= 0x8000; /* It's a replacement block */ 639 } else { 640 /* The first block in a new chain */ 641 nftl->EUNtable[thisVUC] = writeEUN; 642 } 643 644 /* set up the actual EUN we're writing into */ 645 /* Both in our cache... */ 646 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; 647 648 /* ... and on the flash itself */ 649 nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, 650 &retlen, (char *)&oob.u); 651 652 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); 653 654 nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, 655 &retlen, (char *)&oob.u); 656 657 /* we link the new block to the chain only after the 658 block is ready. It avoids the case where the chain 659 could point to a free block */ 660 if (lastEUN != BLOCK_NIL) { 661 /* Both in our cache... */ 662 nftl->ReplUnitTable[lastEUN] = writeEUN; 663 /* ... and on the flash itself */ 664 nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8, 665 8, &retlen, (char *)&oob.u); 666 667 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum 668 = cpu_to_le16(writeEUN); 669 670 nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8, 671 8, &retlen, (char *)&oob.u); 672 } 673 674 return writeEUN; 675 676 } while (silly2--); 677 678 printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", 679 thisVUC); 680 return 0xffff; 681 } 682 683 static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, 684 char *buffer) 685 { 686 struct NFTLrecord *nftl = (void *)mbd; 687 u16 writeEUN; 688 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); 689 size_t retlen; 690 struct nftl_oob oob; 691 692 writeEUN = NFTL_findwriteunit(nftl, block); 693 694 if (writeEUN == BLOCK_NIL) { 695 printk(KERN_WARNING 696 "NFTL_writeblock(): Cannot find block to write to\n"); 697 /* If we _still_ haven't got a block to use, we're screwed */ 698 return 1; 699 } 700 701 memset(&oob, 0xff, sizeof(struct nftl_oob)); 702 oob.b.Status = oob.b.Status1 = SECTOR_USED; 703 704 nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, 705 512, &retlen, (char *)buffer, (char *)&oob); 706 return 0; 707 } 708 #endif /* CONFIG_NFTL_RW */ 709 710 static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, 711 char *buffer) 712 { 713 struct NFTLrecord *nftl = (void *)mbd; 714 struct mtd_info *mtd = nftl->mbd.mtd; 715 u16 lastgoodEUN; 716 u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)]; 717 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); 718 unsigned int status; 719 int silly = MAX_LOOPS; 720 size_t retlen; 721 struct nftl_bci bci; 722 723 lastgoodEUN = BLOCK_NIL; 724 725 if (thisEUN != BLOCK_NIL) { 726 while (thisEUN < nftl->nb_blocks) { 727 if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + 728 blockofs, 8, &retlen, 729 (char *)&bci) < 0) 730 status = SECTOR_IGNORE; 731 else 732 status = bci.Status | bci.Status1; 733 734 switch (status) { 735 case SECTOR_FREE: 736 /* no modification of a sector should follow a free sector */ 737 goto the_end; 738 case SECTOR_DELETED: 739 lastgoodEUN = BLOCK_NIL; 740 break; 741 case SECTOR_USED: 742 lastgoodEUN = thisEUN; 743 break; 744 case SECTOR_IGNORE: 745 break; 746 default: 747 printk("Unknown status for block %ld in EUN %d: %x\n", 748 block, thisEUN, status); 749 break; 750 } 751 752 if (!silly--) { 753 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n", 754 block / (nftl->EraseSize / 512)); 755 return 1; 756 } 757 thisEUN = nftl->ReplUnitTable[thisEUN]; 758 } 759 } 760 761 the_end: 762 if (lastgoodEUN == BLOCK_NIL) { 763 /* the requested block is not on the media, return all 0x00 */ 764 memset(buffer, 0, 512); 765 } else { 766 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; 767 size_t retlen; 768 int res = mtd->read(mtd, ptr, 512, &retlen, buffer); 769 770 if (res < 0 && res != -EUCLEAN) 771 return -EIO; 772 } 773 return 0; 774 } 775 776 static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) 777 { 778 struct NFTLrecord *nftl = (void *)dev; 779 780 geo->heads = nftl->heads; 781 geo->sectors = nftl->sectors; 782 geo->cylinders = nftl->cylinders; 783 784 return 0; 785 } 786 787 /**************************************************************************** 788 * 789 * Module stuff 790 * 791 ****************************************************************************/ 792 793 794 static struct mtd_blktrans_ops nftl_tr = { 795 .name = "nftl", 796 .major = NFTL_MAJOR, 797 .part_bits = NFTL_PARTN_BITS, 798 .blksize = 512, 799 .getgeo = nftl_getgeo, 800 .readsect = nftl_readblock, 801 #ifdef CONFIG_NFTL_RW 802 .writesect = nftl_writeblock, 803 #endif 804 .add_mtd = nftl_add_mtd, 805 .remove_dev = nftl_remove_dev, 806 .owner = THIS_MODULE, 807 }; 808 809 extern char nftlmountrev[]; 810 811 static int __init init_nftl(void) 812 { 813 printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev); 814 815 return register_mtd_blktrans(&nftl_tr); 816 } 817 818 static void __exit cleanup_nftl(void) 819 { 820 deregister_mtd_blktrans(&nftl_tr); 821 } 822 823 module_init(init_nftl); 824 module_exit(cleanup_nftl); 825 826 MODULE_LICENSE("GPL"); 827 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al."); 828 MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium"); 829