| 1 | |
| 2 | |
| 3 | * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
|
| 4 | *
|
| 5 | * Copyright © 2002, Greg Ungerer ([email protected])
|
| 6 | *
|
| 7 | * Based heavily on the nftlcore.c code which is:
|
| 8 | * Copyright © 1999 Machine Vision Holdings, Inc.
|
| 9 | * Copyright © 1999 David Woodhouse <[email protected]>
|
| 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 |
|
| 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_obj(*inftl);
|
| 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 |
|
| 72 |
|
| 73 |
|
| 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 |
|
| 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 | * 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 |
|
| 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 |
|
| 373 | if (thisEUN == targetEUN)
|
| 374 | break;
|
| 375 |
|
| 376 |
|
| 377 | inftl->PUtable[prevEUN] = BLOCK_NIL;
|
| 378 |
|
| 379 |
|
| 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 |
|
| 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 | if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
|
| 486 | blockofs, 8, &retlen, (char *)&bci) < 0)
|
| 487 | status = SECTOR_IGNORE;
|
| 488 | else
|
| 489 | status = bci.Status | bci.Status1;
|
| 490 | pr_debug("INFTL: status of block %d in EUN %d is %x\n",
|
| 491 | block , writeEUN, status);
|
| 492 |
|
| 493 | switch(status) {
|
| 494 | case SECTOR_FREE:
|
| 495 | writeEUN = thisEUN;
|
| 496 | break;
|
| 497 | case SECTOR_DELETED:
|
| 498 | case SECTOR_USED:
|
| 499 |
|
| 500 | goto hitused;
|
| 501 | case SECTOR_IGNORE:
|
| 502 | break;
|
| 503 | default:
|
| 504 | |
| 505 | * Invalid block. Don't use it any more.
|
| 506 | * Must implement.
|
| 507 |
|
| 508 | break;
|
| 509 | }
|
| 510 |
|
| 511 | if (!silly--) {
|
| 512 | printk(KERN_WARNING "INFTL: infinite loop in "
|
| 513 | "Virtual Unit Chain 0x%x\n", thisVUC);
|
| 514 | return BLOCK_NIL;
|
| 515 | }
|
| 516 |
|
| 517 |
|
| 518 | thisEUN = inftl->PUtable[thisEUN];
|
| 519 | }
|
| 520 |
|
| 521 | hitused:
|
| 522 | if (writeEUN != BLOCK_NIL)
|
| 523 | return writeEUN;
|
| 524 |
|
| 525 |
|
| 526 | |
| 527 | * OK. We didn't find one in the existing chain, or there
|
| 528 | * is no existing chain. Allocate a new one.
|
| 529 |
|
| 530 | writeEUN = INFTL_findfreeblock(inftl, 0);
|
| 531 |
|
| 532 | if (writeEUN == BLOCK_NIL) {
|
| 533 | |
| 534 | * That didn't work - there were no free blocks just
|
| 535 | * waiting to be picked up. We're going to have to fold
|
| 536 | * a chain to make room.
|
| 537 |
|
| 538 | thisEUN = INFTL_makefreeblock(inftl, block);
|
| 539 |
|
| 540 | |
| 541 | * Hopefully we free something, lets try again.
|
| 542 | * This time we are desperate...
|
| 543 |
|
| 544 | pr_debug("INFTL: using desperate==1 to find free EUN "
|
| 545 | "to accommodate write to VUC %d\n",
|
| 546 | thisVUC);
|
| 547 | writeEUN = INFTL_findfreeblock(inftl, 1);
|
| 548 | if (writeEUN == BLOCK_NIL) {
|
| 549 | |
| 550 | * Ouch. This should never happen - we should
|
| 551 | * always be able to make some room somehow.
|
| 552 | * If we get here, we've allocated more storage
|
| 553 | * space than actual media, or our makefreeblock
|
| 554 | * routine is missing something.
|
| 555 |
|
| 556 | printk(KERN_WARNING "INFTL: cannot make free "
|
| 557 | "space.\n");
|
| 558 | #ifdef DEBUG
|
| 559 | INFTL_dumptables(inftl);
|
| 560 | INFTL_dumpVUchains(inftl);
|
| 561 | #endif
|
| 562 | return BLOCK_NIL;
|
| 563 | }
|
| 564 | }
|
| 565 |
|
| 566 | |
| 567 | * Insert new block into virtual chain. Firstly update the
|
| 568 | * block headers in flash...
|
| 569 |
|
| 570 | anac = 0;
|
| 571 | nacs = 0;
|
| 572 | thisEUN = inftl->VUtable[thisVUC];
|
| 573 | if (thisEUN != BLOCK_NIL) {
|
| 574 | inftl_read_oob(mtd, thisEUN * inftl->EraseSize
|
| 575 | + 8, 8, &retlen, (char *)&oob.u);
|
| 576 | anac = oob.u.a.ANAC + 1;
|
| 577 | nacs = oob.u.a.NACs + 1;
|
| 578 | }
|
| 579 |
|
| 580 | prev_block = inftl->VUtable[thisVUC];
|
| 581 | if (prev_block < inftl->nb_blocks)
|
| 582 | prev_block -= inftl->firstEUN;
|
| 583 |
|
| 584 | parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
|
| 585 | parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
|
| 586 | parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
|
| 587 | parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
|
| 588 |
|
| 589 | oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
|
| 590 | oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
|
| 591 | oob.u.a.ANAC = anac;
|
| 592 | oob.u.a.NACs = nacs;
|
| 593 | oob.u.a.parityPerField = parity;
|
| 594 | oob.u.a.discarded = 0xaa;
|
| 595 |
|
| 596 | inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
|
| 597 | &retlen, (char *)&oob.u);
|
| 598 |
|
| 599 |
|
| 600 | oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
|
| 601 | oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
|
| 602 | oob.u.b.ANAC = anac;
|
| 603 | oob.u.b.NACs = nacs;
|
| 604 | oob.u.b.parityPerField = parity;
|
| 605 | oob.u.b.discarded = 0xaa;
|
| 606 |
|
| 607 | inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
|
| 608 | SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
|
| 609 |
|
| 610 | inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
|
| 611 | inftl->VUtable[thisVUC] = writeEUN;
|
| 612 |
|
| 613 | inftl->numfreeEUNs--;
|
| 614 | return writeEUN;
|
| 615 |
|
| 616 | } while (silly2--);
|
| 617 |
|
| 618 | printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
|
| 619 | "Unit Chain 0x%x\n", thisVUC);
|
| 620 | return BLOCK_NIL;
|
| 621 | }
|
| 622 |
|
| 623 | |
| 624 | * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
|
| 625 |
|
| 626 | static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
|
| 627 | {
|
| 628 | struct mtd_info *mtd = inftl->mbd.mtd;
|
| 629 | unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
|
| 630 | unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
|
| 631 | unsigned int thisEUN, status;
|
| 632 | int block, silly;
|
| 633 | struct inftl_bci bci;
|
| 634 | size_t retlen;
|
| 635 |
|
| 636 | pr_debug("INFTL: INFTL_trydeletechain(inftl=%p,"
|
| 637 | "thisVUC=%d)\n", inftl, thisVUC);
|
| 638 |
|
| 639 | memset(BlockUsed, 0, sizeof(BlockUsed));
|
| 640 | memset(BlockDeleted, 0, sizeof(BlockDeleted));
|
| 641 |
|
| 642 | thisEUN = inftl->VUtable[thisVUC];
|
| 643 | if (thisEUN == BLOCK_NIL) {
|
| 644 | printk(KERN_WARNING "INFTL: trying to delete non-existent "
|
| 645 | "Virtual Unit Chain %d!\n", thisVUC);
|
| 646 | return;
|
| 647 | }
|
| 648 |
|
| 649 | |
| 650 | * Scan through the Erase Units to determine whether any data is in
|
| 651 | * each of the 512-byte blocks within the Chain.
|
| 652 |
|
| 653 | silly = MAX_LOOPS;
|
| 654 | while (thisEUN < inftl->nb_blocks) {
|
| 655 | for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
|
| 656 | if (BlockUsed[block] || BlockDeleted[block])
|
| 657 | continue;
|
| 658 |
|
| 659 | if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
|
| 660 | + (block * SECTORSIZE), 8 , &retlen,
|
| 661 | (char *)&bci) < 0)
|
| 662 | status = SECTOR_IGNORE;
|
| 663 | else
|
| 664 | status = bci.Status | bci.Status1;
|
| 665 |
|
| 666 | switch(status) {
|
| 667 | case SECTOR_FREE:
|
| 668 | case SECTOR_IGNORE:
|
| 669 | break;
|
| 670 | case SECTOR_USED:
|
| 671 | BlockUsed[block] = 1;
|
| 672 | continue;
|
| 673 | case SECTOR_DELETED:
|
| 674 | BlockDeleted[block] = 1;
|
| 675 | continue;
|
| 676 | default:
|
| 677 | printk(KERN_WARNING "INFTL: unknown status "
|
| 678 | "for block %d in EUN %d: 0x%x\n",
|
| 679 | block, thisEUN, status);
|
| 680 | }
|
| 681 | }
|
| 682 |
|
| 683 | if (!silly--) {
|
| 684 | printk(KERN_WARNING "INFTL: infinite loop in Virtual "
|
| 685 | "Unit Chain 0x%x\n", thisVUC);
|
| 686 | return;
|
| 687 | }
|
| 688 |
|
| 689 | thisEUN = inftl->PUtable[thisEUN];
|
| 690 | }
|
| 691 |
|
| 692 | for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
|
| 693 | if (BlockUsed[block])
|
| 694 | return;
|
| 695 |
|
| 696 | |
| 697 | * For each block in the chain free it and make it available
|
| 698 | * for future use. Erase from the oldest unit first.
|
| 699 |
|
| 700 | pr_debug("INFTL: deleting empty VUC %d\n", thisVUC);
|
| 701 |
|
| 702 | for (;;) {
|
| 703 | u16 *prevEUN = &inftl->VUtable[thisVUC];
|
| 704 | thisEUN = *prevEUN;
|
| 705 |
|
| 706 |
|
| 707 | if (thisEUN == BLOCK_NIL) {
|
| 708 | pr_debug("INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
|
| 709 | return;
|
| 710 | }
|
| 711 |
|
| 712 |
|
| 713 | while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
|
| 714 | BUG_ON(thisEUN >= inftl->nb_blocks);
|
| 715 |
|
| 716 | prevEUN = &inftl->PUtable[thisEUN];
|
| 717 | thisEUN = *prevEUN;
|
| 718 | }
|
| 719 |
|
| 720 | pr_debug("Deleting EUN %d from VUC %d\n",
|
| 721 | thisEUN, thisVUC);
|
| 722 |
|
| 723 | if (INFTL_formatblock(inftl, thisEUN) < 0) {
|
| 724 | |
| 725 | * Could not erase : mark block as reserved.
|
| 726 |
|
| 727 | inftl->PUtable[thisEUN] = BLOCK_RESERVED;
|
| 728 | } else {
|
| 729 |
|
| 730 | inftl->PUtable[thisEUN] = BLOCK_FREE;
|
| 731 | inftl->numfreeEUNs++;
|
| 732 | }
|
| 733 |
|
| 734 |
|
| 735 | *prevEUN = BLOCK_NIL;
|
| 736 |
|
| 737 | |
| 738 | requests while we're doing this -- if there's
|
| 739 | free space why should others be made to wait?
|
| 740 | cond_resched();
|
| 741 | }
|
| 742 |
|
| 743 | inftl->VUtable[thisVUC] = BLOCK_NIL;
|
| 744 | }
|
| 745 |
|
| 746 | static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
|
| 747 | {
|
| 748 | unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
|
| 749 | unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
|
| 750 | struct mtd_info *mtd = inftl->mbd.mtd;
|
| 751 | unsigned int status;
|
| 752 | int silly = MAX_LOOPS;
|
| 753 | size_t retlen;
|
| 754 | struct inftl_bci bci;
|
| 755 |
|
| 756 | pr_debug("INFTL: INFTL_deleteblock(inftl=%p,"
|
| 757 | "block=%d)\n", inftl, block);
|
| 758 |
|
| 759 | while (thisEUN < inftl->nb_blocks) {
|
| 760 | if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
|
| 761 | blockofs, 8, &retlen, (char *)&bci) < 0)
|
| 762 | status = SECTOR_IGNORE;
|
| 763 | else
|
| 764 | status = bci.Status | bci.Status1;
|
| 765 |
|
| 766 | switch (status) {
|
| 767 | case SECTOR_FREE:
|
| 768 | case SECTOR_IGNORE:
|
| 769 | break;
|
| 770 | case SECTOR_DELETED:
|
| 771 | thisEUN = BLOCK_NIL;
|
| 772 | goto foundit;
|
| 773 | case SECTOR_USED:
|
| 774 | goto foundit;
|
| 775 | default:
|
| 776 | printk(KERN_WARNING "INFTL: unknown status for "
|
| 777 | "block %d in EUN %d: 0x%x\n",
|
| 778 | block, thisEUN, status);
|
| 779 | break;
|
| 780 | }
|
| 781 |
|
| 782 | if (!silly--) {
|
| 783 | printk(KERN_WARNING "INFTL: infinite loop in Virtual "
|
| 784 | "Unit Chain 0x%x\n",
|
| 785 | block / (inftl->EraseSize / SECTORSIZE));
|
| 786 | return 1;
|
| 787 | }
|
| 788 | thisEUN = inftl->PUtable[thisEUN];
|
| 789 | }
|
| 790 |
|
| 791 | foundit:
|
| 792 | if (thisEUN != BLOCK_NIL) {
|
| 793 | loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
|
| 794 |
|
| 795 | if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
|
| 796 | return -EIO;
|
| 797 | bci.Status = bci.Status1 = SECTOR_DELETED;
|
| 798 | if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
|
| 799 | return -EIO;
|
| 800 | INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
|
| 801 | }
|
| 802 | return 0;
|
| 803 | }
|
| 804 |
|
| 805 | static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
|
| 806 | char *buffer)
|
| 807 | {
|
| 808 | struct INFTLrecord *inftl = (void *)mbd;
|
| 809 | unsigned int writeEUN;
|
| 810 | unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
|
| 811 | size_t retlen;
|
| 812 | struct inftl_oob oob;
|
| 813 | char *p, *pend;
|
| 814 |
|
| 815 | pr_debug("INFTL: inftl_writeblock(inftl=%p,block=%ld,"
|
| 816 | "buffer=%p)\n", inftl, block, buffer);
|
| 817 |
|
| 818 |
|
| 819 | pend = buffer + SECTORSIZE;
|
| 820 | for (p = buffer; p < pend && !*p; p++)
|
| 821 | ;
|
| 822 |
|
| 823 | if (p < pend) {
|
| 824 | writeEUN = INFTL_findwriteunit(inftl, block);
|
| 825 |
|
| 826 | if (writeEUN == BLOCK_NIL) {
|
| 827 | printk(KERN_WARNING "inftl_writeblock(): cannot find "
|
| 828 | "block to write to\n");
|
| 829 | |
| 830 | * If we _still_ haven't got a block to use,
|
| 831 | * we're screwed.
|
| 832 |
|
| 833 | return 1;
|
| 834 | }
|
| 835 |
|
| 836 | memset(&oob, 0xff, sizeof(struct inftl_oob));
|
| 837 | oob.b.Status = oob.b.Status1 = SECTOR_USED;
|
| 838 |
|
| 839 | inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
|
| 840 | blockofs, SECTORSIZE, &retlen, (char *)buffer,
|
| 841 | (char *)&oob);
|
| 842 | |
| 843 | * need to write SECTOR_USED flags since they are not written
|
| 844 | * in mtd_writeecc
|
| 845 |
|
| 846 | } else {
|
| 847 | INFTL_deleteblock(inftl, block);
|
| 848 | }
|
| 849 |
|
| 850 | return 0;
|
| 851 | }
|
| 852 |
|
| 853 | static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
|
| 854 | char *buffer)
|
| 855 | {
|
| 856 | struct INFTLrecord *inftl = (void *)mbd;
|
| 857 | unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
|
| 858 | unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
|
| 859 | struct mtd_info *mtd = inftl->mbd.mtd;
|
| 860 | unsigned int status;
|
| 861 | int silly = MAX_LOOPS;
|
| 862 | struct inftl_bci bci;
|
| 863 | size_t retlen;
|
| 864 |
|
| 865 | pr_debug("INFTL: inftl_readblock(inftl=%p,block=%ld,"
|
| 866 | "buffer=%p)\n", inftl, block, buffer);
|
| 867 |
|
| 868 | while (thisEUN < inftl->nb_blocks) {
|
| 869 | if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
|
| 870 | blockofs, 8, &retlen, (char *)&bci) < 0)
|
| 871 | status = SECTOR_IGNORE;
|
| 872 | else
|
| 873 | status = bci.Status | bci.Status1;
|
| 874 |
|
| 875 | switch (status) {
|
| 876 | case SECTOR_DELETED:
|
| 877 | thisEUN = BLOCK_NIL;
|
| 878 | goto foundit;
|
| 879 | case SECTOR_USED:
|
| 880 | goto foundit;
|
| 881 | case SECTOR_FREE:
|
| 882 | case SECTOR_IGNORE:
|
| 883 | break;
|
| 884 | default:
|
| 885 | printk(KERN_WARNING "INFTL: unknown status for "
|
| 886 | "block %ld in EUN %d: 0x%04x\n",
|
| 887 | block, thisEUN, status);
|
| 888 | break;
|
| 889 | }
|
| 890 |
|
| 891 | if (!silly--) {
|
| 892 | printk(KERN_WARNING "INFTL: infinite loop in "
|
| 893 | "Virtual Unit Chain 0x%lx\n",
|
| 894 | block / (inftl->EraseSize / SECTORSIZE));
|
| 895 | return 1;
|
| 896 | }
|
| 897 |
|
| 898 | thisEUN = inftl->PUtable[thisEUN];
|
| 899 | }
|
| 900 |
|
| 901 | foundit:
|
| 902 | if (thisEUN == BLOCK_NIL) {
|
| 903 |
|
| 904 | memset(buffer, 0, SECTORSIZE);
|
| 905 | } else {
|
| 906 | size_t retlen;
|
| 907 | loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
|
| 908 | int ret = mtd_read(mtd, ptr, SECTORSIZE, &retlen, buffer);
|
| 909 |
|
| 910 |
|
| 911 | if (ret < 0 && !mtd_is_bitflip(ret))
|
| 912 | return -EIO;
|
| 913 | }
|
| 914 | return 0;
|
| 915 | }
|
| 916 |
|
| 917 | static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
|
| 918 | {
|
| 919 | struct INFTLrecord *inftl = (void *)dev;
|
| 920 |
|
| 921 | geo->heads = inftl->heads;
|
| 922 | geo->sectors = inftl->sectors;
|
| 923 | geo->cylinders = inftl->cylinders;
|
| 924 |
|
| 925 | return 0;
|
| 926 | }
|
| 927 |
|
| 928 | static struct mtd_blktrans_ops inftl_tr = {
|
| 929 | .name = "inftl",
|
| 930 | .major = INFTL_MAJOR,
|
| 931 | .part_bits = INFTL_PARTN_BITS,
|
| 932 | .blksize = 512,
|
| 933 | .getgeo = inftl_getgeo,
|
| 934 | .readsect = inftl_readblock,
|
| 935 | .writesect = inftl_writeblock,
|
| 936 | .add_mtd = inftl_add_mtd,
|
| 937 | .remove_dev = inftl_remove_dev,
|
| 938 | .owner = THIS_MODULE,
|
| 939 | };
|
| 940 |
|
| 941 | module_mtd_blktrans(inftl_tr);
|
| 942 |
|
| 943 | MODULE_LICENSE("GPL");
|
| 944 | MODULE_AUTHOR("Greg Ungerer <[email protected]>, David Woodhouse <[email protected]>, Fabrice Bellard <[email protected]> et al.");
|
| 945 | MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");
|