Merge branch 'master' of ssh://www.aleph1.co.uk/home/aleph1/git/yaffs2
[yaffs2.git] / mtdemul / nandemul2k.c
1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2002-2018 Aleph One Ltd.
5  *
6  * Created by Charles Manning <charles@aleph1.co.uk>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
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 n_bytes)
102 {
103         int pg = page%PAGES_PER_BLOCK;
104         int blk = page/PAGES_PER_BLOCK;
105         if(buffer && n_bytes > 0)
106         {
107                 memcpy(buffer,&ned.block[blk]->page[pg]->data[start],n_bytes);
108         }
109         
110 }
111
112 static void nandemul2k_Program(const void *buffer, int page, int start, int n_bytes)
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 && n_bytes>0)
122         {
123                 *p = *p & *b;
124                 p++;
125                 b++;
126                 n_bytes--;
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_oob (struct mtd_info *mtd, loff_t from, size_t len,
283                                 size_t *retlen, u_char *buf);
284 static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
285                         size_t *retlen, const u_char *buf);
286 static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
287                                 size_t *retlen, const u_char *buf);
288 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,7))
289 static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
290                                 unsigned long count, loff_t to, size_t *retlen);
291 #else
292 static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
293                                 unsigned long count, loff_t to, size_t *retlen);
294 #endif
295 static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
296 static void nand_sync (struct mtd_info *mtd);
297
298
299
300 /*
301  * NAND read
302  */
303 static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
304                         size_t *retlen, u_char *buf)
305 {
306         return nand_read_ecc (mtd, from, len, retlen, buf, NULL,NULL);
307 }
308
309
310 /*
311  * NAND read with ECC
312  */
313 static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
314                                 size_t *retlen, u_char *buf, u_char *oob_buf,struct nand_oobinfo *oobsel)
315 {
316         int     start, page;
317         int n = len;
318         int nToCopy;
319
320
321
322         /* Do not allow reads past end of device */
323         if ((from + len) > mtd->size) {
324                 *retlen = 0;
325                 return -EINVAL;
326         }
327
328
329         /* Initialize return value */
330         *retlen = 0;
331
332         while(n > 0)
333         {
334
335                 /* First we calculate the starting page */
336                 page = from >> NAND_SHIFT;
337
338                 /* Get raw starting column */
339
340                 start = from & (PAGE_DATA_SIZE - 1);
341
342                 // OK now check for the curveball where the start and end are in
343                 // the same page
344                 if((start + n) < PAGE_DATA_SIZE)
345                 {
346                         nToCopy = n;
347                 }
348                 else
349                 {
350                         nToCopy =  PAGE_DATA_SIZE - start;
351                 }
352
353                 nandemul2k_Read(buf, page, start, nToCopy);
354                 nandemul2k_Read(oob_buf,page,PAGE_DATA_SIZE,PAGE_SPARE_SIZE);
355
356                 n -= nToCopy;
357                 from += nToCopy;
358                 buf += nToCopy;
359                 if(oob_buf) oob_buf += PAGE_SPARE_SIZE;
360                 *retlen += nToCopy;
361
362         }
363
364
365         return 0;
366 }
367
368 /*
369  * NAND read out-of-band
370  */
371 static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
372                                 size_t *retlen, u_char *buf)
373 {
374         int col, page;
375
376         T(0,("nand_read_oob: from = 0x%08x, buf = 0x%08x, len = %i\n", (unsigned int) from, (unsigned int) buf,
377                 (int) len));
378
379         /* Shift to get page */
380         page = ((int) from) >> NAND_SHIFT;
381
382         /* Mask to get column */
383         col = from & (PAGE_SPARE_SIZE-1)
384
385         /* Initialize return length value */
386         *retlen = 0;
387
388         /* Do not allow reads past end of device */
389         if ((from + len) > mtd->size) {
390                 T(0,
391                         ("nand_read_oob: Attempt read beyond end of device\n"));
392                 *retlen = 0;
393                 return -EINVAL;
394         }
395
396         nandemul2k_Read(buf,page,PAGE_DATA_SIZE + col,len);
397
398         /* Return happy */
399         *retlen = len;
400         return 0;
401 }
402
403 /*
404  * NAND write
405  */
406 static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
407                         size_t *retlen, const u_char *buf)
408 {
409         return nand_write_ecc (mtd, to, len, retlen, buf, NULL,NULL);
410 }
411
412 /*
413  * NAND write with ECC
414  */
415 static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
416                                 size_t *retlen, const u_char *buf,
417                                 u_char *oob_buf, struct nand_oobinfo *dummy)
418 {
419
420         int     start, page;
421         int n = len;
422         int nToCopy;
423
424
425
426         /* Do not allow reads past end of device */
427         if ((to + len) > mtd->size) {
428                 *retlen = 0;
429                 return -EINVAL;
430         }
431
432
433         /* Initialize return value */
434         *retlen = 0;
435
436         while(n > 0)
437         {
438
439                 /* First we calculate the starting page */
440                 page = to >> NAND_SHIFT;
441
442                 /* Get raw starting column */
443
444                 start = to & (PAGE_DATA_SIZE - 1);
445
446                 // OK now check for the curveball where the start and end are in
447                 // the same page
448                 if((start + n) < PAGE_DATA_SIZE)
449                 {
450                         nToCopy = n;
451                 }
452                 else
453                 {
454                         nToCopy =  PAGE_DATA_SIZE - start;
455                 }
456
457                 nandemul2k_Program(buf, page, start, nToCopy);
458                 nandemul2k_Program(oob_buf, page, PAGE_DATA_SIZE, PAGE_SPARE_SIZE);
459
460                 n -= nToCopy;
461                 to += nToCopy;
462                 buf += nToCopy;
463                 if(oob_buf) oob_buf += PAGE_SPARE_SIZE;
464                 *retlen += nToCopy;
465
466         }
467
468
469         return 0;
470 }
471
472 /*
473  * NAND write out-of-band
474  */
475 static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
476                                 size_t *retlen, const u_char *buf)
477 {
478         int col, page;
479
480
481         T(0,(
482                 "nand_read_oob: to = 0x%08x, len = %i\n", (unsigned int) to,
483                 (int) len));
484
485         /* Shift to get page */
486         page = ((int) to) >> NAND_SHIFT;
487
488         /* Mask to get column */
489         col = to & PAGE_SPARE_SIZE;
490
491         /* Initialize return length value */
492         *retlen = 0;
493
494         /* Do not allow reads past end of device */
495         if ((to + len) > mtd->size) {
496                 T(0,(
497                    "nand_read_oob: Attempt read beyond end of device\n"));
498                 *retlen = 0;
499                 return -EINVAL;
500         }
501
502         nandemul2k_Program(buf,page,512 + col,len);
503
504         /* Return happy */
505         *retlen = len;
506         return 0;
507
508 }
509
510 /*
511  * NAND write with iovec
512  */
513 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,7))
514 static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
515                                 unsigned long count, loff_t to, size_t *retlen)
516 #else
517 static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
518                                 unsigned long count, loff_t to, size_t *retlen)
519 #endif
520 {
521         return -EINVAL;
522 }
523
524 /*
525  * NAND erase a block
526  */
527 static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
528 {
529         int i, nBlocks,block;
530
531         T(0,(
532                 "nand_erase: start = 0x%08x, len = %i\n",
533                 (unsigned int) instr->addr, (unsigned int) instr->len));
534
535         /* Start address must align on block boundary */
536         if (instr->addr & (mtd->erasesize - 1)) {
537                 T(0,(
538                         "nand_erase: Unaligned address\n"));
539                 return -EINVAL;
540         }
541
542         /* Length must align on block boundary */
543         if (instr->len & (mtd->erasesize - 1)) {
544                 T(0,(
545                         "nand_erase: Length not block aligned\n"));
546                 return -EINVAL;
547         }
548
549         /* Do not allow erase past end of device */
550         if ((instr->len + instr->addr) > mtd->size) {
551                 T(0,(
552                         "nand_erase: Erase past end of device\n"));
553                 return -EINVAL;
554         }
555
556         nBlocks = instr->len >> (NAND_SHIFT + BLK_SHIFT);
557         block = instr->addr >> (NAND_SHIFT + BLK_SHIFT);
558
559         for(i = 0; i < nBlocks; i++)
560         {
561                 nandemul2k_DoErase(block);
562                 block++;
563         }
564         
565         instr->state = MTD_ERASE_DONE; /* Changed state to done */
566         instr->callback(instr);        /* ... and wake up */
567
568         return 0;
569
570
571 }
572
573
574 static int nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
575 {
576         return 0;
577 }
578
579 static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
580 {
581         return 0;
582 }
583
584
585 /*
586  * NAND sync
587  */
588 static void nand_sync (struct mtd_info *mtd)
589 {
590         T(0,("nand_sync: called\n"));
591
592 }
593
594 /*
595  * Scan for the NAND device
596  */
597 static int nandemul2k_scan (struct mtd_info *mtd,int nchips)
598 {
599         mtd->writesize = PAGE_DATA_SIZE;
600         mtd->oobsize   = PAGE_SPARE_SIZE;
601         mtd->oobavail  = PAGE_SPARE_SIZE/2; /* Simulate using up some for other uses */
602         mtd->erasesize = PAGE_DATA_SIZE * PAGES_PER_BLOCK;
603         mtd->size = sizeInMB * 1024*1024;
604
605
606
607         /* Fill in remaining MTD driver data */
608         mtd->type = MTD_NANDFLASH;
609         mtd->flags = MTD_CAP_NANDFLASH;
610         mtd->owner = THIS_MODULE;
611         mtd->erase = nand_erase;
612         mtd->point = NULL;
613         mtd->unpoint = NULL;
614         mtd->read = nand_read;
615         mtd->write = nand_write;
616         mtd->read_oob = nand_read_oob;
617         mtd->write_oob = nand_write_oob;
618         mtd->read_oob = nand_read_oob;
619         mtd->write_oob = nand_write_oob;
620         mtd->block_isbad = nand_block_isbad;
621         mtd->block_markbad = nand_block_markbad;
622         mtd->readv = NULL;
623         mtd->writev = nand_writev;
624         mtd->sync = nand_sync;
625         mtd->lock = NULL;
626         mtd->unlock = NULL;
627         mtd->suspend = NULL;
628         mtd->resume = NULL;
629
630         mtd->name = "NANDemul2k";
631
632         /* Return happy */
633         return 0;
634 }
635
636 #if 0
637 #ifdef MODULE
638 MODULE_PARM(sizeInMB, "i");
639
640 __setup("sizeInMB=",sizeInMB);
641 #endif
642 #endif
643
644 /*
645  * Define partitions for flash devices
646  */
647
648 static struct mtd_partition nandemul2k_partition[] =
649 {
650         { .name         = "NANDemul partition 1",
651           .offset       = 0,
652           .size         = 0 },
653 };
654
655 static int nPartitions = sizeof(nandemul2k_partition)/sizeof(nandemul2k_partition[0]);
656
657 /*
658  * Main initialization routine
659  */
660 int __init nandemul2k_init (void)
661 {
662
663         // Do the nand init
664         
665         CheckInit();
666
667         nandemul2k_scan(&nandemul2k_mtd,1);
668
669         // Build the partition table
670
671         nandemul2k_partition[0].size = sizeInMB * 1024 * 1024;
672
673         // Register the partition
674         add_mtd_partitions(&nandemul2k_mtd,nandemul2k_partition,nPartitions);
675
676         return 0;
677
678 }
679
680 module_init(nandemul2k_init);
681
682 /*
683  * Clean up routine
684  */
685 #ifdef MODULE
686 static void __exit nandemul2k_cleanup (void)
687 {
688
689         nandemul2k_CleanUp();
690
691         /* Unregister partitions */
692         del_mtd_partitions(&nandemul2k_mtd);
693
694         /* Unregister the device */
695         del_mtd_device (&nandemul2k_mtd);
696
697 }
698 module_exit(nandemul2k_cleanup);
699 #endif
700
701 MODULE_LICENSE("GPL");
702 MODULE_AUTHOR("Charles Manning <manningc@aleph1.co.uk>");
703 MODULE_DESCRIPTION("2k Page/128k Block NAND emulated in RAM");
704
705
706
707