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