xref: /openbmc/linux/drivers/mtd/nftlcore.c (revision 7a010c3c)
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 			continue;
623 		}
624 
625 		/* We've found a free block. Insert it into the chain. */
626 
627 		if (lastEUN != BLOCK_NIL) {
628 			thisVUC |= 0x8000; /* It's a replacement block */
629 		} else {
630 			/* The first block in a new chain */
631 			nftl->EUNtable[thisVUC] = writeEUN;
632 		}
633 
634 		/* set up the actual EUN we're writing into */
635 		/* Both in our cache... */
636 		nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
637 
638 		/* ... and on the flash itself */
639 		nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
640 			      &retlen, (char *)&oob.u);
641 
642 		oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
643 
644 		nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
645 			       &retlen, (char *)&oob.u);
646 
647 		/* we link the new block to the chain only after the
648                    block is ready. It avoids the case where the chain
649                    could point to a free block */
650 		if (lastEUN != BLOCK_NIL) {
651 			/* Both in our cache... */
652 			nftl->ReplUnitTable[lastEUN] = writeEUN;
653 			/* ... and on the flash itself */
654 			nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
655 				      8, &retlen, (char *)&oob.u);
656 
657 			oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
658 				= cpu_to_le16(writeEUN);
659 
660 			nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
661 				       8, &retlen, (char *)&oob.u);
662 		}
663 
664 		return writeEUN;
665 
666 	} while (silly2--);
667 
668 	printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
669 	       thisVUC);
670 	return BLOCK_NIL;
671 }
672 
673 static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
674 			   char *buffer)
675 {
676 	struct NFTLrecord *nftl = (void *)mbd;
677 	u16 writeEUN;
678 	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
679 	size_t retlen;
680 	struct nftl_oob oob;
681 
682 	writeEUN = NFTL_findwriteunit(nftl, block);
683 
684 	if (writeEUN == BLOCK_NIL) {
685 		printk(KERN_WARNING
686 		       "NFTL_writeblock(): Cannot find block to write to\n");
687 		/* If we _still_ haven't got a block to use, we're screwed */
688 		return 1;
689 	}
690 
691 	memset(&oob, 0xff, sizeof(struct nftl_oob));
692 	oob.b.Status = oob.b.Status1 = SECTOR_USED;
693 
694 	nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
695 		   512, &retlen, (char *)buffer, (char *)&oob);
696 	return 0;
697 }
698 #endif /* CONFIG_NFTL_RW */
699 
700 static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
701 			  char *buffer)
702 {
703 	struct NFTLrecord *nftl = (void *)mbd;
704 	struct mtd_info *mtd = nftl->mbd.mtd;
705 	u16 lastgoodEUN;
706 	u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
707 	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
708 	unsigned int status;
709 	int silly = MAX_LOOPS;
710 	size_t retlen;
711 	struct nftl_bci bci;
712 
713 	lastgoodEUN = BLOCK_NIL;
714 
715 	if (thisEUN != BLOCK_NIL) {
716 		while (thisEUN < nftl->nb_blocks) {
717 			if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
718 					  blockofs, 8, &retlen,
719 					  (char *)&bci) < 0)
720 				status = SECTOR_IGNORE;
721 			else
722 				status = bci.Status | bci.Status1;
723 
724 			switch (status) {
725 			case SECTOR_FREE:
726 				/* no modification of a sector should follow a free sector */
727 				goto the_end;
728 			case SECTOR_DELETED:
729 				lastgoodEUN = BLOCK_NIL;
730 				break;
731 			case SECTOR_USED:
732 				lastgoodEUN = thisEUN;
733 				break;
734 			case SECTOR_IGNORE:
735 				break;
736 			default:
737 				printk("Unknown status for block %ld in EUN %d: %x\n",
738 				       block, thisEUN, status);
739 				break;
740 			}
741 
742 			if (!silly--) {
743 				printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
744 				       block / (nftl->EraseSize / 512));
745 				return 1;
746 			}
747 			thisEUN = nftl->ReplUnitTable[thisEUN];
748 		}
749 	}
750 
751  the_end:
752 	if (lastgoodEUN == BLOCK_NIL) {
753 		/* the requested block is not on the media, return all 0x00 */
754 		memset(buffer, 0, 512);
755 	} else {
756 		loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
757 		size_t retlen;
758 		int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
759 
760 		if (res < 0 && !mtd_is_bitflip(res))
761 			return -EIO;
762 	}
763 	return 0;
764 }
765 
766 static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
767 {
768 	struct NFTLrecord *nftl = (void *)dev;
769 
770 	geo->heads = nftl->heads;
771 	geo->sectors = nftl->sectors;
772 	geo->cylinders = nftl->cylinders;
773 
774 	return 0;
775 }
776 
777 /****************************************************************************
778  *
779  * Module stuff
780  *
781  ****************************************************************************/
782 
783 
784 static struct mtd_blktrans_ops nftl_tr = {
785 	.name		= "nftl",
786 	.major		= NFTL_MAJOR,
787 	.part_bits	= NFTL_PARTN_BITS,
788 	.blksize 	= 512,
789 	.getgeo		= nftl_getgeo,
790 	.readsect	= nftl_readblock,
791 #ifdef CONFIG_NFTL_RW
792 	.writesect	= nftl_writeblock,
793 #endif
794 	.add_mtd	= nftl_add_mtd,
795 	.remove_dev	= nftl_remove_dev,
796 	.owner		= THIS_MODULE,
797 };
798 
799 module_mtd_blktrans(nftl_tr);
800 
801 MODULE_LICENSE("GPL");
802 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
803 MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
804 MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);
805