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