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