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