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