Blame view

drivers/mtd/inftlcore.c 24.5 KB
97894cda5   Thomas Gleixner   [MTD] core: Clean...
1
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
   * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
   *
a1452a377   David Woodhouse   mtd: Update copyr...
4
   * Copyright © 2002, Greg Ungerer (gerg@snapgear.com)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
   *
   * Based heavily on the nftlcore.c code which is:
a1452a377   David Woodhouse   mtd: Update copyr...
7
8
   * Copyright © 1999 Machine Vision Holdings, Inc.
   * Copyright © 1999 David Woodhouse <dwmw2@infradead.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
29
30
31
32
33
34
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/delay.h>
  #include <linux/slab.h>
  #include <linux/sched.h>
  #include <linux/init.h>
  #include <linux/kmod.h>
  #include <linux/hdreg.h>
  #include <linux/mtd/mtd.h>
  #include <linux/mtd/nftl.h>
  #include <linux/mtd/inftl.h>
9223a456d   Thomas Gleixner   [MTD] Remove read...
35
  #include <linux/mtd/nand.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
41
42
43
44
45
  #include <asm/uaccess.h>
  #include <asm/errno.h>
  #include <asm/io.h>
  
  /*
   * Maximum number of loops while examining next block, to have a
   * chance to detect consistency problems (they should never happen
   * because of the checks done in the mounting.
   */
  #define MAX_LOOPS 10000
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
  static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
  {
  	struct INFTLrecord *inftl;
  	unsigned long temp;
69423d99f   Adrian Hunter   [MTD] update inte...
50
  	if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
55
56
57
58
59
60
61
62
63
  		return;
  	/* OK, this is moderately ugly.  But probably safe.  Alternatives? */
  	if (memcmp(mtd->name, "DiskOnChip", 10))
  		return;
  
  	if (!mtd->block_isbad) {
  		printk(KERN_ERR
  "INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.
  "
  "Please use the new diskonchip driver under the NAND subsystem.
  ");
  		return;
  	}
289c05222   Brian Norris   mtd: replace DEBU...
64
65
  	pr_debug("INFTL: add_mtd for %s
  ", mtd->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66

95b93a0cd   Burman Yan   [MTD] replace kma...
67
  	inftl = kzalloc(sizeof(*inftl), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68

0870066d7   Brian Norris   mtd: remove print...
69
  	if (!inftl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
  
  	inftl->mbd.mtd = mtd;
  	inftl->mbd.devnum = -1;
191876729   Richard Purdie   [MTD] Allow varia...
74

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  	inftl->mbd.tr = tr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76

9223a456d   Thomas Gleixner   [MTD] Remove read...
77
  	if (INFTL_mount(inftl) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
  		printk(KERN_WARNING "INFTL: could not mount device
  ");
  		kfree(inftl);
  		return;
9223a456d   Thomas Gleixner   [MTD] Remove read...
82
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  
  	/* OK, it's a new one. Set up all the data structures. */
  
  	/* Calculate geometry */
  	inftl->cylinders = 1024;
  	inftl->heads = 16;
  
  	temp = inftl->cylinders * inftl->heads;
  	inftl->sectors = inftl->mbd.size / temp;
  	if (inftl->mbd.size % temp) {
  		inftl->sectors++;
  		temp = inftl->cylinders * inftl->sectors;
  		inftl->heads = inftl->mbd.size / temp;
  
  		if (inftl->mbd.size % temp) {
  			inftl->heads++;
  			temp = inftl->heads * inftl->sectors;
  			inftl->cylinders = inftl->mbd.size / temp;
  		}
  	}
  
  	if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {
  		/*
97894cda5   Thomas Gleixner   [MTD] core: Clean...
106
  		  Oh no we don't have
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
111
112
113
114
  		   mbd.size == heads * cylinders * sectors
  		*/
  		printk(KERN_WARNING "INFTL: cannot calculate a geometry to "
  		       "match size of 0x%lx.
  ", inftl->mbd.size);
  		printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "
  			"(== 0x%lx sects)
  ",
97894cda5   Thomas Gleixner   [MTD] core: Clean...
115
  			inftl->cylinders, inftl->heads , inftl->sectors,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
  			(long)inftl->cylinders * (long)inftl->heads *
  			(long)inftl->sectors );
  	}
  
  	if (add_mtd_blktrans_dev(&inftl->mbd)) {
fa671646f   Jesper Juhl   [PATCH] kfree cle...
121
122
  		kfree(inftl->PUtable);
  		kfree(inftl->VUtable);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
125
126
  		kfree(inftl);
  		return;
  	}
  #ifdef PSYCHO_DEBUG
8b68a1263   Eric Sesterhenn / snakebyte   [PATCH] Fix debug...
127
128
  	printk(KERN_INFO "INFTL: Found new inftl%c
  ", inftl->mbd.devnum + 'a');
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
132
133
134
135
  #endif
  	return;
  }
  
  static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
  {
  	struct INFTLrecord *inftl = (void *)dev;
289c05222   Brian Norris   mtd: replace DEBU...
136
137
  	pr_debug("INFTL: remove_dev (i=%d)
  ", dev->devnum);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
  
  	del_mtd_blktrans_dev(dev);
fa671646f   Jesper Juhl   [PATCH] kfree cle...
140
141
  	kfree(inftl->PUtable);
  	kfree(inftl->VUtable);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
145
146
147
148
  }
  
  /*
   * Actual INFTL access routines.
   */
  
  /*
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
149
150
151
152
153
154
155
   * Read oob data from flash
   */
  int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
  		   size_t *retlen, uint8_t *buf)
  {
  	struct mtd_oob_ops ops;
  	int res;
0612b9ddc   Brian Norris   mtd: rename MTD_O...
156
  	ops.mode = MTD_OPS_PLACE_OOB;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
157
158
159
160
  	ops.ooboffs = offs & (mtd->writesize - 1);
  	ops.ooblen = len;
  	ops.oobbuf = buf;
  	ops.datbuf = NULL;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
161

fd2819bbc   Artem Bityutskiy   mtd: introduce mt...
162
  	res = mtd_read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
7014568ba   Vitaly Wool   [MTD] [NAND] remo...
163
  	*retlen = ops.oobretlen;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
164
165
166
167
168
169
170
171
172
173
174
  	return res;
  }
  
  /*
   * Write oob data to flash
   */
  int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
  		    size_t *retlen, uint8_t *buf)
  {
  	struct mtd_oob_ops ops;
  	int res;
0612b9ddc   Brian Norris   mtd: rename MTD_O...
175
  	ops.mode = MTD_OPS_PLACE_OOB;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
176
177
178
179
  	ops.ooboffs = offs & (mtd->writesize - 1);
  	ops.ooblen = len;
  	ops.oobbuf = buf;
  	ops.datbuf = NULL;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
180

a2cc5ba07   Artem Bityutskiy   mtd: introduce mt...
181
  	res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
7014568ba   Vitaly Wool   [MTD] [NAND] remo...
182
  	*retlen = ops.oobretlen;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
183
184
185
186
187
188
189
190
191
192
193
  	return res;
  }
  
  /*
   * Write data and oob to flash
   */
  static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
  		       size_t *retlen, uint8_t *buf, uint8_t *oob)
  {
  	struct mtd_oob_ops ops;
  	int res;
0612b9ddc   Brian Norris   mtd: rename MTD_O...
194
  	ops.mode = MTD_OPS_PLACE_OOB;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
195
196
197
198
199
  	ops.ooboffs = offs;
  	ops.ooblen = mtd->oobsize;
  	ops.oobbuf = oob;
  	ops.datbuf = buf;
  	ops.len = len;
a2cc5ba07   Artem Bityutskiy   mtd: introduce mt...
200
  	res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
201
202
203
204
205
  	*retlen = ops.retlen;
  	return res;
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
209
210
211
212
   * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
   *	This function is used when the give Virtual Unit Chain.
   */
  static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
  {
  	u16 pot = inftl->LastFreeEUN;
  	int silly = inftl->nb_blocks;
0a32a1026   Brian Norris   mtd: cleanup styl...
213
214
215
  	pr_debug("INFTL: INFTL_findfreeblock(inftl=%p,desperate=%d)
  ",
  			inftl, desperate);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
217
218
219
220
221
  
  	/*
  	 * Normally, we force a fold to happen before we run out of free
  	 * blocks completely.
  	 */
  	if (!desperate && inftl->numfreeEUNs < 2) {
0a32a1026   Brian Norris   mtd: cleanup styl...
222
223
224
  		pr_debug("INFTL: there are too few free EUNs (%d)
  ",
  				inftl->numfreeEUNs);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
225
  		return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
  	}
  
  	/* Scan for a free block */
  	do {
  		if (inftl->PUtable[pot] == BLOCK_FREE) {
  			inftl->LastFreeEUN = pot;
  			return pot;
  		}
  
  		if (++pot > inftl->lastEUN)
  			pot = 0;
  
  		if (!silly--) {
  			printk(KERN_WARNING "INFTL: no free blocks found!  "
  				"EUN range = %d - %d
  ", 0, inftl->LastFreeEUN);
  			return BLOCK_NIL;
  		}
  	} while (pot != inftl->LastFreeEUN);
  
  	return BLOCK_NIL;
  }
  
  static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
  {
  	u16 BlockMap[MAX_SECTORS_PER_UNIT];
  	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
  	unsigned int thisEUN, prevEUN, status;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
254
  	struct mtd_info *mtd = inftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
  	int block, silly;
  	unsigned int targetEUN;
  	struct inftl_oob oob;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
258
  	size_t retlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259

0a32a1026   Brian Norris   mtd: cleanup styl...
260
261
262
  	pr_debug("INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,pending=%d)
  ",
  			inftl, thisVUC, pendingblock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
266
267
268
269
270
271
272
273
274
  
  	memset(BlockMap, 0xff, sizeof(BlockMap));
  	memset(BlockDeleted, 0, sizeof(BlockDeleted));
  
  	thisEUN = targetEUN = inftl->VUtable[thisVUC];
  
  	if (thisEUN == BLOCK_NIL) {
  		printk(KERN_WARNING "INFTL: trying to fold non-existent "
  		       "Virtual Unit Chain %d!
  ", thisVUC);
  		return BLOCK_NIL;
  	}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
275

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
277
278
279
  	/*
  	 * Scan to find the Erase Unit which holds the actual data for each
  	 * 512-byte block within the Chain.
  	 */
9223a456d   Thomas Gleixner   [MTD] Remove read...
280
  	silly = MAX_LOOPS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
  	while (thisEUN < inftl->nb_blocks) {
  		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
283
284
  			if ((BlockMap[block] != BLOCK_NIL) ||
  			    BlockDeleted[block])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  				continue;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
286
287
288
  			if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
  					   + (block * SECTORSIZE), 16, &retlen,
  					   (char *)&oob) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
  				status = SECTOR_IGNORE;
  			else
9223a456d   Thomas Gleixner   [MTD] Remove read...
291
  				status = oob.b.Status | oob.b.Status1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  
  			switch(status) {
  			case SECTOR_FREE:
  			case SECTOR_IGNORE:
  				break;
  			case SECTOR_USED:
  				BlockMap[block] = thisEUN;
  				continue;
  			case SECTOR_DELETED:
  				BlockDeleted[block] = 1;
  				continue;
  			default:
  				printk(KERN_WARNING "INFTL: unknown status "
  					"for block %d in EUN %d: %x
  ",
  					block, thisEUN, status);
  				break;
  			}
  		}
  
  		if (!silly--) {
  			printk(KERN_WARNING "INFTL: infinite loop in Virtual "
  				"Unit Chain 0x%x
  ", thisVUC);
  			return BLOCK_NIL;
  		}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
318

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
322
323
324
325
326
  		thisEUN = inftl->PUtable[thisEUN];
  	}
  
  	/*
  	 * OK. We now know the location of every block in the Virtual Unit
  	 * Chain, and the Erase Unit into which we are supposed to be copying.
  	 * Go for it.
  	 */
0a32a1026   Brian Norris   mtd: cleanup styl...
327
328
  	pr_debug("INFTL: folding chain %d into unit %d
  ", thisVUC, targetEUN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
331
332
333
334
335
336
337
338
339
340
341
  
  	for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
  		unsigned char movebuf[SECTORSIZE];
  		int ret;
  
  		/*
  		 * If it's in the target EUN already, or if it's pending write,
  		 * do nothing.
  		 */
  		if (BlockMap[block] == targetEUN || (pendingblock ==
  		    (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
  			continue;
  		}
9223a456d   Thomas Gleixner   [MTD] Remove read...
342
  		/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
  		 * Copy only in non free block (free blocks can only
                   * happen in case of media errors or deleted blocks).
  		 */
9223a456d   Thomas Gleixner   [MTD] Remove read...
346
347
  		if (BlockMap[block] == BLOCK_NIL)
  			continue;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
348

329ad399a   Artem Bityutskiy   mtd: introduce mt...
349
350
351
352
353
  		ret = mtd_read(mtd,
  			       (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE),
  			       SECTORSIZE,
  			       &retlen,
  			       movebuf);
d57f40544   Brian Norris   mtd: utilize `mtd...
354
  		if (ret < 0 && !mtd_is_bitflip(ret)) {
329ad399a   Artem Bityutskiy   mtd: introduce mt...
355
356
357
358
359
  			ret = mtd_read(mtd,
  				       (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE),
  				       SECTORSIZE,
  				       &retlen,
  				       movebuf);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
360
  			if (ret != -EIO)
0a32a1026   Brian Norris   mtd: cleanup styl...
361
362
  				pr_debug("INFTL: error went away on retry?
  ");
9223a456d   Thomas Gleixner   [MTD] Remove read...
363
364
365
  		}
  		memset(&oob, 0xff, sizeof(struct inftl_oob));
  		oob.b.Status = oob.b.Status1 = SECTOR_USED;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
366
367
368
  		inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
  			    (block * SECTORSIZE), SECTORSIZE, &retlen,
  			    movebuf, (char *)&oob);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
372
373
374
375
376
  	}
  
  	/*
  	 * Newest unit in chain now contains data from _all_ older units.
  	 * So go through and erase each unit in chain, oldest first. (This
  	 * is important, by doing oldest first if we crash/reboot then it
  	 * it is relatively simple to clean up the mess).
  	 */
0a32a1026   Brian Norris   mtd: cleanup styl...
377
378
  	pr_debug("INFTL: want to erase virtual chain %d
  ", thisVUC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
381
382
383
384
385
386
387
388
389
390
391
  
  	for (;;) {
  		/* Find oldest unit in chain. */
  		thisEUN = inftl->VUtable[thisVUC];
  		prevEUN = BLOCK_NIL;
  		while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
  			prevEUN = thisEUN;
  			thisEUN = inftl->PUtable[thisEUN];
  		}
  
  		/* Check if we are all done */
  		if (thisEUN == targetEUN)
  			break;
63fd7f30f   Daniel Rosenthal   [MTD] [INFTL] Fix...
392
393
394
395
  		/* Unlink the last block from the chain. */
  		inftl->PUtable[prevEUN] = BLOCK_NIL;
  
  		/* Now try to erase it. */
9223a456d   Thomas Gleixner   [MTD] Remove read...
396
  		if (INFTL_formatblock(inftl, thisEUN) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
400
  			/*
  			 * Could not erase : mark block as reserved.
  			 */
  			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
9223a456d   Thomas Gleixner   [MTD] Remove read...
401
  		} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
403
  			/* Correctly erased : mark it as free */
  			inftl->PUtable[thisEUN] = BLOCK_FREE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
  			inftl->numfreeEUNs++;
9223a456d   Thomas Gleixner   [MTD] Remove read...
405
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
408
409
410
411
412
413
  	}
  
  	return targetEUN;
  }
  
  static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
  {
  	/*
97894cda5   Thomas Gleixner   [MTD] core: Clean...
414
  	 * This is the part that needs some cleverness applied.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
417
418
419
420
421
422
423
  	 * For now, I'm doing the minimum applicable to actually
  	 * get the thing to work.
  	 * Wear-levelling and other clever stuff needs to be implemented
  	 * and we also need to do some assessment of the results when
  	 * the system loses power half-way through the routine.
  	 */
  	u16 LongestChain = 0;
  	u16 ChainLength = 0, thislen;
  	u16 chain, EUN;
289c05222   Brian Norris   mtd: replace DEBU...
424
  	pr_debug("INFTL: INFTL_makefreeblock(inftl=%p,"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
  		"pending=%d)
  ", inftl, pendingblock);
  
  	for (chain = 0; chain < inftl->nb_blocks; chain++) {
  		EUN = inftl->VUtable[chain];
  		thislen = 0;
  
  		while (EUN <= inftl->lastEUN) {
  			thislen++;
  			EUN = inftl->PUtable[EUN];
  			if (thislen > 0xff00) {
  				printk(KERN_WARNING "INFTL: endless loop in "
  					"Virtual Chain %d: Unit %x
  ",
  					chain, EUN);
  				/*
  				 * Actually, don't return failure.
  				 * Just ignore this chain and get on with it.
  				 */
  				thislen = 0;
  				break;
  			}
  		}
  
  		if (thislen > ChainLength) {
  			ChainLength = thislen;
  			LongestChain = chain;
  		}
  	}
  
  	if (ChainLength < 2) {
  		printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
  			"for folding. Failing request
  ");
  		return BLOCK_NIL;
  	}
  
  	return INFTL_foldchain(inftl, LongestChain, pendingblock);
  }
  
  static int nrbits(unsigned int val, int bitcount)
  {
  	int i, total = 0;
  
  	for (i = 0; (i < bitcount); i++)
  		total += (((0x1 << i) & val) ? 1 : 0);
  	return total;
  }
  
  /*
97894cda5   Thomas Gleixner   [MTD] core: Clean...
475
   * INFTL_findwriteunit: Return the unit number into which we can write
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
478
479
480
481
482
   *                      for this block. Make it available if it isn't already.
   */
  static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
  {
  	unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
  	unsigned int thisEUN, writeEUN, prev_block, status;
  	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
483
  	struct mtd_info *mtd = inftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484
485
486
487
488
  	struct inftl_oob oob;
  	struct inftl_bci bci;
  	unsigned char anac, nacs, parity;
  	size_t retlen;
  	int silly, silly2 = 3;
0a32a1026   Brian Norris   mtd: cleanup styl...
489
490
491
  	pr_debug("INFTL: INFTL_findwriteunit(inftl=%p,block=%d)
  ",
  			inftl, block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
493
494
495
496
497
498
499
500
501
502
  
  	do {
  		/*
  		 * Scan the media to find a unit in the VUC which has
  		 * a free space for the block in question.
  		 */
  		writeEUN = BLOCK_NIL;
  		thisEUN = inftl->VUtable[thisVUC];
  		silly = MAX_LOOPS;
  
  		while (thisEUN <= inftl->lastEUN) {
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
503
504
  			inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
  				       blockofs, 8, &retlen, (char *)&bci);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505

9223a456d   Thomas Gleixner   [MTD] Remove read...
506
  			status = bci.Status | bci.Status1;
0a32a1026   Brian Norris   mtd: cleanup styl...
507
508
509
  			pr_debug("INFTL: status of block %d in EUN %d is %x
  ",
  					block , writeEUN, status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
  
  			switch(status) {
  			case SECTOR_FREE:
  				writeEUN = thisEUN;
  				break;
  			case SECTOR_DELETED:
  			case SECTOR_USED:
  				/* Can't go any further */
  				goto hitused;
  			case SECTOR_IGNORE:
  				break;
  			default:
  				/*
  				 * Invalid block. Don't use it any more.
  				 * Must implement.
  				 */
97894cda5   Thomas Gleixner   [MTD] core: Clean...
526
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
  			}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
528
529
  
  			if (!silly--) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
531
532
  				printk(KERN_WARNING "INFTL: infinite loop in "
  					"Virtual Unit Chain 0x%x
  ", thisVUC);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
533
  				return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
535
536
537
538
539
540
541
542
543
544
545
  			}
  
  			/* Skip to next block in chain */
  			thisEUN = inftl->PUtable[thisEUN];
  		}
  
  hitused:
  		if (writeEUN != BLOCK_NIL)
  			return writeEUN;
  
  
  		/*
97894cda5   Thomas Gleixner   [MTD] core: Clean...
546
  		 * OK. We didn't find one in the existing chain, or there
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
548
549
550
551
552
553
554
555
556
  		 * is no existing chain. Allocate a new one.
  		 */
  		writeEUN = INFTL_findfreeblock(inftl, 0);
  
  		if (writeEUN == BLOCK_NIL) {
  			/*
  			 * That didn't work - there were no free blocks just
  			 * waiting to be picked up. We're going to have to fold
  			 * a chain to make room.
  			 */
6ad08ddd9   Mohanlal Jangir   mtd: inftl: fix f...
557
  			thisEUN = INFTL_makefreeblock(inftl, block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
561
562
  
  			/*
  			 * Hopefully we free something, lets try again.
  			 * This time we are desperate...
  			 */
0a32a1026   Brian Norris   mtd: cleanup styl...
563
564
565
566
  			pr_debug("INFTL: using desperate==1 to find free EUN "
  					"to accommodate write to VUC %d
  ",
  					thisVUC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
568
569
570
  			writeEUN = INFTL_findfreeblock(inftl, 1);
  			if (writeEUN == BLOCK_NIL) {
  				/*
  				 * Ouch. This should never happen - we should
97894cda5   Thomas Gleixner   [MTD] core: Clean...
571
572
  				 * always be able to make some room somehow.
  				 * If we get here, we've allocated more storage
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
574
575
576
577
578
579
580
581
582
583
  				 * space than actual media, or our makefreeblock
  				 * routine is missing something.
  				 */
  				printk(KERN_WARNING "INFTL: cannot make free "
  					"space.
  ");
  #ifdef DEBUG
  				INFTL_dumptables(inftl);
  				INFTL_dumpVUchains(inftl);
  #endif
  				return BLOCK_NIL;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
584
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
586
587
588
589
590
591
592
593
594
  		}
  
  		/*
  		 * Insert new block into virtual chain. Firstly update the
  		 * block headers in flash...
  		 */
  		anac = 0;
  		nacs = 0;
  		thisEUN = inftl->VUtable[thisVUC];
  		if (thisEUN != BLOCK_NIL) {
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
595
596
  			inftl_read_oob(mtd, thisEUN * inftl->EraseSize
  				       + 8, 8, &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
598
599
600
601
602
603
604
605
606
607
608
  			anac = oob.u.a.ANAC + 1;
  			nacs = oob.u.a.NACs + 1;
  		}
  
  		prev_block = inftl->VUtable[thisVUC];
  		if (prev_block < inftl->nb_blocks)
  			prev_block -= inftl->firstEUN;
  
  		parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
  		parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
  		parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
  		parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
609

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
611
612
613
614
615
  		oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
  		oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
  		oob.u.a.ANAC = anac;
  		oob.u.a.NACs = nacs;
  		oob.u.a.parityPerField = parity;
  		oob.u.a.discarded = 0xaa;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
616
617
  		inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
  				&retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
619
620
621
622
623
624
625
  
  		/* Also back up header... */
  		oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
  		oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
  		oob.u.b.ANAC = anac;
  		oob.u.b.NACs = nacs;
  		oob.u.b.parityPerField = parity;
  		oob.u.b.discarded = 0xaa;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
626
627
  		inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
  				SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
630
631
632
633
634
635
636
637
638
639
  
  		inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
  		inftl->VUtable[thisVUC] = writeEUN;
  
  		inftl->numfreeEUNs--;
  		return writeEUN;
  
  	} while (silly2--);
  
  	printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
  		"Unit Chain 0x%x
  ", thisVUC);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
640
  	return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
643
644
645
646
647
  }
  
  /*
   * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
   */
  static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
  {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
648
  	struct mtd_info *mtd = inftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
650
651
652
653
654
  	unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
  	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
  	unsigned int thisEUN, status;
  	int block, silly;
  	struct inftl_bci bci;
  	size_t retlen;
289c05222   Brian Norris   mtd: replace DEBU...
655
  	pr_debug("INFTL: INFTL_trydeletechain(inftl=%p,"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
658
659
660
661
662
663
664
665
666
667
668
  		"thisVUC=%d)
  ", inftl, thisVUC);
  
  	memset(BlockUsed, 0, sizeof(BlockUsed));
  	memset(BlockDeleted, 0, sizeof(BlockDeleted));
  
  	thisEUN = inftl->VUtable[thisVUC];
  	if (thisEUN == BLOCK_NIL) {
  		printk(KERN_WARNING "INFTL: trying to delete non-existent "
  		       "Virtual Unit Chain %d!
  ", thisVUC);
  		return;
  	}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
669

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
671
672
673
674
675
676
677
678
  	/*
  	 * Scan through the Erase Units to determine whether any data is in
  	 * each of the 512-byte blocks within the Chain.
  	 */
  	silly = MAX_LOOPS;
  	while (thisEUN < inftl->nb_blocks) {
  		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
  			if (BlockUsed[block] || BlockDeleted[block])
  				continue;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
679
680
  			if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
  					   + (block * SECTORSIZE), 8 , &retlen,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
681
  					  (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
  				status = SECTOR_IGNORE;
  			else
  				status = bci.Status | bci.Status1;
  
  			switch(status) {
  			case SECTOR_FREE:
  			case SECTOR_IGNORE:
  				break;
  			case SECTOR_USED:
  				BlockUsed[block] = 1;
  				continue;
  			case SECTOR_DELETED:
  				BlockDeleted[block] = 1;
  				continue;
  			default:
  				printk(KERN_WARNING "INFTL: unknown status "
  					"for block %d in EUN %d: 0x%x
  ",
  					block, thisEUN, status);
  			}
  		}
  
  		if (!silly--) {
  			printk(KERN_WARNING "INFTL: infinite loop in Virtual "
  				"Unit Chain 0x%x
  ", thisVUC);
  			return;
  		}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
710

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
712
713
714
715
716
717
718
719
720
721
  		thisEUN = inftl->PUtable[thisEUN];
  	}
  
  	for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
  		if (BlockUsed[block])
  			return;
  
  	/*
  	 * For each block in the chain free it and make it available
  	 * for future use. Erase from the oldest unit first.
  	 */
289c05222   Brian Norris   mtd: replace DEBU...
722
723
  	pr_debug("INFTL: deleting empty VUC %d
  ", thisVUC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
725
726
727
728
729
730
  
  	for (;;) {
  		u16 *prevEUN = &inftl->VUtable[thisVUC];
  		thisEUN = *prevEUN;
  
  		/* If the chain is all gone already, we're done */
  		if (thisEUN == BLOCK_NIL) {
289c05222   Brian Norris   mtd: replace DEBU...
731
732
  			pr_debug("INFTL: Empty VUC %d for deletion was already absent
  ", thisEUN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
733
734
735
736
737
738
739
740
741
742
  			return;
  		}
  
  		/* Find oldest unit in chain. */
  		while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
  			BUG_ON(thisEUN >= inftl->nb_blocks);
  
  			prevEUN = &inftl->PUtable[thisEUN];
  			thisEUN = *prevEUN;
  		}
289c05222   Brian Norris   mtd: replace DEBU...
743
744
  		pr_debug("Deleting EUN %d from VUC %d
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
  		      thisEUN, thisVUC);
9223a456d   Thomas Gleixner   [MTD] Remove read...
746
  		if (INFTL_formatblock(inftl, thisEUN) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
748
749
750
  			/*
  			 * Could not erase : mark block as reserved.
  			 */
  			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
9223a456d   Thomas Gleixner   [MTD] Remove read...
751
  		} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
  			/* Correctly erased : mark it as free */
  			inftl->PUtable[thisEUN] = BLOCK_FREE;
  			inftl->numfreeEUNs++;
  		}
  
  		/* Now sort out whatever was pointing to it... */
  		*prevEUN = BLOCK_NIL;
  
  		/* Ideally we'd actually be responsive to new
  		   requests while we're doing this -- if there's
  		   free space why should others be made to wait? */
  		cond_resched();
  	}
  
  	inftl->VUtable[thisVUC] = BLOCK_NIL;
  }
  
  static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
  {
  	unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
  	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
773
  	struct mtd_info *mtd = inftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
775
776
777
  	unsigned int status;
  	int silly = MAX_LOOPS;
  	size_t retlen;
  	struct inftl_bci bci;
289c05222   Brian Norris   mtd: replace DEBU...
778
  	pr_debug("INFTL: INFTL_deleteblock(inftl=%p,"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
781
782
  		"block=%d)
  ", inftl, block);
  
  	while (thisEUN < inftl->nb_blocks) {
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
783
784
  		if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
  				   blockofs, 8, &retlen, (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
  			status = SECTOR_IGNORE;
  		else
  			status = bci.Status | bci.Status1;
  
  		switch (status) {
  		case SECTOR_FREE:
  		case SECTOR_IGNORE:
  			break;
  		case SECTOR_DELETED:
  			thisEUN = BLOCK_NIL;
  			goto foundit;
  		case SECTOR_USED:
  			goto foundit;
  		default:
  			printk(KERN_WARNING "INFTL: unknown status for "
  				"block %d in EUN %d: 0x%x
  ",
  				block, thisEUN, status);
  			break;
  		}
  
  		if (!silly--) {
  			printk(KERN_WARNING "INFTL: infinite loop in Virtual "
  				"Unit Chain 0x%x
  ",
  				block / (inftl->EraseSize / SECTORSIZE));
  			return 1;
  		}
  		thisEUN = inftl->PUtable[thisEUN];
  	}
  
  foundit:
  	if (thisEUN != BLOCK_NIL) {
  		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
819
  		if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
820
821
  			return -EIO;
  		bci.Status = bci.Status1 = SECTOR_DELETED;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
822
  		if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
823
824
825
826
827
  			return -EIO;
  		INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
  	}
  	return 0;
  }
97894cda5   Thomas Gleixner   [MTD] core: Clean...
828
  static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
829
830
831
832
833
834
835
836
  			    char *buffer)
  {
  	struct INFTLrecord *inftl = (void *)mbd;
  	unsigned int writeEUN;
  	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
  	size_t retlen;
  	struct inftl_oob oob;
  	char *p, *pend;
289c05222   Brian Norris   mtd: replace DEBU...
837
  	pr_debug("INFTL: inftl_writeblock(inftl=%p,block=%ld,"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
  		"buffer=%p)
  ", inftl, block, buffer);
  
  	/* Is block all zero? */
  	pend = buffer + SECTORSIZE;
  	for (p = buffer; p < pend && !*p; p++)
  		;
  
  	if (p < pend) {
  		writeEUN = INFTL_findwriteunit(inftl, block);
  
  		if (writeEUN == BLOCK_NIL) {
  			printk(KERN_WARNING "inftl_writeblock(): cannot find "
  				"block to write to
  ");
  			/*
  			 * If we _still_ haven't got a block to use,
  			 * we're screwed.
  			 */
  			return 1;
  		}
  
  		memset(&oob, 0xff, sizeof(struct inftl_oob));
  		oob.b.Status = oob.b.Status1 = SECTOR_USED;
9223a456d   Thomas Gleixner   [MTD] Remove read...
862

8593fbc68   Thomas Gleixner   [MTD] Rework the ...
863
864
865
  		inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
  			    blockofs, SECTORSIZE, &retlen, (char *)buffer,
  			    (char *)&oob);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
  		/*
  		 * need to write SECTOR_USED flags since they are not written
  		 * in mtd_writeecc
  		 */
  	} else {
  		INFTL_deleteblock(inftl, block);
  	}
  
  	return 0;
  }
  
  static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
  			   char *buffer)
  {
  	struct INFTLrecord *inftl = (void *)mbd;
  	unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
  	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
883
  	struct mtd_info *mtd = inftl->mbd.mtd;
9223a456d   Thomas Gleixner   [MTD] Remove read...
884
  	unsigned int status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
  	int silly = MAX_LOOPS;
9223a456d   Thomas Gleixner   [MTD] Remove read...
886
  	struct inftl_bci bci;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
  	size_t retlen;
289c05222   Brian Norris   mtd: replace DEBU...
888
  	pr_debug("INFTL: inftl_readblock(inftl=%p,block=%ld,"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
890
891
892
  		"buffer=%p)
  ", inftl, block, buffer);
  
  	while (thisEUN < inftl->nb_blocks) {
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
893
  		if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
894
  				  blockofs, 8, &retlen, (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
  			status = SECTOR_IGNORE;
  		else
  			status = bci.Status | bci.Status1;
  
  		switch (status) {
  		case SECTOR_DELETED:
  			thisEUN = BLOCK_NIL;
  			goto foundit;
  		case SECTOR_USED:
  			goto foundit;
  		case SECTOR_FREE:
  		case SECTOR_IGNORE:
  			break;
  		default:
  			printk(KERN_WARNING "INFTL: unknown status for "
  				"block %ld in EUN %d: 0x%04x
  ",
  				block, thisEUN, status);
  			break;
  		}
  
  		if (!silly--) {
  			printk(KERN_WARNING "INFTL: infinite loop in "
  				"Virtual Unit Chain 0x%lx
  ",
  				block / (inftl->EraseSize / SECTORSIZE));
  			return 1;
  		}
  
  		thisEUN = inftl->PUtable[thisEUN];
  	}
  
  foundit:
  	if (thisEUN == BLOCK_NIL) {
  		/* The requested block is not on the media, return all 0x00 */
  		memset(buffer, 0, SECTORSIZE);
  	} else {
9223a456d   Thomas Gleixner   [MTD] Remove read...
932
  		size_t retlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
933
  		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
329ad399a   Artem Bityutskiy   mtd: introduce mt...
934
  		int ret = mtd_read(mtd, ptr, SECTORSIZE, &retlen, buffer);
9a1fcdfd4   Thomas Gleixner   [MTD] NAND Signal...
935
936
  
  		/* Handle corrected bit flips gracefully */
d57f40544   Brian Norris   mtd: utilize `mtd...
937
  		if (ret < 0 && !mtd_is_bitflip(ret))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
  			return -EIO;
  	}
  	return 0;
  }
  
  static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
  {
  	struct INFTLrecord *inftl = (void *)dev;
  
  	geo->heads = inftl->heads;
  	geo->sectors = inftl->sectors;
  	geo->cylinders = inftl->cylinders;
  
  	return 0;
  }
  
  static struct mtd_blktrans_ops inftl_tr = {
  	.name		= "inftl",
  	.major		= INFTL_MAJOR,
  	.part_bits	= INFTL_PARTN_BITS,
191876729   Richard Purdie   [MTD] Allow varia...
958
  	.blksize 	= 512,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
959
960
961
962
963
964
965
  	.getgeo		= inftl_getgeo,
  	.readsect	= inftl_readblock,
  	.writesect	= inftl_writeblock,
  	.add_mtd	= inftl_add_mtd,
  	.remove_dev	= inftl_remove_dev,
  	.owner		= THIS_MODULE,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
966
967
  static int __init init_inftl(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
968
969
970
971
972
973
974
975
976
977
978
979
980
981
  	return register_mtd_blktrans(&inftl_tr);
  }
  
  static void __exit cleanup_inftl(void)
  {
  	deregister_mtd_blktrans(&inftl_tr);
  }
  
  module_init(init_inftl);
  module_exit(cleanup_inftl);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
  MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");