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