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