tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / drivers / mtd / tests / sprd_mtdtest / nandflash_ecctest / nandflash_ecctest.c
1 #define pr_fmt(fmt)     KBUILD_MODNAME ": " fmt
2
3 #include <linux/init.h>
4 #include <linux/list.h>
5 #include <linux/module.h>
6 #include <linux/err.h>
7 #include <linux/slab.h>
8 #include <linux/vmalloc.h>
9 #include <linux/random.h>
10 #include <linux/mtd/mtd.h>
11 #include <linux/moduleparam.h>
12
13 struct mtd_part {
14         struct mtd_info mtd;
15         struct mtd_info *master;
16         loff_t offset;
17         struct list_head list;
18 };
19
20 #define CBIT(v, n) ((v) & (1 << (n)))
21 #define BCLR(v, n) ((v) = (v) & ~(1 << (n)))
22
23 static struct mtd_info *mtd = NULL;
24 static int dev = 4;
25 module_param(dev, int, S_IRUGO);
26 MODULE_PARM_DESC(dev, "MTD device number to use");
27
28 static int insert_biterror(unsigned char *buf)
29 {
30         unsigned int bit = 0;
31         unsigned int byte = 0;
32
33         while (byte < mtd->writesize) {
34                 for (bit = 0; bit <= 7; bit++) {
35                         if (CBIT(buf[byte], bit)) {
36                                 BCLR(buf[byte], bit);
37                                 pr_info("Inserted biterror @ %u/%u\n", byte, bit);
38                                 return 0;
39                         }
40                 }
41                 byte++;
42         }
43         pr_err("biterror: Failed to find a '1' bit\n");
44         return -EIO;
45 }
46
47 static int erase_eraseblock(unsigned int ebnum)
48 {
49         int err = 0;
50         struct erase_info ei = {0};
51
52         ei.mtd  = mtd;
53         ei.addr = ebnum * mtd->erasesize;
54         ei.len  = mtd->erasesize;
55
56         err = mtd_erase(mtd, &ei);
57         if (err) {
58                 pr_err("err %d while erasing EB %d\n", err, ebnum);
59                 return err;
60         }
61
62         if (ei.state == MTD_ERASE_FAILED) {
63                 pr_err("some erase error occurred at EB %d\n", ebnum);
64                 return -EIO;
65         }
66
67         return 0;
68 }
69
70 static int write_oob(loff_t offset, unsigned char *databuf, unsigned int len, unsigned char *oobbuf, unsigned int ooblen, unsigned int mode)
71 {
72         struct mtd_oob_ops ops = {0};
73         int err = 0;
74
75         ops.mode      = mode;
76         ops.len       = len;
77         ops.retlen    = 0;
78         ops.ooblen    = ooblen;
79         ops.oobretlen = 0;
80         ops.ooboffs   = 0;
81         ops.datbuf    = databuf;
82         ops.oobbuf    = oobbuf;
83         err = mtd->_write_oob(mtd, offset, &ops);
84         if (err || ops.oobretlen != ops.ooblen || ops.retlen != ops.len) {
85                 pr_err("write address: 0x%.8x's failed, err:%d!", (unsigned int)offset, err);
86                 if(err == 0) {
87                         pr_err("expect:%d , %d real:%d, %d\n", ops.len, ops.ooblen, ops.retlen, ops.oobretlen);
88                         err = -1;
89                 }
90         }
91
92         return err;
93 }
94
95 static int read_oob(loff_t offset, unsigned char *databuf, unsigned int len, unsigned char *oobbuf, unsigned int ooblen, unsigned int mode)
96 {
97         struct mtd_oob_ops ops = {0};
98         int err = 0;
99
100         ops.mode      = mode;
101         ops.len       = len;
102         ops.retlen    = 0;
103         ops.ooblen    = ooblen;
104         ops.oobretlen = 0;
105         ops.ooboffs   = 0;
106         ops.datbuf    = databuf;
107         ops.oobbuf    = oobbuf;
108         err = mtd->_read_oob(mtd, offset, &ops);
109         if (err || ops.oobretlen != ops.ooblen || ops.retlen != ops.len) {
110                 pr_err("read address: 0x%.8x failed, err:%d!\n", (unsigned int)offset, err);
111                 if(err == 0) {
112                         pr_err("expect:%d , %d real:%d, %d\n", ops.len, ops.ooblen, ops.retlen, ops.oobretlen);
113                         err = -1;
114                 } else if(err == -EUCLEAN) {
115                         err = 0;
116                 }
117         }
118
119         return err;
120 }
121
122 static int __init nandflash_ecctest_init(void)
123 {
124         unsigned int read_len = 0;
125         int err = 0;
126         loff_t offset = 0;
127         unsigned int j = 0;
128         struct rnd_state rnd_state;
129         unsigned int corrected_num = 0;
130         unsigned char *oobbuf_w = NULL;
131         unsigned char *oobbuf_r = NULL;
132         unsigned char *pagebuf_r = NULL;
133         unsigned char *pagebuf_w = NULL;
134         unsigned char *pagebuf_v = NULL;
135         struct mtd_ecc_stats stats = {0};
136         struct mtd_part *mtd_part = NULL;
137
138         printk("\n");
139         pr_info("--------------------------------------------------------------------------\n");
140         /*prepare work*/
141         mtd = get_mtd_device(NULL, 4);
142         if (IS_ERR(mtd)) {
143                 err = PTR_ERR(mtd);
144                 pr_err("Cannot get MTD device\n");
145                 return err;
146         }
147
148         mtd_part = (struct mtd_part *)mtd;
149
150         if(!mtd->_block_isbad || !mtd->_block_markbad || !mtd->_read_oob || !mtd->_write_oob ||
151            !mtd->_read || !mtd->_write) {
152                 pr_err("Some mtd's method may be NULL!");
153                 goto out;
154         }
155
156         pagebuf_r = vmalloc(mtd->writesize);
157         oobbuf_r = vmalloc(mtd->oobsize);
158         pagebuf_w = vmalloc(mtd->writesize);
159         pagebuf_v = vmalloc(mtd->writesize);
160         oobbuf_w = vmalloc(mtd->oobsize);
161         if(!pagebuf_r || !oobbuf_r || !pagebuf_w || !pagebuf_v || !oobbuf_w) {
162                 pr_err("Alloc buf failed!");
163                 goto out;
164         }
165
166         //find a vailid block
167         while(offset < mtd->size) {
168                 if(mtd->_block_isbad(mtd, offset)) {
169                         offset += mtd->erasesize;
170                 } else {
171                         break;
172                 }
173         }
174
175         if(offset >= mtd->size) {
176                 pr_err("The whole mtd_partion are bad!\n");
177                 goto out;
178         }
179
180         err = erase_eraseblock(((long)offset)/mtd->erasesize);
181         if(err) {
182                 pr_err("Erase failed at EB: %d\n", ((unsigned int)offset)/mtd->erasesize);
183                 goto out;
184         }
185         pr_info("Erase block %d successfully!\n", ((unsigned int)offset)/mtd->erasesize);
186
187         //prepare value
188         prandom_seed_state(&rnd_state, 1);
189         prandom_bytes_state(&rnd_state, oobbuf_w, mtd->oobsize);
190         prandom_bytes_state(&rnd_state, pagebuf_w, mtd->writesize);
191         memcpy(pagebuf_v, pagebuf_w, mtd->writesize);
192
193         err = write_oob(offset, pagebuf_w, mtd->writesize, oobbuf_w, mtd->oobsize, MTD_OPS_PLACE_OOB);
194         if(err != 0) {
195                 pr_err("1.1 write failed!\n");
196                 goto erase;
197         }
198
199         err = read_oob(offset, pagebuf_r, mtd->writesize, oobbuf_r, mtd->oobsize, MTD_OPS_PLACE_OOB);
200         if(err != 0) {
201                 pr_err("1.1 read failed!\n");
202                 goto erase;
203         }
204
205         if(memcmp(pagebuf_v, pagebuf_r, mtd->writesize) != 0) {
206                 pr_err("1.1 compare page failed!\n");
207                 goto erase;
208         }
209         memcpy(oobbuf_w, oobbuf_r, mtd->oobsize);
210
211         for(j = 0; j < mtd->writesize * 8; j ++)
212         {
213                 offset += mtd->writesize;
214                 err = insert_biterror(pagebuf_w);
215                 if(err) {
216                         pr_err("Insert biterror failed!\n");
217                         goto erase;
218                 }
219                 pr_info("Insert %d bit-flip sucessfully!\n", j + 1);
220
221                 err = write_oob(offset, pagebuf_w, mtd->writesize, oobbuf_w, mtd->oobsize, MTD_OPS_RAW);
222                 if(err != 0) {
223                         pr_err("2.1 write failed!\n");
224                         goto erase;
225                 }
226
227                 stats = mtd_part->master->ecc_stats;
228                 err = read_oob(offset, pagebuf_r, mtd->writesize, oobbuf_r, mtd->oobsize, MTD_OPS_PLACE_OOB);
229                 if(err != 0) {
230                         pr_err("2.1 read failed!\n");
231                         break;
232                 }
233
234                 if(memcmp(pagebuf_v, pagebuf_r, mtd->writesize) != 0) {
235                         pr_err("2.2 compare page failed!\n");
236                         goto erase;
237                 }
238
239                 corrected_num = mtd_part->master->ecc_stats.corrected - stats.corrected;
240                 if(corrected_num != (j + 1)) {
241                         pr_err("Bit-flip num is not match, expected:%d, really:%d\n", j + 1, corrected_num);
242                         goto erase;
243                 }
244         }
245
246         pr_info("Test successfully, this system can corrected %d bit-flip at most!\n", j);
247 erase:
248         err = erase_eraseblock(((unsigned int)offset)/mtd->erasesize);
249         if(err) {
250                 pr_err("1.3 erase failed at EB: %d\n", ((unsigned int)offset)/mtd->erasesize);
251                 goto out;
252         }
253 out:
254         vfree(pagebuf_r);
255         vfree(oobbuf_r);
256         vfree(pagebuf_w);
257         vfree(oobbuf_w);
258         pr_info("--------------------------------------------------------------------------\n");
259         return -1;
260 }
261
262 static void __exit nandflash_ecctest_exit(void)
263 {
264         put_mtd_device(mtd);
265         return;
266 }
267
268 module_init(nandflash_ecctest_init);
269 module_exit(nandflash_ecctest_exit);
270
271 MODULE_DESCRIPTION("Test ecc strength");
272 MODULE_AUTHOR("ming.tang");
273 MODULE_LICENSE("GPL");