| 1 | |
| 2 | #define pr_fmt(fmt) "mtd_test: " fmt
|
| 3 |
|
| 4 | #include <linux/module.h>
|
| 5 | #include <linux/sched.h>
|
| 6 | #include <linux/printk.h>
|
| 7 |
|
| 8 | #include "mtd_test.h"
|
| 9 |
|
| 10 | int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum)
|
| 11 | {
|
| 12 | int err;
|
| 13 | struct erase_info ei;
|
| 14 | loff_t addr = (loff_t)ebnum * mtd->erasesize;
|
| 15 |
|
| 16 | memset(&ei, 0, sizeof(struct erase_info));
|
| 17 | ei.addr = addr;
|
| 18 | ei.len = mtd->erasesize;
|
| 19 |
|
| 20 | err = mtd_erase(mtd, &ei);
|
| 21 | if (err) {
|
| 22 | pr_info("error %d while erasing EB %d\n", err, ebnum);
|
| 23 | return err;
|
| 24 | }
|
| 25 |
|
| 26 | return 0;
|
| 27 | }
|
| 28 | EXPORT_SYMBOL_GPL(mtdtest_erase_eraseblock);
|
| 29 |
|
| 30 | static int is_block_bad(struct mtd_info *mtd, unsigned int ebnum)
|
| 31 | {
|
| 32 | int ret;
|
| 33 | loff_t addr = (loff_t)ebnum * mtd->erasesize;
|
| 34 |
|
| 35 | ret = mtd_block_isbad(mtd, addr);
|
| 36 | if (ret)
|
| 37 | pr_info("block %d is bad\n", ebnum);
|
| 38 |
|
| 39 | return ret;
|
| 40 | }
|
| 41 |
|
| 42 | int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
|
| 43 | unsigned int eb, int ebcnt)
|
| 44 | {
|
| 45 | int i, bad = 0;
|
| 46 |
|
| 47 | if (!mtd_can_have_bb(mtd))
|
| 48 | return 0;
|
| 49 |
|
| 50 | pr_info("scanning for bad eraseblocks\n");
|
| 51 | for (i = 0; i < ebcnt; ++i) {
|
| 52 | bbt[i] = is_block_bad(mtd, eb + i) ? 1 : 0;
|
| 53 | if (bbt[i])
|
| 54 | bad += 1;
|
| 55 | cond_resched();
|
| 56 | }
|
| 57 | pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
|
| 58 |
|
| 59 | return 0;
|
| 60 | }
|
| 61 | EXPORT_SYMBOL_GPL(mtdtest_scan_for_bad_eraseblocks);
|
| 62 |
|
| 63 | int mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
|
| 64 | unsigned int eb, int ebcnt)
|
| 65 | {
|
| 66 | int err;
|
| 67 | unsigned int i;
|
| 68 |
|
| 69 | for (i = 0; i < ebcnt; ++i) {
|
| 70 | if (bbt[i])
|
| 71 | continue;
|
| 72 | err = mtdtest_erase_eraseblock(mtd, eb + i);
|
| 73 | if (err)
|
| 74 | return err;
|
| 75 | cond_resched();
|
| 76 | }
|
| 77 |
|
| 78 | return 0;
|
| 79 | }
|
| 80 | EXPORT_SYMBOL_GPL(mtdtest_erase_good_eraseblocks);
|
| 81 |
|
| 82 | int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf)
|
| 83 | {
|
| 84 | size_t read;
|
| 85 | int err;
|
| 86 |
|
| 87 | err = mtd_read(mtd, addr, size, &read, buf);
|
| 88 |
|
| 89 | if (mtd_is_bitflip(err))
|
| 90 | err = 0;
|
| 91 | if (!err && read != size)
|
| 92 | err = -EIO;
|
| 93 | if (err)
|
| 94 | pr_err("error: read failed at %#llx\n", addr);
|
| 95 |
|
| 96 | return err;
|
| 97 | }
|
| 98 | EXPORT_SYMBOL_GPL(mtdtest_read);
|
| 99 |
|
| 100 | int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size,
|
| 101 | const void *buf)
|
| 102 | {
|
| 103 | size_t written;
|
| 104 | int err;
|
| 105 |
|
| 106 | err = mtd_write(mtd, addr, size, &written, buf);
|
| 107 | if (!err && written != size)
|
| 108 | err = -EIO;
|
| 109 | if (err)
|
| 110 | pr_err("error: write failed at %#llx\n", addr);
|
| 111 |
|
| 112 | return err;
|
| 113 | }
|
| 114 | EXPORT_SYMBOL_GPL(mtdtest_write);
|
| 115 |
|
| 116 | MODULE_LICENSE("GPL");
|
| 117 | MODULE_DESCRIPTION("MTD function test helpers");
|
| 118 | MODULE_AUTHOR("Akinobu Mita");
|