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