1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#define PRERELEASE
23
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <asm/errno.h>
27#include <asm/io.h>
28#include <linux/uaccess.h>
29#include <linux/delay.h>
30#include <linux/slab.h>
31#include <linux/init.h>
32#include <linux/hdreg.h>
33#include <linux/blkdev.h>
34
35#include <linux/kmod.h>
36#include <linux/mtd/mtd.h>
37#include <linux/mtd/rawnand.h>
38#include <linux/mtd/nftl.h>
39#include <linux/mtd/blktrans.h>
40
41
42
43
44
45#define MAX_LOOPS 10000
46
47
48static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
49{
50 struct NFTLrecord *nftl;
51 unsigned long temp;
52
53 if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX)
54 return;
55
56 if (memcmp(mtd->name, "DiskOnChip", 10))
57 return;
58
59 pr_debug("NFTL: add_mtd for %s\n", mtd->name);
60
61 nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
62
63 if (!nftl)
64 return;
65
66 nftl->mbd.mtd = mtd;
67 nftl->mbd.devnum = -1;
68
69 nftl->mbd.tr = tr;
70
71 if (NFTL_mount(nftl) < 0) {
72 printk(KERN_WARNING "NFTL: could not mount device\n");
73 kfree(nftl);
74 return;
75 }
76
77
78
79
80 nftl->cylinders = 1024;
81 nftl->heads = 16;
82
83 temp = nftl->cylinders * nftl->heads;
84 nftl->sectors = nftl->mbd.size / temp;
85 if (nftl->mbd.size % temp) {
86 nftl->sectors++;
87 temp = nftl->cylinders * nftl->sectors;
88 nftl->heads = nftl->mbd.size / temp;
89
90 if (nftl->mbd.size % temp) {
91 nftl->heads++;
92 temp = nftl->heads * nftl->sectors;
93 nftl->cylinders = nftl->mbd.size / temp;
94 }
95 }
96
97 if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
98
99
100
101
102 printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
103 "match size of 0x%lx.\n", nftl->mbd.size);
104 printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
105 "(== 0x%lx sects)\n",
106 nftl->cylinders, nftl->heads , nftl->sectors,
107 (long)nftl->cylinders * (long)nftl->heads *
108 (long)nftl->sectors );
109 }
110
111 if (add_mtd_blktrans_dev(&nftl->mbd)) {
112 kfree(nftl->ReplUnitTable);
113 kfree(nftl->EUNtable);
114 kfree(nftl);
115 return;
116 }
117#ifdef PSYCHO_DEBUG
118 printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
119#endif
120}
121
122static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
123{
124 struct NFTLrecord *nftl = (void *)dev;
125
126 pr_debug("NFTL: remove_dev (i=%d)\n", dev->devnum);
127
128 del_mtd_blktrans_dev(dev);
129 kfree(nftl->ReplUnitTable);
130 kfree(nftl->EUNtable);
131}
132
133
134
135
136int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
137 size_t *retlen, uint8_t *buf)
138{
139 loff_t mask = mtd->writesize - 1;
140 struct mtd_oob_ops ops;
141 int res;
142
143 ops.mode = MTD_OPS_PLACE_OOB;
144 ops.ooboffs = offs & mask;
145 ops.ooblen = len;
146 ops.oobbuf = buf;
147 ops.datbuf = NULL;
148
149 res = mtd_read_oob(mtd, offs & ~mask, &ops);
150 *retlen = ops.oobretlen;
151 return res;
152}
153
154
155
156
157int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
158 size_t *retlen, uint8_t *buf)
159{
160 loff_t mask = mtd->writesize - 1;
161 struct mtd_oob_ops ops;
162 int res;
163
164 ops.mode = MTD_OPS_PLACE_OOB;
165 ops.ooboffs = offs & mask;
166 ops.ooblen = len;
167 ops.oobbuf = buf;
168 ops.datbuf = NULL;
169
170 res = mtd_write_oob(mtd, offs & ~mask, &ops);
171 *retlen = ops.oobretlen;
172 return res;
173}
174
175#ifdef CONFIG_NFTL_RW
176
177
178
179
180static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
181 size_t *retlen, uint8_t *buf, uint8_t *oob)
182{
183 loff_t mask = mtd->writesize - 1;
184 struct mtd_oob_ops ops;
185 int res;
186
187 ops.mode = MTD_OPS_PLACE_OOB;
188 ops.ooboffs = offs & mask;
189 ops.ooblen = mtd->oobsize;
190 ops.oobbuf = oob;
191 ops.datbuf = buf;
192 ops.len = len;
193
194 res = mtd_write_oob(mtd, offs & ~mask, &ops);
195 *retlen = ops.retlen;
196 return res;
197}
198
199
200
201
202
203static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
204{
205
206
207
208
209 u16 pot = nftl->LastFreeEUN;
210 int silly = nftl->nb_blocks;
211
212
213 if (!desperate && nftl->numfreeEUNs < 2) {
214 pr_debug("NFTL_findfreeblock: there are too few free EUNs\n");
215 return BLOCK_NIL;
216 }
217
218
219 do {
220 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
221 nftl->LastFreeEUN = pot;
222 nftl->numfreeEUNs--;
223 return pot;
224 }
225
226
227
228
229
230 if (++pot > nftl->lastEUN)
231 pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
232
233 if (!silly--) {
234 printk("Argh! No free blocks found! LastFreeEUN = %d, "
235 "FirstEUN = %d\n", nftl->LastFreeEUN,
236 le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
237 return BLOCK_NIL;
238 }
239 } while (pot != nftl->LastFreeEUN);
240
241 return BLOCK_NIL;
242}
243
244static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
245{
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;
251 int block;
252 int silly;
253 unsigned int targetEUN;
254 struct nftl_oob oob;
255 int inplace = 1;
256 size_t retlen;
257
258 memset(BlockMap, 0xff, sizeof(BlockMap));
259 memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
260
261 thisEUN = nftl->EUNtable[thisVUC];
262
263 if (thisEUN == BLOCK_NIL) {
264 printk(KERN_WARNING "Trying to fold non-existent "
265 "Virtual Unit Chain %d!\n", thisVUC);
266 return BLOCK_NIL;
267 }
268
269
270
271
272 silly = MAX_LOOPS;
273 targetEUN = BLOCK_NIL;
274 while (thisEUN <= nftl->lastEUN ) {
275 unsigned int status, foldmark;
276
277 targetEUN = thisEUN;
278 for (block = 0; block < nftl->EraseSize / 512; block ++) {
279 nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
280 (block * 512), 16 , &retlen,
281 (char *)&oob);
282 if (block == 2) {
283 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
284 if (foldmark == FOLD_MARK_IN_PROGRESS) {
285 pr_debug("Write Inhibited on EUN %d\n", thisEUN);
286 inplace = 0;
287 } else {
288
289
290
291 inplace = 1;
292 }
293 }
294 status = oob.b.Status | oob.b.Status1;
295 BlockLastState[block] = status;
296
297 switch(status) {
298 case SECTOR_FREE:
299 BlockFreeFound[block] = 1;
300 break;
301
302 case SECTOR_USED:
303 if (!BlockFreeFound[block])
304 BlockMap[block] = thisEUN;
305 else
306 printk(KERN_WARNING
307 "SECTOR_USED found after SECTOR_FREE "
308 "in Virtual Unit Chain %d for block %d\n",
309 thisVUC, block);
310 break;
311 case SECTOR_DELETED:
312 if (!BlockFreeFound[block])
313 BlockMap[block] = BLOCK_NIL;
314 else
315 printk(KERN_WARNING
316 "SECTOR_DELETED found after SECTOR_FREE "
317 "in Virtual Unit Chain %d for block %d\n",
318 thisVUC, block);
319 break;
320
321 case SECTOR_IGNORE:
322 break;
323 default:
324 printk("Unknown status for block %d in EUN %d: %x\n",
325 block, thisEUN, status);
326 }
327 }
328
329 if (!silly--) {
330 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
331 thisVUC);
332 return BLOCK_NIL;
333 }
334
335 thisEUN = nftl->ReplUnitTable[thisEUN];
336 }
337
338 if (inplace) {
339
340
341
342
343
344
345
346 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
347 if (BlockLastState[block] != SECTOR_FREE &&
348 BlockMap[block] != BLOCK_NIL &&
349 BlockMap[block] != targetEUN) {
350 pr_debug("Setting inplace to 0. VUC %d, "
351 "block %d was %x lastEUN, "
352 "and is in EUN %d (%s) %d\n",
353 thisVUC, block, BlockLastState[block],
354 BlockMap[block],
355 BlockMap[block]== targetEUN ? "==" : "!=",
356 targetEUN);
357 inplace = 0;
358 break;
359 }
360 }
361
362 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
363 pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
364 BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
365 SECTOR_FREE) {
366 pr_debug("Pending write not free in EUN %d. "
367 "Folding out of place.\n", targetEUN);
368 inplace = 0;
369 }
370 }
371
372 if (!inplace) {
373 pr_debug("Cannot fold Virtual Unit Chain %d in place. "
374 "Trying out-of-place\n", thisVUC);
375
376 targetEUN = NFTL_findfreeblock(nftl, 1);
377 if (targetEUN == BLOCK_NIL) {
378
379
380
381
382
383
384 printk(KERN_WARNING
385 "NFTL_findfreeblock(desperate) returns 0xffff.\n");
386 return BLOCK_NIL;
387 }
388 } else {
389
390
391
392
393 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
394 oob.u.c.unused = 0xffffffff;
395 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
396 8, &retlen, (char *)&oob.u);
397 }
398
399
400
401
402
403 pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN);
404 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
405 unsigned char movebuf[512];
406 int ret;
407
408
409 if (BlockMap[block] == targetEUN ||
410 (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
411 continue;
412 }
413
414
415
416 if (BlockMap[block] == BLOCK_NIL)
417 continue;
418
419 ret = mtd_read(mtd,
420 (nftl->EraseSize * BlockMap[block]) + (block * 512),
421 512,
422 &retlen,
423 movebuf);
424 if (ret < 0 && !mtd_is_bitflip(ret)) {
425 ret = mtd_read(mtd,
426 (nftl->EraseSize * BlockMap[block]) + (block * 512),
427 512,
428 &retlen,
429 movebuf);
430 if (ret != -EIO)
431 printk("Error went away on retry.\n");
432 }
433 memset(&oob, 0xff, sizeof(struct nftl_oob));
434 oob.b.Status = oob.b.Status1 = SECTOR_USED;
435
436 nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
437 (block * 512), 512, &retlen, movebuf, (char *)&oob);
438 }
439
440
441 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
442 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL;
443
444 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
445 8, &retlen, (char *)&oob.u);
446
447
448
449
450
451
452
453
454 thisEUN = nftl->EUNtable[thisVUC];
455 pr_debug("Want to erase\n");
456
457
458
459 while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
460 unsigned int EUNtmp;
461
462 EUNtmp = nftl->ReplUnitTable[thisEUN];
463
464 if (NFTL_formatblock(nftl, thisEUN) < 0) {
465
466
467 nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
468 } else {
469
470 nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
471 nftl->numfreeEUNs++;
472 }
473 thisEUN = EUNtmp;
474 }
475
476
477 nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
478 nftl->EUNtable[thisVUC] = targetEUN;
479
480 return targetEUN;
481}
482
483static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
484{
485
486
487
488
489
490
491
492 u16 LongestChain = 0;
493 u16 ChainLength = 0, thislen;
494 u16 chain, EUN;
495
496 for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
497 EUN = nftl->EUNtable[chain];
498 thislen = 0;
499
500 while (EUN <= nftl->lastEUN) {
501 thislen++;
502
503 EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
504 if (thislen > 0xff00) {
505 printk("Endless loop in Virtual Chain %d: Unit %x\n",
506 chain, EUN);
507 }
508 if (thislen > 0xff10) {
509
510
511 thislen = 0;
512 break;
513 }
514 }
515
516 if (thislen > ChainLength) {
517
518 ChainLength = thislen;
519 LongestChain = chain;
520 }
521 }
522
523 if (ChainLength < 2) {
524 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
525 "Failing request\n");
526 return BLOCK_NIL;
527 }
528
529 return NFTL_foldchain (nftl, LongestChain, pendingblock);
530}
531
532
533
534
535static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
536{
537 u16 lastEUN;
538 u16 thisVUC = block / (nftl->EraseSize / 512);
539 struct mtd_info *mtd = nftl->mbd.mtd;
540 unsigned int writeEUN;
541 unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
542 size_t retlen;
543 int silly, silly2 = 3;
544 struct nftl_oob oob;
545
546 do {
547
548
549
550
551
552
553
554 lastEUN = BLOCK_NIL;
555 writeEUN = nftl->EUNtable[thisVUC];
556 silly = MAX_LOOPS;
557 while (writeEUN <= nftl->lastEUN) {
558 struct nftl_bci bci;
559 size_t retlen;
560 unsigned int status;
561
562 lastEUN = writeEUN;
563
564 nftl_read_oob(mtd,
565 (writeEUN * nftl->EraseSize) + blockofs,
566 8, &retlen, (char *)&bci);
567
568 pr_debug("Status of block %d in EUN %d is %x\n",
569 block , writeEUN, le16_to_cpu(bci.Status));
570
571 status = bci.Status | bci.Status1;
572 switch(status) {
573 case SECTOR_FREE:
574 return writeEUN;
575
576 case SECTOR_DELETED:
577 case SECTOR_USED:
578 case SECTOR_IGNORE:
579 break;
580 default:
581
582 break;
583 }
584
585 if (!silly--) {
586 printk(KERN_WARNING
587 "Infinite loop in Virtual Unit Chain 0x%x\n",
588 thisVUC);
589 return BLOCK_NIL;
590 }
591
592
593 writeEUN = nftl->ReplUnitTable[writeEUN];
594 }
595
596
597
598
599
600 writeEUN = NFTL_findfreeblock(nftl, 0);
601
602 if (writeEUN == BLOCK_NIL) {
603
604
605
606
607
608
609
610
611
612 writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL);
613
614 if (writeEUN == BLOCK_NIL) {
615
616
617
618
619
620
621 pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
622 writeEUN = NFTL_findfreeblock(nftl, 1);
623 }
624 if (writeEUN == BLOCK_NIL) {
625
626
627
628
629
630
631 printk(KERN_WARNING "Cannot make free space.\n");
632 return BLOCK_NIL;
633 }
634
635 lastEUN = BLOCK_NIL;
636 continue;
637 }
638
639
640
641 if (lastEUN != BLOCK_NIL) {
642 thisVUC |= 0x8000;
643 } else {
644
645 nftl->EUNtable[thisVUC] = writeEUN;
646 }
647
648
649
650 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
651
652
653 nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
654 &retlen, (char *)&oob.u);
655
656 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
657
658 nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
659 &retlen, (char *)&oob.u);
660
661
662
663
664 if (lastEUN != BLOCK_NIL) {
665
666 nftl->ReplUnitTable[lastEUN] = writeEUN;
667
668 nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
669 8, &retlen, (char *)&oob.u);
670
671 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
672 = cpu_to_le16(writeEUN);
673
674 nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
675 8, &retlen, (char *)&oob.u);
676 }
677
678 return writeEUN;
679
680 } while (silly2--);
681
682 printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
683 thisVUC);
684 return BLOCK_NIL;
685}
686
687static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
688 char *buffer)
689{
690 struct NFTLrecord *nftl = (void *)mbd;
691 u16 writeEUN;
692 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
693 size_t retlen;
694 struct nftl_oob oob;
695
696 writeEUN = NFTL_findwriteunit(nftl, block);
697
698 if (writeEUN == BLOCK_NIL) {
699 printk(KERN_WARNING
700 "NFTL_writeblock(): Cannot find block to write to\n");
701
702 return 1;
703 }
704
705 memset(&oob, 0xff, sizeof(struct nftl_oob));
706 oob.b.Status = oob.b.Status1 = SECTOR_USED;
707
708 nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
709 512, &retlen, (char *)buffer, (char *)&oob);
710 return 0;
711}
712#endif
713
714static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
715 char *buffer)
716{
717 struct NFTLrecord *nftl = (void *)mbd;
718 struct mtd_info *mtd = nftl->mbd.mtd;
719 u16 lastgoodEUN;
720 u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
721 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
722 unsigned int status;
723 int silly = MAX_LOOPS;
724 size_t retlen;
725 struct nftl_bci bci;
726
727 lastgoodEUN = BLOCK_NIL;
728
729 if (thisEUN != BLOCK_NIL) {
730 while (thisEUN < nftl->nb_blocks) {
731 if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
732 blockofs, 8, &retlen,
733 (char *)&bci) < 0)
734 status = SECTOR_IGNORE;
735 else
736 status = bci.Status | bci.Status1;
737
738 switch (status) {
739 case SECTOR_FREE:
740
741 goto the_end;
742 case SECTOR_DELETED:
743 lastgoodEUN = BLOCK_NIL;
744 break;
745 case SECTOR_USED:
746 lastgoodEUN = thisEUN;
747 break;
748 case SECTOR_IGNORE:
749 break;
750 default:
751 printk("Unknown status for block %ld in EUN %d: %x\n",
752 block, thisEUN, status);
753 break;
754 }
755
756 if (!silly--) {
757 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
758 block / (nftl->EraseSize / 512));
759 return 1;
760 }
761 thisEUN = nftl->ReplUnitTable[thisEUN];
762 }
763 }
764
765 the_end:
766 if (lastgoodEUN == BLOCK_NIL) {
767
768 memset(buffer, 0, 512);
769 } else {
770 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
771 size_t retlen;
772 int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
773
774 if (res < 0 && !mtd_is_bitflip(res))
775 return -EIO;
776 }
777 return 0;
778}
779
780static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
781{
782 struct NFTLrecord *nftl = (void *)dev;
783
784 geo->heads = nftl->heads;
785 geo->sectors = nftl->sectors;
786 geo->cylinders = nftl->cylinders;
787
788 return 0;
789}
790
791
792
793
794
795
796
797
798static struct mtd_blktrans_ops nftl_tr = {
799 .name = "nftl",
800 .major = NFTL_MAJOR,
801 .part_bits = NFTL_PARTN_BITS,
802 .blksize = 512,
803 .getgeo = nftl_getgeo,
804 .readsect = nftl_readblock,
805#ifdef CONFIG_NFTL_RW
806 .writesect = nftl_writeblock,
807#endif
808 .add_mtd = nftl_add_mtd,
809 .remove_dev = nftl_remove_dev,
810 .owner = THIS_MODULE,
811};
812
813static int __init init_nftl(void)
814{
815 return register_mtd_blktrans(&nftl_tr);
816}
817
818static void __exit cleanup_nftl(void)
819{
820 deregister_mtd_blktrans(&nftl_tr);
821}
822
823module_init(init_nftl);
824module_exit(cleanup_nftl);
825
826MODULE_LICENSE("GPL");
827MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
828MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
829MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);
830