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