| // SPDX-License-Identifier: GPL-2.0 | 
 | #define pr_fmt(fmt) "mtd_test: " fmt | 
 |  | 
 | #include <linux/module.h> | 
 | #include <linux/sched.h> | 
 | #include <linux/printk.h> | 
 |  | 
 | #include "mtd_test.h" | 
 |  | 
 | int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum) | 
 | { | 
 | 	int err; | 
 | 	struct erase_info ei; | 
 | 	loff_t addr = (loff_t)ebnum * mtd->erasesize; | 
 |  | 
 | 	memset(&ei, 0, sizeof(struct erase_info)); | 
 | 	ei.addr = addr; | 
 | 	ei.len  = mtd->erasesize; | 
 |  | 
 | 	err = mtd_erase(mtd, &ei); | 
 | 	if (err) { | 
 | 		pr_info("error %d while erasing EB %d\n", err, ebnum); | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int is_block_bad(struct mtd_info *mtd, unsigned int ebnum) | 
 | { | 
 | 	int ret; | 
 | 	loff_t addr = (loff_t)ebnum * mtd->erasesize; | 
 |  | 
 | 	ret = mtd_block_isbad(mtd, addr); | 
 | 	if (ret) | 
 | 		pr_info("block %d is bad\n", ebnum); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, | 
 | 					unsigned int eb, int ebcnt) | 
 | { | 
 | 	int i, bad = 0; | 
 |  | 
 | 	if (!mtd_can_have_bb(mtd)) | 
 | 		return 0; | 
 |  | 
 | 	pr_info("scanning for bad eraseblocks\n"); | 
 | 	for (i = 0; i < ebcnt; ++i) { | 
 | 		bbt[i] = is_block_bad(mtd, eb + i) ? 1 : 0; | 
 | 		if (bbt[i]) | 
 | 			bad += 1; | 
 | 		cond_resched(); | 
 | 	} | 
 | 	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, | 
 | 				unsigned int eb, int ebcnt) | 
 | { | 
 | 	int err; | 
 | 	unsigned int i; | 
 |  | 
 | 	for (i = 0; i < ebcnt; ++i) { | 
 | 		if (bbt[i]) | 
 | 			continue; | 
 | 		err = mtdtest_erase_eraseblock(mtd, eb + i); | 
 | 		if (err) | 
 | 			return err; | 
 | 		cond_resched(); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf) | 
 | { | 
 | 	size_t read; | 
 | 	int err; | 
 |  | 
 | 	err = mtd_read(mtd, addr, size, &read, buf); | 
 | 	/* Ignore corrected ECC errors */ | 
 | 	if (mtd_is_bitflip(err)) | 
 | 		err = 0; | 
 | 	if (!err && read != size) | 
 | 		err = -EIO; | 
 | 	if (err) | 
 | 		pr_err("error: read failed at %#llx\n", addr); | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size, | 
 | 		const void *buf) | 
 | { | 
 | 	size_t written; | 
 | 	int err; | 
 |  | 
 | 	err = mtd_write(mtd, addr, size, &written, buf); | 
 | 	if (!err && written != size) | 
 | 		err = -EIO; | 
 | 	if (err) | 
 | 		pr_err("error: write failed at %#llx\n", addr); | 
 |  | 
 | 	return err; | 
 | } |