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