1 | // SPDX-License-Identifier: GPL-2.0 |
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, instr: &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 | |
29 | static int is_block_bad(struct mtd_info *mtd, unsigned int ebnum) |
30 | { |
31 | int ret; |
32 | loff_t addr = (loff_t)ebnum * mtd->erasesize; |
33 | |
34 | ret = mtd_block_isbad(mtd, ofs: addr); |
35 | if (ret) |
36 | pr_info("block %d is bad\n" , ebnum); |
37 | |
38 | return ret; |
39 | } |
40 | |
41 | int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, |
42 | unsigned int eb, int ebcnt) |
43 | { |
44 | int i, bad = 0; |
45 | |
46 | if (!mtd_can_have_bb(mtd)) |
47 | return 0; |
48 | |
49 | pr_info("scanning for bad eraseblocks\n" ); |
50 | for (i = 0; i < ebcnt; ++i) { |
51 | bbt[i] = is_block_bad(mtd, ebnum: eb + i) ? 1 : 0; |
52 | if (bbt[i]) |
53 | bad += 1; |
54 | cond_resched(); |
55 | } |
56 | pr_info("scanned %d eraseblocks, %d are bad\n" , i, bad); |
57 | |
58 | return 0; |
59 | } |
60 | |
61 | int mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, |
62 | unsigned int eb, int ebcnt) |
63 | { |
64 | int err; |
65 | unsigned int i; |
66 | |
67 | for (i = 0; i < ebcnt; ++i) { |
68 | if (bbt[i]) |
69 | continue; |
70 | err = mtdtest_erase_eraseblock(mtd, ebnum: eb + i); |
71 | if (err) |
72 | return err; |
73 | cond_resched(); |
74 | } |
75 | |
76 | return 0; |
77 | } |
78 | |
79 | int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf) |
80 | { |
81 | size_t read; |
82 | int err; |
83 | |
84 | err = mtd_read(mtd, from: addr, len: size, retlen: &read, buf); |
85 | /* Ignore corrected ECC errors */ |
86 | if (mtd_is_bitflip(err)) |
87 | err = 0; |
88 | if (!err && read != size) |
89 | err = -EIO; |
90 | if (err) |
91 | pr_err("error: read failed at %#llx\n" , addr); |
92 | |
93 | return err; |
94 | } |
95 | |
96 | int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size, |
97 | const void *buf) |
98 | { |
99 | size_t written; |
100 | int err; |
101 | |
102 | err = mtd_write(mtd, to: addr, len: size, retlen: &written, buf); |
103 | if (!err && written != size) |
104 | err = -EIO; |
105 | if (err) |
106 | pr_err("error: write failed at %#llx\n" , addr); |
107 | |
108 | return err; |
109 | } |
110 | |