root/drivers/mtd/inftlcore.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. inftl_add_mtd
  2. inftl_remove_dev
  3. inftl_read_oob
  4. inftl_write_oob
  5. inftl_write
  6. INFTL_findfreeblock
  7. INFTL_foldchain
  8. INFTL_makefreeblock
  9. nrbits
  10. INFTL_findwriteunit
  11. INFTL_trydeletechain
  12. INFTL_deleteblock
  13. inftl_writeblock
  14. inftl_readblock
  15. inftl_getgeo
  16. init_inftl
  17. cleanup_inftl

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
   4  *
   5  * Copyright © 2002, Greg Ungerer (gerg@snapgear.com)
   6  *
   7  * Based heavily on the nftlcore.c code which is:
   8  * Copyright © 1999 Machine Vision Holdings, Inc.
   9  * Copyright © 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 
  35 static 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 
 117 static 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  */
 136 int 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  */
 156 int 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  */
 176 static 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  */
 198 static 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 
 236 static 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 
 395 static 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 
 447 static 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  */
 460 static 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 
 520 hitused:
 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  */
 625 static 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 
 745 static 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 
 790 foundit:
 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 
 804 static 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 
 852 static 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 
 900 foundit:
 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 
 916 static 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 
 927 static 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 
 940 static int __init init_inftl(void)
 941 {
 942         return register_mtd_blktrans(&inftl_tr);
 943 }
 944 
 945 static void __exit cleanup_inftl(void)
 946 {
 947         deregister_mtd_blktrans(&inftl_tr);
 948 }
 949 
 950 module_init(init_inftl);
 951 module_exit(cleanup_inftl);
 952 
 953 MODULE_LICENSE("GPL");
 954 MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
 955 MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");

/* [<][>][^][v][top][bottom][index][help] */