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