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