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