Blame view

drivers/mtd/nftlcore.c 23.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
a1452a377   David Woodhouse   mtd: Update copyr...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   * Linux driver for NAND Flash Translation Layer
   *
   * Copyright © 1999 Machine Vision Holdings, Inc.
   * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
   *
   * 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
20
21
22
   */
  
  #define PRERELEASE
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <asm/errno.h>
  #include <asm/io.h>
  #include <asm/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
  #include <linux/delay.h>
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
  #include <linux/init.h>
  #include <linux/hdreg.h>
e7f521636   Scott James Remnant   [MTD] Auto-load n...
32
  #include <linux/blkdev.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  
  #include <linux/kmod.h>
  #include <linux/mtd/mtd.h>
  #include <linux/mtd/nand.h>
  #include <linux/mtd/nftl.h>
  #include <linux/mtd/blktrans.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
  
  
  static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
  {
  	struct NFTLrecord *nftl;
  	unsigned long temp;
69423d99f   Adrian Hunter   [MTD] update inte...
51
  	if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
55
  		return;
  	/* OK, this is moderately ugly.  But probably safe.  Alternatives? */
  	if (memcmp(mtd->name, "DiskOnChip", 10))
  		return;
8f461a730   Artem Bityutskiy   mtd: introduce mt...
56
  	if (!mtd_can_have_bb(mtd)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
59
60
61
62
63
  		printk(KERN_ERR
  "NFTL 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("NFTL: add_mtd for %s
  ", mtd->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66

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

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  	nftl->mbd.tr = tr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  
          if (NFTL_mount(nftl) < 0) {
  		printk(KERN_WARNING "NFTL: could not mount device
  ");
  		kfree(nftl);
  		return;
          }
  
  	/* OK, it's a new one. Set up all the data structures. */
  
  	/* Calculate geometry */
  	nftl->cylinders = 1024;
  	nftl->heads = 16;
  
  	temp = nftl->cylinders * nftl->heads;
  	nftl->sectors = nftl->mbd.size / temp;
  	if (nftl->mbd.size % temp) {
  		nftl->sectors++;
  		temp = nftl->cylinders * nftl->sectors;
  		nftl->heads = nftl->mbd.size / temp;
  
  		if (nftl->mbd.size % temp) {
  			nftl->heads++;
  			temp = nftl->heads * nftl->sectors;
  			nftl->cylinders = nftl->mbd.size / temp;
  		}
  	}
  
  	if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->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 "NFTL: cannot calculate a geometry to "
  		       "match size of 0x%lx.
  ", nftl->mbd.size);
  		printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
  			"(== 0x%lx sects)
  ",
97894cda5   Thomas Gleixner   [MTD] core: Clean...
115
  			nftl->cylinders, nftl->heads , nftl->sectors,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
  			(long)nftl->cylinders * (long)nftl->heads *
  			(long)nftl->sectors );
  	}
  
  	if (add_mtd_blktrans_dev(&nftl->mbd)) {
fa671646f   Jesper Juhl   [PATCH] kfree cle...
121
122
  		kfree(nftl->ReplUnitTable);
  		kfree(nftl->EUNtable);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
125
126
127
128
129
130
131
132
133
134
  		kfree(nftl);
  		return;
  	}
  #ifdef PSYCHO_DEBUG
  	printk(KERN_INFO "NFTL: Found new nftl%c
  ", nftl->mbd.devnum + 'a');
  #endif
  }
  
  static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
  {
  	struct NFTLrecord *nftl = (void *)dev;
289c05222   Brian Norris   mtd: replace DEBU...
135
136
  	pr_debug("NFTL: 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(nftl->ReplUnitTable);
  	kfree(nftl->EUNtable);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
  }
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
142
143
144
145
146
147
  /*
   * Read oob data from flash
   */
  int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
  		  size_t *retlen, uint8_t *buf)
  {
16f05c2b6   Dimitri Gorokhovik   mtd: nftl: fix of...
148
  	loff_t mask = mtd->writesize - 1;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
149
150
  	struct mtd_oob_ops ops;
  	int res;
0612b9ddc   Brian Norris   mtd: rename MTD_O...
151
  	ops.mode = MTD_OPS_PLACE_OOB;
16f05c2b6   Dimitri Gorokhovik   mtd: nftl: fix of...
152
  	ops.ooboffs = offs & mask;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
153
154
155
  	ops.ooblen = len;
  	ops.oobbuf = buf;
  	ops.datbuf = NULL;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
156

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

a2cc5ba07   Artem Bityutskiy   mtd: introduce mt...
177
  	res = mtd_write_oob(mtd, offs & ~mask, &ops);
7014568ba   Vitaly Wool   [MTD] [NAND] remo...
178
  	*retlen = ops.oobretlen;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
179
180
  	return res;
  }
553a80120   Frederik Deweerdt   [MTD] fix nftl_wr...
181
  #ifdef CONFIG_NFTL_RW
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
182
183
184
185
186
187
  /*
   * Write data and oob to flash
   */
  static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
  		      size_t *retlen, uint8_t *buf, uint8_t *oob)
  {
16f05c2b6   Dimitri Gorokhovik   mtd: nftl: fix of...
188
  	loff_t mask = mtd->writesize - 1;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
189
190
  	struct mtd_oob_ops ops;
  	int res;
0612b9ddc   Brian Norris   mtd: rename MTD_O...
191
  	ops.mode = MTD_OPS_PLACE_OOB;
16f05c2b6   Dimitri Gorokhovik   mtd: nftl: fix of...
192
  	ops.ooboffs = offs & mask;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
193
194
195
196
  	ops.ooblen = mtd->oobsize;
  	ops.oobbuf = oob;
  	ops.datbuf = buf;
  	ops.len = len;
a2cc5ba07   Artem Bityutskiy   mtd: introduce mt...
197
  	res = mtd_write_oob(mtd, offs & ~mask, &ops);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
198
199
200
  	*retlen = ops.retlen;
  	return res;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  /* Actual NFTL access routines */
  /* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
   *	when the give Virtual Unit Chain
   */
  static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
  {
  	/* For a given Virtual Unit Chain: find or create a free block and
  	   add it to the chain */
  	/* We're passed the number of the last EUN in the chain, to save us from
  	   having to look it up again */
  	u16 pot = nftl->LastFreeEUN;
  	int silly = nftl->nb_blocks;
  
  	/* Normally, we force a fold to happen before we run out of free blocks completely */
  	if (!desperate && nftl->numfreeEUNs < 2) {
289c05222   Brian Norris   mtd: replace DEBU...
216
217
  		pr_debug("NFTL_findfreeblock: there are too few free EUNs
  ");
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
218
  		return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  	}
  
  	/* Scan for a free block */
  	do {
  		if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
  			nftl->LastFreeEUN = pot;
  			nftl->numfreeEUNs--;
  			return pot;
  		}
  
  		/* This will probably point to the MediaHdr unit itself,
  		   right at the beginning of the partition. But that unit
  		   (and the backup unit too) should have the UCI set
  		   up so that it's not selected for overwriting */
  		if (++pot > nftl->lastEUN)
  			pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
  
  		if (!silly--) {
  			printk("Argh! No free blocks found! LastFreeEUN = %d, "
97894cda5   Thomas Gleixner   [MTD] core: Clean...
238
239
  			       "FirstEUN = %d
  ", nftl->LastFreeEUN,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
  			       le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
241
  			return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
  		}
  	} while (pot != nftl->LastFreeEUN);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
244
  	return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
248
  }
  
  static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
  {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
249
  	struct mtd_info *mtd = nftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
253
254
255
256
257
258
  	u16 BlockMap[MAX_SECTORS_PER_UNIT];
  	unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
  	unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
  	unsigned int thisEUN;
  	int block;
  	int silly;
  	unsigned int targetEUN;
  	struct nftl_oob oob;
  	int inplace = 1;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
259
  	size_t retlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
263
264
265
266
267
268
269
270
271
  
  	memset(BlockMap, 0xff, sizeof(BlockMap));
  	memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
  
  	thisEUN = nftl->EUNtable[thisVUC];
  
  	if (thisEUN == BLOCK_NIL) {
  		printk(KERN_WARNING "Trying to fold non-existent "
  		       "Virtual Unit Chain %d!
  ", thisVUC);
  		return BLOCK_NIL;
  	}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
272

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
  	/* Scan to find the Erase Unit which holds the actual data for each
  	   512-byte block within the Chain.
  	*/
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
276
  	silly = MAX_LOOPS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
  	targetEUN = BLOCK_NIL;
  	while (thisEUN <= nftl->lastEUN ) {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
279
  		unsigned int status, foldmark;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
  
  		targetEUN = thisEUN;
  		for (block = 0; block < nftl->EraseSize / 512; block ++) {
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
283
  			nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
284
285
  				      (block * 512), 16 , &retlen,
  				      (char *)&oob);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
  			if (block == 2) {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
287
288
  				foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
  				if (foldmark == FOLD_MARK_IN_PROGRESS) {
289c05222   Brian Norris   mtd: replace DEBU...
289
290
  					pr_debug("Write Inhibited on EUN %d
  ", thisEUN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
293
294
295
296
297
298
  					inplace = 0;
  				} else {
  					/* There's no other reason not to do inplace,
  					   except ones that come later. So we don't need
  					   to preserve inplace */
  					inplace = 1;
  				}
  			}
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
299
  			status = oob.b.Status | oob.b.Status1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
303
304
305
306
307
308
309
310
  			BlockLastState[block] = status;
  
  			switch(status) {
  			case SECTOR_FREE:
  				BlockFreeFound[block] = 1;
  				break;
  
  			case SECTOR_USED:
  				if (!BlockFreeFound[block])
  					BlockMap[block] = thisEUN;
  				else
97894cda5   Thomas Gleixner   [MTD] core: Clean...
311
  					printk(KERN_WARNING
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
314
315
316
317
318
319
320
  					       "SECTOR_USED found after SECTOR_FREE "
  					       "in Virtual Unit Chain %d for block %d
  ",
  					       thisVUC, block);
  				break;
  			case SECTOR_DELETED:
  				if (!BlockFreeFound[block])
  					BlockMap[block] = BLOCK_NIL;
  				else
97894cda5   Thomas Gleixner   [MTD] core: Clean...
321
  					printk(KERN_WARNING
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
  					       "SECTOR_DELETED found after SECTOR_FREE "
  					       "in Virtual Unit Chain %d for block %d
  ",
  					       thisVUC, block);
  				break;
  
  			case SECTOR_IGNORE:
  				break;
  			default:
  				printk("Unknown status for block %d in EUN %d: %x
  ",
  				       block, thisEUN, status);
  			}
  		}
  
  		if (!silly--) {
  			printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x
  ",
  			       thisVUC);
  			return BLOCK_NIL;
  		}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
343

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
346
347
348
349
  		thisEUN = nftl->ReplUnitTable[thisEUN];
  	}
  
  	if (inplace) {
  		/* We're being asked to be a fold-in-place. Check
  		   that all blocks which actually have data associated
97894cda5   Thomas Gleixner   [MTD] core: Clean...
350
  		   with them (i.e. BlockMap[block] != BLOCK_NIL) are
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
354
355
356
357
358
  		   either already present or SECTOR_FREE in the target
  		   block. If not, we're going to have to fold out-of-place
  		   anyway.
  		*/
  		for (block = 0; block < nftl->EraseSize / 512 ; block++) {
  			if (BlockLastState[block] != SECTOR_FREE &&
  			    BlockMap[block] != BLOCK_NIL &&
  			    BlockMap[block] != targetEUN) {
289c05222   Brian Norris   mtd: replace DEBU...
359
  				pr_debug("Setting inplace to 0. VUC %d, "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
362
363
  				      "block %d was %x lastEUN, "
  				      "and is in EUN %d (%s) %d
  ",
  				      thisVUC, block, BlockLastState[block],
97894cda5   Thomas Gleixner   [MTD] core: Clean...
364
  				      BlockMap[block],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
370
371
372
373
374
375
  				      BlockMap[block]== targetEUN ? "==" : "!=",
  				      targetEUN);
  				inplace = 0;
  				break;
  			}
  		}
  
  		if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
  		    pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
  		    BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
  		    SECTOR_FREE) {
289c05222   Brian Norris   mtd: replace DEBU...
376
  			pr_debug("Pending write not free in EUN %d. "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
381
  			      "Folding out of place.
  ", targetEUN);
  			inplace = 0;
  		}
  	}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
382

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
  	if (!inplace) {
289c05222   Brian Norris   mtd: replace DEBU...
384
  		pr_debug("Cannot fold Virtual Unit Chain %d in place. "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
387
388
389
  		      "Trying out-of-place
  ", thisVUC);
  		/* We need to find a targetEUN to fold into. */
  		targetEUN = NFTL_findfreeblock(nftl, 1);
  		if (targetEUN == BLOCK_NIL) {
97894cda5   Thomas Gleixner   [MTD] core: Clean...
390
  			/* Ouch. Now we're screwed. We need to do a
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
  			   fold-in-place of another chain to make room
  			   for this one. We need a better way of selecting
97894cda5   Thomas Gleixner   [MTD] core: Clean...
393
  			   which chain to fold, because makefreeblock will
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
395
396
397
398
399
400
401
  			   only ask us to fold the same one again.
  			*/
  			printk(KERN_WARNING
  			       "NFTL_findfreeblock(desperate) returns 0xffff.
  ");
  			return BLOCK_NIL;
  		}
  	} else {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
402
403
404
405
406
407
  		/* We put a fold mark in the chain we are folding only if we
                 fold in place to help the mount check code. If we do not fold in
                 place, it is possible to find the valid chain by selecting the
                 longer one */
  		oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
  		oob.u.c.unused = 0xffffffff;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
408
  		nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
409
410
  			       8, &retlen, (char *)&oob.u);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
412
413
414
415
  
  	/* 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.
  	*/
289c05222   Brian Norris   mtd: replace DEBU...
416
417
  	pr_debug("Folding chain %d into unit %d
  ", thisVUC, targetEUN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
420
421
422
423
424
425
426
  	for (block = 0; block < nftl->EraseSize / 512 ; block++) {
  		unsigned char movebuf[512];
  		int ret;
  
  		/* If it's in the target EUN already, or if it's pending write, do nothing */
  		if (BlockMap[block] == targetEUN ||
  		    (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
  			continue;
  		}
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
427
  		/* copy only in non free block (free blocks can only
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
                     happen in case of media errors or deleted blocks) */
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
429
430
  		if (BlockMap[block] == BLOCK_NIL)
  			continue;
329ad399a   Artem Bityutskiy   mtd: introduce mt...
431
432
433
434
435
  		ret = mtd_read(mtd,
  			       (nftl->EraseSize * BlockMap[block]) + (block * 512),
  			       512,
  			       &retlen,
  			       movebuf);
d57f40544   Brian Norris   mtd: utilize `mtd...
436
  		if (ret < 0 && !mtd_is_bitflip(ret)) {
329ad399a   Artem Bityutskiy   mtd: introduce mt...
437
438
439
440
441
  			ret = mtd_read(mtd,
  				       (nftl->EraseSize * BlockMap[block]) + (block * 512),
  				       512,
  				       &retlen,
  				       movebuf);
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
442
443
444
445
  			if (ret != -EIO)
  				printk("Error went away on retry.
  ");
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
  		memset(&oob, 0xff, sizeof(struct nftl_oob));
  		oob.b.Status = oob.b.Status1 = SECTOR_USED;
9223a456d   Thomas Gleixner   [MTD] Remove read...
448

8593fbc68   Thomas Gleixner   [MTD] Rework the ...
449
450
  		nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
  			   (block * 512), 512, &retlen, movebuf, (char *)&oob);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
  	}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
452

f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
453
454
  	/* add the header so that it is now a valid chain */
  	oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
455
  	oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
456

8593fbc68   Thomas Gleixner   [MTD] Rework the ...
457
  	nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
458
  		       8, &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
  
  	/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
97894cda5   Thomas Gleixner   [MTD] core: Clean...
461
  	/* At this point, we have two different chains for this Virtual Unit, and no way to tell
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
465
466
  	   them apart. If we crash now, we get confused. However, both contain the same data, so we
  	   shouldn't actually lose data in this case. It's just that when we load up on a medium which
  	   has duplicate chains, we need to free one of the chains because it's not necessary any more.
  	*/
  	thisEUN = nftl->EUNtable[thisVUC];
289c05222   Brian Norris   mtd: replace DEBU...
467
468
  	pr_debug("Want to erase
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469

97894cda5   Thomas Gleixner   [MTD] core: Clean...
470
  	/* For each block in the old chain (except the targetEUN of course),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
472
473
  	   free it and make it available for future use */
  	while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
  		unsigned int EUNtmp;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
474
  		EUNtmp = nftl->ReplUnitTable[thisEUN];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475

f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
476
  		if (NFTL_formatblock(nftl, thisEUN) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
479
  			/* could not erase : mark block as reserved
  			 */
  			nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
480
  		} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
483
  			/* correctly erased : mark it as free */
  			nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
  			nftl->numfreeEUNs++;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
484
485
  		}
  		thisEUN = EUNtmp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
  	}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
487

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
489
490
491
492
493
494
495
496
  	/* Make this the new start of chain for thisVUC */
  	nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
  	nftl->EUNtable[thisVUC] = targetEUN;
  
  	return targetEUN;
  }
  
  static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
  {
97894cda5   Thomas Gleixner   [MTD] core: Clean...
497
  	/* This is the part that needs some cleverness applied.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
  	   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;
  
  	for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
  		EUN = nftl->EUNtable[chain];
  		thislen = 0;
  
  		while (EUN <= nftl->lastEUN) {
  			thislen++;
  			//printk("VUC %d reaches len %d with EUN %d
  ", chain, thislen, EUN);
  			EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
  			if (thislen > 0xff00) {
  				printk("Endless loop in Virtual Chain %d: Unit %x
  ",
  				       chain, EUN);
  			}
  			if (thislen > 0xff10) {
  				/* Actually, don't return failure. Just ignore this chain and
  				   get on with it. */
  				thislen = 0;
  				break;
  			}
  		}
  
  		if (thislen > ChainLength) {
  			//printk("New longest chain is %d with length %d
  ", chain, thislen);
  			ChainLength = thislen;
  			LongestChain = chain;
  		}
  	}
  
  	if (ChainLength < 2) {
  		printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
  		       "Failing request
  ");
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
542
  		return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
544
545
546
  	}
  
  	return NFTL_foldchain (nftl, LongestChain, pendingblock);
  }
97894cda5   Thomas Gleixner   [MTD] core: Clean...
547
  /* NFTL_findwriteunit: Return the unit number into which we can write
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
549
550
551
552
553
                         for this block. Make it available if it isn't already
  */
  static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
  {
  	u16 lastEUN;
  	u16 thisVUC = block / (nftl->EraseSize / 512);
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
554
  	struct mtd_info *mtd = nftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
556
557
558
559
560
561
562
563
564
  	unsigned int writeEUN;
  	unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
  	size_t retlen;
  	int silly, silly2 = 3;
  	struct nftl_oob oob;
  
  	do {
  		/* Scan the media to find a unit in the VUC which has
  		   a free space for the block in question.
  		*/
97894cda5   Thomas Gleixner   [MTD] core: Clean...
565
  		/* This condition catches the 0x[7f]fff cases, as well as
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
567
568
569
  		   being a sanity check for past-end-of-media access
  		*/
  		lastEUN = BLOCK_NIL;
  		writeEUN = nftl->EUNtable[thisVUC];
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
570
  		silly = MAX_LOOPS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
572
573
  		while (writeEUN <= nftl->lastEUN) {
  			struct nftl_bci bci;
  			size_t retlen;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
574
  			unsigned int status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
576
  
  			lastEUN = writeEUN;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
577
  			nftl_read_oob(mtd,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
578
579
  				      (writeEUN * nftl->EraseSize) + blockofs,
  				      8, &retlen, (char *)&bci);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
580

289c05222   Brian Norris   mtd: replace DEBU...
581
582
  			pr_debug("Status of block %d in EUN %d is %x
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
  			      block , writeEUN, le16_to_cpu(bci.Status));
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
584
  			status = bci.Status | bci.Status1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
586
587
588
589
590
591
592
593
594
  			switch(status) {
  			case SECTOR_FREE:
  				return writeEUN;
  
  			case SECTOR_DELETED:
  			case SECTOR_USED:
  			case SECTOR_IGNORE:
  				break;
  			default:
  				// Invalid block. Don't use it any more. Must implement.
97894cda5   Thomas Gleixner   [MTD] core: Clean...
595
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
  			}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
597
598
  
  			if (!silly--) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
599
600
601
602
  				printk(KERN_WARNING
  				       "Infinite loop in Virtual Unit Chain 0x%x
  ",
  				       thisVUC);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
603
  				return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
606
607
608
  			}
  
  			/* Skip to next block in chain */
  			writeEUN = nftl->ReplUnitTable[writeEUN];
  		}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
609
  		/* OK. We didn't find one in the existing chain, or there
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
611
612
613
614
615
616
617
618
619
620
621
622
  		   is no existing chain. */
  
  		/* Try to find an already-free block */
  		writeEUN = NFTL_findfreeblock(nftl, 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.
  			*/
  
  			/* First remember the start of this chain */
  			//u16 startEUN = nftl->EUNtable[thisVUC];
97894cda5   Thomas Gleixner   [MTD] core: Clean...
623

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
  			//printk("Write to VirtualUnitChain %d, calling makefreeblock()
  ", thisVUC);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
626
  			writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
628
  
  			if (writeEUN == BLOCK_NIL) {
97894cda5   Thomas Gleixner   [MTD] core: Clean...
629
  				/* OK, we accept that the above comment is
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
632
633
634
  				   lying - there may have been free blocks
  				   last time we called NFTL_findfreeblock(),
  				   but they are reserved for when we're
  				   desperate. Well, now we're desperate.
  				*/
289c05222   Brian Norris   mtd: replace DEBU...
635
636
  				pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d
  ", thisVUC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
638
639
640
  				writeEUN = NFTL_findfreeblock(nftl, 1);
  			}
  			if (writeEUN == BLOCK_NIL) {
  				/* Ouch. This should never happen - we should
97894cda5   Thomas Gleixner   [MTD] core: Clean...
641
642
  				   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
643
644
645
646
647
648
  				   space than actual media, or our makefreeblock
  				   routine is missing something.
  				*/
  				printk(KERN_WARNING "Cannot make free space.
  ");
  				return BLOCK_NIL;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
649
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
651
652
653
654
655
656
  			//printk("Restarting scan
  ");
  			lastEUN = BLOCK_NIL;
  			continue;
  		}
  
  		/* We've found a free block. Insert it into the chain. */
97894cda5   Thomas Gleixner   [MTD] core: Clean...
657

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
  		if (lastEUN != BLOCK_NIL) {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
659
  			thisVUC |= 0x8000; /* It's a replacement block */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
  		} else {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
661
662
  			/* The first block in a new chain */
  			nftl->EUNtable[thisVUC] = writeEUN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
665
666
667
668
669
  		}
  
  		/* set up the actual EUN we're writing into */
  		/* Both in our cache... */
  		nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
  
  		/* ... and on the flash itself */
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
670
  		nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
671
  			      &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
673
  
  		oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
674
  		nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
675
  			       &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676

f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
677
  		/* we link the new block to the chain only after the
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678
679
                     block is ready. It avoids the case where the chain
                     could point to a free block */
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
680
  		if (lastEUN != BLOCK_NIL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
  			/* Both in our cache... */
  			nftl->ReplUnitTable[lastEUN] = writeEUN;
  			/* ... and on the flash itself */
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
684
  			nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
685
  				      8, &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
687
688
  
  			oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
  				= cpu_to_le16(writeEUN);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
689
  			nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
690
  				       8, &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
692
693
694
695
696
697
698
699
  		}
  
  		return writeEUN;
  
  	} while (silly2--);
  
  	printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x
  ",
  	       thisVUC);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
700
  	return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
  }
  
  static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
  			   char *buffer)
  {
  	struct NFTLrecord *nftl = (void *)mbd;
  	u16 writeEUN;
  	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
  	size_t retlen;
  	struct nftl_oob oob;
  
  	writeEUN = NFTL_findwriteunit(nftl, block);
  
  	if (writeEUN == BLOCK_NIL) {
  		printk(KERN_WARNING
  		       "NFTL_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 nftl_oob));
  	oob.b.Status = oob.b.Status1 = SECTOR_USED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724

8593fbc68   Thomas Gleixner   [MTD] Rework the ...
725
726
  	nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
  		   512, &retlen, (char *)buffer, (char *)&oob);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
728
729
730
731
732
733
734
  	return 0;
  }
  #endif /* CONFIG_NFTL_RW */
  
  static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
  			  char *buffer)
  {
  	struct NFTLrecord *nftl = (void *)mbd;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
735
  	struct mtd_info *mtd = nftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
737
738
  	u16 lastgoodEUN;
  	u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
  	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
739
  	unsigned int status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
  	int silly = MAX_LOOPS;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
741
742
  	size_t retlen;
  	struct nftl_bci bci;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
743
744
  
  	lastgoodEUN = BLOCK_NIL;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
745
  	if (thisEUN != BLOCK_NIL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
746
  		while (thisEUN < nftl->nb_blocks) {
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
747
  			if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
748
749
  					  blockofs, 8, &retlen,
  					  (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
  				status = SECTOR_IGNORE;
  			else
  				status = bci.Status | bci.Status1;
  
  			switch (status) {
  			case SECTOR_FREE:
  				/* no modification of a sector should follow a free sector */
  				goto the_end;
  			case SECTOR_DELETED:
  				lastgoodEUN = BLOCK_NIL;
  				break;
  			case SECTOR_USED:
  				lastgoodEUN = thisEUN;
  				break;
  			case SECTOR_IGNORE:
  				break;
  			default:
  				printk("Unknown status for block %ld in EUN %d: %x
  ",
  				       block, thisEUN, status);
  				break;
  			}
  
  			if (!silly--) {
  				printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx
  ",
  				       block / (nftl->EraseSize / 512));
  				return 1;
  			}
  			thisEUN = nftl->ReplUnitTable[thisEUN];
  		}
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
781
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
783
784
785
786
787
788
789
  
   the_end:
  	if (lastgoodEUN == BLOCK_NIL) {
  		/* the requested block is not on the media, return all 0x00 */
  		memset(buffer, 0, 512);
  	} else {
  		loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
  		size_t retlen;
329ad399a   Artem Bityutskiy   mtd: introduce mt...
790
  		int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
9a1fcdfd4   Thomas Gleixner   [MTD] NAND Signal...
791

d57f40544   Brian Norris   mtd: utilize `mtd...
792
  		if (res < 0 && !mtd_is_bitflip(res))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
819
  			return -EIO;
  	}
  	return 0;
  }
  
  static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
  {
  	struct NFTLrecord *nftl = (void *)dev;
  
  	geo->heads = nftl->heads;
  	geo->sectors = nftl->sectors;
  	geo->cylinders = nftl->cylinders;
  
  	return 0;
  }
  
  /****************************************************************************
   *
   * Module stuff
   *
   ****************************************************************************/
  
  
  static struct mtd_blktrans_ops nftl_tr = {
  	.name		= "nftl",
  	.major		= NFTL_MAJOR,
  	.part_bits	= NFTL_PARTN_BITS,
191876729   Richard Purdie   [MTD] Allow varia...
820
  	.blksize 	= 512,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821
822
823
824
825
826
827
828
829
  	.getgeo		= nftl_getgeo,
  	.readsect	= nftl_readblock,
  #ifdef CONFIG_NFTL_RW
  	.writesect	= nftl_writeblock,
  #endif
  	.add_mtd	= nftl_add_mtd,
  	.remove_dev	= nftl_remove_dev,
  	.owner		= THIS_MODULE,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
830
831
  static int __init init_nftl(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832
833
834
835
836
837
838
839
840
841
842
843
844
845
  	return register_mtd_blktrans(&nftl_tr);
  }
  
  static void __exit cleanup_nftl(void)
  {
  	deregister_mtd_blktrans(&nftl_tr);
  }
  
  module_init(init_nftl);
  module_exit(cleanup_nftl);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
  MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
e7f521636   Scott James Remnant   [MTD] Auto-load n...
846
  MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);