24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <asm/errno.h>
28 #include <asm/uaccess.h>
30 #include <linux/slab.h>
45 #define MAX_LOOPS 10000
67 nftl->
mbd.devnum = -1;
85 if (nftl->
mbd.size % temp) {
90 if (nftl->
mbd.size % temp) {
103 "match size of 0x%lx.\n", nftl->
mbd.size);
105 "(== 0x%lx sects)\n",
170 res = mtd_write_oob(mtd, offs & ~mask, &ops);
175 #ifdef CONFIG_NFTL_RW
194 res = mtd_write_oob(mtd, offs & ~mask, &ops);
195 *retlen =
ops.retlen;
203 static u16 NFTL_findfreeblock(
struct NFTLrecord *nftl,
int desperate )
214 pr_debug(
"NFTL_findfreeblock: there are too few free EUNs\n");
234 printk(
"Argh! No free blocks found! LastFreeEUN = %d, "
244 static u16 NFTL_foldchain (
struct NFTLrecord *nftl,
unsigned thisVUC,
unsigned pendingblock )
250 unsigned int thisEUN;
253 unsigned int targetEUN;
258 memset(BlockMap, 0xff,
sizeof(BlockMap));
259 memset(BlockFreeFound, 0,
sizeof(BlockFreeFound));
265 "Virtual Unit Chain %d!\n", thisVUC);
274 while (thisEUN <= nftl->lastEUN ) {
275 unsigned int status, foldmark;
278 for (block = 0; block < nftl->
EraseSize / 512; block ++) {
280 (block * 512), 16 , &retlen,
283 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
285 pr_debug(
"Write Inhibited on EUN %d\n", thisEUN);
294 status = oob.b.Status | oob.b.Status1;
299 BlockFreeFound[
block] = 1;
303 if (!BlockFreeFound[block])
304 BlockMap[
block] = thisEUN;
307 "SECTOR_USED found after SECTOR_FREE "
308 "in Virtual Unit Chain %d for block %d\n",
312 if (!BlockFreeFound[block])
316 "SECTOR_DELETED found after SECTOR_FREE "
317 "in Virtual Unit Chain %d for block %d\n",
324 printk(
"Unknown status for block %d in EUN %d: %x\n",
325 block, thisEUN, status);
346 for (block = 0; block < nftl->
EraseSize / 512 ; block++) {
349 BlockMap[block] != targetEUN) {
350 pr_debug(
"Setting inplace to 0. VUC %d, "
351 "block %d was %x lastEUN, "
352 "and is in EUN %d (%s) %d\n",
353 thisVUC, block, BlockLastState[block],
355 BlockMap[block]== targetEUN ?
"==" :
"!=",
362 if (pendingblock >= (thisVUC * (nftl->
EraseSize / 512)) &&
363 pendingblock < ((thisVUC + 1)* (nftl->
EraseSize / 512)) &&
364 BlockLastState[pendingblock - (thisVUC * (nftl->
EraseSize / 512))] !=
366 pr_debug(
"Pending write not free in EUN %d. "
367 "Folding out of place.\n", targetEUN);
373 pr_debug(
"Cannot fold Virtual Unit Chain %d in place. "
374 "Trying out-of-place\n", thisVUC);
376 targetEUN = NFTL_findfreeblock(nftl, 1);
385 "NFTL_findfreeblock(desperate) returns 0xffff.\n");
394 oob.u.c.unused = 0xffffffff;
396 8, &retlen, (
char *)&oob.u);
403 pr_debug(
"Folding chain %d into unit %d\n", thisVUC, targetEUN);
404 for (block = 0; block < nftl->
EraseSize / 512 ; block++) {
405 unsigned char movebuf[512];
409 if (BlockMap[block] == targetEUN ||
410 (pendingblock == (thisVUC * (nftl->
EraseSize / 512) + block))) {
420 (nftl->
EraseSize * BlockMap[block]) + (block * 512),
424 if (ret < 0 && !mtd_is_bitflip(ret)) {
426 (nftl->
EraseSize * BlockMap[block]) + (block * 512),
431 printk(
"Error went away on retry.\n");
436 nftl_write(nftl->
mbd.mtd, (nftl->
EraseSize * targetEUN) +
437 (block * 512), 512, &retlen, movebuf, (
char *)&oob);
441 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum =
cpu_to_le16(thisVUC);
442 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum =
BLOCK_NIL;
445 8, &retlen, (
char *)&oob.u);
459 while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
478 nftl->
EUNtable[thisVUC] = targetEUN;
483 static u16 NFTL_makefreeblock(
struct NFTLrecord *nftl ,
unsigned pendingblock)
492 u16 LongestChain = 0;
493 u16 ChainLength = 0, thislen;
500 while (EUN <= nftl->lastEUN) {
504 if (thislen > 0xff00) {
505 printk(
"Endless loop in Virtual Chain %d: Unit %x\n",
508 if (thislen > 0xff10) {
516 if (thislen > ChainLength) {
518 ChainLength = thislen;
519 LongestChain =
chain;
523 if (ChainLength < 2) {
525 "Failing request\n");
529 return NFTL_foldchain (nftl, LongestChain, pendingblock);
535 static inline u16 NFTL_findwriteunit(
struct NFTLrecord *nftl,
unsigned block)
540 unsigned int writeEUN;
541 unsigned long blockofs = (block * 512) & (nftl->
EraseSize -1);
543 int silly, silly2 = 3;
557 while (writeEUN <= nftl->lastEUN) {
566 8, &retlen, (
char *)&bci);
568 pr_debug(
"Status of block %d in EUN %d is %x\n",
571 status = bci.Status | bci.Status1;
587 "Infinite loop in Virtual Unit Chain 0x%x\n",
600 writeEUN = NFTL_findfreeblock(nftl, 0);
612 writeEUN = NFTL_makefreeblock(nftl,
BLOCK_NIL);
621 pr_debug(
"Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
622 writeEUN = NFTL_findfreeblock(nftl, 1);
654 &retlen, (
char *)&oob.u);
656 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum =
cpu_to_le16(thisVUC);
659 &retlen, (
char *)&oob.u);
669 8, &retlen, (
char *)&oob.u);
671 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
675 8, &retlen, (
char *)&oob.u);
687 static int nftl_writeblock(
struct mtd_blktrans_dev *mbd,
unsigned long block,
692 unsigned long blockofs = (block * 512) & (nftl->
EraseSize - 1);
696 writeEUN = NFTL_findwriteunit(nftl, block);
700 "NFTL_writeblock(): Cannot find block to write to\n");
708 nftl_write(nftl->
mbd.mtd, (writeEUN * nftl->
EraseSize) + blockofs,
709 512, &retlen, (
char *)buffer, (
char *)&oob);
714 static int nftl_readblock(
struct mtd_blktrans_dev *mbd,
unsigned long block,
721 unsigned long blockofs = (block * 512) & (nftl->
EraseSize - 1);
730 while (thisEUN < nftl->nb_blocks) {
732 blockofs, 8, &retlen,
736 status = bci.Status | bci.Status1;
746 lastgoodEUN = thisEUN;
751 printk(
"Unknown status for block %ld in EUN %d: %x\n",
752 block, thisEUN, status);
770 loff_t
ptr = (lastgoodEUN * nftl->
EraseSize) + blockofs;
772 int res =
mtd_read(mtd, ptr, 512, &retlen, buffer);
774 if (res < 0 && !mtd_is_bitflip(res))
803 .getgeo = nftl_getgeo,
804 .readsect = nftl_readblock,
805 #ifdef CONFIG_NFTL_RW
806 .writesect = nftl_writeblock,
808 .add_mtd = nftl_add_mtd,
809 .remove_dev = nftl_remove_dev,
813 static int __init init_nftl(
void)
818 static void __exit cleanup_nftl(
void)
828 MODULE_DESCRIPTION(
"Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");