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