linux/drivers/mtd/nftlcore.c
<<
>>
Prefs
   1/* 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
  38static 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
 121static 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 */
 136int 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 */
 156int 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 */
 178static 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 */
 200static 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
 241static 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
 476static 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*/
 528static 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
 680static 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
 707static 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
 773static 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
 791static 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
 806extern char nftlmountrev[];
 807
 808static 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
 815static void __exit cleanup_nftl(void)
 816{
 817        deregister_mtd_blktrans(&nftl_tr);
 818}
 819
 820module_init(init_nftl);
 821module_exit(cleanup_nftl);
 822
 823MODULE_LICENSE("GPL");
 824MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
 825MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
 826