xref: /linux/drivers/mtd/inftlcore.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
4  *
5  * Copyright © 2002, Greg Ungerer (gerg@snapgear.com)
6  *
7  * Based heavily on the nftlcore.c code which is:
8  * Copyright © 1999 Machine Vision Holdings, Inc.
9  * Copyright © 1999 David Woodhouse <dwmw2@infradead.org>
10  */
11 
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/delay.h>
15 #include <linux/slab.h>
16 #include <linux/sched.h>
17 #include <linux/init.h>
18 #include <linux/kmod.h>
19 #include <linux/hdreg.h>
20 #include <linux/mtd/mtd.h>
21 #include <linux/mtd/nftl.h>
22 #include <linux/mtd/inftl.h>
23 #include <linux/mtd/rawnand.h>
24 #include <linux/uaccess.h>
25 #include <asm/errno.h>
26 #include <asm/io.h>
27 
28 /*
29  * Maximum number of loops while examining next block, to have a
30  * chance to detect consistency problems (they should never happen
31  * because of the checks done in the mounting.
32  */
33 #define MAX_LOOPS 10000
34 
inftl_add_mtd(struct mtd_blktrans_ops * tr,struct mtd_info * mtd)35 static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
36 {
37 	struct INFTLrecord *inftl;
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 	if (!mtd->_block_isbad) {
47 		printk(KERN_ERR
48 "INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
49 "Please use the new diskonchip driver under the NAND subsystem.\n");
50 		return;
51 	}
52 
53 	pr_debug("INFTL: add_mtd for %s\n", mtd->name);
54 
55 	inftl = kzalloc(sizeof(*inftl), GFP_KERNEL);
56 
57 	if (!inftl)
58 		return;
59 
60 	inftl->mbd.mtd = mtd;
61 	inftl->mbd.devnum = -1;
62 
63 	inftl->mbd.tr = tr;
64 
65 	if (INFTL_mount(inftl) < 0) {
66 		printk(KERN_WARNING "INFTL: could not mount device\n");
67 		kfree(inftl);
68 		return;
69 	}
70 
71 	/* OK, it's a new one. Set up all the data structures. */
72 
73 	/* Calculate geometry */
74 	inftl->cylinders = 1024;
75 	inftl->heads = 16;
76 
77 	temp = inftl->cylinders * inftl->heads;
78 	inftl->sectors = inftl->mbd.size / temp;
79 	if (inftl->mbd.size % temp) {
80 		inftl->sectors++;
81 		temp = inftl->cylinders * inftl->sectors;
82 		inftl->heads = inftl->mbd.size / temp;
83 
84 		if (inftl->mbd.size % temp) {
85 			inftl->heads++;
86 			temp = inftl->heads * inftl->sectors;
87 			inftl->cylinders = inftl->mbd.size / temp;
88 		}
89 	}
90 
91 	if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {
92 		/*
93 		  Oh no we don't have
94 		   mbd.size == heads * cylinders * sectors
95 		*/
96 		printk(KERN_WARNING "INFTL: cannot calculate a geometry to "
97 		       "match size of 0x%lx.\n", inftl->mbd.size);
98 		printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "
99 			"(== 0x%lx sects)\n",
100 			inftl->cylinders, inftl->heads , inftl->sectors,
101 			(long)inftl->cylinders * (long)inftl->heads *
102 			(long)inftl->sectors );
103 	}
104 
105 	if (add_mtd_blktrans_dev(&inftl->mbd)) {
106 		kfree(inftl->PUtable);
107 		kfree(inftl->VUtable);
108 		kfree(inftl);
109 		return;
110 	}
111 #ifdef PSYCHO_DEBUG
112 	printk(KERN_INFO "INFTL: Found new inftl%c\n", inftl->mbd.devnum + 'a');
113 #endif
114 	return;
115 }
116 
inftl_remove_dev(struct mtd_blktrans_dev * dev)117 static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
118 {
119 	struct INFTLrecord *inftl = (void *)dev;
120 
121 	pr_debug("INFTL: remove_dev (i=%d)\n", dev->devnum);
122 
123 	del_mtd_blktrans_dev(dev);
124 
125 	kfree(inftl->PUtable);
126 	kfree(inftl->VUtable);
127 }
128 
129 /*
130  * Actual INFTL access routines.
131  */
132 
133 /*
134  * Read oob data from flash
135  */
inftl_read_oob(struct mtd_info * mtd,loff_t offs,size_t len,size_t * retlen,uint8_t * buf)136 int inftl_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_OPS_PLACE_OOB;
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  */
inftl_write_oob(struct mtd_info * mtd,loff_t offs,size_t len,size_t * retlen,uint8_t * buf)156 int inftl_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_OPS_PLACE_OOB;
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 /*
174  * Write data and oob to flash
175  */
inftl_write(struct mtd_info * mtd,loff_t offs,size_t len,size_t * retlen,uint8_t * buf,uint8_t * oob)176 static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
177 		       size_t *retlen, uint8_t *buf, uint8_t *oob)
178 {
179 	struct mtd_oob_ops ops = { };
180 	int res;
181 
182 	ops.mode = MTD_OPS_PLACE_OOB;
183 	ops.ooboffs = offs;
184 	ops.ooblen = mtd->oobsize;
185 	ops.oobbuf = oob;
186 	ops.datbuf = buf;
187 	ops.len = len;
188 
189 	res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
190 	*retlen = ops.retlen;
191 	return res;
192 }
193 
194 /*
195  * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
196  *	This function is used when the give Virtual Unit Chain.
197  */
INFTL_findfreeblock(struct INFTLrecord * inftl,int desperate)198 static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
199 {
200 	u16 pot = inftl->LastFreeEUN;
201 	int silly = inftl->nb_blocks;
202 
203 	pr_debug("INFTL: INFTL_findfreeblock(inftl=%p,desperate=%d)\n",
204 			inftl, desperate);
205 
206 	/*
207 	 * Normally, we force a fold to happen before we run out of free
208 	 * blocks completely.
209 	 */
210 	if (!desperate && inftl->numfreeEUNs < 2) {
211 		pr_debug("INFTL: there are too few free EUNs (%d)\n",
212 				inftl->numfreeEUNs);
213 		return BLOCK_NIL;
214 	}
215 
216 	/* Scan for a free block */
217 	do {
218 		if (inftl->PUtable[pot] == BLOCK_FREE) {
219 			inftl->LastFreeEUN = pot;
220 			return pot;
221 		}
222 
223 		if (++pot > inftl->lastEUN)
224 			pot = 0;
225 
226 		if (!silly--) {
227 			printk(KERN_WARNING "INFTL: no free blocks found!  "
228 				"EUN range = %d - %d\n", 0, inftl->LastFreeEUN);
229 			return BLOCK_NIL;
230 		}
231 	} while (pot != inftl->LastFreeEUN);
232 
233 	return BLOCK_NIL;
234 }
235 
INFTL_foldchain(struct INFTLrecord * inftl,unsigned thisVUC,unsigned pendingblock)236 static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
237 {
238 	u16 BlockMap[MAX_SECTORS_PER_UNIT];
239 	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
240 	unsigned int thisEUN, prevEUN, status;
241 	struct mtd_info *mtd = inftl->mbd.mtd;
242 	int block, silly;
243 	unsigned int targetEUN;
244 	struct inftl_oob oob;
245 	size_t retlen;
246 
247 	pr_debug("INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,pending=%d)\n",
248 			inftl, thisVUC, pendingblock);
249 
250 	memset(BlockMap, 0xff, sizeof(BlockMap));
251 	memset(BlockDeleted, 0, sizeof(BlockDeleted));
252 
253 	thisEUN = targetEUN = inftl->VUtable[thisVUC];
254 
255 	if (thisEUN == BLOCK_NIL) {
256 		printk(KERN_WARNING "INFTL: trying to fold non-existent "
257 		       "Virtual Unit Chain %d!\n", thisVUC);
258 		return BLOCK_NIL;
259 	}
260 
261 	/*
262 	 * Scan to find the Erase Unit which holds the actual data for each
263 	 * 512-byte block within the Chain.
264 	 */
265 	silly = MAX_LOOPS;
266 	while (thisEUN < inftl->nb_blocks) {
267 		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
268 			if ((BlockMap[block] != BLOCK_NIL) ||
269 			    BlockDeleted[block])
270 				continue;
271 
272 			if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
273 					   + (block * SECTORSIZE), 16, &retlen,
274 					   (char *)&oob) < 0)
275 				status = SECTOR_IGNORE;
276 			else
277 				status = oob.b.Status | oob.b.Status1;
278 
279 			switch(status) {
280 			case SECTOR_FREE:
281 			case SECTOR_IGNORE:
282 				break;
283 			case SECTOR_USED:
284 				BlockMap[block] = thisEUN;
285 				continue;
286 			case SECTOR_DELETED:
287 				BlockDeleted[block] = 1;
288 				continue;
289 			default:
290 				printk(KERN_WARNING "INFTL: unknown status "
291 					"for block %d in EUN %d: %x\n",
292 					block, thisEUN, status);
293 				break;
294 			}
295 		}
296 
297 		if (!silly--) {
298 			printk(KERN_WARNING "INFTL: infinite loop in Virtual "
299 				"Unit Chain 0x%x\n", thisVUC);
300 			return BLOCK_NIL;
301 		}
302 
303 		thisEUN = inftl->PUtable[thisEUN];
304 	}
305 
306 	/*
307 	 * OK. We now know the location of every block in the Virtual Unit
308 	 * Chain, and the Erase Unit into which we are supposed to be copying.
309 	 * Go for it.
310 	 */
311 	pr_debug("INFTL: folding chain %d into unit %d\n", thisVUC, targetEUN);
312 
313 	for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
314 		unsigned char movebuf[SECTORSIZE];
315 		int ret;
316 
317 		/*
318 		 * If it's in the target EUN already, or if it's pending write,
319 		 * do nothing.
320 		 */
321 		if (BlockMap[block] == targetEUN || (pendingblock ==
322 		    (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
323 			continue;
324 		}
325 
326 		/*
327 		 * Copy only in non free block (free blocks can only
328                  * happen in case of media errors or deleted blocks).
329 		 */
330 		if (BlockMap[block] == BLOCK_NIL)
331 			continue;
332 
333 		ret = mtd_read(mtd,
334 			       (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE),
335 			       SECTORSIZE,
336 			       &retlen,
337 			       movebuf);
338 		if (ret < 0 && !mtd_is_bitflip(ret)) {
339 			ret = mtd_read(mtd,
340 				       (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE),
341 				       SECTORSIZE,
342 				       &retlen,
343 				       movebuf);
344 			if (ret != -EIO)
345 				pr_debug("INFTL: error went away on retry?\n");
346 		}
347 		memset(&oob, 0xff, sizeof(struct inftl_oob));
348 		oob.b.Status = oob.b.Status1 = SECTOR_USED;
349 
350 		inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
351 			    (block * SECTORSIZE), SECTORSIZE, &retlen,
352 			    movebuf, (char *)&oob);
353 	}
354 
355 	/*
356 	 * Newest unit in chain now contains data from _all_ older units.
357 	 * So go through and erase each unit in chain, oldest first. (This
358 	 * is important, by doing oldest first if we crash/reboot then it
359 	 * is relatively simple to clean up the mess).
360 	 */
361 	pr_debug("INFTL: want to erase virtual chain %d\n", thisVUC);
362 
363 	for (;;) {
364 		/* Find oldest unit in chain. */
365 		thisEUN = inftl->VUtable[thisVUC];
366 		prevEUN = BLOCK_NIL;
367 		while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
368 			prevEUN = thisEUN;
369 			thisEUN = inftl->PUtable[thisEUN];
370 		}
371 
372 		/* Check if we are all done */
373 		if (thisEUN == targetEUN)
374 			break;
375 
376 		/* Unlink the last block from the chain. */
377 		inftl->PUtable[prevEUN] = BLOCK_NIL;
378 
379 		/* Now try to erase it. */
380 		if (INFTL_formatblock(inftl, thisEUN) < 0) {
381 			/*
382 			 * Could not erase : mark block as reserved.
383 			 */
384 			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
385 		} else {
386 			/* Correctly erased : mark it as free */
387 			inftl->PUtable[thisEUN] = BLOCK_FREE;
388 			inftl->numfreeEUNs++;
389 		}
390 	}
391 
392 	return targetEUN;
393 }
394 
INFTL_makefreeblock(struct INFTLrecord * inftl,unsigned pendingblock)395 static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
396 {
397 	/*
398 	 * This is the part that needs some cleverness applied.
399 	 * For now, I'm doing the minimum applicable to actually
400 	 * get the thing to work.
401 	 * Wear-levelling and other clever stuff needs to be implemented
402 	 * and we also need to do some assessment of the results when
403 	 * the system loses power half-way through the routine.
404 	 */
405 	u16 LongestChain = 0;
406 	u16 ChainLength = 0, thislen;
407 	u16 chain, EUN;
408 
409 	pr_debug("INFTL: INFTL_makefreeblock(inftl=%p,"
410 		"pending=%d)\n", inftl, pendingblock);
411 
412 	for (chain = 0; chain < inftl->nb_blocks; chain++) {
413 		EUN = inftl->VUtable[chain];
414 		thislen = 0;
415 
416 		while (EUN <= inftl->lastEUN) {
417 			thislen++;
418 			EUN = inftl->PUtable[EUN];
419 			if (thislen > 0xff00) {
420 				printk(KERN_WARNING "INFTL: endless loop in "
421 					"Virtual Chain %d: Unit %x\n",
422 					chain, EUN);
423 				/*
424 				 * Actually, don't return failure.
425 				 * Just ignore this chain and get on with it.
426 				 */
427 				thislen = 0;
428 				break;
429 			}
430 		}
431 
432 		if (thislen > ChainLength) {
433 			ChainLength = thislen;
434 			LongestChain = chain;
435 		}
436 	}
437 
438 	if (ChainLength < 2) {
439 		printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
440 			"for folding. Failing request\n");
441 		return BLOCK_NIL;
442 	}
443 
444 	return INFTL_foldchain(inftl, LongestChain, pendingblock);
445 }
446 
nrbits(unsigned int val,int bitcount)447 static int nrbits(unsigned int val, int bitcount)
448 {
449 	int i, total = 0;
450 
451 	for (i = 0; (i < bitcount); i++)
452 		total += (((0x1 << i) & val) ? 1 : 0);
453 	return total;
454 }
455 
456 /*
457  * INFTL_findwriteunit: Return the unit number into which we can write
458  *                      for this block. Make it available if it isn't already.
459  */
INFTL_findwriteunit(struct INFTLrecord * inftl,unsigned block)460 static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
461 {
462 	unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
463 	unsigned int thisEUN, writeEUN, prev_block, status;
464 	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
465 	struct mtd_info *mtd = inftl->mbd.mtd;
466 	struct inftl_oob oob;
467 	struct inftl_bci bci;
468 	unsigned char anac, nacs, parity;
469 	size_t retlen;
470 	int silly, silly2 = 3;
471 
472 	pr_debug("INFTL: INFTL_findwriteunit(inftl=%p,block=%d)\n",
473 			inftl, block);
474 
475 	do {
476 		/*
477 		 * Scan the media to find a unit in the VUC which has
478 		 * a free space for the block in question.
479 		 */
480 		writeEUN = BLOCK_NIL;
481 		thisEUN = inftl->VUtable[thisVUC];
482 		silly = MAX_LOOPS;
483 
484 		while (thisEUN <= inftl->lastEUN) {
485 			inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
486 				       blockofs, 8, &retlen, (char *)&bci);
487 
488 			status = bci.Status | bci.Status1;
489 			pr_debug("INFTL: status of block %d in EUN %d is %x\n",
490 					block , writeEUN, status);
491 
492 			switch(status) {
493 			case SECTOR_FREE:
494 				writeEUN = thisEUN;
495 				break;
496 			case SECTOR_DELETED:
497 			case SECTOR_USED:
498 				/* Can't go any further */
499 				goto hitused;
500 			case SECTOR_IGNORE:
501 				break;
502 			default:
503 				/*
504 				 * Invalid block. Don't use it any more.
505 				 * Must implement.
506 				 */
507 				break;
508 			}
509 
510 			if (!silly--) {
511 				printk(KERN_WARNING "INFTL: infinite loop in "
512 					"Virtual Unit Chain 0x%x\n", thisVUC);
513 				return BLOCK_NIL;
514 			}
515 
516 			/* Skip to next block in chain */
517 			thisEUN = inftl->PUtable[thisEUN];
518 		}
519 
520 hitused:
521 		if (writeEUN != BLOCK_NIL)
522 			return writeEUN;
523 
524 
525 		/*
526 		 * OK. We didn't find one in the existing chain, or there
527 		 * is no existing chain. Allocate a new one.
528 		 */
529 		writeEUN = INFTL_findfreeblock(inftl, 0);
530 
531 		if (writeEUN == BLOCK_NIL) {
532 			/*
533 			 * That didn't work - there were no free blocks just
534 			 * waiting to be picked up. We're going to have to fold
535 			 * a chain to make room.
536 			 */
537 			thisEUN = INFTL_makefreeblock(inftl, block);
538 
539 			/*
540 			 * Hopefully we free something, lets try again.
541 			 * This time we are desperate...
542 			 */
543 			pr_debug("INFTL: using desperate==1 to find free EUN "
544 					"to accommodate write to VUC %d\n",
545 					thisVUC);
546 			writeEUN = INFTL_findfreeblock(inftl, 1);
547 			if (writeEUN == BLOCK_NIL) {
548 				/*
549 				 * Ouch. This should never happen - we should
550 				 * always be able to make some room somehow.
551 				 * If we get here, we've allocated more storage
552 				 * space than actual media, or our makefreeblock
553 				 * routine is missing something.
554 				 */
555 				printk(KERN_WARNING "INFTL: cannot make free "
556 					"space.\n");
557 #ifdef DEBUG
558 				INFTL_dumptables(inftl);
559 				INFTL_dumpVUchains(inftl);
560 #endif
561 				return BLOCK_NIL;
562 			}
563 		}
564 
565 		/*
566 		 * Insert new block into virtual chain. Firstly update the
567 		 * block headers in flash...
568 		 */
569 		anac = 0;
570 		nacs = 0;
571 		thisEUN = inftl->VUtable[thisVUC];
572 		if (thisEUN != BLOCK_NIL) {
573 			inftl_read_oob(mtd, thisEUN * inftl->EraseSize
574 				       + 8, 8, &retlen, (char *)&oob.u);
575 			anac = oob.u.a.ANAC + 1;
576 			nacs = oob.u.a.NACs + 1;
577 		}
578 
579 		prev_block = inftl->VUtable[thisVUC];
580 		if (prev_block < inftl->nb_blocks)
581 			prev_block -= inftl->firstEUN;
582 
583 		parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
584 		parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
585 		parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
586 		parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
587 
588 		oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
589 		oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
590 		oob.u.a.ANAC = anac;
591 		oob.u.a.NACs = nacs;
592 		oob.u.a.parityPerField = parity;
593 		oob.u.a.discarded = 0xaa;
594 
595 		inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
596 				&retlen, (char *)&oob.u);
597 
598 		/* Also back up header... */
599 		oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
600 		oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
601 		oob.u.b.ANAC = anac;
602 		oob.u.b.NACs = nacs;
603 		oob.u.b.parityPerField = parity;
604 		oob.u.b.discarded = 0xaa;
605 
606 		inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
607 				SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
608 
609 		inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
610 		inftl->VUtable[thisVUC] = writeEUN;
611 
612 		inftl->numfreeEUNs--;
613 		return writeEUN;
614 
615 	} while (silly2--);
616 
617 	printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
618 		"Unit Chain 0x%x\n", thisVUC);
619 	return BLOCK_NIL;
620 }
621 
622 /*
623  * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
624  */
INFTL_trydeletechain(struct INFTLrecord * inftl,unsigned thisVUC)625 static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
626 {
627 	struct mtd_info *mtd = inftl->mbd.mtd;
628 	unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
629 	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
630 	unsigned int thisEUN, status;
631 	int block, silly;
632 	struct inftl_bci bci;
633 	size_t retlen;
634 
635 	pr_debug("INFTL: INFTL_trydeletechain(inftl=%p,"
636 		"thisVUC=%d)\n", inftl, thisVUC);
637 
638 	memset(BlockUsed, 0, sizeof(BlockUsed));
639 	memset(BlockDeleted, 0, sizeof(BlockDeleted));
640 
641 	thisEUN = inftl->VUtable[thisVUC];
642 	if (thisEUN == BLOCK_NIL) {
643 		printk(KERN_WARNING "INFTL: trying to delete non-existent "
644 		       "Virtual Unit Chain %d!\n", thisVUC);
645 		return;
646 	}
647 
648 	/*
649 	 * Scan through the Erase Units to determine whether any data is in
650 	 * each of the 512-byte blocks within the Chain.
651 	 */
652 	silly = MAX_LOOPS;
653 	while (thisEUN < inftl->nb_blocks) {
654 		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
655 			if (BlockUsed[block] || BlockDeleted[block])
656 				continue;
657 
658 			if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
659 					   + (block * SECTORSIZE), 8 , &retlen,
660 					  (char *)&bci) < 0)
661 				status = SECTOR_IGNORE;
662 			else
663 				status = bci.Status | bci.Status1;
664 
665 			switch(status) {
666 			case SECTOR_FREE:
667 			case SECTOR_IGNORE:
668 				break;
669 			case SECTOR_USED:
670 				BlockUsed[block] = 1;
671 				continue;
672 			case SECTOR_DELETED:
673 				BlockDeleted[block] = 1;
674 				continue;
675 			default:
676 				printk(KERN_WARNING "INFTL: unknown status "
677 					"for block %d in EUN %d: 0x%x\n",
678 					block, thisEUN, status);
679 			}
680 		}
681 
682 		if (!silly--) {
683 			printk(KERN_WARNING "INFTL: infinite loop in Virtual "
684 				"Unit Chain 0x%x\n", thisVUC);
685 			return;
686 		}
687 
688 		thisEUN = inftl->PUtable[thisEUN];
689 	}
690 
691 	for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
692 		if (BlockUsed[block])
693 			return;
694 
695 	/*
696 	 * For each block in the chain free it and make it available
697 	 * for future use. Erase from the oldest unit first.
698 	 */
699 	pr_debug("INFTL: deleting empty VUC %d\n", thisVUC);
700 
701 	for (;;) {
702 		u16 *prevEUN = &inftl->VUtable[thisVUC];
703 		thisEUN = *prevEUN;
704 
705 		/* If the chain is all gone already, we're done */
706 		if (thisEUN == BLOCK_NIL) {
707 			pr_debug("INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
708 			return;
709 		}
710 
711 		/* Find oldest unit in chain. */
712 		while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
713 			BUG_ON(thisEUN >= inftl->nb_blocks);
714 
715 			prevEUN = &inftl->PUtable[thisEUN];
716 			thisEUN = *prevEUN;
717 		}
718 
719 		pr_debug("Deleting EUN %d from VUC %d\n",
720 		      thisEUN, thisVUC);
721 
722 		if (INFTL_formatblock(inftl, thisEUN) < 0) {
723 			/*
724 			 * Could not erase : mark block as reserved.
725 			 */
726 			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
727 		} else {
728 			/* Correctly erased : mark it as free */
729 			inftl->PUtable[thisEUN] = BLOCK_FREE;
730 			inftl->numfreeEUNs++;
731 		}
732 
733 		/* Now sort out whatever was pointing to it... */
734 		*prevEUN = BLOCK_NIL;
735 
736 		/* Ideally we'd actually be responsive to new
737 		   requests while we're doing this -- if there's
738 		   free space why should others be made to wait? */
739 		cond_resched();
740 	}
741 
742 	inftl->VUtable[thisVUC] = BLOCK_NIL;
743 }
744 
INFTL_deleteblock(struct INFTLrecord * inftl,unsigned block)745 static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
746 {
747 	unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
748 	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
749 	struct mtd_info *mtd = inftl->mbd.mtd;
750 	unsigned int status;
751 	int silly = MAX_LOOPS;
752 	size_t retlen;
753 	struct inftl_bci bci;
754 
755 	pr_debug("INFTL: INFTL_deleteblock(inftl=%p,"
756 		"block=%d)\n", inftl, block);
757 
758 	while (thisEUN < inftl->nb_blocks) {
759 		if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
760 				   blockofs, 8, &retlen, (char *)&bci) < 0)
761 			status = SECTOR_IGNORE;
762 		else
763 			status = bci.Status | bci.Status1;
764 
765 		switch (status) {
766 		case SECTOR_FREE:
767 		case SECTOR_IGNORE:
768 			break;
769 		case SECTOR_DELETED:
770 			thisEUN = BLOCK_NIL;
771 			goto foundit;
772 		case SECTOR_USED:
773 			goto foundit;
774 		default:
775 			printk(KERN_WARNING "INFTL: unknown status for "
776 				"block %d in EUN %d: 0x%x\n",
777 				block, thisEUN, status);
778 			break;
779 		}
780 
781 		if (!silly--) {
782 			printk(KERN_WARNING "INFTL: infinite loop in Virtual "
783 				"Unit Chain 0x%x\n",
784 				block / (inftl->EraseSize / SECTORSIZE));
785 			return 1;
786 		}
787 		thisEUN = inftl->PUtable[thisEUN];
788 	}
789 
790 foundit:
791 	if (thisEUN != BLOCK_NIL) {
792 		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
793 
794 		if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
795 			return -EIO;
796 		bci.Status = bci.Status1 = SECTOR_DELETED;
797 		if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
798 			return -EIO;
799 		INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
800 	}
801 	return 0;
802 }
803 
inftl_writeblock(struct mtd_blktrans_dev * mbd,unsigned long block,char * buffer)804 static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
805 			    char *buffer)
806 {
807 	struct INFTLrecord *inftl = (void *)mbd;
808 	unsigned int writeEUN;
809 	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
810 	size_t retlen;
811 	struct inftl_oob oob;
812 	char *p, *pend;
813 
814 	pr_debug("INFTL: inftl_writeblock(inftl=%p,block=%ld,"
815 		"buffer=%p)\n", inftl, block, buffer);
816 
817 	/* Is block all zero? */
818 	pend = buffer + SECTORSIZE;
819 	for (p = buffer; p < pend && !*p; p++)
820 		;
821 
822 	if (p < pend) {
823 		writeEUN = INFTL_findwriteunit(inftl, block);
824 
825 		if (writeEUN == BLOCK_NIL) {
826 			printk(KERN_WARNING "inftl_writeblock(): cannot find "
827 				"block to write to\n");
828 			/*
829 			 * If we _still_ haven't got a block to use,
830 			 * we're screwed.
831 			 */
832 			return 1;
833 		}
834 
835 		memset(&oob, 0xff, sizeof(struct inftl_oob));
836 		oob.b.Status = oob.b.Status1 = SECTOR_USED;
837 
838 		inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
839 			    blockofs, SECTORSIZE, &retlen, (char *)buffer,
840 			    (char *)&oob);
841 		/*
842 		 * need to write SECTOR_USED flags since they are not written
843 		 * in mtd_writeecc
844 		 */
845 	} else {
846 		INFTL_deleteblock(inftl, block);
847 	}
848 
849 	return 0;
850 }
851 
inftl_readblock(struct mtd_blktrans_dev * mbd,unsigned long block,char * buffer)852 static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
853 			   char *buffer)
854 {
855 	struct INFTLrecord *inftl = (void *)mbd;
856 	unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
857 	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
858 	struct mtd_info *mtd = inftl->mbd.mtd;
859 	unsigned int status;
860 	int silly = MAX_LOOPS;
861 	struct inftl_bci bci;
862 	size_t retlen;
863 
864 	pr_debug("INFTL: inftl_readblock(inftl=%p,block=%ld,"
865 		"buffer=%p)\n", inftl, block, buffer);
866 
867 	while (thisEUN < inftl->nb_blocks) {
868 		if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
869 				  blockofs, 8, &retlen, (char *)&bci) < 0)
870 			status = SECTOR_IGNORE;
871 		else
872 			status = bci.Status | bci.Status1;
873 
874 		switch (status) {
875 		case SECTOR_DELETED:
876 			thisEUN = BLOCK_NIL;
877 			goto foundit;
878 		case SECTOR_USED:
879 			goto foundit;
880 		case SECTOR_FREE:
881 		case SECTOR_IGNORE:
882 			break;
883 		default:
884 			printk(KERN_WARNING "INFTL: unknown status for "
885 				"block %ld in EUN %d: 0x%04x\n",
886 				block, thisEUN, status);
887 			break;
888 		}
889 
890 		if (!silly--) {
891 			printk(KERN_WARNING "INFTL: infinite loop in "
892 				"Virtual Unit Chain 0x%lx\n",
893 				block / (inftl->EraseSize / SECTORSIZE));
894 			return 1;
895 		}
896 
897 		thisEUN = inftl->PUtable[thisEUN];
898 	}
899 
900 foundit:
901 	if (thisEUN == BLOCK_NIL) {
902 		/* The requested block is not on the media, return all 0x00 */
903 		memset(buffer, 0, SECTORSIZE);
904 	} else {
905 		size_t retlen;
906 		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
907 		int ret = mtd_read(mtd, ptr, SECTORSIZE, &retlen, buffer);
908 
909 		/* Handle corrected bit flips gracefully */
910 		if (ret < 0 && !mtd_is_bitflip(ret))
911 			return -EIO;
912 	}
913 	return 0;
914 }
915 
inftl_getgeo(struct mtd_blktrans_dev * dev,struct hd_geometry * geo)916 static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
917 {
918 	struct INFTLrecord *inftl = (void *)dev;
919 
920 	geo->heads = inftl->heads;
921 	geo->sectors = inftl->sectors;
922 	geo->cylinders = inftl->cylinders;
923 
924 	return 0;
925 }
926 
927 static struct mtd_blktrans_ops inftl_tr = {
928 	.name		= "inftl",
929 	.major		= INFTL_MAJOR,
930 	.part_bits	= INFTL_PARTN_BITS,
931 	.blksize 	= 512,
932 	.getgeo		= inftl_getgeo,
933 	.readsect	= inftl_readblock,
934 	.writesect	= inftl_writeblock,
935 	.add_mtd	= inftl_add_mtd,
936 	.remove_dev	= inftl_remove_dev,
937 	.owner		= THIS_MODULE,
938 };
939 
940 module_mtd_blktrans(inftl_tr);
941 
942 MODULE_LICENSE("GPL");
943 MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
944 MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");
945