ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / mtd / nftlcore.c
1 /* Linux driver for NAND Flash Translation Layer      */
2 /* (c) 1999 Machine Vision Holdings, Inc.             */
3 /* Author: David Woodhouse <dwmw2@infradead.org>      */
4 /* $Id: nftlcore.c,v 1.94 2003/06/23 12:00:08 dwmw2 Exp $ */
5
6 /*
7   The contents of this file are distributed under the GNU General
8   Public License version 2. The author places no additional
9   restrictions of any kind on it.
10  */
11
12 #define PRERELEASE
13
14 #include <linux/config.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <asm/errno.h>
18 #include <asm/io.h>
19 #include <asm/uaccess.h>
20 #include <linux/miscdevice.h>
21 #include <linux/pci.h>
22 #include <linux/delay.h>
23 #include <linux/slab.h>
24 #include <linux/sched.h>
25 #include <linux/init.h>
26 #include <linux/hdreg.h>
27
28 #include <linux/kmod.h>
29 #include <linux/mtd/mtd.h>
30 #include <linux/mtd/nand.h>
31 #include <linux/mtd/nftl.h>
32 #include <linux/mtd/blktrans.h>
33
34 /* maximum number of loops while examining next block, to have a
35    chance to detect consistency problems (they should never happen
36    because of the checks done in the mounting */
37
38 #define MAX_LOOPS 10000
39
40
41 static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
42 {
43         struct NFTLrecord *nftl;
44         unsigned long temp;
45
46         if (mtd->ecctype != MTD_ECC_RS_DiskOnChip)
47                 return;
48
49         DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
50
51         nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
52
53         if (!nftl) {
54                 printk(KERN_WARNING "NFTL: out of memory for data structures\n");
55                 return;
56         }
57         memset(nftl, 0, sizeof(*nftl));
58
59         nftl->mbd.mtd = mtd;
60         nftl->mbd.devnum = -1;
61         nftl->mbd.blksize = 512;
62         nftl->mbd.tr = tr;
63
64         if (NFTL_mount(nftl) < 0) {
65                 printk(KERN_WARNING "NFTL: could not mount device\n");
66                 kfree(nftl);
67                 return;
68         }
69
70         /* OK, it's a new one. Set up all the data structures. */
71
72         /* Calculate geometry */
73         nftl->cylinders = 1024;
74         nftl->heads = 16;
75
76         temp = nftl->cylinders * nftl->heads;
77         nftl->sectors = nftl->mbd.size / temp;
78         if (nftl->mbd.size % temp) {
79                 nftl->sectors++;
80                 temp = nftl->cylinders * nftl->sectors;
81                 nftl->heads = nftl->mbd.size / temp;
82
83                 if (nftl->mbd.size % temp) {
84                         nftl->heads++;
85                         temp = nftl->heads * nftl->sectors;
86                         nftl->cylinders = nftl->mbd.size / temp;
87                 }
88         }
89
90         if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
91                 /*
92                   Oh no we don't have 
93                    mbd.size == heads * cylinders * sectors
94                 */
95                 printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
96                        "match size of 0x%lx.\n", nftl->mbd.size);
97                 printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
98                         "(== 0x%lx sects)\n",
99                         nftl->cylinders, nftl->heads , nftl->sectors, 
100                         (long)nftl->cylinders * (long)nftl->heads *
101                         (long)nftl->sectors );
102         }
103
104         if (add_mtd_blktrans_dev(&nftl->mbd)) {
105                 if (nftl->ReplUnitTable)
106                         kfree(nftl->ReplUnitTable);
107                 if (nftl->EUNtable)
108                         kfree(nftl->EUNtable);
109                 kfree(nftl);
110                 return;
111         }
112 #ifdef PSYCHO_DEBUG
113         printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
114 #endif
115 }
116
117 static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
118 {
119         struct NFTLrecord *nftl = (void *)dev;
120
121         DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum);
122
123         del_mtd_blktrans_dev(dev);
124         if (nftl->ReplUnitTable)
125                 kfree(nftl->ReplUnitTable);
126         if (nftl->EUNtable)
127                 kfree(nftl->EUNtable);
128         kfree(nftl);
129 }
130
131 #ifdef CONFIG_NFTL_RW
132
133 /* Actual NFTL access routines */
134 /* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
135  *      when the give Virtual Unit Chain
136  */
137 static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
138 {
139         /* For a given Virtual Unit Chain: find or create a free block and
140            add it to the chain */
141         /* We're passed the number of the last EUN in the chain, to save us from
142            having to look it up again */
143         u16 pot = nftl->LastFreeEUN;
144         int silly = nftl->nb_blocks;
145
146         /* Normally, we force a fold to happen before we run out of free blocks completely */
147         if (!desperate && nftl->numfreeEUNs < 2) {
148                 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
149                 return 0xffff;
150         }
151
152         /* Scan for a free block */
153         do {
154                 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
155                         nftl->LastFreeEUN = pot;
156                         nftl->numfreeEUNs--;
157                         return pot;
158                 }
159
160                 /* This will probably point to the MediaHdr unit itself,
161                    right at the beginning of the partition. But that unit
162                    (and the backup unit too) should have the UCI set
163                    up so that it's not selected for overwriting */
164                 if (++pot > nftl->lastEUN)
165                         pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
166
167                 if (!silly--) {
168                         printk("Argh! No free blocks found! LastFreeEUN = %d, "
169                                "FirstEUN = %d\n", nftl->LastFreeEUN, 
170                                le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
171                         return 0xffff;
172                 }
173         } while (pot != nftl->LastFreeEUN);
174
175         return 0xffff;
176 }
177
178 static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
179 {
180         u16 BlockMap[MAX_SECTORS_PER_UNIT];
181         unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
182         unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
183         unsigned int thisEUN;
184         int block;
185         int silly;
186         unsigned int targetEUN;
187         struct nftl_oob oob;
188         int inplace = 1;
189         size_t retlen;
190
191         memset(BlockMap, 0xff, sizeof(BlockMap));
192         memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
193
194         thisEUN = nftl->EUNtable[thisVUC];
195
196         if (thisEUN == BLOCK_NIL) {
197                 printk(KERN_WARNING "Trying to fold non-existent "
198                        "Virtual Unit Chain %d!\n", thisVUC);
199                 return BLOCK_NIL;
200         }
201         
202         /* Scan to find the Erase Unit which holds the actual data for each
203            512-byte block within the Chain.
204         */
205         silly = MAX_LOOPS;
206         targetEUN = BLOCK_NIL;
207         while (thisEUN <= nftl->lastEUN ) {
208                 unsigned int status, foldmark;
209
210                 targetEUN = thisEUN;
211                 for (block = 0; block < nftl->EraseSize / 512; block ++) {
212                         MTD_READOOB(nftl->mbd.mtd,
213                                     (thisEUN * nftl->EraseSize) + (block * 512),
214                                     16 , &retlen, (char *)&oob);
215                         if (block == 2) {
216                                 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
217                                 if (foldmark == FOLD_MARK_IN_PROGRESS) {
218                                         DEBUG(MTD_DEBUG_LEVEL1, 
219                                               "Write Inhibited on EUN %d\n", thisEUN);
220                                         inplace = 0;
221                                 } else {
222                                         /* There's no other reason not to do inplace,
223                                            except ones that come later. So we don't need
224                                            to preserve inplace */
225                                         inplace = 1;
226                                 }
227                         }
228                         status = oob.b.Status | oob.b.Status1;
229                         BlockLastState[block] = status;
230
231                         switch(status) {
232                         case SECTOR_FREE:
233                                 BlockFreeFound[block] = 1;
234                                 break;
235
236                         case SECTOR_USED:
237                                 if (!BlockFreeFound[block])
238                                         BlockMap[block] = thisEUN;
239                                 else
240                                         printk(KERN_WARNING 
241                                                "SECTOR_USED found after SECTOR_FREE "
242                                                "in Virtual Unit Chain %d for block %d\n",
243                                                thisVUC, block);
244                                 break;
245                         case SECTOR_DELETED:
246                                 if (!BlockFreeFound[block])
247                                         BlockMap[block] = BLOCK_NIL;
248                                 else
249                                         printk(KERN_WARNING 
250                                                "SECTOR_DELETED found after SECTOR_FREE "
251                                                "in Virtual Unit Chain %d for block %d\n",
252                                                thisVUC, block);
253                                 break;
254
255                         case SECTOR_IGNORE:
256                                 break;
257                         default:
258                                 printk("Unknown status for block %d in EUN %d: %x\n",
259                                        block, thisEUN, status);
260                         }
261                 }
262
263                 if (!silly--) {
264                         printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
265                                thisVUC);
266                         return BLOCK_NIL;
267                 }
268                 
269                 thisEUN = nftl->ReplUnitTable[thisEUN];
270         }
271
272         if (inplace) {
273                 /* We're being asked to be a fold-in-place. Check
274                    that all blocks which actually have data associated
275                    with them (i.e. BlockMap[block] != BLOCK_NIL) are 
276                    either already present or SECTOR_FREE in the target
277                    block. If not, we're going to have to fold out-of-place
278                    anyway.
279                 */
280                 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
281                         if (BlockLastState[block] != SECTOR_FREE &&
282                             BlockMap[block] != BLOCK_NIL &&
283                             BlockMap[block] != targetEUN) {
284                                 DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
285                                       "block %d was %x lastEUN, "
286                                       "and is in EUN %d (%s) %d\n",
287                                       thisVUC, block, BlockLastState[block],
288                                       BlockMap[block], 
289                                       BlockMap[block]== targetEUN ? "==" : "!=",
290                                       targetEUN);
291                                 inplace = 0;
292                                 break;
293                         }
294                 }
295
296                 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
297                     pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
298                     BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
299                     SECTOR_FREE) {
300                         DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
301                               "Folding out of place.\n", targetEUN);
302                         inplace = 0;
303                 }
304         }
305         
306         if (!inplace) {
307                 DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
308                       "Trying out-of-place\n", thisVUC);
309                 /* We need to find a targetEUN to fold into. */
310                 targetEUN = NFTL_findfreeblock(nftl, 1);
311                 if (targetEUN == BLOCK_NIL) {
312                         /* Ouch. Now we're screwed. We need to do a 
313                            fold-in-place of another chain to make room
314                            for this one. We need a better way of selecting
315                            which chain to fold, because makefreeblock will 
316                            only ask us to fold the same one again.
317                         */
318                         printk(KERN_WARNING
319                                "NFTL_findfreeblock(desperate) returns 0xffff.\n");
320                         return BLOCK_NIL;
321                 }
322         } else {
323             /* We put a fold mark in the chain we are folding only if
324                we fold in place to help the mount check code. If we do
325                not fold in place, it is possible to find the valid
326                chain by selecting the longer one */
327             oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
328             oob.u.c.unused = 0xffffffff;
329             MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, 
330                          8, &retlen, (char *)&oob.u);
331         }
332
333         /* OK. We now know the location of every block in the Virtual Unit Chain,
334            and the Erase Unit into which we are supposed to be copying.
335            Go for it.
336         */
337         DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
338         for (block = 0; block < nftl->EraseSize / 512 ; block++) {
339                 unsigned char movebuf[512];
340                 int ret;
341
342                 /* If it's in the target EUN already, or if it's pending write, do nothing */
343                 if (BlockMap[block] == targetEUN ||
344                     (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
345                         continue;
346                 }
347
348                 /* copy only in non free block (free blocks can only
349                    happen in case of media errors or deleted blocks) */
350                 if (BlockMap[block] == BLOCK_NIL)
351                         continue;
352                 
353                 ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
354                                   512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); 
355                 if (ret < 0) {
356                     ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block])
357                                       + (block * 512), 512, &retlen,
358                                       movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); 
359                     if (ret != -EIO) 
360                         printk("Error went away on retry.\n");
361                 }
362                 MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512),
363                              512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
364         }
365         
366         /* add the header so that it is now a valid chain */
367         oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
368                 = cpu_to_le16(thisVUC);
369         oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
370         
371         MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8, 
372                      8, &retlen, (char *)&oob.u);
373
374         /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
375
376         /* At this point, we have two different chains for this Virtual Unit, and no way to tell 
377            them apart. If we crash now, we get confused. However, both contain the same data, so we
378            shouldn't actually lose data in this case. It's just that when we load up on a medium which
379            has duplicate chains, we need to free one of the chains because it's not necessary any more.
380         */
381         thisEUN = nftl->EUNtable[thisVUC];
382         DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
383
384         /* For each block in the old chain (except the targetEUN of course), 
385            free it and make it available for future use */
386         while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
387                 unsigned int EUNtmp;
388
389                 EUNtmp = nftl->ReplUnitTable[thisEUN];
390
391                 if (NFTL_formatblock(nftl, thisEUN) < 0) {
392                         /* could not erase : mark block as reserved
393                          * FixMe: Update Bad Unit Table on disk
394                          */
395                         nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
396                 } else {
397                         /* correctly erased : mark it as free */
398                         nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
399                         nftl->numfreeEUNs++;
400                 }
401                 thisEUN = EUNtmp;
402         }
403         
404         /* Make this the new start of chain for thisVUC */
405         nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
406         nftl->EUNtable[thisVUC] = targetEUN;
407
408         return targetEUN;
409 }
410
411 u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
412 {
413         /* This is the part that needs some cleverness applied. 
414            For now, I'm doing the minimum applicable to actually
415            get the thing to work.
416            Wear-levelling and other clever stuff needs to be implemented
417            and we also need to do some assessment of the results when
418            the system loses power half-way through the routine.
419         */
420         u16 LongestChain = 0;
421         u16 ChainLength = 0, thislen;
422         u16 chain, EUN;
423
424         for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
425                 EUN = nftl->EUNtable[chain];
426                 thislen = 0;
427
428                 while (EUN <= nftl->lastEUN) {
429                         thislen++;
430                         //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
431                         EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
432                         if (thislen > 0xff00) {
433                                 printk("Endless loop in Virtual Chain %d: Unit %x\n",
434                                        chain, EUN);
435                         }
436                         if (thislen > 0xff10) {
437                                 /* Actually, don't return failure. Just ignore this chain and
438                                    get on with it. */
439                                 thislen = 0;
440                                 break;
441                         }
442                 }
443
444                 if (thislen > ChainLength) {
445                         //printk("New longest chain is %d with length %d\n", chain, thislen);
446                         ChainLength = thislen;
447                         LongestChain = chain;
448                 }
449         }
450
451         if (ChainLength < 2) {
452                 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
453                        "Failing request\n");
454                 return 0xffff;
455         }
456
457         return NFTL_foldchain (nftl, LongestChain, pendingblock);
458 }
459
460 /* NFTL_findwriteunit: Return the unit number into which we can write 
461                        for this block. Make it available if it isn't already
462 */
463 static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
464 {
465         u16 lastEUN;
466         u16 thisVUC = block / (nftl->EraseSize / 512);
467         unsigned int writeEUN;
468         unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
469         size_t retlen;
470         int silly, silly2 = 3;
471         struct nftl_oob oob;
472
473         do {
474                 /* Scan the media to find a unit in the VUC which has
475                    a free space for the block in question.
476                 */
477
478                 /* This condition catches the 0x[7f]fff cases, as well as 
479                    being a sanity check for past-end-of-media access
480                 */
481                 lastEUN = BLOCK_NIL;
482                 writeEUN = nftl->EUNtable[thisVUC];
483                 silly = MAX_LOOPS;
484                 while (writeEUN <= nftl->lastEUN) {
485                         struct nftl_bci bci;
486                         size_t retlen;
487                         unsigned int status;
488
489                         lastEUN = writeEUN;
490
491                         MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
492                                     8, &retlen, (char *)&bci);
493                         
494                         DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
495                               block , writeEUN, le16_to_cpu(bci.Status));
496
497                         status = bci.Status | bci.Status1;
498                         switch(status) {
499                         case SECTOR_FREE:
500                                 return writeEUN;
501
502                         case SECTOR_DELETED:
503                         case SECTOR_USED:
504                         case SECTOR_IGNORE:
505                                 break;
506                         default:
507                                 // Invalid block. Don't use it any more. Must implement.
508                                 break;                  
509                         }
510                         
511                         if (!silly--) { 
512                                 printk(KERN_WARNING
513                                        "Infinite loop in Virtual Unit Chain 0x%x\n",
514                                        thisVUC);
515                                 return 0xffff;
516                         }
517
518                         /* Skip to next block in chain */
519                         writeEUN = nftl->ReplUnitTable[writeEUN];
520                 }
521
522                 /* OK. We didn't find one in the existing chain, or there 
523                    is no existing chain. */
524
525                 /* Try to find an already-free block */
526                 writeEUN = NFTL_findfreeblock(nftl, 0);
527
528                 if (writeEUN == BLOCK_NIL) {
529                         /* That didn't work - there were no free blocks just
530                            waiting to be picked up. We're going to have to fold
531                            a chain to make room.
532                         */
533
534                         /* First remember the start of this chain */
535                         //u16 startEUN = nftl->EUNtable[thisVUC];
536                         
537                         //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
538                         writeEUN = NFTL_makefreeblock(nftl, 0xffff);
539
540                         if (writeEUN == BLOCK_NIL) {
541                                 /* OK, we accept that the above comment is 
542                                    lying - there may have been free blocks
543                                    last time we called NFTL_findfreeblock(),
544                                    but they are reserved for when we're
545                                    desperate. Well, now we're desperate.
546                                 */
547                                 DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
548                                 writeEUN = NFTL_findfreeblock(nftl, 1);
549                         }
550                         if (writeEUN == BLOCK_NIL) {
551                                 /* Ouch. This should never happen - we should
552                                    always be able to make some room somehow. 
553                                    If we get here, we've allocated more storage 
554                                    space than actual media, or our makefreeblock
555                                    routine is missing something.
556                                 */
557                                 printk(KERN_WARNING "Cannot make free space.\n");
558                                 return BLOCK_NIL;
559                         }                       
560                         //printk("Restarting scan\n");
561                         lastEUN = BLOCK_NIL;
562                         continue;
563                 }
564
565                 /* We've found a free block. Insert it into the chain. */
566                 
567                 if (lastEUN != BLOCK_NIL) {
568                     thisVUC |= 0x8000; /* It's a replacement block */
569                 } else {
570                     /* The first block in a new chain */
571                     nftl->EUNtable[thisVUC] = writeEUN;
572                 }
573
574                 /* set up the actual EUN we're writing into */
575                 /* Both in our cache... */
576                 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
577
578                 /* ... and on the flash itself */
579                 MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
580                             &retlen, (char *)&oob.u);
581
582                 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
583
584                 MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
585                              &retlen, (char *)&oob.u);
586
587                 /* we link the new block to the chain only after the
588                    block is ready. It avoids the case where the chain
589                    could point to a free block */
590                 if (lastEUN != BLOCK_NIL) {
591                         /* Both in our cache... */
592                         nftl->ReplUnitTable[lastEUN] = writeEUN;
593                         /* ... and on the flash itself */
594                         MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
595                                     8, &retlen, (char *)&oob.u);
596
597                         oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
598                                 = cpu_to_le16(writeEUN);
599
600                         MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
601                                      8, &retlen, (char *)&oob.u);
602                 }
603
604                 return writeEUN;
605
606         } while (silly2--);
607
608         printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
609                thisVUC);
610         return 0xffff;
611 }
612
613 static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
614                            char *buffer)
615 {
616         struct NFTLrecord *nftl = (void *)mbd;
617         u16 writeEUN;
618         unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
619         size_t retlen;
620         u8 eccbuf[6];
621
622         writeEUN = NFTL_findwriteunit(nftl, block);
623
624         if (writeEUN == BLOCK_NIL) {
625                 printk(KERN_WARNING
626                        "NFTL_writeblock(): Cannot find block to write to\n");
627                 /* If we _still_ haven't got a block to use, we're screwed */
628                 return 1;
629         }
630
631         MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
632                      512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP);
633         /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */
634
635         return 0;
636 }
637 #endif /* CONFIG_NFTL_RW */
638
639 static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
640                           char *buffer)
641 {
642         struct NFTLrecord *nftl = (void *)mbd;
643         u16 lastgoodEUN;
644         u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
645         unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
646         unsigned int status;
647         int silly = MAX_LOOPS;
648         size_t retlen;
649         struct nftl_bci bci;
650
651         lastgoodEUN = BLOCK_NIL;
652
653         if (thisEUN != BLOCK_NIL) {
654                 while (thisEUN < nftl->nb_blocks) {
655                         if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs,
656                                         8, &retlen, (char *)&bci) < 0)
657                                 status = SECTOR_IGNORE;
658                         else
659                                 status = bci.Status | bci.Status1;
660
661                         switch (status) {
662                         case SECTOR_FREE:
663                                 /* no modification of a sector should follow a free sector */
664                                 goto the_end;
665                         case SECTOR_DELETED:
666                                 lastgoodEUN = BLOCK_NIL;
667                                 break;
668                         case SECTOR_USED:
669                                 lastgoodEUN = thisEUN;
670                                 break;
671                         case SECTOR_IGNORE:
672                                 break;
673                         default:
674                                 printk("Unknown status for block %ld in EUN %d: %x\n",
675                                        block, thisEUN, status);
676                                 break;
677                         }
678
679                         if (!silly--) {
680                                 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
681                                        block / (nftl->EraseSize / 512));
682                                 return 1;
683                         }
684                         thisEUN = nftl->ReplUnitTable[thisEUN];
685                 }
686         }
687
688  the_end:
689         if (lastgoodEUN == BLOCK_NIL) {
690                 /* the requested block is not on the media, return all 0x00 */
691                 memset(buffer, 0, 512);
692         } else {
693                 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
694                 size_t retlen;
695                 u_char eccbuf[6];
696                 if (MTD_READECC(nftl->mbd.mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
697                         return -EIO;
698         }
699         return 0;
700 }
701
702 static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
703 {
704         struct NFTLrecord *nftl = (void *)dev;
705
706         geo->heads = nftl->heads;
707         geo->sectors = nftl->sectors;
708         geo->cylinders = nftl->cylinders;
709
710         return 0;
711 }
712
713 /****************************************************************************
714  *
715  * Module stuff
716  *
717  ****************************************************************************/
718
719
720 struct mtd_blktrans_ops nftl_tr = {
721         .name           = "nftl",
722         .major          = NFTL_MAJOR,
723         .part_bits      = NFTL_PARTN_BITS,
724         .getgeo         = nftl_getgeo,
725         .readsect       = nftl_readblock,
726 #ifdef CONFIG_NFTL_RW
727         .writesect      = nftl_writeblock,
728 #endif
729         .add_mtd        = nftl_add_mtd,
730         .remove_dev     = nftl_remove_dev,
731         .owner          = THIS_MODULE,
732 };
733
734 extern char nftlmountrev[];
735
736 int __init init_nftl(void)
737 {
738         printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.94 $, nftlmount.c %s\n", nftlmountrev);
739
740         return register_mtd_blktrans(&nftl_tr);
741 }
742
743 static void __exit cleanup_nftl(void)
744 {
745         deregister_mtd_blktrans(&nftl_tr);
746 }
747
748 module_init(init_nftl);
749 module_exit(cleanup_nftl);
750
751 MODULE_LICENSE("GPL");
752 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
753 MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");