Blame view

drivers/mtd/nftlcore.c 23 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;
818b97392   Huang Shijie   mtd: nand: add a ...
51
  	if (!mtd_type_is_nand(mtd) || 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;
289c05222   Brian Norris   mtd: replace DEBU...
56
57
  	pr_debug("NFTL: add_mtd for %s
  ", mtd->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58

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

0870066d7   Brian Norris   mtd: remove print...
61
  	if (!nftl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
  
  	nftl->mbd.mtd = mtd;
  	nftl->mbd.devnum = -1;
191876729   Richard Purdie   [MTD] Allow varia...
66

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
  	nftl->mbd.tr = tr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  
          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...
98
  		  Oh no we don't have
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
101
102
103
104
105
106
  		   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...
107
  			nftl->cylinders, nftl->heads , nftl->sectors,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
111
112
  			(long)nftl->cylinders * (long)nftl->heads *
  			(long)nftl->sectors );
  	}
  
  	if (add_mtd_blktrans_dev(&nftl->mbd)) {
fa671646f   Jesper Juhl   [PATCH] kfree cle...
113
114
  		kfree(nftl->ReplUnitTable);
  		kfree(nftl->EUNtable);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
120
121
122
123
124
125
126
  		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...
127
128
  	pr_debug("NFTL: remove_dev (i=%d)
  ", dev->devnum);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
  
  	del_mtd_blktrans_dev(dev);
fa671646f   Jesper Juhl   [PATCH] kfree cle...
131
132
  	kfree(nftl->ReplUnitTable);
  	kfree(nftl->EUNtable);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
  }
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
134
135
136
137
138
139
  /*
   * 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...
140
  	loff_t mask = mtd->writesize - 1;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
141
142
  	struct mtd_oob_ops ops;
  	int res;
0612b9ddc   Brian Norris   mtd: rename MTD_O...
143
  	ops.mode = MTD_OPS_PLACE_OOB;
16f05c2b6   Dimitri Gorokhovik   mtd: nftl: fix of...
144
  	ops.ooboffs = offs & mask;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
145
146
147
  	ops.ooblen = len;
  	ops.oobbuf = buf;
  	ops.datbuf = NULL;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
148

fd2819bbc   Artem Bityutskiy   mtd: introduce mt...
149
  	res = mtd_read_oob(mtd, offs & ~mask, &ops);
7014568ba   Vitaly Wool   [MTD] [NAND] remo...
150
  	*retlen = ops.oobretlen;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
151
152
153
154
155
156
157
158
159
  	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...
160
  	loff_t mask = mtd->writesize - 1;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
161
162
  	struct mtd_oob_ops ops;
  	int res;
0612b9ddc   Brian Norris   mtd: rename MTD_O...
163
  	ops.mode = MTD_OPS_PLACE_OOB;
16f05c2b6   Dimitri Gorokhovik   mtd: nftl: fix of...
164
  	ops.ooboffs = offs & mask;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
165
166
167
  	ops.ooblen = len;
  	ops.oobbuf = buf;
  	ops.datbuf = NULL;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
168

a2cc5ba07   Artem Bityutskiy   mtd: introduce mt...
169
  	res = mtd_write_oob(mtd, offs & ~mask, &ops);
7014568ba   Vitaly Wool   [MTD] [NAND] remo...
170
  	*retlen = ops.oobretlen;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
171
172
  	return res;
  }
553a80120   Frederik Deweerdt   [MTD] fix nftl_wr...
173
  #ifdef CONFIG_NFTL_RW
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
174
175
176
177
178
179
  /*
   * 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...
180
  	loff_t mask = mtd->writesize - 1;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
181
182
  	struct mtd_oob_ops ops;
  	int res;
0612b9ddc   Brian Norris   mtd: rename MTD_O...
183
  	ops.mode = MTD_OPS_PLACE_OOB;
16f05c2b6   Dimitri Gorokhovik   mtd: nftl: fix of...
184
  	ops.ooboffs = offs & mask;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
185
186
187
188
  	ops.ooblen = mtd->oobsize;
  	ops.oobbuf = oob;
  	ops.datbuf = buf;
  	ops.len = len;
a2cc5ba07   Artem Bityutskiy   mtd: introduce mt...
189
  	res = mtd_write_oob(mtd, offs & ~mask, &ops);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
190
191
192
  	*retlen = ops.retlen;
  	return res;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  /* 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...
208
209
  		pr_debug("NFTL_findfreeblock: there are too few free EUNs
  ");
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
210
  		return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  	}
  
  	/* 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...
230
231
  			       "FirstEUN = %d
  ", nftl->LastFreeEUN,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  			       le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
233
  			return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
  		}
  	} while (pot != nftl->LastFreeEUN);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
236
  	return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
240
  }
  
  static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
  {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
241
  	struct mtd_info *mtd = nftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
244
245
246
247
248
249
250
  	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...
251
  	size_t retlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
256
257
258
259
260
261
262
263
  
  	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...
264

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
  	/* 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...
268
  	silly = MAX_LOOPS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
  	targetEUN = BLOCK_NIL;
  	while (thisEUN <= nftl->lastEUN ) {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
271
  		unsigned int status, foldmark;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
274
  
  		targetEUN = thisEUN;
  		for (block = 0; block < nftl->EraseSize / 512; block ++) {
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
275
  			nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
276
277
  				      (block * 512), 16 , &retlen,
  				      (char *)&oob);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
  			if (block == 2) {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
279
280
  				foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
  				if (foldmark == FOLD_MARK_IN_PROGRESS) {
289c05222   Brian Norris   mtd: replace DEBU...
281
282
  					pr_debug("Write Inhibited on EUN %d
  ", thisEUN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
287
288
289
290
  					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...
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
  			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...
303
  					printk(KERN_WARNING
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
307
308
309
310
311
312
  					       "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...
313
  					printk(KERN_WARNING
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
  					       "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...
335

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
340
341
  		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...
342
  		   with them (i.e. BlockMap[block] != BLOCK_NIL) are
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
347
348
349
350
  		   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...
351
  				pr_debug("Setting inplace to 0. VUC %d, "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
353
354
355
  				      "block %d was %x lastEUN, "
  				      "and is in EUN %d (%s) %d
  ",
  				      thisVUC, block, BlockLastState[block],
97894cda5   Thomas Gleixner   [MTD] core: Clean...
356
  				      BlockMap[block],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
359
360
361
362
363
364
365
366
367
  				      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...
368
  			pr_debug("Pending write not free in EUN %d. "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
372
373
  			      "Folding out of place.
  ", targetEUN);
  			inplace = 0;
  		}
  	}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
374

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  	if (!inplace) {
289c05222   Brian Norris   mtd: replace DEBU...
376
  		pr_debug("Cannot fold Virtual Unit Chain %d in place. "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
381
  		      "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...
382
  			/* Ouch. Now we're screwed. We need to do a
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
  			   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...
385
  			   which chain to fold, because makefreeblock will
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
388
389
390
391
392
393
  			   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...
394
395
396
397
398
399
  		/* 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 ...
400
  		nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
401
402
  			       8, &retlen, (char *)&oob.u);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
406
407
  
  	/* 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...
408
409
  	pr_debug("Folding chain %d into unit %d
  ", thisVUC, targetEUN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
411
412
413
414
415
416
417
418
  	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...
419
  		/* copy only in non free block (free blocks can only
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
                     happen in case of media errors or deleted blocks) */
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
421
422
  		if (BlockMap[block] == BLOCK_NIL)
  			continue;
329ad399a   Artem Bityutskiy   mtd: introduce mt...
423
424
425
426
427
  		ret = mtd_read(mtd,
  			       (nftl->EraseSize * BlockMap[block]) + (block * 512),
  			       512,
  			       &retlen,
  			       movebuf);
d57f40544   Brian Norris   mtd: utilize `mtd...
428
  		if (ret < 0 && !mtd_is_bitflip(ret)) {
329ad399a   Artem Bityutskiy   mtd: introduce mt...
429
430
431
432
433
  			ret = mtd_read(mtd,
  				       (nftl->EraseSize * BlockMap[block]) + (block * 512),
  				       512,
  				       &retlen,
  				       movebuf);
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
434
435
436
437
  			if (ret != -EIO)
  				printk("Error went away on retry.
  ");
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
439
  		memset(&oob, 0xff, sizeof(struct nftl_oob));
  		oob.b.Status = oob.b.Status1 = SECTOR_USED;
9223a456d   Thomas Gleixner   [MTD] Remove read...
440

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

f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
445
446
  	/* 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...
447
  	oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
448

8593fbc68   Thomas Gleixner   [MTD] Rework the ...
449
  	nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
450
  		       8, &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
  
  	/* 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...
453
  	/* 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
454
455
456
457
458
  	   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...
459
460
  	pr_debug("Want to erase
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461

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

f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
468
  		if (NFTL_formatblock(nftl, thisEUN) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
470
471
  			/* could not erase : mark block as reserved
  			 */
  			nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
472
  		} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
  			/* correctly erased : mark it as free */
  			nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
  			nftl->numfreeEUNs++;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
476
477
  		}
  		thisEUN = EUNtmp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
  	}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
479

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
481
482
483
484
485
486
487
488
  	/* 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...
489
  	/* This is the part that needs some cleverness applied.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
491
492
493
494
495
496
497
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
  	   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...
534
  		return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
537
538
  	}
  
  	return NFTL_foldchain (nftl, LongestChain, pendingblock);
  }
97894cda5   Thomas Gleixner   [MTD] core: Clean...
539
  /* NFTL_findwriteunit: Return the unit number into which we can write
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
542
543
544
545
                         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...
546
  	struct mtd_info *mtd = nftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
548
549
550
551
552
553
554
555
556
  	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...
557
  		/* This condition catches the 0x[7f]fff cases, as well as
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
561
  		   being a sanity check for past-end-of-media access
  		*/
  		lastEUN = BLOCK_NIL;
  		writeEUN = nftl->EUNtable[thisVUC];
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
562
  		silly = MAX_LOOPS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
564
565
  		while (writeEUN <= nftl->lastEUN) {
  			struct nftl_bci bci;
  			size_t retlen;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
566
  			unsigned int status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
568
  
  			lastEUN = writeEUN;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
569
  			nftl_read_oob(mtd,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
570
571
  				      (writeEUN * nftl->EraseSize) + blockofs,
  				      8, &retlen, (char *)&bci);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
572

289c05222   Brian Norris   mtd: replace DEBU...
573
574
  			pr_debug("Status of block %d in EUN %d is %x
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
  			      block , writeEUN, le16_to_cpu(bci.Status));
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
576
  			status = bci.Status | bci.Status1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
579
580
581
582
583
584
585
586
  			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...
587
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
  			}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
589
590
  
  			if (!silly--) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
592
593
594
  				printk(KERN_WARNING
  				       "Infinite loop in Virtual Unit Chain 0x%x
  ",
  				       thisVUC);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
595
  				return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
598
599
600
  			}
  
  			/* Skip to next block in chain */
  			writeEUN = nftl->ReplUnitTable[writeEUN];
  		}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
601
  		/* OK. We didn't find one in the existing chain, or there
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
603
604
605
606
607
608
609
610
611
612
613
614
  		   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...
615

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
617
  			//printk("Write to VirtualUnitChain %d, calling makefreeblock()
  ", thisVUC);
70ec3bb8e   Julia Lawall   mtd: Use BLOCK_NI...
618
  			writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
  
  			if (writeEUN == BLOCK_NIL) {
97894cda5   Thomas Gleixner   [MTD] core: Clean...
621
  				/* OK, we accept that the above comment is
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
623
624
625
626
  				   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...
627
628
  				pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d
  ", thisVUC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
630
631
632
  				writeEUN = NFTL_findfreeblock(nftl, 1);
  			}
  			if (writeEUN == BLOCK_NIL) {
  				/* Ouch. This should never happen - we should
97894cda5   Thomas Gleixner   [MTD] core: Clean...
633
634
  				   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
635
636
637
638
639
640
  				   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...
641
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
644
645
646
647
648
  			//printk("Restarting scan
  ");
  			lastEUN = BLOCK_NIL;
  			continue;
  		}
  
  		/* We've found a free block. Insert it into the chain. */
97894cda5   Thomas Gleixner   [MTD] core: Clean...
649

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
  		if (lastEUN != BLOCK_NIL) {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
651
  			thisVUC |= 0x8000; /* It's a replacement block */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
  		} else {
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
653
654
  			/* The first block in a new chain */
  			nftl->EUNtable[thisVUC] = writeEUN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
656
657
658
659
660
661
  		}
  
  		/* 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 ...
662
  		nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
663
  			      &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
665
  
  		oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
666
  		nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
667
  			       &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668

f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
669
  		/* we link the new block to the chain only after the
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
671
                     block is ready. It avoids the case where the chain
                     could point to a free block */
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
672
  		if (lastEUN != BLOCK_NIL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
673
674
675
  			/* Both in our cache... */
  			nftl->ReplUnitTable[lastEUN] = writeEUN;
  			/* ... and on the flash itself */
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
676
  			nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
677
  				      8, &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678
679
680
  
  			oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
  				= cpu_to_le16(writeEUN);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
681
  			nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
682
  				       8, &retlen, (char *)&oob.u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
683
684
685
686
687
688
689
690
691
  		}
  
  		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...
692
  	return BLOCK_NIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
  }
  
  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
716

8593fbc68   Thomas Gleixner   [MTD] Rework the ...
717
718
  	nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
  		   512, &retlen, (char *)buffer, (char *)&oob);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
720
721
722
723
724
725
726
  	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...
727
  	struct mtd_info *mtd = nftl->mbd.mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
730
  	u16 lastgoodEUN;
  	u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
  	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
731
  	unsigned int status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
732
  	int silly = MAX_LOOPS;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
733
734
  	size_t retlen;
  	struct nftl_bci bci;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
735
736
  
  	lastgoodEUN = BLOCK_NIL;
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
737
  	if (thisEUN != BLOCK_NIL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
738
  		while (thisEUN < nftl->nb_blocks) {
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
739
  			if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
f4a43cfce   Thomas Gleixner   [MTD] Remove sill...
740
741
  					  blockofs, 8, &retlen,
  					  (char *)&bci) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
  				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...
773
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
775
776
777
778
779
780
781
  
   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...
782
  		int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
9a1fcdfd4   Thomas Gleixner   [MTD] NAND Signal...
783

d57f40544   Brian Norris   mtd: utilize `mtd...
784
  		if (res < 0 && !mtd_is_bitflip(res))
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
  			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...
812
  	.blksize 	= 512,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
813
814
815
816
817
818
819
820
821
  	.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
822
823
  static int __init init_nftl(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
824
825
826
827
828
829
830
831
832
833
834
835
836
837
  	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...
838
  MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);