Blame view

drivers/mtd/ftl.c 30.6 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /* This version ported to the Linux-MTD system by dwmw2@infradead.org
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
   *
   * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
   * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
   *
   * Based on:
   */
  /*======================================================================
  
      A Flash Translation Layer memory card driver
  
      This driver implements a disk-like block device driver with an
      apparent block size of 512 bytes for flash memory cards.
  
      ftl_cs.c 1.62 2000/02/01 00:59:04
  
      The contents of this file are subject to the Mozilla Public
      License Version 1.1 (the "License"); you may not use this file
      except in compliance with the License. You may obtain a copy of
      the License at http://www.mozilla.org/MPL/
  
      Software distributed under the License is distributed on an "AS
      IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
      implied. See the License for the specific language governing
      rights and limitations under the License.
  
      The initial developer of the original code is David A. Hinds
      <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
a1452a377   David Woodhouse   mtd: Update copyr...
29
      are Copyright © 1999 David A. Hinds.  All Rights Reserved.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  
      Alternatively, the contents of this file may be used under the
      terms of the GNU General Public License version 2 (the "GPL"), in
      which case the provisions of the GPL are applicable instead of the
      above.  If you wish to allow the use of your version of this file
      only under the terms of the GPL and not to allow others to use
      your version of this file under the MPL, indicate your decision
      by deleting the provisions above and replace them with the notice
      and other provisions required by the GPL.  If you do not delete
      the provisions above, a recipient may use your version of this
      file under either the MPL or the GPL.
  
      LEGAL NOTE: The FTL format is patented by M-Systems.  They have
      granted a license for its use with PCMCIA devices:
  
       "M-Systems grants a royalty-free, non-exclusive license under
        any presently existing M-Systems intellectual property rights
        necessary for the design and development of FTL-compatible
        drivers, file systems and utilities using the data formats with
        PCMCIA PC Cards as described in the PCMCIA Flash Translation
        Layer (FTL) Specification."
  
      Use of the FTL format for non-PCMCIA applications may be an
      infringement of these patents.  For additional information,
631dd1a88   Justin P. Mattock   Update broken web...
54
      contact M-Systems directly. M-Systems since acquired by Sandisk. 
97894cda5   Thomas Gleixner   [MTD] core: Clean...
55

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
61
62
  ======================================================================*/
  #include <linux/mtd/blktrans.h>
  #include <linux/module.h>
  #include <linux/mtd/mtd.h>
  /*#define PSYCHO_DEBUG */
  
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
67
68
69
70
71
72
  #include <linux/ptrace.h>
  #include <linux/slab.h>
  #include <linux/string.h>
  #include <linux/timer.h>
  #include <linux/major.h>
  #include <linux/fs.h>
  #include <linux/init.h>
  #include <linux/hdreg.h>
  #include <linux/vmalloc.h>
  #include <linux/blkpg.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
73
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
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
106
107
108
109
110
111
  
  #include <linux/mtd/ftl.h>
  
  /*====================================================================*/
  
  /* Parameters that can be set with 'insmod' */
  static int shuffle_freq = 50;
  module_param(shuffle_freq, int, 0);
  
  /*====================================================================*/
  
  /* Major device # for FTL device */
  #ifndef FTL_MAJOR
  #define FTL_MAJOR	44
  #endif
  
  
  /*====================================================================*/
  
  /* Maximum number of separate memory devices we'll allow */
  #define MAX_DEV		4
  
  /* Maximum number of regions per device */
  #define MAX_REGION	4
  
  /* Maximum number of partitions in an FTL region */
  #define PART_BITS	4
  
  /* Maximum number of outstanding erase requests per socket */
  #define MAX_ERASE	8
  
  /* Sector size -- shouldn't need to change */
  #define SECTOR_SIZE	512
  
  
  /* Each memory region corresponds to a minor device */
  typedef struct partition_t {
      struct mtd_blktrans_dev mbd;
3854be771   David Woodhouse   [MTD] Remove stra...
112
113
      uint32_t		state;
      uint32_t		*VirtualBlockMap;
3854be771   David Woodhouse   [MTD] Remove stra...
114
      uint32_t		FreeTotal;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
      struct eun_info_t {
3854be771   David Woodhouse   [MTD] Remove stra...
116
117
118
119
  	uint32_t		Offset;
  	uint32_t		EraseCount;
  	uint32_t		Free;
  	uint32_t		Deleted;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
      } *EUNInfo;
      struct xfer_info_t {
3854be771   David Woodhouse   [MTD] Remove stra...
122
123
124
  	uint32_t		Offset;
  	uint32_t		EraseCount;
  	uint16_t		state;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
      } *XferInfo;
3854be771   David Woodhouse   [MTD] Remove stra...
126
127
128
129
      uint16_t		bam_index;
      uint32_t		*bam_cache;
      uint16_t		DataUnits;
      uint32_t		BlocksPerUnit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
      erase_unit_header_t	header;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  } partition_t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
135
136
137
138
139
140
  /* Partition state flags */
  #define FTL_FORMATTED	0x01
  
  /* Transfer unit states */
  #define XFER_UNKNOWN	0x00
  #define XFER_ERASING	0x01
  #define XFER_ERASED	0x02
  #define XFER_PREPARED	0x03
  #define XFER_FAILED	0x04
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
145
  /*======================================================================
  
      Scan_header() checks to see if a memory region contains an FTL
      partition.  build_maps() reads all the erase unit headers, builds
      the erase unit map, and then builds the virtual page map.
97894cda5   Thomas Gleixner   [MTD] core: Clean...
146

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  ======================================================================*/
  
  static int scan_header(partition_t *part)
  {
      erase_unit_header_t header;
      loff_t offset, max_offset;
      size_t ret;
      int err;
      part->header.FormattedSize = 0;
      max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
      /* Search first megabyte for a valid FTL header */
      for (offset = 0;
  	 (offset + sizeof(header)) < max_offset;
  	 offset += part->mbd.mtd->erasesize ? : 0x2000) {
329ad399a   Artem Bityutskiy   mtd: introduce mt...
161
162
  	err = mtd_read(part->mbd.mtd, offset, sizeof(header), &ret,
                         (unsigned char *)&header);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
163
164
  
  	if (err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  	    return err;
  
  	if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
      }
  
      if (offset == max_offset) {
  	printk(KERN_NOTICE "ftl_cs: FTL header not found.
  ");
  	return -ENOENT;
      }
      if (header.BlockSize != 9 ||
  	(header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
  	(header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
  	printk(KERN_NOTICE "ftl_cs: FTL header corrupt!
  ");
  	return -1;
      }
      if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
  	printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x
  ",
  	       1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
  	return -1;
      }
      part->header = header;
      return 0;
  }
  
  static int build_maps(partition_t *part)
  {
      erase_unit_header_t header;
3854be771   David Woodhouse   [MTD] Remove stra...
195
196
      uint16_t xvalid, xtrans, i;
      unsigned blocks, j;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
201
202
203
      int hdr_ok, ret = -1;
      ssize_t retval;
      loff_t offset;
  
      /* Set up erase unit maps */
      part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
  	part->header.NumTransferUnits;
6da2ec560   Kees Cook   treewide: kmalloc...
204
205
      part->EUNInfo = kmalloc_array(part->DataUnits, sizeof(struct eun_info_t),
                                    GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
209
210
      if (!part->EUNInfo)
  	    goto out;
      for (i = 0; i < part->DataUnits; i++)
  	part->EUNInfo[i].Offset = 0xffffffff;
      part->XferInfo =
6da2ec560   Kees Cook   treewide: kmalloc...
211
212
213
  	kmalloc_array(part->header.NumTransferUnits,
                        sizeof(struct xfer_info_t),
                        GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
217
218
219
220
      if (!part->XferInfo)
  	    goto out_EUNInfo;
  
      xvalid = xtrans = 0;
      for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
  	offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
  		      << part->header.EraseUnitSize);
329ad399a   Artem Bityutskiy   mtd: introduce mt...
221
222
  	ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval,
                         (unsigned char *)&header);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
223
224
  
  	if (ret)
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
253
254
255
256
257
258
259
260
261
262
263
264
  	    goto out_XferInfo;
  
  	ret = -1;
  	/* Is this a transfer partition? */
  	hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
  	if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
  	    (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
  	    part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
  	    part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
  		le32_to_cpu(header.EraseCount);
  	    xvalid++;
  	} else {
  	    if (xtrans == part->header.NumTransferUnits) {
  		printk(KERN_NOTICE "ftl_cs: format error: too many "
  		       "transfer units!
  ");
  		goto out_XferInfo;
  	    }
  	    if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
  		part->XferInfo[xtrans].state = XFER_PREPARED;
  		part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
  	    } else {
  		part->XferInfo[xtrans].state = XFER_UNKNOWN;
  		/* Pick anything reasonable for the erase count */
  		part->XferInfo[xtrans].EraseCount =
  		    le32_to_cpu(part->header.EraseCount);
  	    }
  	    part->XferInfo[xtrans].Offset = offset;
  	    xtrans++;
  	}
      }
      /* Check for format trouble */
      header = part->header;
      if ((xtrans != header.NumTransferUnits) ||
  	(xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
  	printk(KERN_NOTICE "ftl_cs: format error: erase units "
  	       "don't add up!
  ");
  	goto out_XferInfo;
      }
97894cda5   Thomas Gleixner   [MTD] core: Clean...
265

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
      /* Set up virtual page map */
      blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
42bc47b35   Kees Cook   treewide: Use arr...
268
      part->VirtualBlockMap = vmalloc(array_size(blocks, sizeof(uint32_t)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
      if (!part->VirtualBlockMap)
  	    goto out_XferInfo;
3854be771   David Woodhouse   [MTD] Remove stra...
271
      memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
      part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
6da2ec560   Kees Cook   treewide: kmalloc...
273
274
      part->bam_cache = kmalloc_array(part->BlocksPerUnit, sizeof(uint32_t),
                                      GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
277
278
279
280
281
282
283
284
      if (!part->bam_cache)
  	    goto out_VirtualBlockMap;
  
      part->bam_index = 0xffff;
      part->FreeTotal = 0;
  
      for (i = 0; i < part->DataUnits; i++) {
  	part->EUNInfo[i].Free = 0;
  	part->EUNInfo[i].Deleted = 0;
  	offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
285

329ad399a   Artem Bityutskiy   mtd: introduce mt...
286
287
288
  	ret = mtd_read(part->mbd.mtd, offset,
                         part->BlocksPerUnit * sizeof(uint32_t), &retval,
                         (unsigned char *)part->bam_cache);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
289
290
  
  	if (ret)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
293
294
295
296
297
298
299
300
301
302
303
304
  		goto out_bam_cache;
  
  	for (j = 0; j < part->BlocksPerUnit; j++) {
  	    if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
  		part->EUNInfo[i].Free++;
  		part->FreeTotal++;
  	    } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
  		     (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
  		part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
  		    (i << header.EraseUnitSize) + (j << header.BlockSize);
  	    else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
  		part->EUNInfo[i].Deleted++;
  	}
      }
97894cda5   Thomas Gleixner   [MTD] core: Clean...
305

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
      ret = 0;
      goto out;
  
  out_bam_cache:
      kfree(part->bam_cache);
  out_VirtualBlockMap:
      vfree(part->VirtualBlockMap);
  out_XferInfo:
      kfree(part->XferInfo);
  out_EUNInfo:
      kfree(part->EUNInfo);
  out:
      return ret;
  } /* build_maps */
  
  /*======================================================================
  
      Erase_xfer() schedules an asynchronous erase operation for a
      transfer unit.
97894cda5   Thomas Gleixner   [MTD] core: Clean...
325

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
328
  ======================================================================*/
  
  static int erase_xfer(partition_t *part,
3854be771   David Woodhouse   [MTD] Remove stra...
329
  		      uint16_t xfernum)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
333
334
335
  {
      int ret;
      struct xfer_info_t *xfer;
      struct erase_info *erase;
  
      xfer = &part->XferInfo[xfernum];
289c05222   Brian Norris   mtd: replace DEBU...
336
337
      pr_debug("ftl_cs: erasing xfer unit at 0x%x
  ", xfer->Offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
340
      xfer->state = XFER_ERASING;
  
      /* Is there a free erase slot? Always in MTD. */
97894cda5   Thomas Gleixner   [MTD] core: Clean...
341

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
      erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
343
      if (!erase)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
              return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
      erase->addr = xfer->Offset;
      erase->len = 1 << part->header.EraseUnitSize;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
347

7e1f0dc05   Artem Bityutskiy   mtd: introduce mt...
348
      ret = mtd_erase(part->mbd.mtd, erase);
884cfd902   Boris Brezillon   mtd: Stop assumin...
349
350
351
352
353
354
355
356
      if (!ret) {
  	xfer->state = XFER_ERASED;
  	xfer->EraseCount++;
      } else {
  	xfer->state = XFER_FAILED;
  	pr_notice("ftl_cs: erase failed: err = %d
  ", ret);
      }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357

884cfd902   Boris Brezillon   mtd: Stop assumin...
358
      kfree(erase);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
362
363
364
365
366
  
      return ret;
  } /* erase_xfer */
  
  /*======================================================================
  
      Prepare_xfer() takes a freshly erased transfer unit and gives
      it an appropriate header.
97894cda5   Thomas Gleixner   [MTD] core: Clean...
367

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  ======================================================================*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
372
373
  static int prepare_xfer(partition_t *part, int i)
  {
      erase_unit_header_t header;
      struct xfer_info_t *xfer;
      int nbam, ret;
3854be771   David Woodhouse   [MTD] Remove stra...
374
      uint32_t ctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
377
378
379
      ssize_t retlen;
      loff_t offset;
  
      xfer = &part->XferInfo[i];
      xfer->state = XFER_FAILED;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
380

289c05222   Brian Norris   mtd: replace DEBU...
381
382
      pr_debug("ftl_cs: preparing xfer unit at 0x%x
  ", xfer->Offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
385
386
387
  
      /* Write the transfer unit header */
      header = part->header;
      header.LogicalEUN = cpu_to_le16(0xffff);
      header.EraseCount = cpu_to_le32(xfer->EraseCount);
eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
388
389
      ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen,
                      (u_char *)&header);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
391
392
393
394
395
  
      if (ret) {
  	return ret;
      }
  
      /* Write the BAM stub */
b756816da   Arushi Singhal   mtd: ftl: Use DIV...
396
397
      nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
  			le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
400
  
      offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
      ctl = cpu_to_le32(BLOCK_CONTROL);
3854be771   David Woodhouse   [MTD] Remove stra...
401
      for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402

eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
403
404
  	ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
                          (u_char *)&ctl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
408
409
410
  
  	if (ret)
  	    return ret;
      }
      xfer->state = XFER_PREPARED;
      return 0;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
411

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
414
415
416
417
418
419
420
421
422
  } /* prepare_xfer */
  
  /*======================================================================
  
      Copy_erase_unit() takes a full erase block and a transfer unit,
      copies everything to the transfer unit, then swaps the block
      pointers.
  
      All data blocks are copied to the corresponding blocks in the
      target unit, so the virtual block map does not need to be
      updated.
97894cda5   Thomas Gleixner   [MTD] core: Clean...
423

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
  ======================================================================*/
3854be771   David Woodhouse   [MTD] Remove stra...
425
426
  static int copy_erase_unit(partition_t *part, uint16_t srcunit,
  			   uint16_t xferunit)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
429
430
  {
      u_char buf[SECTOR_SIZE];
      struct eun_info_t *eun;
      struct xfer_info_t *xfer;
3854be771   David Woodhouse   [MTD] Remove stra...
431
432
      uint32_t src, dest, free, i;
      uint16_t unit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
435
      int ret;
      ssize_t retlen;
      loff_t offset;
3854be771   David Woodhouse   [MTD] Remove stra...
436
      uint16_t srcunitswap = cpu_to_le16(srcunit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
438
439
  
      eun = &part->EUNInfo[srcunit];
      xfer = &part->XferInfo[xferunit];
289c05222   Brian Norris   mtd: replace DEBU...
440
441
      pr_debug("ftl_cs: copying block 0x%x to 0x%x
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
  	  eun->Offset, xfer->Offset);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
443

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
445
446
447
      /* Read current BAM */
      if (part->bam_index != srcunit) {
  
  	offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
329ad399a   Artem Bityutskiy   mtd: introduce mt...
448
449
450
  	ret = mtd_read(part->mbd.mtd, offset,
                         part->BlocksPerUnit * sizeof(uint32_t), &retlen,
                         (u_char *)(part->bam_cache));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
453
454
455
  
  	/* mark the cache bad, in case we get an error later */
  	part->bam_index = 0xffff;
  
  	if (ret) {
97894cda5   Thomas Gleixner   [MTD] core: Clean...
456
457
  	    printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
459
460
  	    return ret;
  	}
      }
97894cda5   Thomas Gleixner   [MTD] core: Clean...
461

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
465
      /* Write the LogicalEUN for the transfer unit */
      xfer->state = XFER_UNKNOWN;
      offset = xfer->Offset + 20; /* Bad! */
      unit = cpu_to_le16(0x7fff);
eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
466
467
      ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen,
                      (u_char *)&unit);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
468

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
470
471
472
473
      if (ret) {
  	printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!
  ");
  	return ret;
      }
97894cda5   Thomas Gleixner   [MTD] core: Clean...
474

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
477
478
479
480
481
482
483
484
485
486
      /* Copy all data blocks from source unit to transfer unit */
      src = eun->Offset; dest = xfer->Offset;
  
      free = 0;
      ret = 0;
      for (i = 0; i < part->BlocksPerUnit; i++) {
  	switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
  	case BLOCK_CONTROL:
  	    /* This gets updated later */
  	    break;
  	case BLOCK_DATA:
  	case BLOCK_REPLACEMENT:
329ad399a   Artem Bityutskiy   mtd: introduce mt...
487
488
  	    ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen,
                             (u_char *)buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
491
492
493
  	    if (ret) {
  		printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit
  ");
  		return ret;
              }
eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
494
495
  	    ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen,
                              (u_char *)buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
  	    if (ret)  {
  		printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit
  ");
  		return ret;
              }
  
  	    break;
  	default:
  	    /* All other blocks must be free */
  	    part->bam_cache[i] = cpu_to_le32(0xffffffff);
  	    free++;
  	    break;
  	}
  	src += SECTOR_SIZE;
  	dest += SECTOR_SIZE;
      }
  
      /* Write the BAM to the transfer unit */
eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
514
515
516
517
518
      ret = mtd_write(part->mbd.mtd,
                      xfer->Offset + le32_to_cpu(part->header.BAMOffset),
                      part->BlocksPerUnit * sizeof(int32_t),
                      &retlen,
                      (u_char *)part->bam_cache);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
522
523
      if (ret) {
  	printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit
  ");
  	return ret;
      }
97894cda5   Thomas Gleixner   [MTD] core: Clean...
524

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
      /* All clear? Then update the LogicalEUN again */
eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
526
527
      ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
                      &retlen, (u_char *)&srcunitswap);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
529
530
531
532
  
      if (ret) {
  	printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit
  ");
  	return ret;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
533
      }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
      /* Update the maps and usage stats*/
6166a76f5   Fabian Frederick   mtd: ftl: use swa...
535
536
      swap(xfer->EraseCount, eun->EraseCount);
      swap(xfer->Offset, eun->Offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
538
539
540
      part->FreeTotal -= eun->Free;
      part->FreeTotal += free;
      eun->Free = free;
      eun->Deleted = 0;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
541

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
543
      /* Now, the cache should be valid for the new block */
      part->bam_index = srcunit;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
544

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
547
548
549
550
551
552
553
554
555
556
557
558
      return 0;
  } /* copy_erase_unit */
  
  /*======================================================================
  
      reclaim_block() picks a full erase unit and a transfer unit and
      then calls copy_erase_unit() to copy one to the other.  Then, it
      schedules an erase on the expired block.
  
      What's a good way to decide which transfer unit and which erase
      unit to use?  Beats me.  My way is to always pick the transfer
      unit with the fewest erases, and usually pick the data unit with
      the most deleted blocks.  But with a small probability, pick the
      oldest data unit instead.  This means that we generally postpone
92394b5c2   Brian Norris   mtd: spelling fixes
559
      the next reclamation as long as possible, but shuffle static
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560
      stuff around a bit for wear leveling.
97894cda5   Thomas Gleixner   [MTD] core: Clean...
561

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
563
564
565
  ======================================================================*/
  
  static int reclaim_block(partition_t *part)
  {
3854be771   David Woodhouse   [MTD] Remove stra...
566
567
      uint16_t i, eun, xfer;
      uint32_t best;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
      int queued, ret;
289c05222   Brian Norris   mtd: replace DEBU...
569
570
571
572
      pr_debug("ftl_cs: reclaiming space...
  ");
      pr_debug("NumTransferUnits == %x
  ", part->header.NumTransferUnits);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
574
575
576
577
578
579
      /* Pick the least erased transfer unit */
      best = 0xffffffff; xfer = 0xffff;
      do {
  	queued = 0;
  	for (i = 0; i < part->header.NumTransferUnits; i++) {
  	    int n=0;
  	    if (part->XferInfo[i].state == XFER_UNKNOWN) {
289c05222   Brian Norris   mtd: replace DEBU...
580
581
  		pr_debug("XferInfo[%d].state == XFER_UNKNOWN
  ",i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
583
584
585
  		n=1;
  		erase_xfer(part, i);
  	    }
  	    if (part->XferInfo[i].state == XFER_ERASING) {
289c05222   Brian Norris   mtd: replace DEBU...
586
587
  		pr_debug("XferInfo[%d].state == XFER_ERASING
  ",i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
589
590
591
  		n=1;
  		queued = 1;
  	    }
  	    else if (part->XferInfo[i].state == XFER_ERASED) {
289c05222   Brian Norris   mtd: replace DEBU...
592
593
  		pr_debug("XferInfo[%d].state == XFER_ERASED
  ",i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
595
596
597
  		n=1;
  		prepare_xfer(part, i);
  	    }
  	    if (part->XferInfo[i].state == XFER_PREPARED) {
289c05222   Brian Norris   mtd: replace DEBU...
598
599
  		pr_debug("XferInfo[%d].state == XFER_PREPARED
  ",i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
601
602
603
604
605
606
  		n=1;
  		if (part->XferInfo[i].EraseCount <= best) {
  		    best = part->XferInfo[i].EraseCount;
  		    xfer = i;
  		}
  	    }
  		if (!n)
289c05222   Brian Norris   mtd: replace DEBU...
607
608
  		    pr_debug("XferInfo[%d].state == %x
  ",i, part->XferInfo[i].state);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609
610
611
612
  
  	}
  	if (xfer == 0xffff) {
  	    if (queued) {
289c05222   Brian Norris   mtd: replace DEBU...
613
  		pr_debug("ftl_cs: waiting for transfer "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
614
615
  		      "unit to be prepared...
  ");
327cf2922   Artem Bityutskiy   mtd: do not use m...
616
  		mtd_sync(part->mbd.mtd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
619
620
621
622
623
  	    } else {
  		static int ne = 0;
  		if (++ne < 5)
  		    printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
  			   "suitable transfer units!
  ");
  		else
289c05222   Brian Norris   mtd: replace DEBU...
624
  		    pr_debug("ftl_cs: reclaim failed: no "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
626
  			  "suitable transfer units!
  ");
97894cda5   Thomas Gleixner   [MTD] core: Clean...
627

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
630
631
632
633
634
  		return -EIO;
  	    }
  	}
      } while (xfer == 0xffff);
  
      eun = 0;
      if ((jiffies % shuffle_freq) == 0) {
289c05222   Brian Norris   mtd: replace DEBU...
635
636
  	pr_debug("ftl_cs: recycling freshest block...
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
  	best = 0xffffffff;
  	for (i = 0; i < part->DataUnits; i++)
  	    if (part->EUNInfo[i].EraseCount <= best) {
  		best = part->EUNInfo[i].EraseCount;
  		eun = i;
  	    }
      } else {
  	best = 0;
  	for (i = 0; i < part->DataUnits; i++)
  	    if (part->EUNInfo[i].Deleted >= best) {
  		best = part->EUNInfo[i].Deleted;
  		eun = i;
  	    }
  	if (best == 0) {
  	    static int ne = 0;
  	    if (++ne < 5)
  		printk(KERN_NOTICE "ftl_cs: reclaim failed: "
  		       "no free blocks!
  ");
  	    else
289c05222   Brian Norris   mtd: replace DEBU...
657
  		pr_debug("ftl_cs: reclaim failed: "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
  		       "no free blocks!
  ");
  
  	    return -EIO;
  	}
      }
      ret = copy_erase_unit(part, eun, xfer);
      if (!ret)
  	erase_xfer(part, xfer);
      else
  	printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!
  ");
      return ret;
  } /* reclaim_block */
  
  /*======================================================================
  
      Find_free() searches for a free block.  If necessary, it updates
      the BAM cache for the erase unit containing the free block.  It
      returns the block index -- the erase unit is just the currently
      cached unit.  If there are no free blocks, it returns 0 -- this
      is never a valid data block because it contains the header.
97894cda5   Thomas Gleixner   [MTD] core: Clean...
680

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
  ======================================================================*/
  
  #ifdef PSYCHO_DEBUG
  static void dump_lists(partition_t *part)
  {
      int i;
      printk(KERN_DEBUG "ftl_cs: Free total = %d
  ", part->FreeTotal);
      for (i = 0; i < part->DataUnits; i++)
  	printk(KERN_DEBUG "ftl_cs:   unit %d: %d phys, %d free, "
  	       "%d deleted
  ", i,
  	       part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
  	       part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
  }
  #endif
3854be771   David Woodhouse   [MTD] Remove stra...
697
  static uint32_t find_free(partition_t *part)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
  {
3854be771   David Woodhouse   [MTD] Remove stra...
699
700
      uint16_t stop, eun;
      uint32_t blk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
      size_t retlen;
      int ret;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
703

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
705
706
707
708
709
710
711
712
713
714
      /* Find an erase unit with some free space */
      stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
      eun = stop;
      do {
  	if (part->EUNInfo[eun].Free != 0) break;
  	/* Wrap around at end of table */
  	if (++eun == part->DataUnits) eun = 0;
      } while (eun != stop);
  
      if (part->EUNInfo[eun].Free == 0)
  	return 0;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
715

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
717
718
719
      /* Is this unit's BAM cached? */
      if (eun != part->bam_index) {
  	/* Invalidate cache */
  	part->bam_index = 0xffff;
329ad399a   Artem Bityutskiy   mtd: introduce mt...
720
721
722
723
724
  	ret = mtd_read(part->mbd.mtd,
                         part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
                         part->BlocksPerUnit * sizeof(uint32_t),
                         &retlen,
                         (u_char *)(part->bam_cache));
97894cda5   Thomas Gleixner   [MTD] core: Clean...
725

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
  	if (ret) {
  	    printk(KERN_WARNING"ftl: Error reading BAM in find_free
  ");
  	    return 0;
  	}
  	part->bam_index = eun;
      }
  
      /* Find a free block */
      for (blk = 0; blk < part->BlocksPerUnit; blk++)
  	if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
      if (blk == part->BlocksPerUnit) {
  #ifdef PSYCHO_DEBUG
  	static int ne = 0;
  	if (++ne == 1)
  	    dump_lists(part);
  #endif
  	printk(KERN_NOTICE "ftl_cs: bad free list!
  ");
  	return 0;
      }
289c05222   Brian Norris   mtd: replace DEBU...
747
748
      pr_debug("ftl_cs: found free block at %d in %d
  ", blk, eun);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
749
      return blk;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
750

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
751
752
753
754
755
756
  } /* find_free */
  
  
  /*======================================================================
  
      Read a series of sectors from an FTL partition.
97894cda5   Thomas Gleixner   [MTD] core: Clean...
757

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
759
760
761
762
  ======================================================================*/
  
  static int ftl_read(partition_t *part, caddr_t buffer,
  		    u_long sector, u_long nblocks)
  {
3854be771   David Woodhouse   [MTD] Remove stra...
763
      uint32_t log_addr, bsize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
764
765
766
      u_long i;
      int ret;
      size_t offset, retlen;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
767

289c05222   Brian Norris   mtd: replace DEBU...
768
769
      pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
  	  part, sector, nblocks);
      if (!(part->state & FTL_FORMATTED)) {
  	printk(KERN_NOTICE "ftl_cs: bad partition
  ");
  	return -EIO;
      }
      bsize = 1 << part->header.EraseUnitSize;
  
      for (i = 0; i < nblocks; i++) {
  	if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
  	    printk(KERN_NOTICE "ftl_cs: bad read offset
  ");
  	    return -EIO;
  	}
  	log_addr = part->VirtualBlockMap[sector+i];
  	if (log_addr == 0xffffffff)
  	    memset(buffer, 0, SECTOR_SIZE);
  	else {
  	    offset = (part->EUNInfo[log_addr / bsize].Offset
  			  + (log_addr % bsize));
329ad399a   Artem Bityutskiy   mtd: introduce mt...
790
791
  	    ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
                             (u_char *)buffer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
  
  	    if (ret) {
  		printk(KERN_WARNING "Error reading MTD device in ftl_read()
  ");
  		return ret;
  	    }
  	}
  	buffer += SECTOR_SIZE;
      }
      return 0;
  } /* ftl_read */
  
  /*======================================================================
  
      Write a series of sectors to an FTL partition
97894cda5   Thomas Gleixner   [MTD] core: Clean...
807

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
808
  ======================================================================*/
3854be771   David Woodhouse   [MTD] Remove stra...
809
810
  static int set_bam_entry(partition_t *part, uint32_t log_addr,
  			 uint32_t virt_addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811
  {
3854be771   David Woodhouse   [MTD] Remove stra...
812
      uint32_t bsize, blk, le_virt_addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
813
  #ifdef PSYCHO_DEBUG
3854be771   David Woodhouse   [MTD] Remove stra...
814
      uint32_t old_addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
815
  #endif
3854be771   David Woodhouse   [MTD] Remove stra...
816
      uint16_t eun;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
818
      int ret;
      size_t retlen, offset;
289c05222   Brian Norris   mtd: replace DEBU...
819
820
      pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821
822
823
824
  	  part, log_addr, virt_addr);
      bsize = 1 << part->header.EraseUnitSize;
      eun = log_addr / bsize;
      blk = (log_addr % bsize) / SECTOR_SIZE;
3854be771   David Woodhouse   [MTD] Remove stra...
825
      offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
  		  le32_to_cpu(part->header.BAMOffset));
97894cda5   Thomas Gleixner   [MTD] core: Clean...
827

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
  #ifdef PSYCHO_DEBUG
329ad399a   Artem Bityutskiy   mtd: introduce mt...
829
830
      ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
                     (u_char *)&old_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
      if (ret) {
  	printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d
  ",ret);
  	return ret;
      }
      old_addr = le32_to_cpu(old_addr);
  
      if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
  	((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
  	(!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
  	static int ne = 0;
  	if (++ne < 5) {
  	    printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!
  ");
  	    printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, old = 0x%x"
  		   ", new = 0x%x
  ", log_addr, old_addr, virt_addr);
  	}
  	return -EIO;
      }
  #endif
      le_virt_addr = cpu_to_le32(virt_addr);
      if (part->bam_index == eun) {
  #ifdef PSYCHO_DEBUG
  	if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
  	    static int ne = 0;
  	    if (++ne < 5) {
  		printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
  		       "inconsistency!
  ");
  		printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, cache"
  		       " = 0x%x
  ",
  		       le32_to_cpu(part->bam_cache[blk]), old_addr);
  	    }
  	    return -EIO;
  	}
  #endif
  	part->bam_cache[blk] = le_virt_addr;
      }
eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
871
872
      ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
                      (u_char *)&le_virt_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
874
875
876
877
878
879
880
881
882
883
884
885
886
  
      if (ret) {
  	printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!
  ");
  	printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, new = 0x%x
  ",
  	       log_addr, virt_addr);
      }
      return ret;
  } /* set_bam_entry */
  
  static int ftl_write(partition_t *part, caddr_t buffer,
  		     u_long sector, u_long nblocks)
  {
3854be771   David Woodhouse   [MTD] Remove stra...
887
      uint32_t bsize, log_addr, virt_addr, old_addr, blk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
889
890
      u_long i;
      int ret;
      size_t retlen, offset;
289c05222   Brian Norris   mtd: replace DEBU...
891
892
      pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
895
896
897
898
899
900
901
902
903
904
  	  part, sector, nblocks);
      if (!(part->state & FTL_FORMATTED)) {
  	printk(KERN_NOTICE "ftl_cs: bad partition
  ");
  	return -EIO;
      }
      /* See if we need to reclaim space, before we start */
      while (part->FreeTotal < nblocks) {
  	ret = reclaim_block(part);
  	if (ret)
  	    return ret;
      }
97894cda5   Thomas Gleixner   [MTD] core: Clean...
905

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
      bsize = 1 << part->header.EraseUnitSize;
  
      virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
      for (i = 0; i < nblocks; i++) {
  	if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
  	    printk(KERN_NOTICE "ftl_cs: bad write offset
  ");
  	    return -EIO;
  	}
  
  	/* Grab a free block */
  	blk = find_free(part);
  	if (blk == 0) {
  	    static int ne = 0;
  	    if (++ne < 5)
  		printk(KERN_NOTICE "ftl_cs: internal error: "
  		       "no free blocks!
  ");
  	    return -ENOSPC;
  	}
  
  	/* Tag the BAM entry, and write the new block */
  	log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
  	part->EUNInfo[part->bam_index].Free--;
  	part->FreeTotal--;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
931
  	if (set_bam_entry(part, log_addr, 0xfffffffe))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
932
933
934
935
  	    return -EIO;
  	part->EUNInfo[part->bam_index].Deleted++;
  	offset = (part->EUNInfo[part->bam_index].Offset +
  		      blk * SECTOR_SIZE);
eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
936
  	ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
937
938
939
940
941
942
943
944
945
946
  
  	if (ret) {
  	    printk(KERN_NOTICE "ftl_cs: block write failed!
  ");
  	    printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, virt_addr"
  		   " = 0x%x, Offset = 0x%zx
  ", log_addr, virt_addr,
  		   offset);
  	    return -EIO;
  	}
97894cda5   Thomas Gleixner   [MTD] core: Clean...
947

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
948
949
950
951
952
953
954
955
956
957
958
959
960
961
  	/* Only delete the old entry when the new entry is ready */
  	old_addr = part->VirtualBlockMap[sector+i];
  	if (old_addr != 0xffffffff) {
  	    part->VirtualBlockMap[sector+i] = 0xffffffff;
  	    part->EUNInfo[old_addr/bsize].Deleted++;
  	    if (set_bam_entry(part, old_addr, 0))
  		return -EIO;
  	}
  
  	/* Finally, set up the new pointers */
  	if (set_bam_entry(part, log_addr, virt_addr))
  	    return -EIO;
  	part->VirtualBlockMap[sector+i] = log_addr;
  	part->EUNInfo[part->bam_index].Deleted--;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
962

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
  	buffer += SECTOR_SIZE;
  	virt_addr += SECTOR_SIZE;
      }
      return 0;
  } /* ftl_write */
  
  static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
  {
  	partition_t *part = (void *)dev;
  	u_long sect;
  
  	/* Sort of arbitrary: round size down to 4KiB boundary */
  	sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
  
  	geo->heads = 1;
  	geo->sectors = 8;
  	geo->cylinders = sect >> 3;
  
  	return 0;
  }
  
  static int ftl_readsect(struct mtd_blktrans_dev *dev,
  			      unsigned long block, char *buf)
  {
  	return ftl_read((void *)dev, buf, block, 1);
  }
  
  static int ftl_writesect(struct mtd_blktrans_dev *dev,
  			      unsigned long block, char *buf)
  {
  	return ftl_write((void *)dev, buf, block, 1);
  }
fdc53971b   David Woodhouse   Support 'discard ...
995
996
997
998
999
  static int ftl_discardsect(struct mtd_blktrans_dev *dev,
  			   unsigned long sector, unsigned nr_sects)
  {
  	partition_t *part = (void *)dev;
  	uint32_t bsize = 1 << part->header.EraseUnitSize;
289c05222   Brian Norris   mtd: replace DEBU...
1000
1001
  	pr_debug("FTL erase sector %ld for %d sectors
  ",
fdc53971b   David Woodhouse   Support 'discard ...
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
  	      sector, nr_sects);
  
  	while (nr_sects) {
  		uint32_t old_addr = part->VirtualBlockMap[sector];
  		if (old_addr != 0xffffffff) {
  			part->VirtualBlockMap[sector] = 0xffffffff;
  			part->EUNInfo[old_addr/bsize].Deleted++;
  			if (set_bam_entry(part, old_addr, 0))
  				return -EIO;
  		}
  		nr_sects--;
  		sector++;
  	}
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018
  /*====================================================================*/
5ce45d500   Adrian Bunk   [MTD] ftl.c: make...
1019
  static void ftl_freepart(partition_t *part)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1020
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1021
1022
  	vfree(part->VirtualBlockMap);
  	part->VirtualBlockMap = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1023
1024
  	kfree(part->EUNInfo);
  	part->EUNInfo = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1025
1026
  	kfree(part->XferInfo);
  	part->XferInfo = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1027
1028
  	kfree(part->bam_cache);
  	part->bam_cache = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
1030
1031
1032
1033
  } /* ftl_freepart */
  
  static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
  {
  	partition_t *partition;
95b93a0cd   Burman Yan   [MTD] replace kma...
1034
  	partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
1035

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1036
1037
1038
1039
1040
  	if (!partition) {
  		printk(KERN_WARNING "No memory to scan for FTL on %s
  ",
  		       mtd->name);
  		return;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
1041
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1042

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1043
  	partition->mbd.mtd = mtd;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
1044
  	if ((scan_header(partition) == 0) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045
  	    (build_maps(partition) == 0)) {
97894cda5   Thomas Gleixner   [MTD] core: Clean...
1046

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1047
1048
1049
1050
1051
1052
1053
  		partition->state = FTL_FORMATTED;
  #ifdef PCMCIA_DEBUG
  		printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition
  ",
  		       le32_to_cpu(partition->header.FormattedSize) >> 10);
  #endif
  		partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
191876729   Richard Purdie   [MTD] Allow varia...
1054

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1055
1056
1057
1058
1059
  		partition->mbd.tr = tr;
  		partition->mbd.devnum = -1;
  		if (!add_mtd_blktrans_dev((void *)partition))
  			return;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
1061
1062
1063
1064
1065
1066
  	kfree(partition);
  }
  
  static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
  {
  	del_mtd_blktrans_dev(dev);
  	ftl_freepart((partition_t *)dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1067
  }
5ce45d500   Adrian Bunk   [MTD] ftl.c: make...
1068
  static struct mtd_blktrans_ops ftl_tr = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1069
1070
1071
  	.name		= "ftl",
  	.major		= FTL_MAJOR,
  	.part_bits	= PART_BITS,
191876729   Richard Purdie   [MTD] Allow varia...
1072
  	.blksize 	= SECTOR_SIZE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1073
1074
  	.readsect	= ftl_readsect,
  	.writesect	= ftl_writesect,
fdc53971b   David Woodhouse   Support 'discard ...
1075
  	.discard	= ftl_discardsect,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
1077
1078
1079
1080
  	.getgeo		= ftl_getgeo,
  	.add_mtd	= ftl_add_mtd,
  	.remove_dev	= ftl_remove_dev,
  	.owner		= THIS_MODULE,
  };
627df23c6   Peter Huewe   trivial: mtd: add...
1081
  static int __init init_ftl(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1082
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
  	return register_mtd_blktrans(&ftl_tr);
  }
  
  static void __exit cleanup_ftl(void)
  {
  	deregister_mtd_blktrans(&ftl_tr);
  }
  
  module_init(init_ftl);
  module_exit(cleanup_ftl);
  
  
  MODULE_LICENSE("Dual MPL/GPL");
  MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
  MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");