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 $ */
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.
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <asm/errno.h>
18 #include <asm/uaccess.h>
19 #include <linux/miscdevice.h>
20 #include <linux/pci.h>
21 #include <linux/delay.h>
22 #include <linux/slab.h>
23 #include <linux/sched.h>
24 #include <linux/init.h>
25 #include <linux/hdreg.h>
27 #include <linux/kmod.h>
28 #include <linux/mtd/mtd.h>
29 #include <linux/mtd/nand.h>
30 #include <linux/mtd/nftl.h>
31 #include <linux/mtd/blktrans.h>
33 /* maximum number of loops while examining next block, to have a
34 chance to detect consistency problems (they should never happen
35 because of the checks done in the mounting */
37 #define MAX_LOOPS 10000
40 static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
42 struct NFTLrecord *nftl;
45 if (mtd->type != MTD_NANDFLASH)
47 /* OK, this is moderately ugly. But probably safe. Alternatives? */
48 if (memcmp(mtd->name, "DiskOnChip", 10))
51 if (!mtd->block_isbad) {
53 "NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
54 "Please use the new diskonchip driver under the NAND subsystem.\n");
58 DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
60 nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
63 printk(KERN_WARNING "NFTL: out of memory for data structures\n");
66 memset(nftl, 0, sizeof(*nftl));
69 nftl->mbd.devnum = -1;
73 if (NFTL_mount(nftl) < 0) {
74 printk(KERN_WARNING "NFTL: could not mount device\n");
79 /* OK, it's a new one. Set up all the data structures. */
81 /* Calculate geometry */
82 nftl->cylinders = 1024;
85 temp = nftl->cylinders * nftl->heads;
86 nftl->sectors = nftl->mbd.size / temp;
87 if (nftl->mbd.size % temp) {
89 temp = nftl->cylinders * nftl->sectors;
90 nftl->heads = nftl->mbd.size / temp;
92 if (nftl->mbd.size % temp) {
94 temp = nftl->heads * nftl->sectors;
95 nftl->cylinders = nftl->mbd.size / temp;
99 if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
102 mbd.size == heads * cylinders * sectors
104 printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
105 "match size of 0x%lx.\n", nftl->mbd.size);
106 printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
107 "(== 0x%lx sects)\n",
108 nftl->cylinders, nftl->heads , nftl->sectors,
109 (long)nftl->cylinders * (long)nftl->heads *
110 (long)nftl->sectors );
113 if (add_mtd_blktrans_dev(&nftl->mbd)) {
114 kfree(nftl->ReplUnitTable);
115 kfree(nftl->EUNtable);
120 printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
124 static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
126 struct NFTLrecord *nftl = (void *)dev;
128 DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum);
130 del_mtd_blktrans_dev(dev);
131 kfree(nftl->ReplUnitTable);
132 kfree(nftl->EUNtable);
137 * Read oob data from flash
139 int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
140 size_t *retlen, uint8_t *buf)
142 struct mtd_oob_ops ops;
145 ops.mode = MTD_OOB_PLACE;
146 ops.ooboffs = offs & (mtd->writesize - 1);
151 res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
152 *retlen = ops.oobretlen;
157 * Write oob data to flash
159 int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
160 size_t *retlen, uint8_t *buf)
162 struct mtd_oob_ops ops;
165 ops.mode = MTD_OOB_PLACE;
166 ops.ooboffs = offs & (mtd->writesize - 1);
171 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
172 *retlen = ops.oobretlen;
176 #ifdef CONFIG_NFTL_RW
179 * Write data and oob to flash
181 static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
182 size_t *retlen, uint8_t *buf, uint8_t *oob)
184 struct mtd_oob_ops ops;
187 ops.mode = MTD_OOB_PLACE;
189 ops.ooblen = mtd->oobsize;
194 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
195 *retlen = ops.retlen;
199 /* Actual NFTL access routines */
200 /* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
201 * when the give Virtual Unit Chain
203 static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
205 /* For a given Virtual Unit Chain: find or create a free block and
206 add it to the chain */
207 /* We're passed the number of the last EUN in the chain, to save us from
208 having to look it up again */
209 u16 pot = nftl->LastFreeEUN;
210 int silly = nftl->nb_blocks;
212 /* Normally, we force a fold to happen before we run out of free blocks completely */
213 if (!desperate && nftl->numfreeEUNs < 2) {
214 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
218 /* Scan for a free block */
220 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
221 nftl->LastFreeEUN = pot;
226 /* This will probably point to the MediaHdr unit itself,
227 right at the beginning of the partition. But that unit
228 (and the backup unit too) should have the UCI set
229 up so that it's not selected for overwriting */
230 if (++pot > nftl->lastEUN)
231 pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
234 printk("Argh! No free blocks found! LastFreeEUN = %d, "
235 "FirstEUN = %d\n", nftl->LastFreeEUN,
236 le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
239 } while (pot != nftl->LastFreeEUN);
244 static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
246 struct mtd_info *mtd = nftl->mbd.mtd;
247 u16 BlockMap[MAX_SECTORS_PER_UNIT];
248 unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
249 unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
250 unsigned int thisEUN;
253 unsigned int targetEUN;
258 memset(BlockMap, 0xff, sizeof(BlockMap));
259 memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
261 thisEUN = nftl->EUNtable[thisVUC];
263 if (thisEUN == BLOCK_NIL) {
264 printk(KERN_WARNING "Trying to fold non-existent "
265 "Virtual Unit Chain %d!\n", thisVUC);
269 /* Scan to find the Erase Unit which holds the actual data for each
270 512-byte block within the Chain.
273 targetEUN = BLOCK_NIL;
274 while (thisEUN <= nftl->lastEUN ) {
275 unsigned int status, foldmark;
278 for (block = 0; block < nftl->EraseSize / 512; block ++) {
279 nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
280 (block * 512), 16 , &retlen,
283 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
284 if (foldmark == FOLD_MARK_IN_PROGRESS) {
285 DEBUG(MTD_DEBUG_LEVEL1,
286 "Write Inhibited on EUN %d\n", thisEUN);
289 /* There's no other reason not to do inplace,
290 except ones that come later. So we don't need
291 to preserve inplace */
295 status = oob.b.Status | oob.b.Status1;
296 BlockLastState[block] = status;
300 BlockFreeFound[block] = 1;
304 if (!BlockFreeFound[block])
305 BlockMap[block] = thisEUN;
308 "SECTOR_USED found after SECTOR_FREE "
309 "in Virtual Unit Chain %d for block %d\n",
313 if (!BlockFreeFound[block])
314 BlockMap[block] = BLOCK_NIL;
317 "SECTOR_DELETED found after SECTOR_FREE "
318 "in Virtual Unit Chain %d for block %d\n",
325 printk("Unknown status for block %d in EUN %d: %x\n",
326 block, thisEUN, status);
331 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
336 thisEUN = nftl->ReplUnitTable[thisEUN];
340 /* We're being asked to be a fold-in-place. Check
341 that all blocks which actually have data associated
342 with them (i.e. BlockMap[block] != BLOCK_NIL) are
343 either already present or SECTOR_FREE in the target
344 block. If not, we're going to have to fold out-of-place
347 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
348 if (BlockLastState[block] != SECTOR_FREE &&
349 BlockMap[block] != BLOCK_NIL &&
350 BlockMap[block] != targetEUN) {
351 DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
352 "block %d was %x lastEUN, "
353 "and is in EUN %d (%s) %d\n",
354 thisVUC, block, BlockLastState[block],
356 BlockMap[block]== targetEUN ? "==" : "!=",
363 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
364 pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
365 BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
367 DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
368 "Folding out of place.\n", targetEUN);
374 DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
375 "Trying out-of-place\n", thisVUC);
376 /* We need to find a targetEUN to fold into. */
377 targetEUN = NFTL_findfreeblock(nftl, 1);
378 if (targetEUN == BLOCK_NIL) {
379 /* Ouch. Now we're screwed. We need to do a
380 fold-in-place of another chain to make room
381 for this one. We need a better way of selecting
382 which chain to fold, because makefreeblock will
383 only ask us to fold the same one again.
386 "NFTL_findfreeblock(desperate) returns 0xffff.\n");
390 /* We put a fold mark in the chain we are folding only if we
391 fold in place to help the mount check code. If we do not fold in
392 place, it is possible to find the valid chain by selecting the
394 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
395 oob.u.c.unused = 0xffffffff;
396 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
397 8, &retlen, (char *)&oob.u);
400 /* OK. We now know the location of every block in the Virtual Unit Chain,
401 and the Erase Unit into which we are supposed to be copying.
404 DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
405 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
406 unsigned char movebuf[512];
409 /* If it's in the target EUN already, or if it's pending write, do nothing */
410 if (BlockMap[block] == targetEUN ||
411 (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
415 /* copy only in non free block (free blocks can only
416 happen in case of media errors or deleted blocks) */
417 if (BlockMap[block] == BLOCK_NIL)
420 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
421 512, &retlen, movebuf);
422 if (ret < 0 && ret != -EUCLEAN) {
423 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block])
424 + (block * 512), 512, &retlen,
427 printk("Error went away on retry.\n");
429 memset(&oob, 0xff, sizeof(struct nftl_oob));
430 oob.b.Status = oob.b.Status1 = SECTOR_USED;
432 nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
433 (block * 512), 512, &retlen, movebuf, (char *)&oob);
436 /* add the header so that it is now a valid chain */
437 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
438 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
440 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
441 8, &retlen, (char *)&oob.u);
443 /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
445 /* At this point, we have two different chains for this Virtual Unit, and no way to tell
446 them apart. If we crash now, we get confused. However, both contain the same data, so we
447 shouldn't actually lose data in this case. It's just that when we load up on a medium which
448 has duplicate chains, we need to free one of the chains because it's not necessary any more.
450 thisEUN = nftl->EUNtable[thisVUC];
451 DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
453 /* For each block in the old chain (except the targetEUN of course),
454 free it and make it available for future use */
455 while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
458 EUNtmp = nftl->ReplUnitTable[thisEUN];
460 if (NFTL_formatblock(nftl, thisEUN) < 0) {
461 /* could not erase : mark block as reserved
463 nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
465 /* correctly erased : mark it as free */
466 nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
472 /* Make this the new start of chain for thisVUC */
473 nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
474 nftl->EUNtable[thisVUC] = targetEUN;
479 static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
481 /* This is the part that needs some cleverness applied.
482 For now, I'm doing the minimum applicable to actually
483 get the thing to work.
484 Wear-levelling and other clever stuff needs to be implemented
485 and we also need to do some assessment of the results when
486 the system loses power half-way through the routine.
488 u16 LongestChain = 0;
489 u16 ChainLength = 0, thislen;
492 for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
493 EUN = nftl->EUNtable[chain];
496 while (EUN <= nftl->lastEUN) {
498 //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
499 EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
500 if (thislen > 0xff00) {
501 printk("Endless loop in Virtual Chain %d: Unit %x\n",
504 if (thislen > 0xff10) {
505 /* Actually, don't return failure. Just ignore this chain and
512 if (thislen > ChainLength) {
513 //printk("New longest chain is %d with length %d\n", chain, thislen);
514 ChainLength = thislen;
515 LongestChain = chain;
519 if (ChainLength < 2) {
520 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
521 "Failing request\n");
525 return NFTL_foldchain (nftl, LongestChain, pendingblock);
528 /* NFTL_findwriteunit: Return the unit number into which we can write
529 for this block. Make it available if it isn't already
531 static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
534 u16 thisVUC = block / (nftl->EraseSize / 512);
535 struct mtd_info *mtd = nftl->mbd.mtd;
536 unsigned int writeEUN;
537 unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
539 int silly, silly2 = 3;
543 /* Scan the media to find a unit in the VUC which has
544 a free space for the block in question.
547 /* This condition catches the 0x[7f]fff cases, as well as
548 being a sanity check for past-end-of-media access
551 writeEUN = nftl->EUNtable[thisVUC];
553 while (writeEUN <= nftl->lastEUN) {
561 (writeEUN * nftl->EraseSize) + blockofs,
562 8, &retlen, (char *)&bci);
564 DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
565 block , writeEUN, le16_to_cpu(bci.Status));
567 status = bci.Status | bci.Status1;
577 // Invalid block. Don't use it any more. Must implement.
583 "Infinite loop in Virtual Unit Chain 0x%x\n",
588 /* Skip to next block in chain */
589 writeEUN = nftl->ReplUnitTable[writeEUN];
592 /* OK. We didn't find one in the existing chain, or there
593 is no existing chain. */
595 /* Try to find an already-free block */
596 writeEUN = NFTL_findfreeblock(nftl, 0);
598 if (writeEUN == BLOCK_NIL) {
599 /* That didn't work - there were no free blocks just
600 waiting to be picked up. We're going to have to fold
601 a chain to make room.
604 /* First remember the start of this chain */
605 //u16 startEUN = nftl->EUNtable[thisVUC];
607 //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
608 writeEUN = NFTL_makefreeblock(nftl, 0xffff);
610 if (writeEUN == BLOCK_NIL) {
611 /* OK, we accept that the above comment is
612 lying - there may have been free blocks
613 last time we called NFTL_findfreeblock(),
614 but they are reserved for when we're
615 desperate. Well, now we're desperate.
617 DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
618 writeEUN = NFTL_findfreeblock(nftl, 1);
620 if (writeEUN == BLOCK_NIL) {
621 /* Ouch. This should never happen - we should
622 always be able to make some room somehow.
623 If we get here, we've allocated more storage
624 space than actual media, or our makefreeblock
625 routine is missing something.
627 printk(KERN_WARNING "Cannot make free space.\n");
630 //printk("Restarting scan\n");
635 /* We've found a free block. Insert it into the chain. */
637 if (lastEUN != BLOCK_NIL) {
638 thisVUC |= 0x8000; /* It's a replacement block */
640 /* The first block in a new chain */
641 nftl->EUNtable[thisVUC] = writeEUN;
644 /* set up the actual EUN we're writing into */
645 /* Both in our cache... */
646 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
648 /* ... and on the flash itself */
649 nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
650 &retlen, (char *)&oob.u);
652 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
654 nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
655 &retlen, (char *)&oob.u);
657 /* we link the new block to the chain only after the
658 block is ready. It avoids the case where the chain
659 could point to a free block */
660 if (lastEUN != BLOCK_NIL) {
661 /* Both in our cache... */
662 nftl->ReplUnitTable[lastEUN] = writeEUN;
663 /* ... and on the flash itself */
664 nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
665 8, &retlen, (char *)&oob.u);
667 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
668 = cpu_to_le16(writeEUN);
670 nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
671 8, &retlen, (char *)&oob.u);
678 printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
683 static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
686 struct NFTLrecord *nftl = (void *)mbd;
688 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
692 writeEUN = NFTL_findwriteunit(nftl, block);
694 if (writeEUN == BLOCK_NIL) {
696 "NFTL_writeblock(): Cannot find block to write to\n");
697 /* If we _still_ haven't got a block to use, we're screwed */
701 memset(&oob, 0xff, sizeof(struct nftl_oob));
702 oob.b.Status = oob.b.Status1 = SECTOR_USED;
704 nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
705 512, &retlen, (char *)buffer, (char *)&oob);
708 #endif /* CONFIG_NFTL_RW */
710 static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
713 struct NFTLrecord *nftl = (void *)mbd;
714 struct mtd_info *mtd = nftl->mbd.mtd;
716 u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
717 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
719 int silly = MAX_LOOPS;
723 lastgoodEUN = BLOCK_NIL;
725 if (thisEUN != BLOCK_NIL) {
726 while (thisEUN < nftl->nb_blocks) {
727 if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
728 blockofs, 8, &retlen,
730 status = SECTOR_IGNORE;
732 status = bci.Status | bci.Status1;
736 /* no modification of a sector should follow a free sector */
739 lastgoodEUN = BLOCK_NIL;
742 lastgoodEUN = thisEUN;
747 printk("Unknown status for block %ld in EUN %d: %x\n",
748 block, thisEUN, status);
753 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
754 block / (nftl->EraseSize / 512));
757 thisEUN = nftl->ReplUnitTable[thisEUN];
762 if (lastgoodEUN == BLOCK_NIL) {
763 /* the requested block is not on the media, return all 0x00 */
764 memset(buffer, 0, 512);
766 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
768 int res = mtd->read(mtd, ptr, 512, &retlen, buffer);
770 if (res < 0 && res != -EUCLEAN)
776 static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
778 struct NFTLrecord *nftl = (void *)dev;
780 geo->heads = nftl->heads;
781 geo->sectors = nftl->sectors;
782 geo->cylinders = nftl->cylinders;
787 /****************************************************************************
791 ****************************************************************************/
794 static struct mtd_blktrans_ops nftl_tr = {
797 .part_bits = NFTL_PARTN_BITS,
799 .getgeo = nftl_getgeo,
800 .readsect = nftl_readblock,
801 #ifdef CONFIG_NFTL_RW
802 .writesect = nftl_writeblock,
804 .add_mtd = nftl_add_mtd,
805 .remove_dev = nftl_remove_dev,
806 .owner = THIS_MODULE,
809 extern char nftlmountrev[];
811 static int __init init_nftl(void)
813 printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev);
815 return register_mtd_blktrans(&nftl_tr);
818 static void __exit cleanup_nftl(void)
820 deregister_mtd_blktrans(&nftl_tr);
823 module_init(init_nftl);
824 module_exit(cleanup_nftl);
826 MODULE_LICENSE("GPL");
827 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
828 MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");