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