162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#define pr_fmt(fmt) "mtd_test: " fmt
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/sched.h>
662306a36Sopenharmony_ci#include <linux/printk.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "mtd_test.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ciint mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum)
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	int err;
1362306a36Sopenharmony_ci	struct erase_info ei;
1462306a36Sopenharmony_ci	loff_t addr = (loff_t)ebnum * mtd->erasesize;
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci	memset(&ei, 0, sizeof(struct erase_info));
1762306a36Sopenharmony_ci	ei.addr = addr;
1862306a36Sopenharmony_ci	ei.len  = mtd->erasesize;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	err = mtd_erase(mtd, &ei);
2162306a36Sopenharmony_ci	if (err) {
2262306a36Sopenharmony_ci		pr_info("error %d while erasing EB %d\n", err, ebnum);
2362306a36Sopenharmony_ci		return err;
2462306a36Sopenharmony_ci	}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	return 0;
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int is_block_bad(struct mtd_info *mtd, unsigned int ebnum)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	int ret;
3262306a36Sopenharmony_ci	loff_t addr = (loff_t)ebnum * mtd->erasesize;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	ret = mtd_block_isbad(mtd, addr);
3562306a36Sopenharmony_ci	if (ret)
3662306a36Sopenharmony_ci		pr_info("block %d is bad\n", ebnum);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return ret;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ciint mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
4262306a36Sopenharmony_ci					unsigned int eb, int ebcnt)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	int i, bad = 0;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (!mtd_can_have_bb(mtd))
4762306a36Sopenharmony_ci		return 0;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	pr_info("scanning for bad eraseblocks\n");
5062306a36Sopenharmony_ci	for (i = 0; i < ebcnt; ++i) {
5162306a36Sopenharmony_ci		bbt[i] = is_block_bad(mtd, eb + i) ? 1 : 0;
5262306a36Sopenharmony_ci		if (bbt[i])
5362306a36Sopenharmony_ci			bad += 1;
5462306a36Sopenharmony_ci		cond_resched();
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return 0;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ciint mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
6262306a36Sopenharmony_ci				unsigned int eb, int ebcnt)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	int err;
6562306a36Sopenharmony_ci	unsigned int i;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	for (i = 0; i < ebcnt; ++i) {
6862306a36Sopenharmony_ci		if (bbt[i])
6962306a36Sopenharmony_ci			continue;
7062306a36Sopenharmony_ci		err = mtdtest_erase_eraseblock(mtd, eb + i);
7162306a36Sopenharmony_ci		if (err)
7262306a36Sopenharmony_ci			return err;
7362306a36Sopenharmony_ci		cond_resched();
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciint mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	size_t read;
8262306a36Sopenharmony_ci	int err;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	err = mtd_read(mtd, addr, size, &read, buf);
8562306a36Sopenharmony_ci	/* Ignore corrected ECC errors */
8662306a36Sopenharmony_ci	if (mtd_is_bitflip(err))
8762306a36Sopenharmony_ci		err = 0;
8862306a36Sopenharmony_ci	if (!err && read != size)
8962306a36Sopenharmony_ci		err = -EIO;
9062306a36Sopenharmony_ci	if (err)
9162306a36Sopenharmony_ci		pr_err("error: read failed at %#llx\n", addr);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return err;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ciint mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size,
9762306a36Sopenharmony_ci		const void *buf)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	size_t written;
10062306a36Sopenharmony_ci	int err;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	err = mtd_write(mtd, addr, size, &written, buf);
10362306a36Sopenharmony_ci	if (!err && written != size)
10462306a36Sopenharmony_ci		err = -EIO;
10562306a36Sopenharmony_ci	if (err)
10662306a36Sopenharmony_ci		pr_err("error: write failed at %#llx\n", addr);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return err;
10962306a36Sopenharmony_ci}
110