diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2008-12-08 06:37:48 -0500 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2008-12-08 06:56:14 -0500 |
commit | bf60862a58f7cd881cfe86a3b2aceaea4a22b3b0 (patch) | |
tree | 622e85be4854a4ba65524d0caa4140ab1da9b108 /drivers/mtd/tests/mtd_subpagetest.c | |
parent | 7163cea15f7b362795b858e7c130cd617aecc0aa (diff) |
MTD: tests: add mtd_subpagetest
This tests makes sure sub-pages on NAND MTD device work fine.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Diffstat (limited to 'drivers/mtd/tests/mtd_subpagetest.c')
-rw-r--r-- | drivers/mtd/tests/mtd_subpagetest.c | 525 |
1 files changed, 525 insertions, 0 deletions
diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c new file mode 100644 index 000000000000..4c5e04d0b328 --- /dev/null +++ b/drivers/mtd/tests/mtd_subpagetest.c | |||
@@ -0,0 +1,525 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006-2007 Nokia Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License version 2 as published by | ||
6 | * the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program; see the file COPYING. If not, write to the Free Software | ||
15 | * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
16 | * | ||
17 | * Test sub-page read and write on MTD device. | ||
18 | * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/init.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/moduleparam.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/mtd/mtd.h> | ||
27 | #include <linux/sched.h> | ||
28 | |||
29 | #define PRINT_PREF KERN_INFO "mtd_subpagetest: " | ||
30 | |||
31 | static int dev; | ||
32 | module_param(dev, int, S_IRUGO); | ||
33 | MODULE_PARM_DESC(dev, "MTD device number to use"); | ||
34 | |||
35 | static struct mtd_info *mtd; | ||
36 | static unsigned char *writebuf; | ||
37 | static unsigned char *readbuf; | ||
38 | static unsigned char *bbt; | ||
39 | |||
40 | static int subpgsize; | ||
41 | static int bufsize; | ||
42 | static int ebcnt; | ||
43 | static int pgcnt; | ||
44 | static int errcnt; | ||
45 | static unsigned long next = 1; | ||
46 | |||
47 | static inline unsigned int simple_rand(void) | ||
48 | { | ||
49 | next = next * 1103515245 + 12345; | ||
50 | return (unsigned int)((next / 65536) % 32768); | ||
51 | } | ||
52 | |||
53 | static inline void simple_srand(unsigned long seed) | ||
54 | { | ||
55 | next = seed; | ||
56 | } | ||
57 | |||
58 | static void set_random_data(unsigned char *buf, size_t len) | ||
59 | { | ||
60 | size_t i; | ||
61 | |||
62 | for (i = 0; i < len; ++i) | ||
63 | buf[i] = simple_rand(); | ||
64 | } | ||
65 | |||
66 | static inline void clear_data(unsigned char *buf, size_t len) | ||
67 | { | ||
68 | memset(buf, 0, len); | ||
69 | } | ||
70 | |||
71 | static int erase_eraseblock(int ebnum) | ||
72 | { | ||
73 | int err; | ||
74 | struct erase_info ei; | ||
75 | loff_t addr = ebnum * mtd->erasesize; | ||
76 | |||
77 | memset(&ei, 0, sizeof(struct erase_info)); | ||
78 | ei.mtd = mtd; | ||
79 | ei.addr = addr; | ||
80 | ei.len = mtd->erasesize; | ||
81 | |||
82 | err = mtd->erase(mtd, &ei); | ||
83 | if (err) { | ||
84 | printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); | ||
85 | return err; | ||
86 | } | ||
87 | |||
88 | if (ei.state == MTD_ERASE_FAILED) { | ||
89 | printk(PRINT_PREF "some erase error occurred at EB %d\n", | ||
90 | ebnum); | ||
91 | return -EIO; | ||
92 | } | ||
93 | |||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static int erase_whole_device(void) | ||
98 | { | ||
99 | int err; | ||
100 | unsigned int i; | ||
101 | |||
102 | printk(PRINT_PREF "erasing whole device\n"); | ||
103 | for (i = 0; i < ebcnt; ++i) { | ||
104 | if (bbt[i]) | ||
105 | continue; | ||
106 | err = erase_eraseblock(i); | ||
107 | if (err) | ||
108 | return err; | ||
109 | cond_resched(); | ||
110 | } | ||
111 | printk(PRINT_PREF "erased %u eraseblocks\n", i); | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static int write_eraseblock(int ebnum) | ||
116 | { | ||
117 | size_t written = 0; | ||
118 | int err = 0; | ||
119 | loff_t addr = ebnum * mtd->erasesize; | ||
120 | |||
121 | set_random_data(writebuf, subpgsize); | ||
122 | err = mtd->write(mtd, addr, subpgsize, &written, writebuf); | ||
123 | if (unlikely(err || written != subpgsize)) { | ||
124 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
125 | (long long)addr); | ||
126 | if (written != subpgsize) { | ||
127 | printk(PRINT_PREF " write size: %#x\n", subpgsize); | ||
128 | printk(PRINT_PREF " written: %#x\n", written); | ||
129 | } | ||
130 | return err ? err : -1; | ||
131 | } | ||
132 | |||
133 | addr += subpgsize; | ||
134 | |||
135 | set_random_data(writebuf, subpgsize); | ||
136 | err = mtd->write(mtd, addr, subpgsize, &written, writebuf); | ||
137 | if (unlikely(err || written != subpgsize)) { | ||
138 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
139 | (long long)addr); | ||
140 | if (written != subpgsize) { | ||
141 | printk(PRINT_PREF " write size: %#x\n", subpgsize); | ||
142 | printk(PRINT_PREF " written: %#x\n", written); | ||
143 | } | ||
144 | return err ? err : -1; | ||
145 | } | ||
146 | |||
147 | return err; | ||
148 | } | ||
149 | |||
150 | static int write_eraseblock2(int ebnum) | ||
151 | { | ||
152 | size_t written = 0; | ||
153 | int err = 0, k; | ||
154 | loff_t addr = ebnum * mtd->erasesize; | ||
155 | |||
156 | for (k = 1; k < 33; ++k) { | ||
157 | if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) | ||
158 | break; | ||
159 | set_random_data(writebuf, subpgsize * k); | ||
160 | err = mtd->write(mtd, addr, subpgsize * k, &written, writebuf); | ||
161 | if (unlikely(err || written != subpgsize * k)) { | ||
162 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
163 | (long long)addr); | ||
164 | if (written != subpgsize) { | ||
165 | printk(PRINT_PREF " write size: %#x\n", | ||
166 | subpgsize * k); | ||
167 | printk(PRINT_PREF " written: %#08x\n", | ||
168 | written); | ||
169 | } | ||
170 | return err ? err : -1; | ||
171 | } | ||
172 | addr += subpgsize * k; | ||
173 | } | ||
174 | |||
175 | return err; | ||
176 | } | ||
177 | |||
178 | static void print_subpage(unsigned char *p) | ||
179 | { | ||
180 | int i, j; | ||
181 | |||
182 | for (i = 0; i < subpgsize; ) { | ||
183 | for (j = 0; i < subpgsize && j < 32; ++i, ++j) | ||
184 | printk("%02x", *p++); | ||
185 | printk("\n"); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | static int verify_eraseblock(int ebnum) | ||
190 | { | ||
191 | size_t read = 0; | ||
192 | int err = 0; | ||
193 | loff_t addr = ebnum * mtd->erasesize; | ||
194 | |||
195 | set_random_data(writebuf, subpgsize); | ||
196 | clear_data(readbuf, subpgsize); | ||
197 | read = 0; | ||
198 | err = mtd->read(mtd, addr, subpgsize, &read, readbuf); | ||
199 | if (unlikely(err || read != subpgsize)) { | ||
200 | if (err == -EUCLEAN && read == subpgsize) { | ||
201 | printk(PRINT_PREF "ECC correction at %#llx\n", | ||
202 | (long long)addr); | ||
203 | err = 0; | ||
204 | } else { | ||
205 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
206 | (long long)addr); | ||
207 | return err ? err : -1; | ||
208 | } | ||
209 | } | ||
210 | if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { | ||
211 | printk(PRINT_PREF "error: verify failed at %#llx\n", | ||
212 | (long long)addr); | ||
213 | printk(PRINT_PREF "------------- written----------------\n"); | ||
214 | print_subpage(writebuf); | ||
215 | printk(PRINT_PREF "------------- read ------------------\n"); | ||
216 | print_subpage(readbuf); | ||
217 | printk(PRINT_PREF "-------------------------------------\n"); | ||
218 | errcnt += 1; | ||
219 | } | ||
220 | |||
221 | addr += subpgsize; | ||
222 | |||
223 | set_random_data(writebuf, subpgsize); | ||
224 | clear_data(readbuf, subpgsize); | ||
225 | read = 0; | ||
226 | err = mtd->read(mtd, addr, subpgsize, &read, readbuf); | ||
227 | if (unlikely(err || read != subpgsize)) { | ||
228 | if (err == -EUCLEAN && read == subpgsize) { | ||
229 | printk(PRINT_PREF "ECC correction at %#llx\n", | ||
230 | (long long)addr); | ||
231 | err = 0; | ||
232 | } else { | ||
233 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
234 | (long long)addr); | ||
235 | return err ? err : -1; | ||
236 | } | ||
237 | } | ||
238 | if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { | ||
239 | printk(PRINT_PREF "error: verify failed at %#llx\n", | ||
240 | (long long)addr); | ||
241 | printk(PRINT_PREF "------------- written----------------\n"); | ||
242 | print_subpage(writebuf); | ||
243 | printk(PRINT_PREF "------------- read ------------------\n"); | ||
244 | print_subpage(readbuf); | ||
245 | printk(PRINT_PREF "-------------------------------------\n"); | ||
246 | errcnt += 1; | ||
247 | } | ||
248 | |||
249 | return err; | ||
250 | } | ||
251 | |||
252 | static int verify_eraseblock2(int ebnum) | ||
253 | { | ||
254 | size_t read = 0; | ||
255 | int err = 0, k; | ||
256 | loff_t addr = ebnum * mtd->erasesize; | ||
257 | |||
258 | for (k = 1; k < 33; ++k) { | ||
259 | if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) | ||
260 | break; | ||
261 | set_random_data(writebuf, subpgsize * k); | ||
262 | clear_data(readbuf, subpgsize * k); | ||
263 | read = 0; | ||
264 | err = mtd->read(mtd, addr, subpgsize * k, &read, readbuf); | ||
265 | if (unlikely(err || read != subpgsize * k)) { | ||
266 | if (err == -EUCLEAN && read == subpgsize * k) { | ||
267 | printk(PRINT_PREF "ECC correction at %#llx\n", | ||
268 | (long long)addr); | ||
269 | err = 0; | ||
270 | } else { | ||
271 | printk(PRINT_PREF "error: read failed at " | ||
272 | "%#llx\n", (long long)addr); | ||
273 | return err ? err : -1; | ||
274 | } | ||
275 | } | ||
276 | if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) { | ||
277 | printk(PRINT_PREF "error: verify failed at %#llx\n", | ||
278 | (long long)addr); | ||
279 | errcnt += 1; | ||
280 | } | ||
281 | addr += subpgsize * k; | ||
282 | } | ||
283 | |||
284 | return err; | ||
285 | } | ||
286 | |||
287 | static int verify_eraseblock_ff(int ebnum) | ||
288 | { | ||
289 | uint32_t j; | ||
290 | size_t read = 0; | ||
291 | int err = 0; | ||
292 | loff_t addr = ebnum * mtd->erasesize; | ||
293 | |||
294 | memset(writebuf, 0xff, subpgsize); | ||
295 | for (j = 0; j < mtd->erasesize / subpgsize; ++j) { | ||
296 | clear_data(readbuf, subpgsize); | ||
297 | read = 0; | ||
298 | err = mtd->read(mtd, addr, subpgsize, &read, readbuf); | ||
299 | if (unlikely(err || read != subpgsize)) { | ||
300 | if (err == -EUCLEAN && read == subpgsize) { | ||
301 | printk(PRINT_PREF "ECC correction at %#llx\n", | ||
302 | (long long)addr); | ||
303 | err = 0; | ||
304 | } else { | ||
305 | printk(PRINT_PREF "error: read failed at " | ||
306 | "%#llx\n", (long long)addr); | ||
307 | return err ? err : -1; | ||
308 | } | ||
309 | } | ||
310 | if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { | ||
311 | printk(PRINT_PREF "error: verify 0xff failed at " | ||
312 | "%#llx\n", (long long)addr); | ||
313 | errcnt += 1; | ||
314 | } | ||
315 | addr += subpgsize; | ||
316 | } | ||
317 | |||
318 | return err; | ||
319 | } | ||
320 | |||
321 | static int verify_all_eraseblocks_ff(void) | ||
322 | { | ||
323 | int err; | ||
324 | unsigned int i; | ||
325 | |||
326 | printk(PRINT_PREF "verifying all eraseblocks for 0xff\n"); | ||
327 | for (i = 0; i < ebcnt; ++i) { | ||
328 | if (bbt[i]) | ||
329 | continue; | ||
330 | err = verify_eraseblock_ff(i); | ||
331 | if (err) | ||
332 | return err; | ||
333 | if (i % 256 == 0) | ||
334 | printk(PRINT_PREF "verified up to eraseblock %u\n", i); | ||
335 | cond_resched(); | ||
336 | } | ||
337 | printk(PRINT_PREF "verified %u eraseblocks\n", i); | ||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | static int is_block_bad(int ebnum) | ||
342 | { | ||
343 | loff_t addr = ebnum * mtd->erasesize; | ||
344 | int ret; | ||
345 | |||
346 | ret = mtd->block_isbad(mtd, addr); | ||
347 | if (ret) | ||
348 | printk(PRINT_PREF "block %d is bad\n", ebnum); | ||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | static int scan_for_bad_eraseblocks(void) | ||
353 | { | ||
354 | int i, bad = 0; | ||
355 | |||
356 | bbt = kmalloc(ebcnt, GFP_KERNEL); | ||
357 | if (!bbt) { | ||
358 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
359 | return -ENOMEM; | ||
360 | } | ||
361 | memset(bbt, 0 , ebcnt); | ||
362 | |||
363 | printk(PRINT_PREF "scanning for bad eraseblocks\n"); | ||
364 | for (i = 0; i < ebcnt; ++i) { | ||
365 | bbt[i] = is_block_bad(i) ? 1 : 0; | ||
366 | if (bbt[i]) | ||
367 | bad += 1; | ||
368 | cond_resched(); | ||
369 | } | ||
370 | printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static int __init mtd_subpagetest_init(void) | ||
375 | { | ||
376 | int err = 0; | ||
377 | uint32_t i; | ||
378 | uint64_t tmp; | ||
379 | |||
380 | printk(KERN_INFO "\n"); | ||
381 | printk(KERN_INFO "=================================================\n"); | ||
382 | printk(PRINT_PREF "MTD device: %d\n", dev); | ||
383 | |||
384 | mtd = get_mtd_device(NULL, dev); | ||
385 | if (IS_ERR(mtd)) { | ||
386 | err = PTR_ERR(mtd); | ||
387 | printk(PRINT_PREF "error: cannot get MTD device\n"); | ||
388 | return err; | ||
389 | } | ||
390 | |||
391 | if (mtd->type != MTD_NANDFLASH) { | ||
392 | printk(PRINT_PREF "this test requires NAND flash\n"); | ||
393 | goto out; | ||
394 | } | ||
395 | |||
396 | subpgsize = mtd->writesize >> mtd->subpage_sft; | ||
397 | printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " | ||
398 | "page size %u, subpage size %u, count of eraseblocks %u, " | ||
399 | "pages per eraseblock %u, OOB size %u\n", | ||
400 | (unsigned long long)mtd->size, mtd->erasesize, | ||
401 | mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize); | ||
402 | |||
403 | err = -ENOMEM; | ||
404 | bufsize = subpgsize * 32; | ||
405 | writebuf = kmalloc(bufsize, GFP_KERNEL); | ||
406 | if (!writebuf) { | ||
407 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
408 | goto out; | ||
409 | } | ||
410 | readbuf = kmalloc(bufsize, GFP_KERNEL); | ||
411 | if (!readbuf) { | ||
412 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
413 | goto out; | ||
414 | } | ||
415 | |||
416 | tmp = mtd->size; | ||
417 | do_div(tmp, mtd->erasesize); | ||
418 | ebcnt = tmp; | ||
419 | pgcnt = mtd->erasesize / mtd->writesize; | ||
420 | |||
421 | err = scan_for_bad_eraseblocks(); | ||
422 | if (err) | ||
423 | goto out; | ||
424 | |||
425 | err = erase_whole_device(); | ||
426 | if (err) | ||
427 | goto out; | ||
428 | |||
429 | printk(PRINT_PREF "writing whole device\n"); | ||
430 | simple_srand(1); | ||
431 | for (i = 0; i < ebcnt; ++i) { | ||
432 | if (bbt[i]) | ||
433 | continue; | ||
434 | err = write_eraseblock(i); | ||
435 | if (unlikely(err)) | ||
436 | goto out; | ||
437 | if (i % 256 == 0) | ||
438 | printk(PRINT_PREF "written up to eraseblock %u\n", i); | ||
439 | cond_resched(); | ||
440 | } | ||
441 | printk(PRINT_PREF "written %u eraseblocks\n", i); | ||
442 | |||
443 | simple_srand(1); | ||
444 | printk(PRINT_PREF "verifying all eraseblocks\n"); | ||
445 | for (i = 0; i < ebcnt; ++i) { | ||
446 | if (bbt[i]) | ||
447 | continue; | ||
448 | err = verify_eraseblock(i); | ||
449 | if (unlikely(err)) | ||
450 | goto out; | ||
451 | if (i % 256 == 0) | ||
452 | printk(PRINT_PREF "verified up to eraseblock %u\n", i); | ||
453 | cond_resched(); | ||
454 | } | ||
455 | printk(PRINT_PREF "verified %u eraseblocks\n", i); | ||
456 | |||
457 | err = erase_whole_device(); | ||
458 | if (err) | ||
459 | goto out; | ||
460 | |||
461 | err = verify_all_eraseblocks_ff(); | ||
462 | if (err) | ||
463 | goto out; | ||
464 | |||
465 | /* Write all eraseblocks */ | ||
466 | simple_srand(3); | ||
467 | printk(PRINT_PREF "writing whole device\n"); | ||
468 | for (i = 0; i < ebcnt; ++i) { | ||
469 | if (bbt[i]) | ||
470 | continue; | ||
471 | err = write_eraseblock2(i); | ||
472 | if (unlikely(err)) | ||
473 | goto out; | ||
474 | if (i % 256 == 0) | ||
475 | printk(PRINT_PREF "written up to eraseblock %u\n", i); | ||
476 | cond_resched(); | ||
477 | } | ||
478 | printk(PRINT_PREF "written %u eraseblocks\n", i); | ||
479 | |||
480 | /* Check all eraseblocks */ | ||
481 | simple_srand(3); | ||
482 | printk(PRINT_PREF "verifying all eraseblocks\n"); | ||
483 | for (i = 0; i < ebcnt; ++i) { | ||
484 | if (bbt[i]) | ||
485 | continue; | ||
486 | err = verify_eraseblock2(i); | ||
487 | if (unlikely(err)) | ||
488 | goto out; | ||
489 | if (i % 256 == 0) | ||
490 | printk(PRINT_PREF "verified up to eraseblock %u\n", i); | ||
491 | cond_resched(); | ||
492 | } | ||
493 | printk(PRINT_PREF "verified %u eraseblocks\n", i); | ||
494 | |||
495 | err = erase_whole_device(); | ||
496 | if (err) | ||
497 | goto out; | ||
498 | |||
499 | err = verify_all_eraseblocks_ff(); | ||
500 | if (err) | ||
501 | goto out; | ||
502 | |||
503 | printk(PRINT_PREF "finished with %d errors\n", errcnt); | ||
504 | |||
505 | out: | ||
506 | kfree(bbt); | ||
507 | kfree(readbuf); | ||
508 | kfree(writebuf); | ||
509 | put_mtd_device(mtd); | ||
510 | if (err) | ||
511 | printk(PRINT_PREF "error %d occurred\n", err); | ||
512 | printk(KERN_INFO "=================================================\n"); | ||
513 | return err; | ||
514 | } | ||
515 | module_init(mtd_subpagetest_init); | ||
516 | |||
517 | static void __exit mtd_subpagetest_exit(void) | ||
518 | { | ||
519 | return; | ||
520 | } | ||
521 | module_exit(mtd_subpagetest_exit); | ||
522 | |||
523 | MODULE_DESCRIPTION("Subpage test module"); | ||
524 | MODULE_AUTHOR("Adrian Hunter"); | ||
525 | MODULE_LICENSE("GPL"); | ||