[ARM] Support register switch in nommu mode
[linux-2.6/verdex.git] / drivers / mtd / inftlcore.c
blob8a544890173d3c857c953eb149c68b14b145ba90
1 /*
2 * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
4 * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
6 * Based heavily on the nftlcore.c code which is:
7 * (c) 1999 Machine Vision Holdings, Inc.
8 * Author: David Woodhouse <dwmw2@infradead.org>
10 * $Id: inftlcore.c,v 1.19 2005/11/07 11:14:20 gleixner Exp $
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <linux/config.h>
28 #include <linux/kernel.h>
29 #include <linux/module.h>
30 #include <linux/delay.h>
31 #include <linux/slab.h>
32 #include <linux/sched.h>
33 #include <linux/init.h>
34 #include <linux/kmod.h>
35 #include <linux/hdreg.h>
36 #include <linux/mtd/mtd.h>
37 #include <linux/mtd/nftl.h>
38 #include <linux/mtd/inftl.h>
39 #include <asm/uaccess.h>
40 #include <asm/errno.h>
41 #include <asm/io.h>
44 * Maximum number of loops while examining next block, to have a
45 * chance to detect consistency problems (they should never happen
46 * because of the checks done in the mounting.
48 #define MAX_LOOPS 10000
50 extern void INFTL_dumptables(struct INFTLrecord *inftl);
51 extern void INFTL_dumpVUchains(struct INFTLrecord *inftl);
53 static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
55 struct INFTLrecord *inftl;
56 unsigned long temp;
58 if (mtd->type != MTD_NANDFLASH)
59 return;
60 /* OK, this is moderately ugly. But probably safe. Alternatives? */
61 if (memcmp(mtd->name, "DiskOnChip", 10))
62 return;
64 if (!mtd->block_isbad) {
65 printk(KERN_ERR
66 "INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
67 "Please use the new diskonchip driver under the NAND subsystem.\n");
68 return;
71 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);
73 inftl = kmalloc(sizeof(*inftl), GFP_KERNEL);
75 if (!inftl) {
76 printk(KERN_WARNING "INFTL: Out of memory for data structures\n");
77 return;
79 memset(inftl, 0, sizeof(*inftl));
81 inftl->mbd.mtd = mtd;
82 inftl->mbd.devnum = -1;
83 inftl->mbd.blksize = 512;
84 inftl->mbd.tr = tr;
85 memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
86 inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
88 if (INFTL_mount(inftl) < 0) {
89 printk(KERN_WARNING "INFTL: could not mount device\n");
90 kfree(inftl);
91 return;
94 /* OK, it's a new one. Set up all the data structures. */
96 /* Calculate geometry */
97 inftl->cylinders = 1024;
98 inftl->heads = 16;
100 temp = inftl->cylinders * inftl->heads;
101 inftl->sectors = inftl->mbd.size / temp;
102 if (inftl->mbd.size % temp) {
103 inftl->sectors++;
104 temp = inftl->cylinders * inftl->sectors;
105 inftl->heads = inftl->mbd.size / temp;
107 if (inftl->mbd.size % temp) {
108 inftl->heads++;
109 temp = inftl->heads * inftl->sectors;
110 inftl->cylinders = inftl->mbd.size / temp;
114 if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {
116 Oh no we don't have
117 mbd.size == heads * cylinders * sectors
119 printk(KERN_WARNING "INFTL: cannot calculate a geometry to "
120 "match size of 0x%lx.\n", inftl->mbd.size);
121 printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "
122 "(== 0x%lx sects)\n",
123 inftl->cylinders, inftl->heads , inftl->sectors,
124 (long)inftl->cylinders * (long)inftl->heads *
125 (long)inftl->sectors );
128 if (add_mtd_blktrans_dev(&inftl->mbd)) {
129 kfree(inftl->PUtable);
130 kfree(inftl->VUtable);
131 kfree(inftl);
132 return;
134 #ifdef PSYCHO_DEBUG
135 printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
136 #endif
137 return;
140 static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
142 struct INFTLrecord *inftl = (void *)dev;
144 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum);
146 del_mtd_blktrans_dev(dev);
148 kfree(inftl->PUtable);
149 kfree(inftl->VUtable);
150 kfree(inftl);
154 * Actual INFTL access routines.
158 * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
159 * This function is used when the give Virtual Unit Chain.
161 static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
163 u16 pot = inftl->LastFreeEUN;
164 int silly = inftl->nb_blocks;
166 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=%p,"
167 "desperate=%d)\n", inftl, desperate);
170 * Normally, we force a fold to happen before we run out of free
171 * blocks completely.
173 if (!desperate && inftl->numfreeEUNs < 2) {
174 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free "
175 "EUNs (%d)\n", inftl->numfreeEUNs);
176 return 0xffff;
179 /* Scan for a free block */
180 do {
181 if (inftl->PUtable[pot] == BLOCK_FREE) {
182 inftl->LastFreeEUN = pot;
183 return pot;
186 if (++pot > inftl->lastEUN)
187 pot = 0;
189 if (!silly--) {
190 printk(KERN_WARNING "INFTL: no free blocks found! "
191 "EUN range = %d - %d\n", 0, inftl->LastFreeEUN);
192 return BLOCK_NIL;
194 } while (pot != inftl->LastFreeEUN);
196 return BLOCK_NIL;
199 static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
201 u16 BlockMap[MAX_SECTORS_PER_UNIT];
202 unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
203 unsigned int thisEUN, prevEUN, status;
204 int block, silly;
205 unsigned int targetEUN;
206 struct inftl_oob oob;
207 size_t retlen;
209 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,"
210 "pending=%d)\n", inftl, thisVUC, pendingblock);
212 memset(BlockMap, 0xff, sizeof(BlockMap));
213 memset(BlockDeleted, 0, sizeof(BlockDeleted));
215 thisEUN = targetEUN = inftl->VUtable[thisVUC];
217 if (thisEUN == BLOCK_NIL) {
218 printk(KERN_WARNING "INFTL: trying to fold non-existent "
219 "Virtual Unit Chain %d!\n", thisVUC);
220 return BLOCK_NIL;
224 * Scan to find the Erase Unit which holds the actual data for each
225 * 512-byte block within the Chain.
227 silly = MAX_LOOPS;
228 while (thisEUN < inftl->nb_blocks) {
229 for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
230 if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
231 continue;
233 if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
234 + (block * SECTORSIZE), 16 , &retlen,
235 (char *)&oob) < 0)
236 status = SECTOR_IGNORE;
237 else
238 status = oob.b.Status | oob.b.Status1;
240 switch(status) {
241 case SECTOR_FREE:
242 case SECTOR_IGNORE:
243 break;
244 case SECTOR_USED:
245 BlockMap[block] = thisEUN;
246 continue;
247 case SECTOR_DELETED:
248 BlockDeleted[block] = 1;
249 continue;
250 default:
251 printk(KERN_WARNING "INFTL: unknown status "
252 "for block %d in EUN %d: %x\n",
253 block, thisEUN, status);
254 break;
258 if (!silly--) {
259 printk(KERN_WARNING "INFTL: infinite loop in Virtual "
260 "Unit Chain 0x%x\n", thisVUC);
261 return BLOCK_NIL;
264 thisEUN = inftl->PUtable[thisEUN];
268 * OK. We now know the location of every block in the Virtual Unit
269 * Chain, and the Erase Unit into which we are supposed to be copying.
270 * Go for it.
272 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n",
273 thisVUC, targetEUN);
275 for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
276 unsigned char movebuf[SECTORSIZE];
277 int ret;
280 * If it's in the target EUN already, or if it's pending write,
281 * do nothing.
283 if (BlockMap[block] == targetEUN || (pendingblock ==
284 (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
285 continue;
289 * Copy only in non free block (free blocks can only
290 * happen in case of media errors or deleted blocks).
292 if (BlockMap[block] == BLOCK_NIL)
293 continue;
295 ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
296 BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
297 &retlen, movebuf);
298 if (ret < 0) {
299 ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
300 BlockMap[block]) + (block * SECTORSIZE),
301 SECTORSIZE, &retlen, movebuf);
302 if (ret != -EIO)
303 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
304 "away on retry?\n");
306 memset(&oob, 0xff, sizeof(struct inftl_oob));
307 oob.b.Status = oob.b.Status1 = SECTOR_USED;
308 MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
309 (block * SECTORSIZE), SECTORSIZE, &retlen,
310 movebuf, (char *)&oob, &inftl->oobinfo);
314 * Newest unit in chain now contains data from _all_ older units.
315 * So go through and erase each unit in chain, oldest first. (This
316 * is important, by doing oldest first if we crash/reboot then it
317 * it is relatively simple to clean up the mess).
319 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",
320 thisVUC);
322 for (;;) {
323 /* Find oldest unit in chain. */
324 thisEUN = inftl->VUtable[thisVUC];
325 prevEUN = BLOCK_NIL;
326 while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
327 prevEUN = thisEUN;
328 thisEUN = inftl->PUtable[thisEUN];
331 /* Check if we are all done */
332 if (thisEUN == targetEUN)
333 break;
335 if (INFTL_formatblock(inftl, thisEUN) < 0) {
337 * Could not erase : mark block as reserved.
339 inftl->PUtable[thisEUN] = BLOCK_RESERVED;
340 } else {
341 /* Correctly erased : mark it as free */
342 inftl->PUtable[thisEUN] = BLOCK_FREE;
343 inftl->PUtable[prevEUN] = BLOCK_NIL;
344 inftl->numfreeEUNs++;
348 return targetEUN;
351 static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
354 * This is the part that needs some cleverness applied.
355 * For now, I'm doing the minimum applicable to actually
356 * get the thing to work.
357 * Wear-levelling and other clever stuff needs to be implemented
358 * and we also need to do some assessment of the results when
359 * the system loses power half-way through the routine.
361 u16 LongestChain = 0;
362 u16 ChainLength = 0, thislen;
363 u16 chain, EUN;
365 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=%p,"
366 "pending=%d)\n", inftl, pendingblock);
368 for (chain = 0; chain < inftl->nb_blocks; chain++) {
369 EUN = inftl->VUtable[chain];
370 thislen = 0;
372 while (EUN <= inftl->lastEUN) {
373 thislen++;
374 EUN = inftl->PUtable[EUN];
375 if (thislen > 0xff00) {
376 printk(KERN_WARNING "INFTL: endless loop in "
377 "Virtual Chain %d: Unit %x\n",
378 chain, EUN);
380 * Actually, don't return failure.
381 * Just ignore this chain and get on with it.
383 thislen = 0;
384 break;
388 if (thislen > ChainLength) {
389 ChainLength = thislen;
390 LongestChain = chain;
394 if (ChainLength < 2) {
395 printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
396 "for folding. Failing request\n");
397 return BLOCK_NIL;
400 return INFTL_foldchain(inftl, LongestChain, pendingblock);
403 static int nrbits(unsigned int val, int bitcount)
405 int i, total = 0;
407 for (i = 0; (i < bitcount); i++)
408 total += (((0x1 << i) & val) ? 1 : 0);
409 return total;
413 * INFTL_findwriteunit: Return the unit number into which we can write
414 * for this block. Make it available if it isn't already.
416 static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
418 unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
419 unsigned int thisEUN, writeEUN, prev_block, status;
420 unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
421 struct inftl_oob oob;
422 struct inftl_bci bci;
423 unsigned char anac, nacs, parity;
424 size_t retlen;
425 int silly, silly2 = 3;
427 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=%p,"
428 "block=%d)\n", inftl, block);
430 do {
432 * Scan the media to find a unit in the VUC which has
433 * a free space for the block in question.
435 writeEUN = BLOCK_NIL;
436 thisEUN = inftl->VUtable[thisVUC];
437 silly = MAX_LOOPS;
439 while (thisEUN <= inftl->lastEUN) {
440 MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
441 blockofs, 8, &retlen, (char *)&bci);
443 status = bci.Status | bci.Status1;
444 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
445 "EUN %d is %x\n", block , writeEUN, status);
447 switch(status) {
448 case SECTOR_FREE:
449 writeEUN = thisEUN;
450 break;
451 case SECTOR_DELETED:
452 case SECTOR_USED:
453 /* Can't go any further */
454 goto hitused;
455 case SECTOR_IGNORE:
456 break;
457 default:
459 * Invalid block. Don't use it any more.
460 * Must implement.
462 break;
465 if (!silly--) {
466 printk(KERN_WARNING "INFTL: infinite loop in "
467 "Virtual Unit Chain 0x%x\n", thisVUC);
468 return 0xffff;
471 /* Skip to next block in chain */
472 thisEUN = inftl->PUtable[thisEUN];
475 hitused:
476 if (writeEUN != BLOCK_NIL)
477 return writeEUN;
481 * OK. We didn't find one in the existing chain, or there
482 * is no existing chain. Allocate a new one.
484 writeEUN = INFTL_findfreeblock(inftl, 0);
486 if (writeEUN == BLOCK_NIL) {
488 * That didn't work - there were no free blocks just
489 * waiting to be picked up. We're going to have to fold
490 * a chain to make room.
492 thisEUN = INFTL_makefreeblock(inftl, 0xffff);
495 * Hopefully we free something, lets try again.
496 * This time we are desperate...
498 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 "
499 "to find free EUN to accommodate write to "
500 "VUC %d\n", thisVUC);
501 writeEUN = INFTL_findfreeblock(inftl, 1);
502 if (writeEUN == BLOCK_NIL) {
504 * Ouch. This should never happen - we should
505 * always be able to make some room somehow.
506 * If we get here, we've allocated more storage
507 * space than actual media, or our makefreeblock
508 * routine is missing something.
510 printk(KERN_WARNING "INFTL: cannot make free "
511 "space.\n");
512 #ifdef DEBUG
513 INFTL_dumptables(inftl);
514 INFTL_dumpVUchains(inftl);
515 #endif
516 return BLOCK_NIL;
521 * Insert new block into virtual chain. Firstly update the
522 * block headers in flash...
524 anac = 0;
525 nacs = 0;
526 thisEUN = inftl->VUtable[thisVUC];
527 if (thisEUN != BLOCK_NIL) {
528 MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize
529 + 8, 8, &retlen, (char *)&oob.u);
530 anac = oob.u.a.ANAC + 1;
531 nacs = oob.u.a.NACs + 1;
534 prev_block = inftl->VUtable[thisVUC];
535 if (prev_block < inftl->nb_blocks)
536 prev_block -= inftl->firstEUN;
538 parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
539 parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
540 parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
541 parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
543 oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
544 oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
545 oob.u.a.ANAC = anac;
546 oob.u.a.NACs = nacs;
547 oob.u.a.parityPerField = parity;
548 oob.u.a.discarded = 0xaa;
550 MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8,
551 &retlen, (char *)&oob.u);
553 /* Also back up header... */
554 oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
555 oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
556 oob.u.b.ANAC = anac;
557 oob.u.b.NACs = nacs;
558 oob.u.b.parityPerField = parity;
559 oob.u.b.discarded = 0xaa;
561 MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize +
562 SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
564 inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
565 inftl->VUtable[thisVUC] = writeEUN;
567 inftl->numfreeEUNs--;
568 return writeEUN;
570 } while (silly2--);
572 printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
573 "Unit Chain 0x%x\n", thisVUC);
574 return 0xffff;
578 * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
580 static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
582 unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
583 unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
584 unsigned int thisEUN, status;
585 int block, silly;
586 struct inftl_bci bci;
587 size_t retlen;
589 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=%p,"
590 "thisVUC=%d)\n", inftl, thisVUC);
592 memset(BlockUsed, 0, sizeof(BlockUsed));
593 memset(BlockDeleted, 0, sizeof(BlockDeleted));
595 thisEUN = inftl->VUtable[thisVUC];
596 if (thisEUN == BLOCK_NIL) {
597 printk(KERN_WARNING "INFTL: trying to delete non-existent "
598 "Virtual Unit Chain %d!\n", thisVUC);
599 return;
603 * Scan through the Erase Units to determine whether any data is in
604 * each of the 512-byte blocks within the Chain.
606 silly = MAX_LOOPS;
607 while (thisEUN < inftl->nb_blocks) {
608 for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
609 if (BlockUsed[block] || BlockDeleted[block])
610 continue;
612 if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
613 + (block * SECTORSIZE), 8 , &retlen,
614 (char *)&bci) < 0)
615 status = SECTOR_IGNORE;
616 else
617 status = bci.Status | bci.Status1;
619 switch(status) {
620 case SECTOR_FREE:
621 case SECTOR_IGNORE:
622 break;
623 case SECTOR_USED:
624 BlockUsed[block] = 1;
625 continue;
626 case SECTOR_DELETED:
627 BlockDeleted[block] = 1;
628 continue;
629 default:
630 printk(KERN_WARNING "INFTL: unknown status "
631 "for block %d in EUN %d: 0x%x\n",
632 block, thisEUN, status);
636 if (!silly--) {
637 printk(KERN_WARNING "INFTL: infinite loop in Virtual "
638 "Unit Chain 0x%x\n", thisVUC);
639 return;
642 thisEUN = inftl->PUtable[thisEUN];
645 for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
646 if (BlockUsed[block])
647 return;
650 * For each block in the chain free it and make it available
651 * for future use. Erase from the oldest unit first.
653 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC);
655 for (;;) {
656 u16 *prevEUN = &inftl->VUtable[thisVUC];
657 thisEUN = *prevEUN;
659 /* If the chain is all gone already, we're done */
660 if (thisEUN == BLOCK_NIL) {
661 DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
662 return;
665 /* Find oldest unit in chain. */
666 while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
667 BUG_ON(thisEUN >= inftl->nb_blocks);
669 prevEUN = &inftl->PUtable[thisEUN];
670 thisEUN = *prevEUN;
673 DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
674 thisEUN, thisVUC);
676 if (INFTL_formatblock(inftl, thisEUN) < 0) {
678 * Could not erase : mark block as reserved.
680 inftl->PUtable[thisEUN] = BLOCK_RESERVED;
681 } else {
682 /* Correctly erased : mark it as free */
683 inftl->PUtable[thisEUN] = BLOCK_FREE;
684 inftl->numfreeEUNs++;
687 /* Now sort out whatever was pointing to it... */
688 *prevEUN = BLOCK_NIL;
690 /* Ideally we'd actually be responsive to new
691 requests while we're doing this -- if there's
692 free space why should others be made to wait? */
693 cond_resched();
696 inftl->VUtable[thisVUC] = BLOCK_NIL;
699 static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
701 unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
702 unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
703 unsigned int status;
704 int silly = MAX_LOOPS;
705 size_t retlen;
706 struct inftl_bci bci;
708 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=%p,"
709 "block=%d)\n", inftl, block);
711 while (thisEUN < inftl->nb_blocks) {
712 if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
713 blockofs, 8, &retlen, (char *)&bci) < 0)
714 status = SECTOR_IGNORE;
715 else
716 status = bci.Status | bci.Status1;
718 switch (status) {
719 case SECTOR_FREE:
720 case SECTOR_IGNORE:
721 break;
722 case SECTOR_DELETED:
723 thisEUN = BLOCK_NIL;
724 goto foundit;
725 case SECTOR_USED:
726 goto foundit;
727 default:
728 printk(KERN_WARNING "INFTL: unknown status for "
729 "block %d in EUN %d: 0x%x\n",
730 block, thisEUN, status);
731 break;
734 if (!silly--) {
735 printk(KERN_WARNING "INFTL: infinite loop in Virtual "
736 "Unit Chain 0x%x\n",
737 block / (inftl->EraseSize / SECTORSIZE));
738 return 1;
740 thisEUN = inftl->PUtable[thisEUN];
743 foundit:
744 if (thisEUN != BLOCK_NIL) {
745 loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
747 if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
748 return -EIO;
749 bci.Status = bci.Status1 = SECTOR_DELETED;
750 if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
751 return -EIO;
752 INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
754 return 0;
757 static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
758 char *buffer)
760 struct INFTLrecord *inftl = (void *)mbd;
761 unsigned int writeEUN;
762 unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
763 size_t retlen;
764 struct inftl_oob oob;
765 char *p, *pend;
767 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=%p,block=%ld,"
768 "buffer=%p)\n", inftl, block, buffer);
770 /* Is block all zero? */
771 pend = buffer + SECTORSIZE;
772 for (p = buffer; p < pend && !*p; p++)
775 if (p < pend) {
776 writeEUN = INFTL_findwriteunit(inftl, block);
778 if (writeEUN == BLOCK_NIL) {
779 printk(KERN_WARNING "inftl_writeblock(): cannot find "
780 "block to write to\n");
782 * If we _still_ haven't got a block to use,
783 * we're screwed.
785 return 1;
788 memset(&oob, 0xff, sizeof(struct inftl_oob));
789 oob.b.Status = oob.b.Status1 = SECTOR_USED;
790 MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
791 blockofs, SECTORSIZE, &retlen, (char *)buffer,
792 (char *)&oob, &inftl->oobinfo);
794 * need to write SECTOR_USED flags since they are not written
795 * in mtd_writeecc
797 } else {
798 INFTL_deleteblock(inftl, block);
801 return 0;
804 static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
805 char *buffer)
807 struct INFTLrecord *inftl = (void *)mbd;
808 unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
809 unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
810 unsigned int status;
811 int silly = MAX_LOOPS;
812 struct inftl_bci bci;
813 size_t retlen;
815 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld,"
816 "buffer=%p)\n", inftl, block, buffer);
818 while (thisEUN < inftl->nb_blocks) {
819 if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
820 blockofs, 8, &retlen, (char *)&bci) < 0)
821 status = SECTOR_IGNORE;
822 else
823 status = bci.Status | bci.Status1;
825 switch (status) {
826 case SECTOR_DELETED:
827 thisEUN = BLOCK_NIL;
828 goto foundit;
829 case SECTOR_USED:
830 goto foundit;
831 case SECTOR_FREE:
832 case SECTOR_IGNORE:
833 break;
834 default:
835 printk(KERN_WARNING "INFTL: unknown status for "
836 "block %ld in EUN %d: 0x%04x\n",
837 block, thisEUN, status);
838 break;
841 if (!silly--) {
842 printk(KERN_WARNING "INFTL: infinite loop in "
843 "Virtual Unit Chain 0x%lx\n",
844 block / (inftl->EraseSize / SECTORSIZE));
845 return 1;
848 thisEUN = inftl->PUtable[thisEUN];
851 foundit:
852 if (thisEUN == BLOCK_NIL) {
853 /* The requested block is not on the media, return all 0x00 */
854 memset(buffer, 0, SECTORSIZE);
855 } else {
856 size_t retlen;
857 loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
858 if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
859 buffer))
860 return -EIO;
862 return 0;
865 static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
867 struct INFTLrecord *inftl = (void *)dev;
869 geo->heads = inftl->heads;
870 geo->sectors = inftl->sectors;
871 geo->cylinders = inftl->cylinders;
873 return 0;
876 static struct mtd_blktrans_ops inftl_tr = {
877 .name = "inftl",
878 .major = INFTL_MAJOR,
879 .part_bits = INFTL_PARTN_BITS,
880 .getgeo = inftl_getgeo,
881 .readsect = inftl_readblock,
882 .writesect = inftl_writeblock,
883 .add_mtd = inftl_add_mtd,
884 .remove_dev = inftl_remove_dev,
885 .owner = THIS_MODULE,
888 extern char inftlmountrev[];
890 static int __init init_inftl(void)
892 printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.19 $, "
893 "inftlmount.c %s\n", inftlmountrev);
895 return register_mtd_blktrans(&inftl_tr);
898 static void __exit cleanup_inftl(void)
900 deregister_mtd_blktrans(&inftl_tr);
903 module_init(init_inftl);
904 module_exit(cleanup_inftl);
906 MODULE_LICENSE("GPL");
907 MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
908 MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");