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)
  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 0xffff;
 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] != 0xffff) || BlockDeleted[block])
 285                                continue;
 286
 287                        if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
 288                                           + (block * SECTORSIZE), 16, &retlen,
 289                                           (char *)&oob) < 0)
 290                                status = SECTOR_IGNORE;
 291                        else
 292                                status = oob.b.Status | oob.b.Status1;
 293
 294                        switch(status) {
 295                        case SECTOR_FREE:
 296                        case SECTOR_IGNORE:
 297                                break;
 298                        case SECTOR_USED:
 299                                BlockMap[block] = thisEUN;
 300                                continue;
 301                        case SECTOR_DELETED:
 302                                BlockDeleted[block] = 1;
 303                                continue;
 304                        default:
 305                                printk(KERN_WARNING "INFTL: unknown status "
 306                                        "for block %d in EUN %d: %x\n",
 307                                        block, thisEUN, status);
 308                                break;
 309                        }
 310                }
 311
 312                if (!silly--) {
 313                        printk(KERN_WARNING "INFTL: infinite loop in Virtual "
 314                                "Unit Chain 0x%x\n", thisVUC);
 315                        return BLOCK_NIL;
 316                }
 317
 318                thisEUN = inftl->PUtable[thisEUN];
 319        }
 320
 321        /*
 322         * OK. We now know the location of every block in the Virtual Unit
 323         * Chain, and the Erase Unit into which we are supposed to be copying.
 324         * Go for it.
 325         */
 326        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n",
 327                thisVUC, targetEUN);
 328
 329        for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
 330                unsigned char movebuf[SECTORSIZE];
 331                int ret;
 332
 333                /*
 334                 * If it's in the target EUN already, or if it's pending write,
 335                 * do nothing.
 336                 */
 337                if (BlockMap[block] == targetEUN || (pendingblock ==
 338                    (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
 339                        continue;
 340                }
 341
 342                /*
 343                 * Copy only in non free block (free blocks can only
 344                 * happen in case of media errors or deleted blocks).
 345                 */
 346                if (BlockMap[block] == BLOCK_NIL)
 347                        continue;
 348
 349                ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) +
 350                                (block * SECTORSIZE), SECTORSIZE, &retlen,
 351                                movebuf);
 352                if (ret < 0 && ret != -EUCLEAN) {
 353                        ret = mtd->read(mtd,
 354                                        (inftl->EraseSize * BlockMap[block]) +
 355                                        (block * SECTORSIZE), SECTORSIZE,
 356                                        &retlen, movebuf);
 357                        if (ret != -EIO)
 358                                DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
 359                                      "away on retry?\n");
 360                }
 361                memset(&oob, 0xff, sizeof(struct inftl_oob));
 362                oob.b.Status = oob.b.Status1 = SECTOR_USED;
 363
 364                inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
 365                            (block * SECTORSIZE), SECTORSIZE, &retlen,
 366                            movebuf, (char *)&oob);
 367        }
 368
 369        /*
 370         * Newest unit in chain now contains data from _all_ older units.
 371         * So go through and erase each unit in chain, oldest first. (This
 372         * is important, by doing oldest first if we crash/reboot then it
 373         * it is relatively simple to clean up the mess).
 374         */
 375        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",
 376                thisVUC);
 377
 378        for (;;) {
 379                /* Find oldest unit in chain. */
 380                thisEUN = inftl->VUtable[thisVUC];
 381                prevEUN = BLOCK_NIL;
 382                while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
 383                        prevEUN = thisEUN;
 384                        thisEUN = inftl->PUtable[thisEUN];
 385                }
 386
 387                /* Check if we are all done */
 388                if (thisEUN == targetEUN)
 389                        break;
 390
 391                /* Unlink the last block from the chain. */
 392                inftl->PUtable[prevEUN] = BLOCK_NIL;
 393
 394                /* Now try to erase it. */
 395                if (INFTL_formatblock(inftl, thisEUN) < 0) {
 396                        /*
 397                         * Could not erase : mark block as reserved.
 398                         */
 399                        inftl->PUtable[thisEUN] = BLOCK_RESERVED;
 400                } else {
 401                        /* Correctly erased : mark it as free */
 402                        inftl->PUtable[thisEUN] = BLOCK_FREE;
 403                        inftl->numfreeEUNs++;
 404                }
 405        }
 406
 407        return targetEUN;
 408}
 409
 410static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
 411{
 412        /*
 413         * This is the part that needs some cleverness applied.
 414         * For now, I'm doing the minimum applicable to actually
 415         * get the thing to work.
 416         * Wear-levelling and other clever stuff needs to be implemented
 417         * and we also need to do some assessment of the results when
 418         * the system loses power half-way through the routine.
 419         */
 420        u16 LongestChain = 0;
 421        u16 ChainLength = 0, thislen;
 422        u16 chain, EUN;
 423
 424        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=%p,"
 425                "pending=%d)\n", inftl, pendingblock);
 426
 427        for (chain = 0; chain < inftl->nb_blocks; chain++) {
 428                EUN = inftl->VUtable[chain];
 429                thislen = 0;
 430
 431                while (EUN <= inftl->lastEUN) {
 432                        thislen++;
 433                        EUN = inftl->PUtable[EUN];
 434                        if (thislen > 0xff00) {
 435                                printk(KERN_WARNING "INFTL: endless loop in "
 436                                        "Virtual Chain %d: Unit %x\n",
 437                                        chain, EUN);
 438                                /*
 439                                 * Actually, don't return failure.
 440                                 * Just ignore this chain and get on with it.
 441                                 */
 442                                thislen = 0;
 443                                break;
 444                        }
 445                }
 446
 447                if (thislen > ChainLength) {
 448                        ChainLength = thislen;
 449                        LongestChain = chain;
 450                }
 451        }
 452
 453        if (ChainLength < 2) {
 454                printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
 455                        "for folding. Failing request\n");
 456                return BLOCK_NIL;
 457        }
 458
 459        return INFTL_foldchain(inftl, LongestChain, pendingblock);
 460}
 461
 462static int nrbits(unsigned int val, int bitcount)
 463{
 464        int i, total = 0;
 465
 466        for (i = 0; (i < bitcount); i++)
 467                total += (((0x1 << i) & val) ? 1 : 0);
 468        return total;
 469}
 470
 471/*
 472 * INFTL_findwriteunit: Return the unit number into which we can write
 473 *                      for this block. Make it available if it isn't already.
 474 */
 475static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
 476{
 477        unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
 478        unsigned int thisEUN, writeEUN, prev_block, status;
 479        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
 480        struct mtd_info *mtd = inftl->mbd.mtd;
 481        struct inftl_oob oob;
 482        struct inftl_bci bci;
 483        unsigned char anac, nacs, parity;
 484        size_t retlen;
 485        int silly, silly2 = 3;
 486
 487        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=%p,"
 488                "block=%d)\n", inftl, block);
 489
 490        do {
 491                /*
 492                 * Scan the media to find a unit in the VUC which has
 493                 * a free space for the block in question.
 494                 */
 495                writeEUN = BLOCK_NIL;
 496                thisEUN = inftl->VUtable[thisVUC];
 497                silly = MAX_LOOPS;
 498
 499                while (thisEUN <= inftl->lastEUN) {
 500                        inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
 501                                       blockofs, 8, &retlen, (char *)&bci);
 502
 503                        status = bci.Status | bci.Status1;
 504                        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
 505                                "EUN %d is %x\n", block , writeEUN, status);
 506
 507                        switch(status) {
 508                        case SECTOR_FREE:
 509                                writeEUN = thisEUN;
 510                                break;
 511                        case SECTOR_DELETED:
 512                        case SECTOR_USED:
 513                                /* Can't go any further */
 514                                goto hitused;
 515                        case SECTOR_IGNORE:
 516                                break;
 517                        default:
 518                                /*
 519                                 * Invalid block. Don't use it any more.
 520                                 * Must implement.
 521                                 */
 522                                break;
 523                        }
 524
 525                        if (!silly--) {
 526                                printk(KERN_WARNING "INFTL: infinite loop in "
 527                                        "Virtual Unit Chain 0x%x\n", thisVUC);
 528                                return 0xffff;
 529                        }
 530
 531                        /* Skip to next block in chain */
 532                        thisEUN = inftl->PUtable[thisEUN];
 533                }
 534
 535hitused:
 536                if (writeEUN != BLOCK_NIL)
 537                        return writeEUN;
 538
 539
 540                /*
 541                 * OK. We didn't find one in the existing chain, or there
 542                 * is no existing chain. Allocate a new one.
 543                 */
 544                writeEUN = INFTL_findfreeblock(inftl, 0);
 545
 546                if (writeEUN == BLOCK_NIL) {
 547                        /*
 548                         * That didn't work - there were no free blocks just
 549                         * waiting to be picked up. We're going to have to fold
 550                         * a chain to make room.
 551                         */
 552                        thisEUN = INFTL_makefreeblock(inftl, 0xffff);
 553
 554                        /*
 555                         * Hopefully we free something, lets try again.
 556                         * This time we are desperate...
 557                         */
 558                        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 "
 559                                "to find free EUN to accommodate write to "
 560                                "VUC %d\n", thisVUC);
 561                        writeEUN = INFTL_findfreeblock(inftl, 1);
 562                        if (writeEUN == BLOCK_NIL) {
 563                                /*
 564                                 * Ouch. This should never happen - we should
 565                                 * always be able to make some room somehow.
 566                                 * If we get here, we've allocated more storage
 567                                 * space than actual media, or our makefreeblock
 568                                 * routine is missing something.
 569                                 */
 570                                printk(KERN_WARNING "INFTL: cannot make free "
 571                                        "space.\n");
 572#ifdef DEBUG
 573                                INFTL_dumptables(inftl);
 574                                INFTL_dumpVUchains(inftl);
 575#endif
 576                                return BLOCK_NIL;
 577                        }
 578                }
 579
 580                /*
 581                 * Insert new block into virtual chain. Firstly update the
 582                 * block headers in flash...
 583                 */
 584                anac = 0;
 585                nacs = 0;
 586                thisEUN = inftl->VUtable[thisVUC];
 587                if (thisEUN != BLOCK_NIL) {
 588                        inftl_read_oob(mtd, thisEUN * inftl->EraseSize
 589                                       + 8, 8, &retlen, (char *)&oob.u);
 590                        anac = oob.u.a.ANAC + 1;
 591                        nacs = oob.u.a.NACs + 1;
 592                }
 593
 594                prev_block = inftl->VUtable[thisVUC];
 595                if (prev_block < inftl->nb_blocks)
 596                        prev_block -= inftl->firstEUN;
 597
 598                parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
 599                parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
 600                parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
 601                parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
 602
 603                oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
 604                oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
 605                oob.u.a.ANAC = anac;
 606                oob.u.a.NACs = nacs;
 607                oob.u.a.parityPerField = parity;
 608                oob.u.a.discarded = 0xaa;
 609
 610                inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
 611                                &retlen, (char *)&oob.u);
 612
 613                /* Also back up header... */
 614                oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
 615                oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
 616                oob.u.b.ANAC = anac;
 617                oob.u.b.NACs = nacs;
 618                oob.u.b.parityPerField = parity;
 619                oob.u.b.discarded = 0xaa;
 620
 621                inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
 622                                SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
 623
 624                inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
 625                inftl->VUtable[thisVUC] = writeEUN;
 626
 627                inftl->numfreeEUNs--;
 628                return writeEUN;
 629
 630        } while (silly2--);
 631
 632        printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
 633                "Unit Chain 0x%x\n", thisVUC);
 634        return 0xffff;
 635}
 636
 637/*
 638 * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
 639 */
 640static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
 641{
 642        struct mtd_info *mtd = inftl->mbd.mtd;
 643        unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
 644        unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
 645        unsigned int thisEUN, status;
 646        int block, silly;
 647        struct inftl_bci bci;
 648        size_t retlen;
 649
 650        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=%p,"
 651                "thisVUC=%d)\n", inftl, thisVUC);
 652
 653        memset(BlockUsed, 0, sizeof(BlockUsed));
 654        memset(BlockDeleted, 0, sizeof(BlockDeleted));
 655
 656        thisEUN = inftl->VUtable[thisVUC];
 657        if (thisEUN == BLOCK_NIL) {
 658                printk(KERN_WARNING "INFTL: trying to delete non-existent "
 659                       "Virtual Unit Chain %d!\n", thisVUC);
 660                return;
 661        }
 662
 663        /*
 664         * Scan through the Erase Units to determine whether any data is in
 665         * each of the 512-byte blocks within the Chain.
 666         */
 667        silly = MAX_LOOPS;
 668        while (thisEUN < inftl->nb_blocks) {
 669                for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
 670                        if (BlockUsed[block] || BlockDeleted[block])
 671                                continue;
 672
 673                        if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
 674                                           + (block * SECTORSIZE), 8 , &retlen,
 675                                          (char *)&bci) < 0)
 676                                status = SECTOR_IGNORE;
 677                        else
 678                                status = bci.Status | bci.Status1;
 679
 680                        switch(status) {
 681                        case SECTOR_FREE:
 682                        case SECTOR_IGNORE:
 683                                break;
 684                        case SECTOR_USED:
 685                                BlockUsed[block] = 1;
 686                                continue;
 687                        case SECTOR_DELETED:
 688                                BlockDeleted[block] = 1;
 689                                continue;
 690                        default:
 691                                printk(KERN_WARNING "INFTL: unknown status "
 692                                        "for block %d in EUN %d: 0x%x\n",
 693                                        block, thisEUN, status);
 694                        }
 695                }
 696
 697                if (!silly--) {
 698                        printk(KERN_WARNING "INFTL: infinite loop in Virtual "
 699                                "Unit Chain 0x%x\n", thisVUC);
 700                        return;
 701                }
 702
 703                thisEUN = inftl->PUtable[thisEUN];
 704        }
 705
 706        for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
 707                if (BlockUsed[block])
 708                        return;
 709
 710        /*
 711         * For each block in the chain free it and make it available
 712         * for future use. Erase from the oldest unit first.
 713         */
 714        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC);
 715
 716        for (;;) {
 717                u16 *prevEUN = &inftl->VUtable[thisVUC];
 718                thisEUN = *prevEUN;
 719
 720                /* If the chain is all gone already, we're done */
 721                if (thisEUN == BLOCK_NIL) {
 722                        DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
 723                        return;
 724                }
 725
 726                /* Find oldest unit in chain. */
 727                while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
 728                        BUG_ON(thisEUN >= inftl->nb_blocks);
 729
 730                        prevEUN = &inftl->PUtable[thisEUN];
 731                        thisEUN = *prevEUN;
 732                }
 733
 734                DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
 735                      thisEUN, thisVUC);
 736
 737                if (INFTL_formatblock(inftl, thisEUN) < 0) {
 738                        /*
 739                         * Could not erase : mark block as reserved.
 740                         */
 741                        inftl->PUtable[thisEUN] = BLOCK_RESERVED;
 742                } else {
 743                        /* Correctly erased : mark it as free */
 744                        inftl->PUtable[thisEUN] = BLOCK_FREE;
 745                        inftl->numfreeEUNs++;
 746                }
 747
 748                /* Now sort out whatever was pointing to it... */
 749                *prevEUN = BLOCK_NIL;
 750
 751                /* Ideally we'd actually be responsive to new
 752                   requests while we're doing this -- if there's
 753                   free space why should others be made to wait? */
 754                cond_resched();
 755        }
 756
 757        inftl->VUtable[thisVUC] = BLOCK_NIL;
 758}
 759
 760static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
 761{
 762        unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
 763        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
 764        struct mtd_info *mtd = inftl->mbd.mtd;
 765        unsigned int status;
 766        int silly = MAX_LOOPS;
 767        size_t retlen;
 768        struct inftl_bci bci;
 769
 770        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=%p,"
 771                "block=%d)\n", inftl, block);
 772
 773        while (thisEUN < inftl->nb_blocks) {
 774                if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
 775                                   blockofs, 8, &retlen, (char *)&bci) < 0)
 776                        status = SECTOR_IGNORE;
 777                else
 778                        status = bci.Status | bci.Status1;
 779
 780                switch (status) {
 781                case SECTOR_FREE:
 782                case SECTOR_IGNORE:
 783                        break;
 784                case SECTOR_DELETED:
 785                        thisEUN = BLOCK_NIL;
 786                        goto foundit;
 787                case SECTOR_USED:
 788                        goto foundit;
 789                default:
 790                        printk(KERN_WARNING "INFTL: unknown status for "
 791                                "block %d in EUN %d: 0x%x\n",
 792                                block, thisEUN, status);
 793                        break;
 794                }
 795
 796                if (!silly--) {
 797                        printk(KERN_WARNING "INFTL: infinite loop in Virtual "
 798                                "Unit Chain 0x%x\n",
 799                                block / (inftl->EraseSize / SECTORSIZE));
 800                        return 1;
 801                }
 802                thisEUN = inftl->PUtable[thisEUN];
 803        }
 804
 805foundit:
 806        if (thisEUN != BLOCK_NIL) {
 807                loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
 808
 809                if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
 810                        return -EIO;
 811                bci.Status = bci.Status1 = SECTOR_DELETED;
 812                if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
 813                        return -EIO;
 814                INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
 815        }
 816        return 0;
 817}
 818
 819static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 820                            char *buffer)
 821{
 822        struct INFTLrecord *inftl = (void *)mbd;
 823        unsigned int writeEUN;
 824        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
 825        size_t retlen;
 826        struct inftl_oob oob;
 827        char *p, *pend;
 828
 829        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=%p,block=%ld,"
 830                "buffer=%p)\n", inftl, block, buffer);
 831
 832        /* Is block all zero? */
 833        pend = buffer + SECTORSIZE;
 834        for (p = buffer; p < pend && !*p; p++)
 835                ;
 836
 837        if (p < pend) {
 838                writeEUN = INFTL_findwriteunit(inftl, block);
 839
 840                if (writeEUN == BLOCK_NIL) {
 841                        printk(KERN_WARNING "inftl_writeblock(): cannot find "
 842                                "block to write to\n");
 843                        /*
 844                         * If we _still_ haven't got a block to use,
 845                         * we're screwed.
 846                         */
 847                        return 1;
 848                }
 849
 850                memset(&oob, 0xff, sizeof(struct inftl_oob));
 851                oob.b.Status = oob.b.Status1 = SECTOR_USED;
 852
 853                inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
 854                            blockofs, SECTORSIZE, &retlen, (char *)buffer,
 855                            (char *)&oob);
 856                /*
 857                 * need to write SECTOR_USED flags since they are not written
 858                 * in mtd_writeecc
 859                 */
 860        } else {
 861                INFTL_deleteblock(inftl, block);
 862        }
 863
 864        return 0;
 865}
 866
 867static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 868                           char *buffer)
 869{
 870        struct INFTLrecord *inftl = (void *)mbd;
 871        unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
 872        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
 873        struct mtd_info *mtd = inftl->mbd.mtd;
 874        unsigned int status;
 875        int silly = MAX_LOOPS;
 876        struct inftl_bci bci;
 877        size_t retlen;
 878
 879        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld,"
 880                "buffer=%p)\n", inftl, block, buffer);
 881
 882        while (thisEUN < inftl->nb_blocks) {
 883                if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
 884                                  blockofs, 8, &retlen, (char *)&bci) < 0)
 885                        status = SECTOR_IGNORE;
 886                else
 887                        status = bci.Status | bci.Status1;
 888
 889                switch (status) {
 890                case SECTOR_DELETED:
 891                        thisEUN = BLOCK_NIL;
 892                        goto foundit;
 893                case SECTOR_USED:
 894                        goto foundit;
 895                case SECTOR_FREE:
 896                case SECTOR_IGNORE:
 897                        break;
 898                default:
 899                        printk(KERN_WARNING "INFTL: unknown status for "
 900                                "block %ld in EUN %d: 0x%04x\n",
 901                                block, thisEUN, status);
 902                        break;
 903                }
 904
 905                if (!silly--) {
 906                        printk(KERN_WARNING "INFTL: infinite loop in "
 907                                "Virtual Unit Chain 0x%lx\n",
 908                                block / (inftl->EraseSize / SECTORSIZE));
 909                        return 1;
 910                }
 911
 912                thisEUN = inftl->PUtable[thisEUN];
 913        }
 914
 915foundit:
 916        if (thisEUN == BLOCK_NIL) {
 917                /* The requested block is not on the media, return all 0x00 */
 918                memset(buffer, 0, SECTORSIZE);
 919        } else {
 920                size_t retlen;
 921                loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
 922                int ret = mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer);
 923
 924                /* Handle corrected bit flips gracefully */
 925                if (ret < 0 && ret != -EUCLEAN)
 926                        return -EIO;
 927        }
 928        return 0;
 929}
 930
 931static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
 932{
 933        struct INFTLrecord *inftl = (void *)dev;
 934
 935        geo->heads = inftl->heads;
 936        geo->sectors = inftl->sectors;
 937        geo->cylinders = inftl->cylinders;
 938
 939        return 0;
 940}
 941
 942static struct mtd_blktrans_ops inftl_tr = {
 943        .name           = "inftl",
 944        .major          = INFTL_MAJOR,
 945        .part_bits      = INFTL_PARTN_BITS,
 946        .blksize        = 512,
 947        .getgeo         = inftl_getgeo,
 948        .readsect       = inftl_readblock,
 949        .writesect      = inftl_writeblock,
 950        .add_mtd        = inftl_add_mtd,
 951        .remove_dev     = inftl_remove_dev,
 952        .owner          = THIS_MODULE,
 953};
 954
 955static int __init init_inftl(void)
 956{
 957        return register_mtd_blktrans(&inftl_tr);
 958}
 959
 960static void __exit cleanup_inftl(void)
 961{
 962        deregister_mtd_blktrans(&inftl_tr);
 963}
 964
 965module_init(init_inftl);
 966module_exit(cleanup_inftl);
 967
 968MODULE_LICENSE("GPL");
 969MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
 970MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");
 971