Bounds check on ecc correction
[yaffs2.git] / mtdemul / nandemul2k.c
1 /*
2  * YAFFS: Yet another FFS. A NAND-flash specific file system. 
3  *
4  * Copyright (C) 2002 Aleph One Ltd.
5  *   for Toby Churchill Ltd and Brightstar Engineering
6  *
7  * Created by Charles Manning <charles@aleph1.co.uk>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  *
13  *
14  *  This version hacked for emulating 2kpage NAND for YAFFS2 testing.
15  */
16
17 #include <linux/config.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/version.h>
21 #include <linux/slab.h>
22 #include <linux/init.h>
23 #include <linux/list.h>
24 #include <linux/fs.h>
25 #include <linux/proc_fs.h>
26 #include <linux/pagemap.h>
27 #include <linux/mtd/mtd.h>
28 #include <linux/interrupt.h>
29 #include <linux/string.h>
30 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
31 #include <linux/locks.h>
32 #endif
33
34 #include <asm/uaccess.h>
35 #include <linux/mtd/mtd.h>
36 #include <linux/mtd/partitions.h>
37 #include <linux/mtd/nand.h>
38 #include "../yaffs_nandemul2k.h"
39
40 #define ALLOCATE(x) kmalloc(x,GFP_KERNEL)
41 #define FREE(x)     kfree(x)
42
43
44
45
46
47 #define NAND_SHIFT      (11)   // Shifter for 2k
48 #define PAGE_DATA_SIZE  (1 << NAND_SHIFT)
49 #define PAGE_SPARE_SIZE (64)
50 #define BLK_SHIFT       6
51 #define PAGES_PER_BLOCK (1 << BLK_SHIFT)        // = 64
52
53
54 #define EM_SIZE_IN_MEG 4
55 #define EM_SIZE_IN_BYTES (EM_SIZE_IN_MEG * (1<<20))
56
57 #define PAGE_TOTAL_SIZE (PAGE_DATA_SIZE+PAGE_SPARE_SIZE)
58
59 #define BLOCK_TOTAL_SIZE (PAGES_PER_BLOCK * PAGE_TOTAL_SIZE)
60
61 #define BLOCKS_PER_MEG ((1<<20)/(PAGES_PER_BLOCK * PAGE_DATA_SIZE))
62
63
64 static struct mtd_info nandemul2k_mtd;
65
66 typedef struct 
67 {
68         __u8 data[PAGE_TOTAL_SIZE]; // Data + spare
69         int empty;      // is this empty?
70 } nandemul_Page;
71
72
73 typedef struct
74 {
75         nandemul_Page *page[PAGES_PER_BLOCK];
76         int damaged;    
77 } nandemul_Block;
78
79
80
81 typedef struct
82 {
83         nandemul_Block**block;
84         int nBlocks;
85 } nandemul_Device;
86
87 static nandemul_Device ned;
88
89 static int sizeInMB = EM_SIZE_IN_MEG;
90
91
92 static void nandemul_yield(int n)
93 {
94 #ifdef __KERNEL__
95         if(n > 0) schedule_timeout(n);
96 #endif
97
98 }
99
100
101 static void nandemul2k_Read(void *buffer, int page, int start, int nBytes)
102 {
103         int pg = page%PAGES_PER_BLOCK;
104         int blk = page/PAGES_PER_BLOCK;
105         if(buffer && nBytes > 0)
106         {
107                 memcpy(buffer,&ned.block[blk]->page[pg]->data[start],nBytes);
108         }
109         
110 }
111
112 static void nandemul2k_Program(const void *buffer, int page, int start, int nBytes)
113 {
114         int pg = page%PAGES_PER_BLOCK;
115         int blk = page/PAGES_PER_BLOCK;
116         __u8 *p;
117         __u8 *b = (__u8 *)buffer;
118
119         p = &ned.block[blk]->page[pg]->data[start];
120         
121         while(buffer && nBytes>0)
122         {
123                 *p = *p & *b;
124                 p++;
125                 b++;
126                 nBytes--;
127         }
128 }
129
130 static void nandemul2k_DoErase(int blockNumber)
131 {
132         int i;
133         
134         nandemul_Block *blk;
135         
136         if(blockNumber < 0 || blockNumber >= ned.nBlocks)
137         {
138                 return;
139         }
140         
141         blk = ned.block[blockNumber];
142         
143         for(i = 0; i < PAGES_PER_BLOCK; i++)
144         {
145                 memset(blk->page[i],0xff,sizeof(nandemul_Page));
146                 blk->page[i]->empty = 1;
147         }
148         nandemul_yield(2);
149 }
150
151
152 static int nandemul2k_CalcNBlocks(void)
153 {
154         return EM_SIZE_IN_MEG * BLOCKS_PER_MEG;
155 }
156
157
158
159 static int  CheckInit(void)
160 {
161         static int initialised = 0;
162         
163         int i,j;
164         
165         int fail = 0;
166         int nBlocks; 
167
168         int nAllocated = 0;
169         
170         if(initialised) 
171         {
172                 return 0;
173         }
174         
175         
176         ned.nBlocks = nBlocks = nandemul2k_CalcNBlocks();
177
178         
179         ned.block = ALLOCATE(sizeof(nandemul_Block*) * nBlocks );
180         
181         if(!ned.block) return ENOMEM;
182         
183         
184         
185
186                 
187         for(i=fail=0; i <nBlocks; i++)
188         {
189                 
190                 nandemul_Block *blk;
191                 
192                 if(!(blk = ned.block[i] = ALLOCATE(sizeof(nandemul_Block))))
193                 {
194                  fail = 1;
195                 }  
196                 else
197                 {
198                         for(j = 0; j < PAGES_PER_BLOCK; j++)
199                         {
200                                 if((blk->page[j] = ALLOCATE(sizeof(nandemul_Page))) == 0)
201                                 {
202                                         fail = 1;
203                                 }
204                         }
205                         nandemul2k_DoErase(i);
206                         ned.block[i]->damaged = 0;
207                         nAllocated++;
208                 }
209         }
210         
211         if(fail)
212         {
213                 //Todo thump pages
214                 
215                 for(i = 0; i < nAllocated; i++)
216                 {
217                         FREE(ned.block[i]);
218                 }
219                 FREE(ned.block);
220                 
221                 return ENOMEM;
222         }
223         
224         ned.nBlocks = nBlocks;
225         
226         initialised = 1;
227         
228         return 1;
229 }
230
231
232
233 static void nandemul2k_CleanUp(void)
234 {
235         int i,j;
236         
237         for(i = 0; i < ned.nBlocks; i++)
238         {
239                 for(j = 0; j < PAGES_PER_BLOCK; j++)
240                 {
241                    FREE(ned.block[i]->page[j]);
242                 }
243                 FREE(ned.block[i]);
244                 
245         }
246         FREE(ned.block);
247         ned.block = 0;
248 }
249
250 int nandemul2k_GetBytesPerChunk(void) { return PAGE_DATA_SIZE;}
251
252 int nandemul2k_GetChunksPerBlock(void) { return PAGES_PER_BLOCK; }
253 int nandemul2k_GetNumberOfBlocks(void) {return nandemul2k_CalcNBlocks();}
254
255
256
257 static int nandemul2k_ReadId(__u8 *vendorId, __u8 *deviceId)
258 {
259         *vendorId = 'Y'; 
260         *deviceId = '2';
261         
262         return 1;
263 }
264
265
266 static int nandemul2k_ReadStatus(__u8 *status)
267 {
268                 *status = 0;
269                 return 1;
270 }
271
272
273 #ifdef CONFIG_MTD_NAND_ECC
274 #include <linux/mtd/nand_ecc.h>
275 #endif
276
277 /*
278  * NAND low-level MTD interface functions
279  */
280 static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
281                         size_t *retlen, u_char *buf);
282 static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
283                                 size_t *retlen, u_char *buf, u_char *oob_buf, struct nand_oobinfo *dummy);
284 static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
285                                 size_t *retlen, u_char *buf);
286 static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
287                         size_t *retlen, const u_char *buf);
288 static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
289                                 size_t *retlen, const u_char *buf,
290                                 u_char *oob_buf, struct nand_oobinfo *dummy);
291 static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
292                                 size_t *retlen, const u_char *buf);
293 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,7))
294 static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
295                                 unsigned long count, loff_t to, size_t *retlen);
296 #else
297 static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
298                                 unsigned long count, loff_t to, size_t *retlen);
299 #endif
300 static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
301 static void nand_sync (struct mtd_info *mtd);
302
303
304
305 /*
306  * NAND read
307  */
308 static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
309                         size_t *retlen, u_char *buf)
310 {
311         return nand_read_ecc (mtd, from, len, retlen, buf, NULL,NULL);
312 }
313
314
315 /*
316  * NAND read with ECC
317  */
318 static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
319                                 size_t *retlen, u_char *buf, u_char *oob_buf,struct nand_oobinfo *oobsel)
320 {
321         int     start, page;
322         int n = len;
323         int nToCopy;
324
325
326
327         /* Do not allow reads past end of device */
328         if ((from + len) > mtd->size) {
329                 *retlen = 0;
330                 return -EINVAL;
331         }
332
333
334         /* Initialize return value */
335         *retlen = 0;
336
337         while(n > 0)
338         {
339
340                 /* First we calculate the starting page */
341                 page = from >> NAND_SHIFT;
342
343                 /* Get raw starting column */
344
345                 start = from & (mtd->oobblock-1);
346
347                 // OK now check for the curveball where the start and end are in
348                 // the same page
349                 if((start + n) < mtd->oobblock)
350                 {
351                         nToCopy = n;
352                 }
353                 else
354                 {
355                         nToCopy =  mtd->oobblock - start;
356                 }
357
358                 nandemul2k_Read(buf, page, start, nToCopy);
359                 nandemul2k_Read(oob_buf,page,PAGE_DATA_SIZE,PAGE_SPARE_SIZE);
360
361                 n -= nToCopy;
362                 from += nToCopy;
363                 buf += nToCopy;
364                 if(oob_buf) oob_buf += PAGE_SPARE_SIZE;
365                 *retlen += nToCopy;
366
367         }
368
369
370         return 0;
371 }
372
373 /*
374  * NAND read out-of-band
375  */
376 static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
377                                 size_t *retlen, u_char *buf)
378 {
379         int col, page;
380
381         T(0,("nand_read_oob: from = 0x%08x, buf = 0x%08x, len = %i\n", (unsigned int) from, (unsigned int) buf,
382                 (int) len));
383
384         /* Shift to get page */
385         page = ((int) from) >> NAND_SHIFT;
386
387         /* Mask to get column */
388         col = from & 0x0f;
389
390         /* Initialize return length value */
391         *retlen = 0;
392
393         /* Do not allow reads past end of device */
394         if ((from + len) > mtd->size) {
395                 T(0,
396                         ("nand_read_oob: Attempt read beyond end of device\n"));
397                 *retlen = 0;
398                 return -EINVAL;
399         }
400
401         nandemul2k_Read(buf,page,PAGE_DATA_SIZE + col,len);
402
403         /* Return happy */
404         *retlen = len;
405         return 0;
406 }
407
408 /*
409  * NAND write
410  */
411 static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
412                         size_t *retlen, const u_char *buf)
413 {
414         return nand_write_ecc (mtd, to, len, retlen, buf, NULL,NULL);
415 }
416
417 /*
418  * NAND write with ECC
419  */
420 static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
421                                 size_t *retlen, const u_char *buf,
422                                 u_char *oob_buf, struct nand_oobinfo *dummy)
423 {
424
425         int     start, page;
426         int n = len;
427         int nToCopy;
428
429
430
431         /* Do not allow reads past end of device */
432         if ((to + len) > mtd->size) {
433                 *retlen = 0;
434                 return -EINVAL;
435         }
436
437
438         /* Initialize return value */
439         *retlen = 0;
440
441         while(n > 0)
442         {
443
444                 /* First we calculate the starting page */
445                 page = to >> NAND_SHIFT;
446
447                 /* Get raw starting column */
448
449                 start = to & (mtd->oobblock - 1);
450
451                 // OK now check for the curveball where the start and end are in
452                 // the same page
453                 if((start + n) < mtd->oobblock)
454                 {
455                         nToCopy = n;
456                 }
457                 else
458                 {
459                         nToCopy =  mtd->oobblock - start;
460                 }
461
462                 nandemul2k_Program(buf, page, start, nToCopy);
463                 nandemul2k_Program(oob_buf, page, PAGE_DATA_SIZE, PAGE_SPARE_SIZE);
464
465                 n -= nToCopy;
466                 to += nToCopy;
467                 buf += nToCopy;
468                 if(oob_buf) oob_buf += PAGE_SPARE_SIZE;
469                 *retlen += nToCopy;
470
471         }
472
473
474         return 0;
475 }
476
477 /*
478  * NAND write out-of-band
479  */
480 static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
481                                 size_t *retlen, const u_char *buf)
482 {
483         int col, page;
484
485
486         T(0,(
487                 "nand_read_oob: to = 0x%08x, len = %i\n", (unsigned int) to,
488                 (int) len));
489
490         /* Shift to get page */
491         page = ((int) to) >> NAND_SHIFT;
492
493         /* Mask to get column */
494         col = to & 0x0f;
495
496         /* Initialize return length value */
497         *retlen = 0;
498
499         /* Do not allow reads past end of device */
500         if ((to + len) > mtd->size) {
501                 T(0,(
502                    "nand_read_oob: Attempt read beyond end of device\n"));
503                 *retlen = 0;
504                 return -EINVAL;
505         }
506
507         nandemul2k_Program(buf,page,512 + col,len);
508
509         /* Return happy */
510         *retlen = len;
511         return 0;
512
513 }
514
515 /*
516  * NAND write with iovec
517  */
518 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,7))
519 static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
520                                 unsigned long count, loff_t to, size_t *retlen)
521 #else
522 static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
523                                 unsigned long count, loff_t to, size_t *retlen)
524 #endif
525 {
526         return -EINVAL;
527 }
528
529 /*
530  * NAND erase a block
531  */
532 static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
533 {
534         int i, nBlocks,block;
535
536         T(0,(
537                 "nand_erase: start = 0x%08x, len = %i\n",
538                 (unsigned int) instr->addr, (unsigned int) instr->len));
539
540         /* Start address must align on block boundary */
541         if (instr->addr & (mtd->erasesize - 1)) {
542                 T(0,(
543                         "nand_erase: Unaligned address\n"));
544                 return -EINVAL;
545         }
546
547         /* Length must align on block boundary */
548         if (instr->len & (mtd->erasesize - 1)) {
549                 T(0,(
550                         "nand_erase: Length not block aligned\n"));
551                 return -EINVAL;
552         }
553
554         /* Do not allow erase past end of device */
555         if ((instr->len + instr->addr) > mtd->size) {
556                 T(0,(
557                         "nand_erase: Erase past end of device\n"));
558                 return -EINVAL;
559         }
560
561         nBlocks = instr->len >> (NAND_SHIFT + BLK_SHIFT);
562         block = instr->addr >> (NAND_SHIFT + BLK_SHIFT);
563
564         for(i = 0; i < nBlocks; i++)
565         {
566                 nandemul2k_DoErase(block);
567                 block++;
568         }
569
570
571
572         return 0;
573
574
575 }
576
577
578 static int nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
579 {
580         return 0;
581 }
582
583 static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
584 {
585         return 0;
586 }
587
588
589 /*
590  * NAND sync
591  */
592 static void nand_sync (struct mtd_info *mtd)
593 {
594         T(0,("nand_sync: called\n"));
595
596 }
597
598 /*
599  * Scan for the NAND device
600  */
601 static int nandemul2k_scan (struct mtd_info *mtd,int nchips)
602 {
603         mtd->oobblock = PAGE_DATA_SIZE;
604         mtd->oobsize =  PAGE_SPARE_SIZE;
605         mtd->erasesize = PAGE_DATA_SIZE * PAGES_PER_BLOCK;
606         mtd->size = sizeInMB * 1024*1024;
607
608
609
610         /* Fill in remaining MTD driver data */
611         mtd->type = MTD_NANDFLASH;
612         mtd->flags = MTD_CAP_NANDFLASH;
613         mtd->owner = THIS_MODULE;
614         mtd->ecctype = MTD_ECC_NONE;
615         mtd->erase = nand_erase;
616         mtd->point = NULL;
617         mtd->unpoint = NULL;
618         mtd->read = nand_read;
619         mtd->write = nand_write;
620         mtd->read_ecc = nand_read_ecc;
621         mtd->write_ecc = nand_write_ecc;
622         mtd->read_oob = nand_read_oob;
623         mtd->write_oob = nand_write_oob;
624         mtd->block_isbad = nand_block_isbad;
625         mtd->block_markbad = nand_block_markbad;
626         mtd->readv = NULL;
627         mtd->writev = nand_writev;
628         mtd->sync = nand_sync;
629         mtd->lock = NULL;
630         mtd->unlock = NULL;
631         mtd->suspend = NULL;
632         mtd->resume = NULL;
633
634         mtd->name = "NANDemul2k";
635
636         /* Return happy */
637         return 0;
638 }
639
640 #if 0
641 #ifdef MODULE
642 MODULE_PARM(sizeInMB, "i");
643
644 __setup("sizeInMB=",sizeInMB);
645 #endif
646 #endif
647
648 /*
649  * Define partitions for flash devices
650  */
651
652 static struct mtd_partition nandemul2k_partition[] =
653 {
654         { .name         = "NANDemul partition 1",
655           .offset       = 0,
656           .size         = 0 },
657 };
658
659 static int nPartitions = sizeof(nandemul2k_partition)/sizeof(nandemul2k_partition[0]);
660
661 /*
662  * Main initialization routine
663  */
664 int __init nandemul2k_init (void)
665 {
666
667         // Do the nand init
668         
669         CheckInit();
670
671         nandemul2k_scan(&nandemul2k_mtd,1);
672
673         // Build the partition table
674
675         nandemul2k_partition[0].size = sizeInMB * 1024 * 1024;
676
677         // Register the partition
678         add_mtd_partitions(&nandemul2k_mtd,nandemul2k_partition,nPartitions);
679
680         return 0;
681
682 }
683
684 module_init(nandemul2k_init);
685
686 /*
687  * Clean up routine
688  */
689 #ifdef MODULE
690 static void __exit nandemul2k_cleanup (void)
691 {
692
693         nandemul2k_CleanUp();
694
695         /* Unregister partitions */
696         del_mtd_partitions(&nandemul2k_mtd);
697
698         /* Unregister the device */
699         del_mtd_device (&nandemul2k_mtd);
700
701 }
702 module_exit(nandemul2k_cleanup);
703 #endif
704
705 MODULE_LICENSE("GPL");
706 MODULE_AUTHOR("Charles Manning <manningc@aleph1.co.uk>");
707 MODULE_DESCRIPTION("2k Page/128k Block NAND emulated in RAM");
708
709
710
711