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>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
36
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
40
41
42
43
44
45
  #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;
818b97392   Huang Shijie   mtd: nand: add a ...
50
  	if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
  		return;
  	/* OK, this is moderately ugly.  But probably safe.  Alternatives? */
  	if (memcmp(mtd->name, "DiskOnChip", 10))
  		return;
3c3c10bba   Artem Bityutskiy   mtd: add leading ...
55
  	if (!mtd->_block_isbad) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
61
62
  		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...
63
64
  	pr_debug("INFTL: add_mtd for %s
  ", mtd->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65

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

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

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

9223a456d   Thomas Gleixner   [MTD] Remove read...
76
  	if (INFTL_mount(inftl) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
  		printk(KERN_WARNING "INFTL: could not mount device
  ");
  		kfree(inftl);
  		return;
9223a456d   Thomas Gleixner   [MTD] Remove read...
81
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  
  	/* 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...
105
  		  Oh no we don't have
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
109
110
111
112
113
  		   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...
114
  			inftl->cylinders, inftl->heads , inftl->sectors,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
  			(long)inftl->cylinders * (long)inftl->heads *
  			(long)inftl->sectors );
  	}
  
  	if (add_mtd_blktrans_dev(&inftl->mbd)) {
fa671646f   Jesper Juhl   [PATCH] kfree cle...
120
121
  		kfree(inftl->PUtable);
  		kfree(inftl->VUtable);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
123
124
125
  		kfree(inftl);
  		return;
  	}
  #ifdef PSYCHO_DEBUG
8b68a1263   Eric Sesterhenn / snakebyte   [PATCH] Fix debug...
126
127
  	printk(KERN_INFO "INFTL: Found new inftl%c
  ", inftl->mbd.devnum + 'a');
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
133
134
  #endif
  	return;
  }
  
  static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
  {
  	struct INFTLrecord *inftl = (void *)dev;
289c05222   Brian Norris   mtd: replace DEBU...
135
136
  	pr_debug("INFTL: remove_dev (i=%d)
  ", dev->devnum);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
  
  	del_mtd_blktrans_dev(dev);
fa671646f   Jesper Juhl   [PATCH] kfree cle...
139
140
  	kfree(inftl->PUtable);
  	kfree(inftl->VUtable);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
145
146
147
  }
  
  /*
   * Actual INFTL access routines.
   */
  
  /*
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
148
149
150
151
152
153
154
   * 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...
155
  	ops.mode = MTD_OPS_PLACE_OOB;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
156
157
158
159
  	ops.ooboffs = offs & (mtd->writesize - 1);
  	ops.ooblen = len;
  	ops.oobbuf = buf;
  	ops.datbuf = NULL;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
160

fd2819bbc   Artem Bityutskiy   mtd: introduce mt...
161
  	res = mtd_read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
7014568ba   Vitaly Wool   [MTD] [NAND] remo...
162
  	*retlen = ops.oobretlen;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
163
164
165
166
167
168
169
170
171
172
173
  	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...
174
  	ops.mode = MTD_OPS_PLACE_OOB;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
175
176
177
178
  	ops.ooboffs = offs & (mtd->writesize - 1);
  	ops.ooblen = len;
  	ops.oobbuf = buf;
  	ops.datbuf = NULL;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
179

a2cc5ba07   Artem Bityutskiy   mtd: introduce mt...
180
  	res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
7014568ba   Vitaly Wool   [MTD] [NAND] remo...
181
  	*retlen = ops.oobretlen;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
182
183
184
185
186
187
188
189
190
191
192
  	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...
193
  	ops.mode = MTD_OPS_PLACE_OOB;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
194
195
196
197
198
  	ops.ooboffs = offs;
  	ops.ooblen = mtd->oobsize;
  	ops.oobbuf = oob;
  	ops.datbuf = buf;
  	ops.len = len;
a2cc5ba07   Artem Bityutskiy   mtd: introduce mt...
199
  	res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
200
201
202
203
204
  	*retlen = ops.retlen;
  	return res;
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
208
209
210
211
   * 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...
212
213
214
  	pr_debug("INFTL: INFTL_findfreeblock(inftl=%p,desperate=%d)
  ",
  			inftl, desperate);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
218
219
220
  
  	/*
  	 * 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...
221
222
223
  		pr_debug("INFTL: there are too few free EUNs (%d)
  ",
  				inftl->numfreeEUNs);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
224
  		return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
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
  	}
  
  	/* 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...
253
  	struct mtd_info *mtd = inftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
256
  	int block, silly;
  	unsigned int targetEUN;
  	struct inftl_oob oob;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
257
  	size_t retlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258

0a32a1026   Brian Norris   mtd: cleanup styl...
259
260
261
  	pr_debug("INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,pending=%d)
  ",
  			inftl, thisVUC, pendingblock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
264
265
266
267
268
269
270
271
272
273
  
  	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...
274

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
277
278
  	/*
  	 * 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...
279
  	silly = MAX_LOOPS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
  	while (thisEUN < inftl->nb_blocks) {
  		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
282
283
  			if ((BlockMap[block] != BLOCK_NIL) ||
  			    BlockDeleted[block])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
  				continue;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
285
286
287
  			if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
  					   + (block * SECTORSIZE), 16, &retlen,
  					   (char *)&oob) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
  				status = SECTOR_IGNORE;
  			else
9223a456d   Thomas Gleixner   [MTD] Remove read...
290
  				status = oob.b.Status | oob.b.Status1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
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
  
  			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...
317

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
320
321
322
323
324
325
  		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...
326
327
  	pr_debug("INFTL: folding chain %d into unit %d
  ", thisVUC, targetEUN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
331
332
333
334
335
336
337
338
339
340
  
  	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...
341
  		/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
343
344
  		 * 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...
345
346
  		if (BlockMap[block] == BLOCK_NIL)
  			continue;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
347

329ad399a   Artem Bityutskiy   mtd: introduce mt...
348
349
350
351
352
  		ret = mtd_read(mtd,
  			       (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE),
  			       SECTORSIZE,
  			       &retlen,
  			       movebuf);
d57f40544   Brian Norris   mtd: utilize `mtd...
353
  		if (ret < 0 && !mtd_is_bitflip(ret)) {
329ad399a   Artem Bityutskiy   mtd: introduce mt...
354
355
356
357
358
  			ret = mtd_read(mtd,
  				       (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE),
  				       SECTORSIZE,
  				       &retlen,
  				       movebuf);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
359
  			if (ret != -EIO)
0a32a1026   Brian Norris   mtd: cleanup styl...
360
361
  				pr_debug("INFTL: error went away on retry?
  ");
9223a456d   Thomas Gleixner   [MTD] Remove read...
362
363
364
  		}
  		memset(&oob, 0xff, sizeof(struct inftl_oob));
  		oob.b.Status = oob.b.Status1 = SECTOR_USED;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
365
366
367
  		inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
  			    (block * SECTORSIZE), SECTORSIZE, &retlen,
  			    movebuf, (char *)&oob);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
370
371
372
373
374
375
  	}
  
  	/*
  	 * 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...
376
377
  	pr_debug("INFTL: want to erase virtual chain %d
  ", thisVUC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
380
381
382
383
384
385
386
387
388
389
390
  
  	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...
391
392
393
394
  		/* Unlink the last block from the chain. */
  		inftl->PUtable[prevEUN] = BLOCK_NIL;
  
  		/* Now try to erase it. */
9223a456d   Thomas Gleixner   [MTD] Remove read...
395
  		if (INFTL_formatblock(inftl, thisEUN) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
398
399
  			/*
  			 * Could not erase : mark block as reserved.
  			 */
  			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
9223a456d   Thomas Gleixner   [MTD] Remove read...
400
  		} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
  			/* Correctly erased : mark it as free */
  			inftl->PUtable[thisEUN] = BLOCK_FREE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
  			inftl->numfreeEUNs++;
9223a456d   Thomas Gleixner   [MTD] Remove read...
404
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
408
409
410
411
412
  	}
  
  	return targetEUN;
  }
  
  static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
  {
  	/*
97894cda5   Thomas Gleixner   [MTD] core: Clean...
413
  	 * This is the part that needs some cleverness applied.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
418
419
420
421
422
  	 * 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...
423
  	pr_debug("INFTL: INFTL_makefreeblock(inftl=%p,"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
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
  		"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...
474
   * INFTL_findwriteunit: Return the unit number into which we can write
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
477
478
479
480
481
   *                      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...
482
  	struct mtd_info *mtd = inftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
485
486
487
  	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...
488
489
490
  	pr_debug("INFTL: INFTL_findwriteunit(inftl=%p,block=%d)
  ",
  			inftl, block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
492
493
494
495
496
497
498
499
500
501
  
  	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 ...
502
503
  			inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
  				       blockofs, 8, &retlen, (char *)&bci);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504

9223a456d   Thomas Gleixner   [MTD] Remove read...
505
  			status = bci.Status | bci.Status1;
0a32a1026   Brian Norris   mtd: cleanup styl...
506
507
508
  			pr_debug("INFTL: status of block %d in EUN %d is %x
  ",
  					block , writeEUN, status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
  
  			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...
525
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
  			}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
527
528
  
  			if (!silly--) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
531
  				printk(KERN_WARNING "INFTL: infinite loop in "
  					"Virtual Unit Chain 0x%x
  ", thisVUC);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
532
  				return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
535
536
537
538
539
540
541
542
543
544
  			}
  
  			/* Skip to next block in chain */
  			thisEUN = inftl->PUtable[thisEUN];
  		}
  
  hitused:
  		if (writeEUN != BLOCK_NIL)
  			return writeEUN;
  
  
  		/*
97894cda5   Thomas Gleixner   [MTD] core: Clean...
545
  		 * OK. We didn't find one in the existing chain, or there
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
548
549
550
551
552
553
554
555
  		 * 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...
556
  			thisEUN = INFTL_makefreeblock(inftl, block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
558
559
560
561
  
  			/*
  			 * Hopefully we free something, lets try again.
  			 * This time we are desperate...
  			 */
0a32a1026   Brian Norris   mtd: cleanup styl...
562
563
564
565
  			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
566
567
568
569
  			writeEUN = INFTL_findfreeblock(inftl, 1);
  			if (writeEUN == BLOCK_NIL) {
  				/*
  				 * Ouch. This should never happen - we should
97894cda5   Thomas Gleixner   [MTD] core: Clean...
570
571
  				 * 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
572
573
574
575
576
577
578
579
580
581
582
  				 * 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...
583
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
587
588
589
590
591
592
593
  		}
  
  		/*
  		 * 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 ...
594
595
  			inftl_read_oob(mtd, thisEUN * inftl->EraseSize
  				       + 8, 8, &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
598
599
600
601
602
603
604
605
606
607
  			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...
608

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609
610
611
612
613
614
  		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 ...
615
616
  		inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
  				&retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
619
620
621
622
623
624
  
  		/* 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 ...
625
626
  		inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
  				SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
628
629
630
631
632
633
634
635
636
637
638
  
  		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...
639
  	return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
642
643
644
645
646
  }
  
  /*
   * 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...
647
  	struct mtd_info *mtd = inftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
650
651
652
653
  	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...
654
  	pr_debug("INFTL: INFTL_trydeletechain(inftl=%p,"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
656
657
658
659
660
661
662
663
664
665
666
667
  		"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...
668

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669
670
671
672
673
674
675
676
677
  	/*
  	 * 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 ...
678
679
  			if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
  					   + (block * SECTORSIZE), 8 , &retlen,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
680
  					  (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
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
  				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...
709

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
711
712
713
714
715
716
717
718
719
720
  		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...
721
722
  	pr_debug("INFTL: deleting empty VUC %d
  ", thisVUC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
725
726
727
728
729
  
  	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...
730
731
  			pr_debug("INFTL: Empty VUC %d for deletion was already absent
  ", thisEUN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
732
733
734
735
736
737
738
739
740
741
  			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...
742
743
  		pr_debug("Deleting EUN %d from VUC %d
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
744
  		      thisEUN, thisVUC);
9223a456d   Thomas Gleixner   [MTD] Remove read...
745
  		if (INFTL_formatblock(inftl, thisEUN) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
746
747
748
749
  			/*
  			 * Could not erase : mark block as reserved.
  			 */
  			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
9223a456d   Thomas Gleixner   [MTD] Remove read...
750
  		} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
  			/* 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...
772
  	struct mtd_info *mtd = inftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
773
774
775
776
  	unsigned int status;
  	int silly = MAX_LOOPS;
  	size_t retlen;
  	struct inftl_bci bci;
289c05222   Brian Norris   mtd: replace DEBU...
777
  	pr_debug("INFTL: INFTL_deleteblock(inftl=%p,"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
779
780
781
  		"block=%d)
  ", inftl, block);
  
  	while (thisEUN < inftl->nb_blocks) {
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
782
783
  		if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
  				   blockofs, 8, &retlen, (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
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
  			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 ...
818
  		if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
820
  			return -EIO;
  		bci.Status = bci.Status1 = SECTOR_DELETED;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
821
  		if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
823
824
825
826
  			return -EIO;
  		INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
  	}
  	return 0;
  }
97894cda5   Thomas Gleixner   [MTD] core: Clean...
827
  static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
829
830
831
832
833
834
835
  			    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...
836
  	pr_debug("INFTL: inftl_writeblock(inftl=%p,block=%ld,"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
  		"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...
861

8593fbc68   Thomas Gleixner   [MTD] Rework the ...
862
863
864
  		inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
  			    blockofs, SECTORSIZE, &retlen, (char *)buffer,
  			    (char *)&oob);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
  		/*
  		 * 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...
882
  	struct mtd_info *mtd = inftl->mbd.mtd;
9223a456d   Thomas Gleixner   [MTD] Remove read...
883
  	unsigned int status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
884
  	int silly = MAX_LOOPS;
9223a456d   Thomas Gleixner   [MTD] Remove read...
885
  	struct inftl_bci bci;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
886
  	size_t retlen;
289c05222   Brian Norris   mtd: replace DEBU...
887
  	pr_debug("INFTL: inftl_readblock(inftl=%p,block=%ld,"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
889
890
891
  		"buffer=%p)
  ", inftl, block, buffer);
  
  	while (thisEUN < inftl->nb_blocks) {
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
892
  		if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
893
  				  blockofs, 8, &retlen, (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
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
  			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...
931
  		size_t retlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
932
  		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
329ad399a   Artem Bityutskiy   mtd: introduce mt...
933
  		int ret = mtd_read(mtd, ptr, SECTORSIZE, &retlen, buffer);
9a1fcdfd4   Thomas Gleixner   [MTD] NAND Signal...
934
935
  
  		/* Handle corrected bit flips gracefully */
d57f40544   Brian Norris   mtd: utilize `mtd...
936
  		if (ret < 0 && !mtd_is_bitflip(ret))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
  			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...
957
  	.blksize 	= 512,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
959
960
961
962
963
964
  	.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
965
966
  static int __init init_inftl(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967
968
969
970
971
972
973
974
975
976
977
978
979
980
  	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");