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

Subversion Repositories test_project

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/* Linux driver for NAND Flash Translation Layer      */
2
/* (c) 1999 Machine Vision Holdings, Inc.             */
3
/* Author: David Woodhouse <dwmw2@infradead.org>      */
4
/* $Id: nftlcore.c,v 1.98 2005/11/07 11:14:21 gleixner Exp $ */
5
 
6
/*
7
  The contents of this file are distributed under the GNU General
8
  Public License version 2. The author places no additional
9
  restrictions of any kind on it.
10
 */
11
 
12
#define PRERELEASE
13
 
14
#include <linux/kernel.h>
15
#include <linux/module.h>
16
#include <asm/errno.h>
17
#include <asm/io.h>
18
#include <asm/uaccess.h>
19
#include <linux/miscdevice.h>
20
#include <linux/delay.h>
21
#include <linux/slab.h>
22
#include <linux/init.h>
23
#include <linux/hdreg.h>
24
 
25
#include <linux/kmod.h>
26
#include <linux/mtd/mtd.h>
27
#include <linux/mtd/nand.h>
28
#include <linux/mtd/nftl.h>
29
#include <linux/mtd/blktrans.h>
30
 
31
/* maximum number of loops while examining next block, to have a
32
   chance to detect consistency problems (they should never happen
33
   because of the checks done in the mounting */
34
 
35
#define MAX_LOOPS 10000
36
 
37
 
38
static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
39
{
40
        struct NFTLrecord *nftl;
41
        unsigned long temp;
42
 
43
        if (mtd->type != MTD_NANDFLASH)
44
                return;
45
        /* OK, this is moderately ugly.  But probably safe.  Alternatives? */
46
        if (memcmp(mtd->name, "DiskOnChip", 10))
47
                return;
48
 
49
        if (!mtd->block_isbad) {
50
                printk(KERN_ERR
51
"NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
52
"Please use the new diskonchip driver under the NAND subsystem.\n");
53
                return;
54
        }
55
 
56
        DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
57
 
58
        nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
59
 
60
        if (!nftl) {
61
                printk(KERN_WARNING "NFTL: out of memory for data structures\n");
62
                return;
63
        }
64
 
65
        nftl->mbd.mtd = mtd;
66
        nftl->mbd.devnum = -1;
67
 
68
        nftl->mbd.tr = tr;
69
 
70
        if (NFTL_mount(nftl) < 0) {
71
                printk(KERN_WARNING "NFTL: could not mount device\n");
72
                kfree(nftl);
73
                return;
74
        }
75
 
76
        /* OK, it's a new one. Set up all the data structures. */
77
 
78
        /* Calculate geometry */
79
        nftl->cylinders = 1024;
80
        nftl->heads = 16;
81
 
82
        temp = nftl->cylinders * nftl->heads;
83
        nftl->sectors = nftl->mbd.size / temp;
84
        if (nftl->mbd.size % temp) {
85
                nftl->sectors++;
86
                temp = nftl->cylinders * nftl->sectors;
87
                nftl->heads = nftl->mbd.size / temp;
88
 
89
                if (nftl->mbd.size % temp) {
90
                        nftl->heads++;
91
                        temp = nftl->heads * nftl->sectors;
92
                        nftl->cylinders = nftl->mbd.size / temp;
93
                }
94
        }
95
 
96
        if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
97
                /*
98
                  Oh no we don't have
99
                   mbd.size == heads * cylinders * sectors
100
                */
101
                printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
102
                       "match size of 0x%lx.\n", nftl->mbd.size);
103
                printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
104
                        "(== 0x%lx sects)\n",
105
                        nftl->cylinders, nftl->heads , nftl->sectors,
106
                        (long)nftl->cylinders * (long)nftl->heads *
107
                        (long)nftl->sectors );
108
        }
109
 
110
        if (add_mtd_blktrans_dev(&nftl->mbd)) {
111
                kfree(nftl->ReplUnitTable);
112
                kfree(nftl->EUNtable);
113
                kfree(nftl);
114
                return;
115
        }
116
#ifdef PSYCHO_DEBUG
117
        printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
118
#endif
119
}
120
 
121
static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
122
{
123
        struct NFTLrecord *nftl = (void *)dev;
124
 
125
        DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum);
126
 
127
        del_mtd_blktrans_dev(dev);
128
        kfree(nftl->ReplUnitTable);
129
        kfree(nftl->EUNtable);
130
        kfree(nftl);
131
}
132
 
133
/*
134
 * Read oob data from flash
135
 */
136
int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
137
                  size_t *retlen, uint8_t *buf)
138
{
139
        struct mtd_oob_ops ops;
140
        int res;
141
 
142
        ops.mode = MTD_OOB_PLACE;
143
        ops.ooboffs = offs & (mtd->writesize - 1);
144
        ops.ooblen = len;
145
        ops.oobbuf = buf;
146
        ops.datbuf = NULL;
147
 
148
        res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
149
        *retlen = ops.oobretlen;
150
        return res;
151
}
152
 
153
/*
154
 * Write oob data to flash
155
 */
156
int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
157
                   size_t *retlen, uint8_t *buf)
158
{
159
        struct mtd_oob_ops ops;
160
        int res;
161
 
162
        ops.mode = MTD_OOB_PLACE;
163
        ops.ooboffs = offs & (mtd->writesize - 1);
164
        ops.ooblen = len;
165
        ops.oobbuf = buf;
166
        ops.datbuf = NULL;
167
 
168
        res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
169
        *retlen = ops.oobretlen;
170
        return res;
171
}
172
 
173
#ifdef CONFIG_NFTL_RW
174
 
175
/*
176
 * Write data and oob to flash
177
 */
178
static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
179
                      size_t *retlen, uint8_t *buf, uint8_t *oob)
180
{
181
        struct mtd_oob_ops ops;
182
        int res;
183
 
184
        ops.mode = MTD_OOB_PLACE;
185
        ops.ooboffs = offs;
186
        ops.ooblen = mtd->oobsize;
187
        ops.oobbuf = oob;
188
        ops.datbuf = buf;
189
        ops.len = len;
190
 
191
        res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
192
        *retlen = ops.retlen;
193
        return res;
194
}
195
 
196
/* Actual NFTL access routines */
197
/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
198
 *      when the give Virtual Unit Chain
199
 */
200
static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
201
{
202
        /* For a given Virtual Unit Chain: find or create a free block and
203
           add it to the chain */
204
        /* We're passed the number of the last EUN in the chain, to save us from
205
           having to look it up again */
206
        u16 pot = nftl->LastFreeEUN;
207
        int silly = nftl->nb_blocks;
208
 
209
        /* Normally, we force a fold to happen before we run out of free blocks completely */
210
        if (!desperate && nftl->numfreeEUNs < 2) {
211
                DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
212
                return 0xffff;
213
        }
214
 
215
        /* Scan for a free block */
216
        do {
217
                if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
218
                        nftl->LastFreeEUN = pot;
219
                        nftl->numfreeEUNs--;
220
                        return pot;
221
                }
222
 
223
                /* This will probably point to the MediaHdr unit itself,
224
                   right at the beginning of the partition. But that unit
225
                   (and the backup unit too) should have the UCI set
226
                   up so that it's not selected for overwriting */
227
                if (++pot > nftl->lastEUN)
228
                        pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
229
 
230
                if (!silly--) {
231
                        printk("Argh! No free blocks found! LastFreeEUN = %d, "
232
                               "FirstEUN = %d\n", nftl->LastFreeEUN,
233
                               le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
234
                        return 0xffff;
235
                }
236
        } while (pot != nftl->LastFreeEUN);
237
 
238
        return 0xffff;
239
}
240
 
241
static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
242
{
243
        struct mtd_info *mtd = nftl->mbd.mtd;
244
        u16 BlockMap[MAX_SECTORS_PER_UNIT];
245
        unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
246
        unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
247
        unsigned int thisEUN;
248
        int block;
249
        int silly;
250
        unsigned int targetEUN;
251
        struct nftl_oob oob;
252
        int inplace = 1;
253
        size_t retlen;
254
 
255
        memset(BlockMap, 0xff, sizeof(BlockMap));
256
        memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
257
 
258
        thisEUN = nftl->EUNtable[thisVUC];
259
 
260
        if (thisEUN == BLOCK_NIL) {
261
                printk(KERN_WARNING "Trying to fold non-existent "
262
                       "Virtual Unit Chain %d!\n", thisVUC);
263
                return BLOCK_NIL;
264
        }
265
 
266
        /* Scan to find the Erase Unit which holds the actual data for each
267
           512-byte block within the Chain.
268
        */
269
        silly = MAX_LOOPS;
270
        targetEUN = BLOCK_NIL;
271
        while (thisEUN <= nftl->lastEUN ) {
272
                unsigned int status, foldmark;
273
 
274
                targetEUN = thisEUN;
275
                for (block = 0; block < nftl->EraseSize / 512; block ++) {
276
                        nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
277
                                      (block * 512), 16 , &retlen,
278
                                      (char *)&oob);
279
                        if (block == 2) {
280
                                foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
281
                                if (foldmark == FOLD_MARK_IN_PROGRESS) {
282
                                        DEBUG(MTD_DEBUG_LEVEL1,
283
                                              "Write Inhibited on EUN %d\n", thisEUN);
284
                                        inplace = 0;
285
                                } else {
286
                                        /* There's no other reason not to do inplace,
287
                                           except ones that come later. So we don't need
288
                                           to preserve inplace */
289
                                        inplace = 1;
290
                                }
291
                        }
292
                        status = oob.b.Status | oob.b.Status1;
293
                        BlockLastState[block] = status;
294
 
295
                        switch(status) {
296
                        case SECTOR_FREE:
297
                                BlockFreeFound[block] = 1;
298
                                break;
299
 
300
                        case SECTOR_USED:
301
                                if (!BlockFreeFound[block])
302
                                        BlockMap[block] = thisEUN;
303
                                else
304
                                        printk(KERN_WARNING
305
                                               "SECTOR_USED found after SECTOR_FREE "
306
                                               "in Virtual Unit Chain %d for block %d\n",
307
                                               thisVUC, block);
308
                                break;
309
                        case SECTOR_DELETED:
310
                                if (!BlockFreeFound[block])
311
                                        BlockMap[block] = BLOCK_NIL;
312
                                else
313
                                        printk(KERN_WARNING
314
                                               "SECTOR_DELETED found after SECTOR_FREE "
315
                                               "in Virtual Unit Chain %d for block %d\n",
316
                                               thisVUC, block);
317
                                break;
318
 
319
                        case SECTOR_IGNORE:
320
                                break;
321
                        default:
322
                                printk("Unknown status for block %d in EUN %d: %x\n",
323
                                       block, thisEUN, status);
324
                        }
325
                }
326
 
327
                if (!silly--) {
328
                        printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
329
                               thisVUC);
330
                        return BLOCK_NIL;
331
                }
332
 
333
                thisEUN = nftl->ReplUnitTable[thisEUN];
334
        }
335
 
336
        if (inplace) {
337
                /* We're being asked to be a fold-in-place. Check
338
                   that all blocks which actually have data associated
339
                   with them (i.e. BlockMap[block] != BLOCK_NIL) are
340
                   either already present or SECTOR_FREE in the target
341
                   block. If not, we're going to have to fold out-of-place
342
                   anyway.
343
                */
344
                for (block = 0; block < nftl->EraseSize / 512 ; block++) {
345
                        if (BlockLastState[block] != SECTOR_FREE &&
346
                            BlockMap[block] != BLOCK_NIL &&
347
                            BlockMap[block] != targetEUN) {
348
                                DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
349
                                      "block %d was %x lastEUN, "
350
                                      "and is in EUN %d (%s) %d\n",
351
                                      thisVUC, block, BlockLastState[block],
352
                                      BlockMap[block],
353
                                      BlockMap[block]== targetEUN ? "==" : "!=",
354
                                      targetEUN);
355
                                inplace = 0;
356
                                break;
357
                        }
358
                }
359
 
360
                if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
361
                    pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
362
                    BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
363
                    SECTOR_FREE) {
364
                        DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
365
                              "Folding out of place.\n", targetEUN);
366
                        inplace = 0;
367
                }
368
        }
369
 
370
        if (!inplace) {
371
                DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
372
                      "Trying out-of-place\n", thisVUC);
373
                /* We need to find a targetEUN to fold into. */
374
                targetEUN = NFTL_findfreeblock(nftl, 1);
375
                if (targetEUN == BLOCK_NIL) {
376
                        /* Ouch. Now we're screwed. We need to do a
377
                           fold-in-place of another chain to make room
378
                           for this one. We need a better way of selecting
379
                           which chain to fold, because makefreeblock will
380
                           only ask us to fold the same one again.
381
                        */
382
                        printk(KERN_WARNING
383
                               "NFTL_findfreeblock(desperate) returns 0xffff.\n");
384
                        return BLOCK_NIL;
385
                }
386
        } else {
387
                /* We put a fold mark in the chain we are folding only if we
388
               fold in place to help the mount check code. If we do not fold in
389
               place, it is possible to find the valid chain by selecting the
390
               longer one */
391
                oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
392
                oob.u.c.unused = 0xffffffff;
393
                nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
394
                               8, &retlen, (char *)&oob.u);
395
        }
396
 
397
        /* OK. We now know the location of every block in the Virtual Unit Chain,
398
           and the Erase Unit into which we are supposed to be copying.
399
           Go for it.
400
        */
401
        DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
402
        for (block = 0; block < nftl->EraseSize / 512 ; block++) {
403
                unsigned char movebuf[512];
404
                int ret;
405
 
406
                /* If it's in the target EUN already, or if it's pending write, do nothing */
407
                if (BlockMap[block] == targetEUN ||
408
                    (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
409
                        continue;
410
                }
411
 
412
                /* copy only in non free block (free blocks can only
413
                   happen in case of media errors or deleted blocks) */
414
                if (BlockMap[block] == BLOCK_NIL)
415
                        continue;
416
 
417
                ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
418
                                512, &retlen, movebuf);
419
                if (ret < 0 && ret != -EUCLEAN) {
420
                        ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block])
421
                                        + (block * 512), 512, &retlen,
422
                                        movebuf);
423
                        if (ret != -EIO)
424
                                printk("Error went away on retry.\n");
425
                }
426
                memset(&oob, 0xff, sizeof(struct nftl_oob));
427
                oob.b.Status = oob.b.Status1 = SECTOR_USED;
428
 
429
                nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
430
                           (block * 512), 512, &retlen, movebuf, (char *)&oob);
431
        }
432
 
433
        /* add the header so that it is now a valid chain */
434
        oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
435
        oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
436
 
437
        nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
438
                       8, &retlen, (char *)&oob.u);
439
 
440
        /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
441
 
442
        /* At this point, we have two different chains for this Virtual Unit, and no way to tell
443
           them apart. If we crash now, we get confused. However, both contain the same data, so we
444
           shouldn't actually lose data in this case. It's just that when we load up on a medium which
445
           has duplicate chains, we need to free one of the chains because it's not necessary any more.
446
        */
447
        thisEUN = nftl->EUNtable[thisVUC];
448
        DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
449
 
450
        /* For each block in the old chain (except the targetEUN of course),
451
           free it and make it available for future use */
452
        while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
453
                unsigned int EUNtmp;
454
 
455
                EUNtmp = nftl->ReplUnitTable[thisEUN];
456
 
457
                if (NFTL_formatblock(nftl, thisEUN) < 0) {
458
                        /* could not erase : mark block as reserved
459
                         */
460
                        nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
461
                } else {
462
                        /* correctly erased : mark it as free */
463
                        nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
464
                        nftl->numfreeEUNs++;
465
                }
466
                thisEUN = EUNtmp;
467
        }
468
 
469
        /* Make this the new start of chain for thisVUC */
470
        nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
471
        nftl->EUNtable[thisVUC] = targetEUN;
472
 
473
        return targetEUN;
474
}
475
 
476
static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
477
{
478
        /* This is the part that needs some cleverness applied.
479
           For now, I'm doing the minimum applicable to actually
480
           get the thing to work.
481
           Wear-levelling and other clever stuff needs to be implemented
482
           and we also need to do some assessment of the results when
483
           the system loses power half-way through the routine.
484
        */
485
        u16 LongestChain = 0;
486
        u16 ChainLength = 0, thislen;
487
        u16 chain, EUN;
488
 
489
        for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
490
                EUN = nftl->EUNtable[chain];
491
                thislen = 0;
492
 
493
                while (EUN <= nftl->lastEUN) {
494
                        thislen++;
495
                        //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
496
                        EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
497
                        if (thislen > 0xff00) {
498
                                printk("Endless loop in Virtual Chain %d: Unit %x\n",
499
                                       chain, EUN);
500
                        }
501
                        if (thislen > 0xff10) {
502
                                /* Actually, don't return failure. Just ignore this chain and
503
                                   get on with it. */
504
                                thislen = 0;
505
                                break;
506
                        }
507
                }
508
 
509
                if (thislen > ChainLength) {
510
                        //printk("New longest chain is %d with length %d\n", chain, thislen);
511
                        ChainLength = thislen;
512
                        LongestChain = chain;
513
                }
514
        }
515
 
516
        if (ChainLength < 2) {
517
                printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
518
                       "Failing request\n");
519
                return 0xffff;
520
        }
521
 
522
        return NFTL_foldchain (nftl, LongestChain, pendingblock);
523
}
524
 
525
/* NFTL_findwriteunit: Return the unit number into which we can write
526
                       for this block. Make it available if it isn't already
527
*/
528
static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
529
{
530
        u16 lastEUN;
531
        u16 thisVUC = block / (nftl->EraseSize / 512);
532
        struct mtd_info *mtd = nftl->mbd.mtd;
533
        unsigned int writeEUN;
534
        unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
535
        size_t retlen;
536
        int silly, silly2 = 3;
537
        struct nftl_oob oob;
538
 
539
        do {
540
                /* Scan the media to find a unit in the VUC which has
541
                   a free space for the block in question.
542
                */
543
 
544
                /* This condition catches the 0x[7f]fff cases, as well as
545
                   being a sanity check for past-end-of-media access
546
                */
547
                lastEUN = BLOCK_NIL;
548
                writeEUN = nftl->EUNtable[thisVUC];
549
                silly = MAX_LOOPS;
550
                while (writeEUN <= nftl->lastEUN) {
551
                        struct nftl_bci bci;
552
                        size_t retlen;
553
                        unsigned int status;
554
 
555
                        lastEUN = writeEUN;
556
 
557
                        nftl_read_oob(mtd,
558
                                      (writeEUN * nftl->EraseSize) + blockofs,
559
                                      8, &retlen, (char *)&bci);
560
 
561
                        DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
562
                              block , writeEUN, le16_to_cpu(bci.Status));
563
 
564
                        status = bci.Status | bci.Status1;
565
                        switch(status) {
566
                        case SECTOR_FREE:
567
                                return writeEUN;
568
 
569
                        case SECTOR_DELETED:
570
                        case SECTOR_USED:
571
                        case SECTOR_IGNORE:
572
                                break;
573
                        default:
574
                                // Invalid block. Don't use it any more. Must implement.
575
                                break;
576
                        }
577
 
578
                        if (!silly--) {
579
                                printk(KERN_WARNING
580
                                       "Infinite loop in Virtual Unit Chain 0x%x\n",
581
                                       thisVUC);
582
                                return 0xffff;
583
                        }
584
 
585
                        /* Skip to next block in chain */
586
                        writeEUN = nftl->ReplUnitTable[writeEUN];
587
                }
588
 
589
                /* OK. We didn't find one in the existing chain, or there
590
                   is no existing chain. */
591
 
592
                /* Try to find an already-free block */
593
                writeEUN = NFTL_findfreeblock(nftl, 0);
594
 
595
                if (writeEUN == BLOCK_NIL) {
596
                        /* That didn't work - there were no free blocks just
597
                           waiting to be picked up. We're going to have to fold
598
                           a chain to make room.
599
                        */
600
 
601
                        /* First remember the start of this chain */
602
                        //u16 startEUN = nftl->EUNtable[thisVUC];
603
 
604
                        //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
605
                        writeEUN = NFTL_makefreeblock(nftl, 0xffff);
606
 
607
                        if (writeEUN == BLOCK_NIL) {
608
                                /* OK, we accept that the above comment is
609
                                   lying - there may have been free blocks
610
                                   last time we called NFTL_findfreeblock(),
611
                                   but they are reserved for when we're
612
                                   desperate. Well, now we're desperate.
613
                                */
614
                                DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
615
                                writeEUN = NFTL_findfreeblock(nftl, 1);
616
                        }
617
                        if (writeEUN == BLOCK_NIL) {
618
                                /* Ouch. This should never happen - we should
619
                                   always be able to make some room somehow.
620
                                   If we get here, we've allocated more storage
621
                                   space than actual media, or our makefreeblock
622
                                   routine is missing something.
623
                                */
624
                                printk(KERN_WARNING "Cannot make free space.\n");
625
                                return BLOCK_NIL;
626
                        }
627
                        //printk("Restarting scan\n");
628
                        lastEUN = BLOCK_NIL;
629
                        continue;
630
                }
631
 
632
                /* We've found a free block. Insert it into the chain. */
633
 
634
                if (lastEUN != BLOCK_NIL) {
635
                        thisVUC |= 0x8000; /* It's a replacement block */
636
                } else {
637
                        /* The first block in a new chain */
638
                        nftl->EUNtable[thisVUC] = writeEUN;
639
                }
640
 
641
                /* set up the actual EUN we're writing into */
642
                /* Both in our cache... */
643
                nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
644
 
645
                /* ... and on the flash itself */
646
                nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
647
                              &retlen, (char *)&oob.u);
648
 
649
                oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
650
 
651
                nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
652
                               &retlen, (char *)&oob.u);
653
 
654
                /* we link the new block to the chain only after the
655
                   block is ready. It avoids the case where the chain
656
                   could point to a free block */
657
                if (lastEUN != BLOCK_NIL) {
658
                        /* Both in our cache... */
659
                        nftl->ReplUnitTable[lastEUN] = writeEUN;
660
                        /* ... and on the flash itself */
661
                        nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
662
                                      8, &retlen, (char *)&oob.u);
663
 
664
                        oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
665
                                = cpu_to_le16(writeEUN);
666
 
667
                        nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
668
                                       8, &retlen, (char *)&oob.u);
669
                }
670
 
671
                return writeEUN;
672
 
673
        } while (silly2--);
674
 
675
        printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
676
               thisVUC);
677
        return 0xffff;
678
}
679
 
680
static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
681
                           char *buffer)
682
{
683
        struct NFTLrecord *nftl = (void *)mbd;
684
        u16 writeEUN;
685
        unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
686
        size_t retlen;
687
        struct nftl_oob oob;
688
 
689
        writeEUN = NFTL_findwriteunit(nftl, block);
690
 
691
        if (writeEUN == BLOCK_NIL) {
692
                printk(KERN_WARNING
693
                       "NFTL_writeblock(): Cannot find block to write to\n");
694
                /* If we _still_ haven't got a block to use, we're screwed */
695
                return 1;
696
        }
697
 
698
        memset(&oob, 0xff, sizeof(struct nftl_oob));
699
        oob.b.Status = oob.b.Status1 = SECTOR_USED;
700
 
701
        nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
702
                   512, &retlen, (char *)buffer, (char *)&oob);
703
        return 0;
704
}
705
#endif /* CONFIG_NFTL_RW */
706
 
707
static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
708
                          char *buffer)
709
{
710
        struct NFTLrecord *nftl = (void *)mbd;
711
        struct mtd_info *mtd = nftl->mbd.mtd;
712
        u16 lastgoodEUN;
713
        u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
714
        unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
715
        unsigned int status;
716
        int silly = MAX_LOOPS;
717
        size_t retlen;
718
        struct nftl_bci bci;
719
 
720
        lastgoodEUN = BLOCK_NIL;
721
 
722
        if (thisEUN != BLOCK_NIL) {
723
                while (thisEUN < nftl->nb_blocks) {
724
                        if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
725
                                          blockofs, 8, &retlen,
726
                                          (char *)&bci) < 0)
727
                                status = SECTOR_IGNORE;
728
                        else
729
                                status = bci.Status | bci.Status1;
730
 
731
                        switch (status) {
732
                        case SECTOR_FREE:
733
                                /* no modification of a sector should follow a free sector */
734
                                goto the_end;
735
                        case SECTOR_DELETED:
736
                                lastgoodEUN = BLOCK_NIL;
737
                                break;
738
                        case SECTOR_USED:
739
                                lastgoodEUN = thisEUN;
740
                                break;
741
                        case SECTOR_IGNORE:
742
                                break;
743
                        default:
744
                                printk("Unknown status for block %ld in EUN %d: %x\n",
745
                                       block, thisEUN, status);
746
                                break;
747
                        }
748
 
749
                        if (!silly--) {
750
                                printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
751
                                       block / (nftl->EraseSize / 512));
752
                                return 1;
753
                        }
754
                        thisEUN = nftl->ReplUnitTable[thisEUN];
755
                }
756
        }
757
 
758
 the_end:
759
        if (lastgoodEUN == BLOCK_NIL) {
760
                /* the requested block is not on the media, return all 0x00 */
761
                memset(buffer, 0, 512);
762
        } else {
763
                loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
764
                size_t retlen;
765
                int res = mtd->read(mtd, ptr, 512, &retlen, buffer);
766
 
767
                if (res < 0 && res != -EUCLEAN)
768
                        return -EIO;
769
        }
770
        return 0;
771
}
772
 
773
static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
774
{
775
        struct NFTLrecord *nftl = (void *)dev;
776
 
777
        geo->heads = nftl->heads;
778
        geo->sectors = nftl->sectors;
779
        geo->cylinders = nftl->cylinders;
780
 
781
        return 0;
782
}
783
 
784
/****************************************************************************
785
 *
786
 * Module stuff
787
 *
788
 ****************************************************************************/
789
 
790
 
791
static struct mtd_blktrans_ops nftl_tr = {
792
        .name           = "nftl",
793
        .major          = NFTL_MAJOR,
794
        .part_bits      = NFTL_PARTN_BITS,
795
        .blksize        = 512,
796
        .getgeo         = nftl_getgeo,
797
        .readsect       = nftl_readblock,
798
#ifdef CONFIG_NFTL_RW
799
        .writesect      = nftl_writeblock,
800
#endif
801
        .add_mtd        = nftl_add_mtd,
802
        .remove_dev     = nftl_remove_dev,
803
        .owner          = THIS_MODULE,
804
};
805
 
806
extern char nftlmountrev[];
807
 
808
static int __init init_nftl(void)
809
{
810
        printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev);
811
 
812
        return register_mtd_blktrans(&nftl_tr);
813
}
814
 
815
static void __exit cleanup_nftl(void)
816
{
817
        deregister_mtd_blktrans(&nftl_tr);
818
}
819
 
820
module_init(init_nftl);
821
module_exit(cleanup_nftl);
822
 
823
MODULE_LICENSE("GPL");
824
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
825
MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");

powered by: WebSVN 2.1.0

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