18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "mtd_test: " fmt
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/module.h>
58c2ecf20Sopenharmony_ci#include <linux/sched.h>
68c2ecf20Sopenharmony_ci#include <linux/printk.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "mtd_test.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ciint mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum)
118c2ecf20Sopenharmony_ci{
128c2ecf20Sopenharmony_ci	int err;
138c2ecf20Sopenharmony_ci	struct erase_info ei;
148c2ecf20Sopenharmony_ci	loff_t addr = (loff_t)ebnum * mtd->erasesize;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci	memset(&ei, 0, sizeof(struct erase_info));
178c2ecf20Sopenharmony_ci	ei.addr = addr;
188c2ecf20Sopenharmony_ci	ei.len  = mtd->erasesize;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	err = mtd_erase(mtd, &ei);
218c2ecf20Sopenharmony_ci	if (err) {
228c2ecf20Sopenharmony_ci		pr_info("error %d while erasing EB %d\n", err, ebnum);
238c2ecf20Sopenharmony_ci		return err;
248c2ecf20Sopenharmony_ci	}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	return 0;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int is_block_bad(struct mtd_info *mtd, unsigned int ebnum)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	int ret;
328c2ecf20Sopenharmony_ci	loff_t addr = (loff_t)ebnum * mtd->erasesize;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	ret = mtd_block_isbad(mtd, addr);
358c2ecf20Sopenharmony_ci	if (ret)
368c2ecf20Sopenharmony_ci		pr_info("block %d is bad\n", ebnum);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return ret;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ciint mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
428c2ecf20Sopenharmony_ci					unsigned int eb, int ebcnt)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	int i, bad = 0;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (!mtd_can_have_bb(mtd))
478c2ecf20Sopenharmony_ci		return 0;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	pr_info("scanning for bad eraseblocks\n");
508c2ecf20Sopenharmony_ci	for (i = 0; i < ebcnt; ++i) {
518c2ecf20Sopenharmony_ci		bbt[i] = is_block_bad(mtd, eb + i) ? 1 : 0;
528c2ecf20Sopenharmony_ci		if (bbt[i])
538c2ecf20Sopenharmony_ci			bad += 1;
548c2ecf20Sopenharmony_ci		cond_resched();
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return 0;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ciint mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
628c2ecf20Sopenharmony_ci				unsigned int eb, int ebcnt)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	int err;
658c2ecf20Sopenharmony_ci	unsigned int i;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	for (i = 0; i < ebcnt; ++i) {
688c2ecf20Sopenharmony_ci		if (bbt[i])
698c2ecf20Sopenharmony_ci			continue;
708c2ecf20Sopenharmony_ci		err = mtdtest_erase_eraseblock(mtd, eb + i);
718c2ecf20Sopenharmony_ci		if (err)
728c2ecf20Sopenharmony_ci			return err;
738c2ecf20Sopenharmony_ci		cond_resched();
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciint mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	size_t read;
828c2ecf20Sopenharmony_ci	int err;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	err = mtd_read(mtd, addr, size, &read, buf);
858c2ecf20Sopenharmony_ci	/* Ignore corrected ECC errors */
868c2ecf20Sopenharmony_ci	if (mtd_is_bitflip(err))
878c2ecf20Sopenharmony_ci		err = 0;
888c2ecf20Sopenharmony_ci	if (!err && read != size)
898c2ecf20Sopenharmony_ci		err = -EIO;
908c2ecf20Sopenharmony_ci	if (err)
918c2ecf20Sopenharmony_ci		pr_err("error: read failed at %#llx\n", addr);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return err;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ciint mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size,
978c2ecf20Sopenharmony_ci		const void *buf)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	size_t written;
1008c2ecf20Sopenharmony_ci	int err;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	err = mtd_write(mtd, addr, size, &written, buf);
1038c2ecf20Sopenharmony_ci	if (!err && written != size)
1048c2ecf20Sopenharmony_ci		err = -EIO;
1058c2ecf20Sopenharmony_ci	if (err)
1068c2ecf20Sopenharmony_ci		pr_err("error: write failed at %#llx\n", addr);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return err;
1098c2ecf20Sopenharmony_ci}
110