OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [mtd/] [ftl.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/* This version ported to the Linux-MTD system by dwmw2@infradead.org
2
 * $Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $
3
 *
4
 * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
5
 * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
6
 *
7
 * Based on:
8
 */
9
/*======================================================================
10
 
11
    A Flash Translation Layer memory card driver
12
 
13
    This driver implements a disk-like block device driver with an
14
    apparent block size of 512 bytes for flash memory cards.
15
 
16
    ftl_cs.c 1.62 2000/02/01 00:59:04
17
 
18
    The contents of this file are subject to the Mozilla Public
19
    License Version 1.1 (the "License"); you may not use this file
20
    except in compliance with the License. You may obtain a copy of
21
    the License at http://www.mozilla.org/MPL/
22
 
23
    Software distributed under the License is distributed on an "AS
24
    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
25
    implied. See the License for the specific language governing
26
    rights and limitations under the License.
27
 
28
    The initial developer of the original code is David A. Hinds
29
    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
30
    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
31
 
32
    Alternatively, the contents of this file may be used under the
33
    terms of the GNU General Public License version 2 (the "GPL"), in
34
    which case the provisions of the GPL are applicable instead of the
35
    above.  If you wish to allow the use of your version of this file
36
    only under the terms of the GPL and not to allow others to use
37
    your version of this file under the MPL, indicate your decision
38
    by deleting the provisions above and replace them with the notice
39
    and other provisions required by the GPL.  If you do not delete
40
    the provisions above, a recipient may use your version of this
41
    file under either the MPL or the GPL.
42
 
43
    LEGAL NOTE: The FTL format is patented by M-Systems.  They have
44
    granted a license for its use with PCMCIA devices:
45
 
46
     "M-Systems grants a royalty-free, non-exclusive license under
47
      any presently existing M-Systems intellectual property rights
48
      necessary for the design and development of FTL-compatible
49
      drivers, file systems and utilities using the data formats with
50
      PCMCIA PC Cards as described in the PCMCIA Flash Translation
51
      Layer (FTL) Specification."
52
 
53
    Use of the FTL format for non-PCMCIA applications may be an
54
    infringement of these patents.  For additional information,
55
    contact M-Systems (http://www.m-sys.com) directly.
56
 
57
======================================================================*/
58
#include <linux/mtd/blktrans.h>
59
#include <linux/module.h>
60
#include <linux/mtd/mtd.h>
61
/*#define PSYCHO_DEBUG */
62
 
63
#include <linux/kernel.h>
64
#include <linux/ptrace.h>
65
#include <linux/slab.h>
66
#include <linux/string.h>
67
#include <linux/timer.h>
68
#include <linux/major.h>
69
#include <linux/fs.h>
70
#include <linux/init.h>
71
#include <linux/hdreg.h>
72
#include <linux/vmalloc.h>
73
#include <linux/blkpg.h>
74
#include <asm/uaccess.h>
75
 
76
#include <linux/mtd/ftl.h>
77
 
78
/*====================================================================*/
79
 
80
/* Parameters that can be set with 'insmod' */
81
static int shuffle_freq = 50;
82
module_param(shuffle_freq, int, 0);
83
 
84
/*====================================================================*/
85
 
86
/* Major device # for FTL device */
87
#ifndef FTL_MAJOR
88
#define FTL_MAJOR       44
89
#endif
90
 
91
 
92
/*====================================================================*/
93
 
94
/* Maximum number of separate memory devices we'll allow */
95
#define MAX_DEV         4
96
 
97
/* Maximum number of regions per device */
98
#define MAX_REGION      4
99
 
100
/* Maximum number of partitions in an FTL region */
101
#define PART_BITS       4
102
 
103
/* Maximum number of outstanding erase requests per socket */
104
#define MAX_ERASE       8
105
 
106
/* Sector size -- shouldn't need to change */
107
#define SECTOR_SIZE     512
108
 
109
 
110
/* Each memory region corresponds to a minor device */
111
typedef struct partition_t {
112
    struct mtd_blktrans_dev mbd;
113
    u_int32_t           state;
114
    u_int32_t           *VirtualBlockMap;
115
    u_int32_t           *VirtualPageMap;
116
    u_int32_t           FreeTotal;
117
    struct eun_info_t {
118
        u_int32_t               Offset;
119
        u_int32_t               EraseCount;
120
        u_int32_t               Free;
121
        u_int32_t               Deleted;
122
    } *EUNInfo;
123
    struct xfer_info_t {
124
        u_int32_t               Offset;
125
        u_int32_t               EraseCount;
126
        u_int16_t               state;
127
    } *XferInfo;
128
    u_int16_t           bam_index;
129
    u_int32_t           *bam_cache;
130
    u_int16_t           DataUnits;
131
    u_int32_t           BlocksPerUnit;
132
    erase_unit_header_t header;
133
#if 0
134
    region_info_t       region;
135
    memory_handle_t     handle;
136
#endif
137
} partition_t;
138
 
139
void ftl_freepart(partition_t *part);
140
 
141
/* Partition state flags */
142
#define FTL_FORMATTED   0x01
143
 
144
/* Transfer unit states */
145
#define XFER_UNKNOWN    0x00
146
#define XFER_ERASING    0x01
147
#define XFER_ERASED     0x02
148
#define XFER_PREPARED   0x03
149
#define XFER_FAILED     0x04
150
 
151
/*====================================================================*/
152
 
153
 
154
static void ftl_erase_callback(struct erase_info *done);
155
 
156
 
157
/*======================================================================
158
 
159
    Scan_header() checks to see if a memory region contains an FTL
160
    partition.  build_maps() reads all the erase unit headers, builds
161
    the erase unit map, and then builds the virtual page map.
162
 
163
======================================================================*/
164
 
165
static int scan_header(partition_t *part)
166
{
167
    erase_unit_header_t header;
168
    loff_t offset, max_offset;
169
    size_t ret;
170
    int err;
171
    part->header.FormattedSize = 0;
172
    max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
173
    /* Search first megabyte for a valid FTL header */
174
    for (offset = 0;
175
         (offset + sizeof(header)) < max_offset;
176
         offset += part->mbd.mtd->erasesize ? : 0x2000) {
177
 
178
        err = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret,
179
                              (unsigned char *)&header);
180
 
181
        if (err)
182
            return err;
183
 
184
        if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
185
    }
186
 
187
    if (offset == max_offset) {
188
        printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
189
        return -ENOENT;
190
    }
191
    if (header.BlockSize != 9 ||
192
        (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
193
        (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
194
        printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
195
        return -1;
196
    }
197
    if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
198
        printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
199
               1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
200
        return -1;
201
    }
202
    part->header = header;
203
    return 0;
204
}
205
 
206
static int build_maps(partition_t *part)
207
{
208
    erase_unit_header_t header;
209
    u_int16_t xvalid, xtrans, i;
210
    u_int blocks, j;
211
    int hdr_ok, ret = -1;
212
    ssize_t retval;
213
    loff_t offset;
214
 
215
    /* Set up erase unit maps */
216
    part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
217
        part->header.NumTransferUnits;
218
    part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t),
219
                            GFP_KERNEL);
220
    if (!part->EUNInfo)
221
            goto out;
222
    for (i = 0; i < part->DataUnits; i++)
223
        part->EUNInfo[i].Offset = 0xffffffff;
224
    part->XferInfo =
225
        kmalloc(part->header.NumTransferUnits * sizeof(struct xfer_info_t),
226
                GFP_KERNEL);
227
    if (!part->XferInfo)
228
            goto out_EUNInfo;
229
 
230
    xvalid = xtrans = 0;
231
    for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
232
        offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
233
                      << part->header.EraseUnitSize);
234
        ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &retval,
235
                              (unsigned char *)&header);
236
 
237
        if (ret)
238
            goto out_XferInfo;
239
 
240
        ret = -1;
241
        /* Is this a transfer partition? */
242
        hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
243
        if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
244
            (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
245
            part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
246
            part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
247
                le32_to_cpu(header.EraseCount);
248
            xvalid++;
249
        } else {
250
            if (xtrans == part->header.NumTransferUnits) {
251
                printk(KERN_NOTICE "ftl_cs: format error: too many "
252
                       "transfer units!\n");
253
                goto out_XferInfo;
254
            }
255
            if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
256
                part->XferInfo[xtrans].state = XFER_PREPARED;
257
                part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
258
            } else {
259
                part->XferInfo[xtrans].state = XFER_UNKNOWN;
260
                /* Pick anything reasonable for the erase count */
261
                part->XferInfo[xtrans].EraseCount =
262
                    le32_to_cpu(part->header.EraseCount);
263
            }
264
            part->XferInfo[xtrans].Offset = offset;
265
            xtrans++;
266
        }
267
    }
268
    /* Check for format trouble */
269
    header = part->header;
270
    if ((xtrans != header.NumTransferUnits) ||
271
        (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
272
        printk(KERN_NOTICE "ftl_cs: format error: erase units "
273
               "don't add up!\n");
274
        goto out_XferInfo;
275
    }
276
 
277
    /* Set up virtual page map */
278
    blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
279
    part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int32_t));
280
    if (!part->VirtualBlockMap)
281
            goto out_XferInfo;
282
 
283
    memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int32_t));
284
    part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
285
 
286
    part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int32_t),
287
                              GFP_KERNEL);
288
    if (!part->bam_cache)
289
            goto out_VirtualBlockMap;
290
 
291
    part->bam_index = 0xffff;
292
    part->FreeTotal = 0;
293
 
294
    for (i = 0; i < part->DataUnits; i++) {
295
        part->EUNInfo[i].Free = 0;
296
        part->EUNInfo[i].Deleted = 0;
297
        offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
298
 
299
        ret = part->mbd.mtd->read(part->mbd.mtd, offset,
300
                              part->BlocksPerUnit * sizeof(u_int32_t), &retval,
301
                              (unsigned char *)part->bam_cache);
302
 
303
        if (ret)
304
                goto out_bam_cache;
305
 
306
        for (j = 0; j < part->BlocksPerUnit; j++) {
307
            if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
308
                part->EUNInfo[i].Free++;
309
                part->FreeTotal++;
310
            } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
311
                     (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
312
                part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
313
                    (i << header.EraseUnitSize) + (j << header.BlockSize);
314
            else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
315
                part->EUNInfo[i].Deleted++;
316
        }
317
    }
318
 
319
    ret = 0;
320
    goto out;
321
 
322
out_bam_cache:
323
    kfree(part->bam_cache);
324
out_VirtualBlockMap:
325
    vfree(part->VirtualBlockMap);
326
out_XferInfo:
327
    kfree(part->XferInfo);
328
out_EUNInfo:
329
    kfree(part->EUNInfo);
330
out:
331
    return ret;
332
} /* build_maps */
333
 
334
/*======================================================================
335
 
336
    Erase_xfer() schedules an asynchronous erase operation for a
337
    transfer unit.
338
 
339
======================================================================*/
340
 
341
static int erase_xfer(partition_t *part,
342
                      u_int16_t xfernum)
343
{
344
    int ret;
345
    struct xfer_info_t *xfer;
346
    struct erase_info *erase;
347
 
348
    xfer = &part->XferInfo[xfernum];
349
    DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
350
    xfer->state = XFER_ERASING;
351
 
352
    /* Is there a free erase slot? Always in MTD. */
353
 
354
 
355
    erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
356
    if (!erase)
357
            return -ENOMEM;
358
 
359
    erase->mtd = part->mbd.mtd;
360
    erase->callback = ftl_erase_callback;
361
    erase->addr = xfer->Offset;
362
    erase->len = 1 << part->header.EraseUnitSize;
363
    erase->priv = (u_long)part;
364
 
365
    ret = part->mbd.mtd->erase(part->mbd.mtd, erase);
366
 
367
    if (!ret)
368
            xfer->EraseCount++;
369
    else
370
            kfree(erase);
371
 
372
    return ret;
373
} /* erase_xfer */
374
 
375
/*======================================================================
376
 
377
    Prepare_xfer() takes a freshly erased transfer unit and gives
378
    it an appropriate header.
379
 
380
======================================================================*/
381
 
382
static void ftl_erase_callback(struct erase_info *erase)
383
{
384
    partition_t *part;
385
    struct xfer_info_t *xfer;
386
    int i;
387
 
388
    /* Look up the transfer unit */
389
    part = (partition_t *)(erase->priv);
390
 
391
    for (i = 0; i < part->header.NumTransferUnits; i++)
392
        if (part->XferInfo[i].Offset == erase->addr) break;
393
 
394
    if (i == part->header.NumTransferUnits) {
395
        printk(KERN_NOTICE "ftl_cs: internal error: "
396
               "erase lookup failed!\n");
397
        return;
398
    }
399
 
400
    xfer = &part->XferInfo[i];
401
    if (erase->state == MTD_ERASE_DONE)
402
        xfer->state = XFER_ERASED;
403
    else {
404
        xfer->state = XFER_FAILED;
405
        printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n",
406
               erase->state);
407
    }
408
 
409
    kfree(erase);
410
 
411
} /* ftl_erase_callback */
412
 
413
static int prepare_xfer(partition_t *part, int i)
414
{
415
    erase_unit_header_t header;
416
    struct xfer_info_t *xfer;
417
    int nbam, ret;
418
    u_int32_t ctl;
419
    ssize_t retlen;
420
    loff_t offset;
421
 
422
    xfer = &part->XferInfo[i];
423
    xfer->state = XFER_FAILED;
424
 
425
    DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
426
 
427
    /* Write the transfer unit header */
428
    header = part->header;
429
    header.LogicalEUN = cpu_to_le16(0xffff);
430
    header.EraseCount = cpu_to_le32(xfer->EraseCount);
431
 
432
    ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset, sizeof(header),
433
                           &retlen, (u_char *)&header);
434
 
435
    if (ret) {
436
        return ret;
437
    }
438
 
439
    /* Write the BAM stub */
440
    nbam = (part->BlocksPerUnit * sizeof(u_int32_t) +
441
            le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
442
 
443
    offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
444
    ctl = cpu_to_le32(BLOCK_CONTROL);
445
 
446
    for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) {
447
 
448
        ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t),
449
                               &retlen, (u_char *)&ctl);
450
 
451
        if (ret)
452
            return ret;
453
    }
454
    xfer->state = XFER_PREPARED;
455
    return 0;
456
 
457
} /* prepare_xfer */
458
 
459
/*======================================================================
460
 
461
    Copy_erase_unit() takes a full erase block and a transfer unit,
462
    copies everything to the transfer unit, then swaps the block
463
    pointers.
464
 
465
    All data blocks are copied to the corresponding blocks in the
466
    target unit, so the virtual block map does not need to be
467
    updated.
468
 
469
======================================================================*/
470
 
471
static int copy_erase_unit(partition_t *part, u_int16_t srcunit,
472
                           u_int16_t xferunit)
473
{
474
    u_char buf[SECTOR_SIZE];
475
    struct eun_info_t *eun;
476
    struct xfer_info_t *xfer;
477
    u_int32_t src, dest, free, i;
478
    u_int16_t unit;
479
    int ret;
480
    ssize_t retlen;
481
    loff_t offset;
482
    u_int16_t srcunitswap = cpu_to_le16(srcunit);
483
 
484
    eun = &part->EUNInfo[srcunit];
485
    xfer = &part->XferInfo[xferunit];
486
    DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n",
487
          eun->Offset, xfer->Offset);
488
 
489
 
490
    /* Read current BAM */
491
    if (part->bam_index != srcunit) {
492
 
493
        offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
494
 
495
        ret = part->mbd.mtd->read(part->mbd.mtd, offset,
496
                              part->BlocksPerUnit * sizeof(u_int32_t),
497
                              &retlen, (u_char *) (part->bam_cache));
498
 
499
        /* mark the cache bad, in case we get an error later */
500
        part->bam_index = 0xffff;
501
 
502
        if (ret) {
503
            printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
504
            return ret;
505
        }
506
    }
507
 
508
    /* Write the LogicalEUN for the transfer unit */
509
    xfer->state = XFER_UNKNOWN;
510
    offset = xfer->Offset + 20; /* Bad! */
511
    unit = cpu_to_le16(0x7fff);
512
 
513
    ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t),
514
                           &retlen, (u_char *) &unit);
515
 
516
    if (ret) {
517
        printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
518
        return ret;
519
    }
520
 
521
    /* Copy all data blocks from source unit to transfer unit */
522
    src = eun->Offset; dest = xfer->Offset;
523
 
524
    free = 0;
525
    ret = 0;
526
    for (i = 0; i < part->BlocksPerUnit; i++) {
527
        switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
528
        case BLOCK_CONTROL:
529
            /* This gets updated later */
530
            break;
531
        case BLOCK_DATA:
532
        case BLOCK_REPLACEMENT:
533
            ret = part->mbd.mtd->read(part->mbd.mtd, src, SECTOR_SIZE,
534
                        &retlen, (u_char *) buf);
535
            if (ret) {
536
                printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
537
                return ret;
538
            }
539
 
540
 
541
            ret = part->mbd.mtd->write(part->mbd.mtd, dest, SECTOR_SIZE,
542
                        &retlen, (u_char *) buf);
543
            if (ret)  {
544
                printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
545
                return ret;
546
            }
547
 
548
            break;
549
        default:
550
            /* All other blocks must be free */
551
            part->bam_cache[i] = cpu_to_le32(0xffffffff);
552
            free++;
553
            break;
554
        }
555
        src += SECTOR_SIZE;
556
        dest += SECTOR_SIZE;
557
    }
558
 
559
    /* Write the BAM to the transfer unit */
560
    ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset),
561
                    part->BlocksPerUnit * sizeof(int32_t), &retlen,
562
                    (u_char *)part->bam_cache);
563
    if (ret) {
564
        printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
565
        return ret;
566
    }
567
 
568
 
569
    /* All clear? Then update the LogicalEUN again */
570
    ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t),
571
                           &retlen, (u_char *)&srcunitswap);
572
 
573
    if (ret) {
574
        printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
575
        return ret;
576
    }
577
 
578
 
579
    /* Update the maps and usage stats*/
580
    i = xfer->EraseCount;
581
    xfer->EraseCount = eun->EraseCount;
582
    eun->EraseCount = i;
583
    i = xfer->Offset;
584
    xfer->Offset = eun->Offset;
585
    eun->Offset = i;
586
    part->FreeTotal -= eun->Free;
587
    part->FreeTotal += free;
588
    eun->Free = free;
589
    eun->Deleted = 0;
590
 
591
    /* Now, the cache should be valid for the new block */
592
    part->bam_index = srcunit;
593
 
594
    return 0;
595
} /* copy_erase_unit */
596
 
597
/*======================================================================
598
 
599
    reclaim_block() picks a full erase unit and a transfer unit and
600
    then calls copy_erase_unit() to copy one to the other.  Then, it
601
    schedules an erase on the expired block.
602
 
603
    What's a good way to decide which transfer unit and which erase
604
    unit to use?  Beats me.  My way is to always pick the transfer
605
    unit with the fewest erases, and usually pick the data unit with
606
    the most deleted blocks.  But with a small probability, pick the
607
    oldest data unit instead.  This means that we generally postpone
608
    the next reclaimation as long as possible, but shuffle static
609
    stuff around a bit for wear leveling.
610
 
611
======================================================================*/
612
 
613
static int reclaim_block(partition_t *part)
614
{
615
    u_int16_t i, eun, xfer;
616
    u_int32_t best;
617
    int queued, ret;
618
 
619
    DEBUG(0, "ftl_cs: reclaiming space...\n");
620
    DEBUG(3, "NumTransferUnits == %x\n", part->header.NumTransferUnits);
621
    /* Pick the least erased transfer unit */
622
    best = 0xffffffff; xfer = 0xffff;
623
    do {
624
        queued = 0;
625
        for (i = 0; i < part->header.NumTransferUnits; i++) {
626
            int n=0;
627
            if (part->XferInfo[i].state == XFER_UNKNOWN) {
628
                DEBUG(3,"XferInfo[%d].state == XFER_UNKNOWN\n",i);
629
                n=1;
630
                erase_xfer(part, i);
631
            }
632
            if (part->XferInfo[i].state == XFER_ERASING) {
633
                DEBUG(3,"XferInfo[%d].state == XFER_ERASING\n",i);
634
                n=1;
635
                queued = 1;
636
            }
637
            else if (part->XferInfo[i].state == XFER_ERASED) {
638
                DEBUG(3,"XferInfo[%d].state == XFER_ERASED\n",i);
639
                n=1;
640
                prepare_xfer(part, i);
641
            }
642
            if (part->XferInfo[i].state == XFER_PREPARED) {
643
                DEBUG(3,"XferInfo[%d].state == XFER_PREPARED\n",i);
644
                n=1;
645
                if (part->XferInfo[i].EraseCount <= best) {
646
                    best = part->XferInfo[i].EraseCount;
647
                    xfer = i;
648
                }
649
            }
650
                if (!n)
651
                    DEBUG(3,"XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
652
 
653
        }
654
        if (xfer == 0xffff) {
655
            if (queued) {
656
                DEBUG(1, "ftl_cs: waiting for transfer "
657
                      "unit to be prepared...\n");
658
                if (part->mbd.mtd->sync)
659
                        part->mbd.mtd->sync(part->mbd.mtd);
660
            } else {
661
                static int ne = 0;
662
                if (++ne < 5)
663
                    printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
664
                           "suitable transfer units!\n");
665
                else
666
                    DEBUG(1, "ftl_cs: reclaim failed: no "
667
                          "suitable transfer units!\n");
668
 
669
                return -EIO;
670
            }
671
        }
672
    } while (xfer == 0xffff);
673
 
674
    eun = 0;
675
    if ((jiffies % shuffle_freq) == 0) {
676
        DEBUG(1, "ftl_cs: recycling freshest block...\n");
677
        best = 0xffffffff;
678
        for (i = 0; i < part->DataUnits; i++)
679
            if (part->EUNInfo[i].EraseCount <= best) {
680
                best = part->EUNInfo[i].EraseCount;
681
                eun = i;
682
            }
683
    } else {
684
        best = 0;
685
        for (i = 0; i < part->DataUnits; i++)
686
            if (part->EUNInfo[i].Deleted >= best) {
687
                best = part->EUNInfo[i].Deleted;
688
                eun = i;
689
            }
690
        if (best == 0) {
691
            static int ne = 0;
692
            if (++ne < 5)
693
                printk(KERN_NOTICE "ftl_cs: reclaim failed: "
694
                       "no free blocks!\n");
695
            else
696
                DEBUG(1,"ftl_cs: reclaim failed: "
697
                       "no free blocks!\n");
698
 
699
            return -EIO;
700
        }
701
    }
702
    ret = copy_erase_unit(part, eun, xfer);
703
    if (!ret)
704
        erase_xfer(part, xfer);
705
    else
706
        printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
707
    return ret;
708
} /* reclaim_block */
709
 
710
/*======================================================================
711
 
712
    Find_free() searches for a free block.  If necessary, it updates
713
    the BAM cache for the erase unit containing the free block.  It
714
    returns the block index -- the erase unit is just the currently
715
    cached unit.  If there are no free blocks, it returns 0 -- this
716
    is never a valid data block because it contains the header.
717
 
718
======================================================================*/
719
 
720
#ifdef PSYCHO_DEBUG
721
static void dump_lists(partition_t *part)
722
{
723
    int i;
724
    printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
725
    for (i = 0; i < part->DataUnits; i++)
726
        printk(KERN_DEBUG "ftl_cs:   unit %d: %d phys, %d free, "
727
               "%d deleted\n", i,
728
               part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
729
               part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
730
}
731
#endif
732
 
733
static u_int32_t find_free(partition_t *part)
734
{
735
    u_int16_t stop, eun;
736
    u_int32_t blk;
737
    size_t retlen;
738
    int ret;
739
 
740
    /* Find an erase unit with some free space */
741
    stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
742
    eun = stop;
743
    do {
744
        if (part->EUNInfo[eun].Free != 0) break;
745
        /* Wrap around at end of table */
746
        if (++eun == part->DataUnits) eun = 0;
747
    } while (eun != stop);
748
 
749
    if (part->EUNInfo[eun].Free == 0)
750
        return 0;
751
 
752
    /* Is this unit's BAM cached? */
753
    if (eun != part->bam_index) {
754
        /* Invalidate cache */
755
        part->bam_index = 0xffff;
756
 
757
        ret = part->mbd.mtd->read(part->mbd.mtd,
758
                       part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
759
                       part->BlocksPerUnit * sizeof(u_int32_t),
760
                       &retlen, (u_char *) (part->bam_cache));
761
 
762
        if (ret) {
763
            printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
764
            return 0;
765
        }
766
        part->bam_index = eun;
767
    }
768
 
769
    /* Find a free block */
770
    for (blk = 0; blk < part->BlocksPerUnit; blk++)
771
        if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
772
    if (blk == part->BlocksPerUnit) {
773
#ifdef PSYCHO_DEBUG
774
        static int ne = 0;
775
        if (++ne == 1)
776
            dump_lists(part);
777
#endif
778
        printk(KERN_NOTICE "ftl_cs: bad free list!\n");
779
        return 0;
780
    }
781
    DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk, eun);
782
    return blk;
783
 
784
} /* find_free */
785
 
786
 
787
/*======================================================================
788
 
789
    Read a series of sectors from an FTL partition.
790
 
791
======================================================================*/
792
 
793
static int ftl_read(partition_t *part, caddr_t buffer,
794
                    u_long sector, u_long nblocks)
795
{
796
    u_int32_t log_addr, bsize;
797
    u_long i;
798
    int ret;
799
    size_t offset, retlen;
800
 
801
    DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
802
          part, sector, nblocks);
803
    if (!(part->state & FTL_FORMATTED)) {
804
        printk(KERN_NOTICE "ftl_cs: bad partition\n");
805
        return -EIO;
806
    }
807
    bsize = 1 << part->header.EraseUnitSize;
808
 
809
    for (i = 0; i < nblocks; i++) {
810
        if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
811
            printk(KERN_NOTICE "ftl_cs: bad read offset\n");
812
            return -EIO;
813
        }
814
        log_addr = part->VirtualBlockMap[sector+i];
815
        if (log_addr == 0xffffffff)
816
            memset(buffer, 0, SECTOR_SIZE);
817
        else {
818
            offset = (part->EUNInfo[log_addr / bsize].Offset
819
                          + (log_addr % bsize));
820
            ret = part->mbd.mtd->read(part->mbd.mtd, offset, SECTOR_SIZE,
821
                           &retlen, (u_char *) buffer);
822
 
823
            if (ret) {
824
                printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
825
                return ret;
826
            }
827
        }
828
        buffer += SECTOR_SIZE;
829
    }
830
    return 0;
831
} /* ftl_read */
832
 
833
/*======================================================================
834
 
835
    Write a series of sectors to an FTL partition
836
 
837
======================================================================*/
838
 
839
static int set_bam_entry(partition_t *part, u_int32_t log_addr,
840
                         u_int32_t virt_addr)
841
{
842
    u_int32_t bsize, blk, le_virt_addr;
843
#ifdef PSYCHO_DEBUG
844
    u_int32_t old_addr;
845
#endif
846
    u_int16_t eun;
847
    int ret;
848
    size_t retlen, offset;
849
 
850
    DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
851
          part, log_addr, virt_addr);
852
    bsize = 1 << part->header.EraseUnitSize;
853
    eun = log_addr / bsize;
854
    blk = (log_addr % bsize) / SECTOR_SIZE;
855
    offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int32_t) +
856
                  le32_to_cpu(part->header.BAMOffset));
857
 
858
#ifdef PSYCHO_DEBUG
859
    ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t),
860
                        &retlen, (u_char *)&old_addr);
861
    if (ret) {
862
        printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
863
        return ret;
864
    }
865
    old_addr = le32_to_cpu(old_addr);
866
 
867
    if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
868
        ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
869
        (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
870
        static int ne = 0;
871
        if (++ne < 5) {
872
            printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
873
            printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, old = 0x%x"
874
                   ", new = 0x%x\n", log_addr, old_addr, virt_addr);
875
        }
876
        return -EIO;
877
    }
878
#endif
879
    le_virt_addr = cpu_to_le32(virt_addr);
880
    if (part->bam_index == eun) {
881
#ifdef PSYCHO_DEBUG
882
        if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
883
            static int ne = 0;
884
            if (++ne < 5) {
885
                printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
886
                       "inconsistency!\n");
887
                printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, cache"
888
                       " = 0x%x\n",
889
                       le32_to_cpu(part->bam_cache[blk]), old_addr);
890
            }
891
            return -EIO;
892
        }
893
#endif
894
        part->bam_cache[blk] = le_virt_addr;
895
    }
896
    ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t),
897
                            &retlen, (u_char *)&le_virt_addr);
898
 
899
    if (ret) {
900
        printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
901
        printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, new = 0x%x\n",
902
               log_addr, virt_addr);
903
    }
904
    return ret;
905
} /* set_bam_entry */
906
 
907
static int ftl_write(partition_t *part, caddr_t buffer,
908
                     u_long sector, u_long nblocks)
909
{
910
    u_int32_t bsize, log_addr, virt_addr, old_addr, blk;
911
    u_long i;
912
    int ret;
913
    size_t retlen, offset;
914
 
915
    DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
916
          part, sector, nblocks);
917
    if (!(part->state & FTL_FORMATTED)) {
918
        printk(KERN_NOTICE "ftl_cs: bad partition\n");
919
        return -EIO;
920
    }
921
    /* See if we need to reclaim space, before we start */
922
    while (part->FreeTotal < nblocks) {
923
        ret = reclaim_block(part);
924
        if (ret)
925
            return ret;
926
    }
927
 
928
    bsize = 1 << part->header.EraseUnitSize;
929
 
930
    virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
931
    for (i = 0; i < nblocks; i++) {
932
        if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
933
            printk(KERN_NOTICE "ftl_cs: bad write offset\n");
934
            return -EIO;
935
        }
936
 
937
        /* Grab a free block */
938
        blk = find_free(part);
939
        if (blk == 0) {
940
            static int ne = 0;
941
            if (++ne < 5)
942
                printk(KERN_NOTICE "ftl_cs: internal error: "
943
                       "no free blocks!\n");
944
            return -ENOSPC;
945
        }
946
 
947
        /* Tag the BAM entry, and write the new block */
948
        log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
949
        part->EUNInfo[part->bam_index].Free--;
950
        part->FreeTotal--;
951
        if (set_bam_entry(part, log_addr, 0xfffffffe))
952
            return -EIO;
953
        part->EUNInfo[part->bam_index].Deleted++;
954
        offset = (part->EUNInfo[part->bam_index].Offset +
955
                      blk * SECTOR_SIZE);
956
        ret = part->mbd.mtd->write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
957
                                     buffer);
958
 
959
        if (ret) {
960
            printk(KERN_NOTICE "ftl_cs: block write failed!\n");
961
            printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, virt_addr"
962
                   " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
963
                   offset);
964
            return -EIO;
965
        }
966
 
967
        /* Only delete the old entry when the new entry is ready */
968
        old_addr = part->VirtualBlockMap[sector+i];
969
        if (old_addr != 0xffffffff) {
970
            part->VirtualBlockMap[sector+i] = 0xffffffff;
971
            part->EUNInfo[old_addr/bsize].Deleted++;
972
            if (set_bam_entry(part, old_addr, 0))
973
                return -EIO;
974
        }
975
 
976
        /* Finally, set up the new pointers */
977
        if (set_bam_entry(part, log_addr, virt_addr))
978
            return -EIO;
979
        part->VirtualBlockMap[sector+i] = log_addr;
980
        part->EUNInfo[part->bam_index].Deleted--;
981
 
982
        buffer += SECTOR_SIZE;
983
        virt_addr += SECTOR_SIZE;
984
    }
985
    return 0;
986
} /* ftl_write */
987
 
988
static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
989
{
990
        partition_t *part = (void *)dev;
991
        u_long sect;
992
 
993
        /* Sort of arbitrary: round size down to 4KiB boundary */
994
        sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
995
 
996
        geo->heads = 1;
997
        geo->sectors = 8;
998
        geo->cylinders = sect >> 3;
999
 
1000
        return 0;
1001
}
1002
 
1003
static int ftl_readsect(struct mtd_blktrans_dev *dev,
1004
                              unsigned long block, char *buf)
1005
{
1006
        return ftl_read((void *)dev, buf, block, 1);
1007
}
1008
 
1009
static int ftl_writesect(struct mtd_blktrans_dev *dev,
1010
                              unsigned long block, char *buf)
1011
{
1012
        return ftl_write((void *)dev, buf, block, 1);
1013
}
1014
 
1015
/*====================================================================*/
1016
 
1017
void ftl_freepart(partition_t *part)
1018
{
1019
        vfree(part->VirtualBlockMap);
1020
        part->VirtualBlockMap = NULL;
1021
        kfree(part->VirtualPageMap);
1022
        part->VirtualPageMap = NULL;
1023
        kfree(part->EUNInfo);
1024
        part->EUNInfo = NULL;
1025
        kfree(part->XferInfo);
1026
        part->XferInfo = NULL;
1027
        kfree(part->bam_cache);
1028
        part->bam_cache = NULL;
1029
} /* ftl_freepart */
1030
 
1031
static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1032
{
1033
        partition_t *partition;
1034
 
1035
        partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
1036
 
1037
        if (!partition) {
1038
                printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1039
                       mtd->name);
1040
                return;
1041
        }
1042
 
1043
        partition->mbd.mtd = mtd;
1044
 
1045
        if ((scan_header(partition) == 0) &&
1046
            (build_maps(partition) == 0)) {
1047
 
1048
                partition->state = FTL_FORMATTED;
1049
#ifdef PCMCIA_DEBUG
1050
                printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1051
                       le32_to_cpu(partition->header.FormattedSize) >> 10);
1052
#endif
1053
                partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
1054
 
1055
                partition->mbd.tr = tr;
1056
                partition->mbd.devnum = -1;
1057
                if (!add_mtd_blktrans_dev((void *)partition))
1058
                        return;
1059
        }
1060
 
1061
        ftl_freepart(partition);
1062
        kfree(partition);
1063
}
1064
 
1065
static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1066
{
1067
        del_mtd_blktrans_dev(dev);
1068
        ftl_freepart((partition_t *)dev);
1069
        kfree(dev);
1070
}
1071
 
1072
struct mtd_blktrans_ops ftl_tr = {
1073
        .name           = "ftl",
1074
        .major          = FTL_MAJOR,
1075
        .part_bits      = PART_BITS,
1076
        .blksize        = SECTOR_SIZE,
1077
        .readsect       = ftl_readsect,
1078
        .writesect      = ftl_writesect,
1079
        .getgeo         = ftl_getgeo,
1080
        .add_mtd        = ftl_add_mtd,
1081
        .remove_dev     = ftl_remove_dev,
1082
        .owner          = THIS_MODULE,
1083
};
1084
 
1085
static int init_ftl(void)
1086
{
1087
        DEBUG(0, "$Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $\n");
1088
 
1089
        return register_mtd_blktrans(&ftl_tr);
1090
}
1091
 
1092
static void __exit cleanup_ftl(void)
1093
{
1094
        deregister_mtd_blktrans(&ftl_tr);
1095
}
1096
 
1097
module_init(init_ftl);
1098
module_exit(cleanup_ftl);
1099
 
1100
 
1101
MODULE_LICENSE("Dual MPL/GPL");
1102
MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1103
MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.