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

Subversion Repositories test_project

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
3
 *
4
 * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
5
 *
6
 * Based heavily on the nftlcore.c code which is:
7
 * (c) 1999 Machine Vision Holdings, Inc.
8
 * Author: David Woodhouse <dwmw2@infradead.org>
9
 *
10
 * $Id: inftlcore.c,v 1.19 2005/11/07 11:14:20 gleixner Exp $
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26
 
27
#include <linux/kernel.h>
28
#include <linux/module.h>
29
#include <linux/delay.h>
30
#include <linux/slab.h>
31
#include <linux/sched.h>
32
#include <linux/init.h>
33
#include <linux/kmod.h>
34
#include <linux/hdreg.h>
35
#include <linux/mtd/mtd.h>
36
#include <linux/mtd/nftl.h>
37
#include <linux/mtd/inftl.h>
38
#include <linux/mtd/nand.h>
39
#include <asm/uaccess.h>
40
#include <asm/errno.h>
41
#include <asm/io.h>
42
 
43
/*
44
 * Maximum number of loops while examining next block, to have a
45
 * chance to detect consistency problems (they should never happen
46
 * because of the checks done in the mounting.
47
 */
48
#define MAX_LOOPS 10000
49
 
50
static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
51
{
52
        struct INFTLrecord *inftl;
53
        unsigned long temp;
54
 
55
        if (mtd->type != MTD_NANDFLASH)
56
                return;
57
        /* OK, this is moderately ugly.  But probably safe.  Alternatives? */
58
        if (memcmp(mtd->name, "DiskOnChip", 10))
59
                return;
60
 
61
        if (!mtd->block_isbad) {
62
                printk(KERN_ERR
63
"INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
64
"Please use the new diskonchip driver under the NAND subsystem.\n");
65
                return;
66
        }
67
 
68
        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);
69
 
70
        inftl = kzalloc(sizeof(*inftl), GFP_KERNEL);
71
 
72
        if (!inftl) {
73
                printk(KERN_WARNING "INFTL: Out of memory for data structures\n");
74
                return;
75
        }
76
 
77
        inftl->mbd.mtd = mtd;
78
        inftl->mbd.devnum = -1;
79
 
80
        inftl->mbd.tr = tr;
81
 
82
        if (INFTL_mount(inftl) < 0) {
83
                printk(KERN_WARNING "INFTL: could not mount device\n");
84
                kfree(inftl);
85
                return;
86
        }
87
 
88
        /* OK, it's a new one. Set up all the data structures. */
89
 
90
        /* Calculate geometry */
91
        inftl->cylinders = 1024;
92
        inftl->heads = 16;
93
 
94
        temp = inftl->cylinders * inftl->heads;
95
        inftl->sectors = inftl->mbd.size / temp;
96
        if (inftl->mbd.size % temp) {
97
                inftl->sectors++;
98
                temp = inftl->cylinders * inftl->sectors;
99
                inftl->heads = inftl->mbd.size / temp;
100
 
101
                if (inftl->mbd.size % temp) {
102
                        inftl->heads++;
103
                        temp = inftl->heads * inftl->sectors;
104
                        inftl->cylinders = inftl->mbd.size / temp;
105
                }
106
        }
107
 
108
        if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {
109
                /*
110
                  Oh no we don't have
111
                   mbd.size == heads * cylinders * sectors
112
                */
113
                printk(KERN_WARNING "INFTL: cannot calculate a geometry to "
114
                       "match size of 0x%lx.\n", inftl->mbd.size);
115
                printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "
116
                        "(== 0x%lx sects)\n",
117
                        inftl->cylinders, inftl->heads , inftl->sectors,
118
                        (long)inftl->cylinders * (long)inftl->heads *
119
                        (long)inftl->sectors );
120
        }
121
 
122
        if (add_mtd_blktrans_dev(&inftl->mbd)) {
123
                kfree(inftl->PUtable);
124
                kfree(inftl->VUtable);
125
                kfree(inftl);
126
                return;
127
        }
128
#ifdef PSYCHO_DEBUG
129
        printk(KERN_INFO "INFTL: Found new inftl%c\n", inftl->mbd.devnum + 'a');
130
#endif
131
        return;
132
}
133
 
134
static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
135
{
136
        struct INFTLrecord *inftl = (void *)dev;
137
 
138
        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum);
139
 
140
        del_mtd_blktrans_dev(dev);
141
 
142
        kfree(inftl->PUtable);
143
        kfree(inftl->VUtable);
144
        kfree(inftl);
145
}
146
 
147
/*
148
 * Actual INFTL access routines.
149
 */
150
 
151
/*
152
 * Read oob data from flash
153
 */
154
int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
155
                   size_t *retlen, uint8_t *buf)
156
{
157
        struct mtd_oob_ops ops;
158
        int res;
159
 
160
        ops.mode = MTD_OOB_PLACE;
161
        ops.ooboffs = offs & (mtd->writesize - 1);
162
        ops.ooblen = len;
163
        ops.oobbuf = buf;
164
        ops.datbuf = NULL;
165
 
166
        res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
167
        *retlen = ops.oobretlen;
168
        return res;
169
}
170
 
171
/*
172
 * Write oob data to flash
173
 */
174
int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
175
                    size_t *retlen, uint8_t *buf)
176
{
177
        struct mtd_oob_ops ops;
178
        int res;
179
 
180
        ops.mode = MTD_OOB_PLACE;
181
        ops.ooboffs = offs & (mtd->writesize - 1);
182
        ops.ooblen = len;
183
        ops.oobbuf = buf;
184
        ops.datbuf = NULL;
185
 
186
        res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
187
        *retlen = ops.oobretlen;
188
        return res;
189
}
190
 
191
/*
192
 * Write data and oob to flash
193
 */
194
static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
195
                       size_t *retlen, uint8_t *buf, uint8_t *oob)
196
{
197
        struct mtd_oob_ops ops;
198
        int res;
199
 
200
        ops.mode = MTD_OOB_PLACE;
201
        ops.ooboffs = offs;
202
        ops.ooblen = mtd->oobsize;
203
        ops.oobbuf = oob;
204
        ops.datbuf = buf;
205
        ops.len = len;
206
 
207
        res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
208
        *retlen = ops.retlen;
209
        return res;
210
}
211
 
212
/*
213
 * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
214
 *      This function is used when the give Virtual Unit Chain.
215
 */
216
static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
217
{
218
        u16 pot = inftl->LastFreeEUN;
219
        int silly = inftl->nb_blocks;
220
 
221
        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=%p,"
222
                "desperate=%d)\n", inftl, desperate);
223
 
224
        /*
225
         * Normally, we force a fold to happen before we run out of free
226
         * blocks completely.
227
         */
228
        if (!desperate && inftl->numfreeEUNs < 2) {
229
                DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free "
230
                        "EUNs (%d)\n", inftl->numfreeEUNs);
231
                return 0xffff;
232
        }
233
 
234
        /* Scan for a free block */
235
        do {
236
                if (inftl->PUtable[pot] == BLOCK_FREE) {
237
                        inftl->LastFreeEUN = pot;
238
                        return pot;
239
                }
240
 
241
                if (++pot > inftl->lastEUN)
242
                        pot = 0;
243
 
244
                if (!silly--) {
245
                        printk(KERN_WARNING "INFTL: no free blocks found!  "
246
                                "EUN range = %d - %d\n", 0, inftl->LastFreeEUN);
247
                        return BLOCK_NIL;
248
                }
249
        } while (pot != inftl->LastFreeEUN);
250
 
251
        return BLOCK_NIL;
252
}
253
 
254
static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
255
{
256
        u16 BlockMap[MAX_SECTORS_PER_UNIT];
257
        unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
258
        unsigned int thisEUN, prevEUN, status;
259
        struct mtd_info *mtd = inftl->mbd.mtd;
260
        int block, silly;
261
        unsigned int targetEUN;
262
        struct inftl_oob oob;
263
        size_t retlen;
264
 
265
        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,"
266
                "pending=%d)\n", inftl, thisVUC, pendingblock);
267
 
268
        memset(BlockMap, 0xff, sizeof(BlockMap));
269
        memset(BlockDeleted, 0, sizeof(BlockDeleted));
270
 
271
        thisEUN = targetEUN = inftl->VUtable[thisVUC];
272
 
273
        if (thisEUN == BLOCK_NIL) {
274
                printk(KERN_WARNING "INFTL: trying to fold non-existent "
275
                       "Virtual Unit Chain %d!\n", thisVUC);
276
                return BLOCK_NIL;
277
        }
278
 
279
        /*
280
         * Scan to find the Erase Unit which holds the actual data for each
281
         * 512-byte block within the Chain.
282
         */
283
        silly = MAX_LOOPS;
284
        while (thisEUN < inftl->nb_blocks) {
285
                for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
286
                        if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
287
                                continue;
288
 
289
                        if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
290
                                           + (block * SECTORSIZE), 16, &retlen,
291
                                           (char *)&oob) < 0)
292
                                status = SECTOR_IGNORE;
293
                        else
294
                                status = oob.b.Status | oob.b.Status1;
295
 
296
                        switch(status) {
297
                        case SECTOR_FREE:
298
                        case SECTOR_IGNORE:
299
                                break;
300
                        case SECTOR_USED:
301
                                BlockMap[block] = thisEUN;
302
                                continue;
303
                        case SECTOR_DELETED:
304
                                BlockDeleted[block] = 1;
305
                                continue;
306
                        default:
307
                                printk(KERN_WARNING "INFTL: unknown status "
308
                                        "for block %d in EUN %d: %x\n",
309
                                        block, thisEUN, status);
310
                                break;
311
                        }
312
                }
313
 
314
                if (!silly--) {
315
                        printk(KERN_WARNING "INFTL: infinite loop in Virtual "
316
                                "Unit Chain 0x%x\n", thisVUC);
317
                        return BLOCK_NIL;
318
                }
319
 
320
                thisEUN = inftl->PUtable[thisEUN];
321
        }
322
 
323
        /*
324
         * OK. We now know the location of every block in the Virtual Unit
325
         * Chain, and the Erase Unit into which we are supposed to be copying.
326
         * Go for it.
327
         */
328
        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n",
329
                thisVUC, targetEUN);
330
 
331
        for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
332
                unsigned char movebuf[SECTORSIZE];
333
                int ret;
334
 
335
                /*
336
                 * If it's in the target EUN already, or if it's pending write,
337
                 * do nothing.
338
                 */
339
                if (BlockMap[block] == targetEUN || (pendingblock ==
340
                    (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
341
                        continue;
342
                }
343
 
344
                /*
345
                 * Copy only in non free block (free blocks can only
346
                 * happen in case of media errors or deleted blocks).
347
                 */
348
                if (BlockMap[block] == BLOCK_NIL)
349
                        continue;
350
 
351
                ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) +
352
                                (block * SECTORSIZE), SECTORSIZE, &retlen,
353
                                movebuf);
354
                if (ret < 0 && ret != -EUCLEAN) {
355
                        ret = mtd->read(mtd,
356
                                        (inftl->EraseSize * BlockMap[block]) +
357
                                        (block * SECTORSIZE), SECTORSIZE,
358
                                        &retlen, movebuf);
359
                        if (ret != -EIO)
360
                                DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
361
                                      "away on retry?\n");
362
                }
363
                memset(&oob, 0xff, sizeof(struct inftl_oob));
364
                oob.b.Status = oob.b.Status1 = SECTOR_USED;
365
 
366
                inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
367
                            (block * SECTORSIZE), SECTORSIZE, &retlen,
368
                            movebuf, (char *)&oob);
369
        }
370
 
371
        /*
372
         * Newest unit in chain now contains data from _all_ older units.
373
         * So go through and erase each unit in chain, oldest first. (This
374
         * is important, by doing oldest first if we crash/reboot then it
375
         * it is relatively simple to clean up the mess).
376
         */
377
        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",
378
                thisVUC);
379
 
380
        for (;;) {
381
                /* Find oldest unit in chain. */
382
                thisEUN = inftl->VUtable[thisVUC];
383
                prevEUN = BLOCK_NIL;
384
                while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
385
                        prevEUN = thisEUN;
386
                        thisEUN = inftl->PUtable[thisEUN];
387
                }
388
 
389
                /* Check if we are all done */
390
                if (thisEUN == targetEUN)
391
                        break;
392
 
393
                if (INFTL_formatblock(inftl, thisEUN) < 0) {
394
                        /*
395
                         * Could not erase : mark block as reserved.
396
                         */
397
                        inftl->PUtable[thisEUN] = BLOCK_RESERVED;
398
                } else {
399
                        /* Correctly erased : mark it as free */
400
                        inftl->PUtable[thisEUN] = BLOCK_FREE;
401
                        inftl->PUtable[prevEUN] = BLOCK_NIL;
402
                        inftl->numfreeEUNs++;
403
                }
404
        }
405
 
406
        return targetEUN;
407
}
408
 
409
static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
410
{
411
        /*
412
         * This is the part that needs some cleverness applied.
413
         * For now, I'm doing the minimum applicable to actually
414
         * get the thing to work.
415
         * Wear-levelling and other clever stuff needs to be implemented
416
         * and we also need to do some assessment of the results when
417
         * the system loses power half-way through the routine.
418
         */
419
        u16 LongestChain = 0;
420
        u16 ChainLength = 0, thislen;
421
        u16 chain, EUN;
422
 
423
        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=%p,"
424
                "pending=%d)\n", inftl, pendingblock);
425
 
426
        for (chain = 0; chain < inftl->nb_blocks; chain++) {
427
                EUN = inftl->VUtable[chain];
428
                thislen = 0;
429
 
430
                while (EUN <= inftl->lastEUN) {
431
                        thislen++;
432
                        EUN = inftl->PUtable[EUN];
433
                        if (thislen > 0xff00) {
434
                                printk(KERN_WARNING "INFTL: endless loop in "
435
                                        "Virtual Chain %d: Unit %x\n",
436
                                        chain, EUN);
437
                                /*
438
                                 * Actually, don't return failure.
439
                                 * Just ignore this chain and get on with it.
440
                                 */
441
                                thislen = 0;
442
                                break;
443
                        }
444
                }
445
 
446
                if (thislen > ChainLength) {
447
                        ChainLength = thislen;
448
                        LongestChain = chain;
449
                }
450
        }
451
 
452
        if (ChainLength < 2) {
453
                printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
454
                        "for folding. Failing request\n");
455
                return BLOCK_NIL;
456
        }
457
 
458
        return INFTL_foldchain(inftl, LongestChain, pendingblock);
459
}
460
 
461
static int nrbits(unsigned int val, int bitcount)
462
{
463
        int i, total = 0;
464
 
465
        for (i = 0; (i < bitcount); i++)
466
                total += (((0x1 << i) & val) ? 1 : 0);
467
        return total;
468
}
469
 
470
/*
471
 * INFTL_findwriteunit: Return the unit number into which we can write
472
 *                      for this block. Make it available if it isn't already.
473
 */
474
static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
475
{
476
        unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
477
        unsigned int thisEUN, writeEUN, prev_block, status;
478
        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
479
        struct mtd_info *mtd = inftl->mbd.mtd;
480
        struct inftl_oob oob;
481
        struct inftl_bci bci;
482
        unsigned char anac, nacs, parity;
483
        size_t retlen;
484
        int silly, silly2 = 3;
485
 
486
        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=%p,"
487
                "block=%d)\n", inftl, block);
488
 
489
        do {
490
                /*
491
                 * Scan the media to find a unit in the VUC which has
492
                 * a free space for the block in question.
493
                 */
494
                writeEUN = BLOCK_NIL;
495
                thisEUN = inftl->VUtable[thisVUC];
496
                silly = MAX_LOOPS;
497
 
498
                while (thisEUN <= inftl->lastEUN) {
499
                        inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
500
                                       blockofs, 8, &retlen, (char *)&bci);
501
 
502
                        status = bci.Status | bci.Status1;
503
                        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
504
                                "EUN %d is %x\n", block , writeEUN, status);
505
 
506
                        switch(status) {
507
                        case SECTOR_FREE:
508
                                writeEUN = thisEUN;
509
                                break;
510
                        case SECTOR_DELETED:
511
                        case SECTOR_USED:
512
                                /* Can't go any further */
513
                                goto hitused;
514
                        case SECTOR_IGNORE:
515
                                break;
516
                        default:
517
                                /*
518
                                 * Invalid block. Don't use it any more.
519
                                 * Must implement.
520
                                 */
521
                                break;
522
                        }
523
 
524
                        if (!silly--) {
525
                                printk(KERN_WARNING "INFTL: infinite loop in "
526
                                        "Virtual Unit Chain 0x%x\n", thisVUC);
527
                                return 0xffff;
528
                        }
529
 
530
                        /* Skip to next block in chain */
531
                        thisEUN = inftl->PUtable[thisEUN];
532
                }
533
 
534
hitused:
535
                if (writeEUN != BLOCK_NIL)
536
                        return writeEUN;
537
 
538
 
539
                /*
540
                 * OK. We didn't find one in the existing chain, or there
541
                 * is no existing chain. Allocate a new one.
542
                 */
543
                writeEUN = INFTL_findfreeblock(inftl, 0);
544
 
545
                if (writeEUN == BLOCK_NIL) {
546
                        /*
547
                         * That didn't work - there were no free blocks just
548
                         * waiting to be picked up. We're going to have to fold
549
                         * a chain to make room.
550
                         */
551
                        thisEUN = INFTL_makefreeblock(inftl, 0xffff);
552
 
553
                        /*
554
                         * Hopefully we free something, lets try again.
555
                         * This time we are desperate...
556
                         */
557
                        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 "
558
                                "to find free EUN to accommodate write to "
559
                                "VUC %d\n", thisVUC);
560
                        writeEUN = INFTL_findfreeblock(inftl, 1);
561
                        if (writeEUN == BLOCK_NIL) {
562
                                /*
563
                                 * Ouch. This should never happen - we should
564
                                 * always be able to make some room somehow.
565
                                 * If we get here, we've allocated more storage
566
                                 * space than actual media, or our makefreeblock
567
                                 * routine is missing something.
568
                                 */
569
                                printk(KERN_WARNING "INFTL: cannot make free "
570
                                        "space.\n");
571
#ifdef DEBUG
572
                                INFTL_dumptables(inftl);
573
                                INFTL_dumpVUchains(inftl);
574
#endif
575
                                return BLOCK_NIL;
576
                        }
577
                }
578
 
579
                /*
580
                 * Insert new block into virtual chain. Firstly update the
581
                 * block headers in flash...
582
                 */
583
                anac = 0;
584
                nacs = 0;
585
                thisEUN = inftl->VUtable[thisVUC];
586
                if (thisEUN != BLOCK_NIL) {
587
                        inftl_read_oob(mtd, thisEUN * inftl->EraseSize
588
                                       + 8, 8, &retlen, (char *)&oob.u);
589
                        anac = oob.u.a.ANAC + 1;
590
                        nacs = oob.u.a.NACs + 1;
591
                }
592
 
593
                prev_block = inftl->VUtable[thisVUC];
594
                if (prev_block < inftl->nb_blocks)
595
                        prev_block -= inftl->firstEUN;
596
 
597
                parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
598
                parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
599
                parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
600
                parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
601
 
602
                oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
603
                oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
604
                oob.u.a.ANAC = anac;
605
                oob.u.a.NACs = nacs;
606
                oob.u.a.parityPerField = parity;
607
                oob.u.a.discarded = 0xaa;
608
 
609
                inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
610
                                &retlen, (char *)&oob.u);
611
 
612
                /* Also back up header... */
613
                oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
614
                oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
615
                oob.u.b.ANAC = anac;
616
                oob.u.b.NACs = nacs;
617
                oob.u.b.parityPerField = parity;
618
                oob.u.b.discarded = 0xaa;
619
 
620
                inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
621
                                SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
622
 
623
                inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
624
                inftl->VUtable[thisVUC] = writeEUN;
625
 
626
                inftl->numfreeEUNs--;
627
                return writeEUN;
628
 
629
        } while (silly2--);
630
 
631
        printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
632
                "Unit Chain 0x%x\n", thisVUC);
633
        return 0xffff;
634
}
635
 
636
/*
637
 * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
638
 */
639
static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
640
{
641
        struct mtd_info *mtd = inftl->mbd.mtd;
642
        unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
643
        unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
644
        unsigned int thisEUN, status;
645
        int block, silly;
646
        struct inftl_bci bci;
647
        size_t retlen;
648
 
649
        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=%p,"
650
                "thisVUC=%d)\n", inftl, thisVUC);
651
 
652
        memset(BlockUsed, 0, sizeof(BlockUsed));
653
        memset(BlockDeleted, 0, sizeof(BlockDeleted));
654
 
655
        thisEUN = inftl->VUtable[thisVUC];
656
        if (thisEUN == BLOCK_NIL) {
657
                printk(KERN_WARNING "INFTL: trying to delete non-existent "
658
                       "Virtual Unit Chain %d!\n", thisVUC);
659
                return;
660
        }
661
 
662
        /*
663
         * Scan through the Erase Units to determine whether any data is in
664
         * each of the 512-byte blocks within the Chain.
665
         */
666
        silly = MAX_LOOPS;
667
        while (thisEUN < inftl->nb_blocks) {
668
                for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
669
                        if (BlockUsed[block] || BlockDeleted[block])
670
                                continue;
671
 
672
                        if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
673
                                           + (block * SECTORSIZE), 8 , &retlen,
674
                                          (char *)&bci) < 0)
675
                                status = SECTOR_IGNORE;
676
                        else
677
                                status = bci.Status | bci.Status1;
678
 
679
                        switch(status) {
680
                        case SECTOR_FREE:
681
                        case SECTOR_IGNORE:
682
                                break;
683
                        case SECTOR_USED:
684
                                BlockUsed[block] = 1;
685
                                continue;
686
                        case SECTOR_DELETED:
687
                                BlockDeleted[block] = 1;
688
                                continue;
689
                        default:
690
                                printk(KERN_WARNING "INFTL: unknown status "
691
                                        "for block %d in EUN %d: 0x%x\n",
692
                                        block, thisEUN, status);
693
                        }
694
                }
695
 
696
                if (!silly--) {
697
                        printk(KERN_WARNING "INFTL: infinite loop in Virtual "
698
                                "Unit Chain 0x%x\n", thisVUC);
699
                        return;
700
                }
701
 
702
                thisEUN = inftl->PUtable[thisEUN];
703
        }
704
 
705
        for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
706
                if (BlockUsed[block])
707
                        return;
708
 
709
        /*
710
         * For each block in the chain free it and make it available
711
         * for future use. Erase from the oldest unit first.
712
         */
713
        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC);
714
 
715
        for (;;) {
716
                u16 *prevEUN = &inftl->VUtable[thisVUC];
717
                thisEUN = *prevEUN;
718
 
719
                /* If the chain is all gone already, we're done */
720
                if (thisEUN == BLOCK_NIL) {
721
                        DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
722
                        return;
723
                }
724
 
725
                /* Find oldest unit in chain. */
726
                while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
727
                        BUG_ON(thisEUN >= inftl->nb_blocks);
728
 
729
                        prevEUN = &inftl->PUtable[thisEUN];
730
                        thisEUN = *prevEUN;
731
                }
732
 
733
                DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
734
                      thisEUN, thisVUC);
735
 
736
                if (INFTL_formatblock(inftl, thisEUN) < 0) {
737
                        /*
738
                         * Could not erase : mark block as reserved.
739
                         */
740
                        inftl->PUtable[thisEUN] = BLOCK_RESERVED;
741
                } else {
742
                        /* Correctly erased : mark it as free */
743
                        inftl->PUtable[thisEUN] = BLOCK_FREE;
744
                        inftl->numfreeEUNs++;
745
                }
746
 
747
                /* Now sort out whatever was pointing to it... */
748
                *prevEUN = BLOCK_NIL;
749
 
750
                /* Ideally we'd actually be responsive to new
751
                   requests while we're doing this -- if there's
752
                   free space why should others be made to wait? */
753
                cond_resched();
754
        }
755
 
756
        inftl->VUtable[thisVUC] = BLOCK_NIL;
757
}
758
 
759
static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
760
{
761
        unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
762
        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
763
        struct mtd_info *mtd = inftl->mbd.mtd;
764
        unsigned int status;
765
        int silly = MAX_LOOPS;
766
        size_t retlen;
767
        struct inftl_bci bci;
768
 
769
        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=%p,"
770
                "block=%d)\n", inftl, block);
771
 
772
        while (thisEUN < inftl->nb_blocks) {
773
                if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
774
                                   blockofs, 8, &retlen, (char *)&bci) < 0)
775
                        status = SECTOR_IGNORE;
776
                else
777
                        status = bci.Status | bci.Status1;
778
 
779
                switch (status) {
780
                case SECTOR_FREE:
781
                case SECTOR_IGNORE:
782
                        break;
783
                case SECTOR_DELETED:
784
                        thisEUN = BLOCK_NIL;
785
                        goto foundit;
786
                case SECTOR_USED:
787
                        goto foundit;
788
                default:
789
                        printk(KERN_WARNING "INFTL: unknown status for "
790
                                "block %d in EUN %d: 0x%x\n",
791
                                block, thisEUN, status);
792
                        break;
793
                }
794
 
795
                if (!silly--) {
796
                        printk(KERN_WARNING "INFTL: infinite loop in Virtual "
797
                                "Unit Chain 0x%x\n",
798
                                block / (inftl->EraseSize / SECTORSIZE));
799
                        return 1;
800
                }
801
                thisEUN = inftl->PUtable[thisEUN];
802
        }
803
 
804
foundit:
805
        if (thisEUN != BLOCK_NIL) {
806
                loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
807
 
808
                if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
809
                        return -EIO;
810
                bci.Status = bci.Status1 = SECTOR_DELETED;
811
                if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
812
                        return -EIO;
813
                INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
814
        }
815
        return 0;
816
}
817
 
818
static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
819
                            char *buffer)
820
{
821
        struct INFTLrecord *inftl = (void *)mbd;
822
        unsigned int writeEUN;
823
        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
824
        size_t retlen;
825
        struct inftl_oob oob;
826
        char *p, *pend;
827
 
828
        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=%p,block=%ld,"
829
                "buffer=%p)\n", inftl, block, buffer);
830
 
831
        /* Is block all zero? */
832
        pend = buffer + SECTORSIZE;
833
        for (p = buffer; p < pend && !*p; p++)
834
                ;
835
 
836
        if (p < pend) {
837
                writeEUN = INFTL_findwriteunit(inftl, block);
838
 
839
                if (writeEUN == BLOCK_NIL) {
840
                        printk(KERN_WARNING "inftl_writeblock(): cannot find "
841
                                "block to write to\n");
842
                        /*
843
                         * If we _still_ haven't got a block to use,
844
                         * we're screwed.
845
                         */
846
                        return 1;
847
                }
848
 
849
                memset(&oob, 0xff, sizeof(struct inftl_oob));
850
                oob.b.Status = oob.b.Status1 = SECTOR_USED;
851
 
852
                inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
853
                            blockofs, SECTORSIZE, &retlen, (char *)buffer,
854
                            (char *)&oob);
855
                /*
856
                 * need to write SECTOR_USED flags since they are not written
857
                 * in mtd_writeecc
858
                 */
859
        } else {
860
                INFTL_deleteblock(inftl, block);
861
        }
862
 
863
        return 0;
864
}
865
 
866
static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
867
                           char *buffer)
868
{
869
        struct INFTLrecord *inftl = (void *)mbd;
870
        unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
871
        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
872
        struct mtd_info *mtd = inftl->mbd.mtd;
873
        unsigned int status;
874
        int silly = MAX_LOOPS;
875
        struct inftl_bci bci;
876
        size_t retlen;
877
 
878
        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld,"
879
                "buffer=%p)\n", inftl, block, buffer);
880
 
881
        while (thisEUN < inftl->nb_blocks) {
882
                if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
883
                                  blockofs, 8, &retlen, (char *)&bci) < 0)
884
                        status = SECTOR_IGNORE;
885
                else
886
                        status = bci.Status | bci.Status1;
887
 
888
                switch (status) {
889
                case SECTOR_DELETED:
890
                        thisEUN = BLOCK_NIL;
891
                        goto foundit;
892
                case SECTOR_USED:
893
                        goto foundit;
894
                case SECTOR_FREE:
895
                case SECTOR_IGNORE:
896
                        break;
897
                default:
898
                        printk(KERN_WARNING "INFTL: unknown status for "
899
                                "block %ld in EUN %d: 0x%04x\n",
900
                                block, thisEUN, status);
901
                        break;
902
                }
903
 
904
                if (!silly--) {
905
                        printk(KERN_WARNING "INFTL: infinite loop in "
906
                                "Virtual Unit Chain 0x%lx\n",
907
                                block / (inftl->EraseSize / SECTORSIZE));
908
                        return 1;
909
                }
910
 
911
                thisEUN = inftl->PUtable[thisEUN];
912
        }
913
 
914
foundit:
915
        if (thisEUN == BLOCK_NIL) {
916
                /* The requested block is not on the media, return all 0x00 */
917
                memset(buffer, 0, SECTORSIZE);
918
        } else {
919
                size_t retlen;
920
                loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
921
                int ret = mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer);
922
 
923
                /* Handle corrected bit flips gracefully */
924
                if (ret < 0 && ret != -EUCLEAN)
925
                        return -EIO;
926
        }
927
        return 0;
928
}
929
 
930
static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
931
{
932
        struct INFTLrecord *inftl = (void *)dev;
933
 
934
        geo->heads = inftl->heads;
935
        geo->sectors = inftl->sectors;
936
        geo->cylinders = inftl->cylinders;
937
 
938
        return 0;
939
}
940
 
941
static struct mtd_blktrans_ops inftl_tr = {
942
        .name           = "inftl",
943
        .major          = INFTL_MAJOR,
944
        .part_bits      = INFTL_PARTN_BITS,
945
        .blksize        = 512,
946
        .getgeo         = inftl_getgeo,
947
        .readsect       = inftl_readblock,
948
        .writesect      = inftl_writeblock,
949
        .add_mtd        = inftl_add_mtd,
950
        .remove_dev     = inftl_remove_dev,
951
        .owner          = THIS_MODULE,
952
};
953
 
954
static int __init init_inftl(void)
955
{
956
        printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.19 $, "
957
                "inftlmount.c %s\n", inftlmountrev);
958
 
959
        return register_mtd_blktrans(&inftl_tr);
960
}
961
 
962
static void __exit cleanup_inftl(void)
963
{
964
        deregister_mtd_blktrans(&inftl_tr);
965
}
966
 
967
module_init(init_inftl);
968
module_exit(cleanup_inftl);
969
 
970
MODULE_LICENSE("GPL");
971
MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
972
MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");

powered by: WebSVN 2.1.0

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