162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright © 2012 NetCommWireless
462306a36Sopenharmony_ci * Iwo Mergler <Iwo.Mergler@netcommwireless.com.au>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Test for multi-bit error recovery on a NAND page This mostly tests the
762306a36Sopenharmony_ci * ECC controller / driver.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * There are two test modes:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *	0 - artificially inserting bit errors until the ECC fails
1262306a36Sopenharmony_ci *	    This is the default method and fairly quick. It should
1362306a36Sopenharmony_ci *	    be independent of the quality of the FLASH.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *	1 - re-writing the same pattern repeatedly until the ECC fails.
1662306a36Sopenharmony_ci *	    This method relies on the physics of NAND FLASH to eventually
1762306a36Sopenharmony_ci *	    generate '0' bits if '1' has been written sufficient times.
1862306a36Sopenharmony_ci *	    Depending on the NAND, the first bit errors will appear after
1962306a36Sopenharmony_ci *	    1000 or more writes and then will usually snowball, reaching the
2062306a36Sopenharmony_ci *	    limits of the ECC quickly.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci *	    The test stops after 10000 cycles, should your FLASH be
2362306a36Sopenharmony_ci *	    exceptionally good and not generate bit errors before that. Try
2462306a36Sopenharmony_ci *	    a different page in that case.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * Please note that neither of these tests will significantly 'use up' any
2762306a36Sopenharmony_ci * FLASH endurance. Only a maximum of two erase operations will be performed.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <linux/init.h>
3362306a36Sopenharmony_ci#include <linux/module.h>
3462306a36Sopenharmony_ci#include <linux/moduleparam.h>
3562306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
3662306a36Sopenharmony_ci#include <linux/err.h>
3762306a36Sopenharmony_ci#include <linux/mtd/rawnand.h>
3862306a36Sopenharmony_ci#include <linux/slab.h>
3962306a36Sopenharmony_ci#include "mtd_test.h"
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int dev;
4262306a36Sopenharmony_cimodule_param(dev, int, S_IRUGO);
4362306a36Sopenharmony_ciMODULE_PARM_DESC(dev, "MTD device number to use");
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic unsigned page_offset;
4662306a36Sopenharmony_cimodule_param(page_offset, uint, S_IRUGO);
4762306a36Sopenharmony_ciMODULE_PARM_DESC(page_offset, "Page number relative to dev start");
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic unsigned seed;
5062306a36Sopenharmony_cimodule_param(seed, uint, S_IRUGO);
5162306a36Sopenharmony_ciMODULE_PARM_DESC(seed, "Random seed");
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic int mode;
5462306a36Sopenharmony_cimodule_param(mode, int, S_IRUGO);
5562306a36Sopenharmony_ciMODULE_PARM_DESC(mode, "0=incremental errors, 1=overwrite test");
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic unsigned max_overwrite = 10000;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic loff_t   offset;     /* Offset of the page we're using. */
6062306a36Sopenharmony_cistatic unsigned eraseblock; /* Eraseblock number for our page. */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* We assume that the ECC can correct up to a certain number
6362306a36Sopenharmony_ci * of biterrors per subpage. */
6462306a36Sopenharmony_cistatic unsigned subsize;  /* Size of subpages */
6562306a36Sopenharmony_cistatic unsigned subcount; /* Number of subpages per page */
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic struct mtd_info *mtd;   /* MTD device */
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic uint8_t *wbuffer; /* One page write / compare buffer */
7062306a36Sopenharmony_cistatic uint8_t *rbuffer; /* One page read buffer */
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* 'random' bytes from known offsets */
7362306a36Sopenharmony_cistatic uint8_t hash(unsigned offset)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	unsigned v = offset;
7662306a36Sopenharmony_ci	unsigned char c;
7762306a36Sopenharmony_ci	v ^= 0x7f7edfd3;
7862306a36Sopenharmony_ci	v = v ^ (v >> 3);
7962306a36Sopenharmony_ci	v = v ^ (v >> 5);
8062306a36Sopenharmony_ci	v = v ^ (v >> 13);
8162306a36Sopenharmony_ci	c = v & 0xFF;
8262306a36Sopenharmony_ci	/* Reverse bits of result. */
8362306a36Sopenharmony_ci	c = (c & 0x0F) << 4 | (c & 0xF0) >> 4;
8462306a36Sopenharmony_ci	c = (c & 0x33) << 2 | (c & 0xCC) >> 2;
8562306a36Sopenharmony_ci	c = (c & 0x55) << 1 | (c & 0xAA) >> 1;
8662306a36Sopenharmony_ci	return c;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/* Writes wbuffer to page */
9062306a36Sopenharmony_cistatic int write_page(int log)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	if (log)
9362306a36Sopenharmony_ci		pr_info("write_page\n");
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return mtdtest_write(mtd, offset, mtd->writesize, wbuffer);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* Re-writes the data area while leaving the OOB alone. */
9962306a36Sopenharmony_cistatic int rewrite_page(int log)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	int err = 0;
10262306a36Sopenharmony_ci	struct mtd_oob_ops ops = { };
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (log)
10562306a36Sopenharmony_ci		pr_info("rewrite page\n");
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	ops.mode      = MTD_OPS_RAW; /* No ECC */
10862306a36Sopenharmony_ci	ops.len       = mtd->writesize;
10962306a36Sopenharmony_ci	ops.retlen    = 0;
11062306a36Sopenharmony_ci	ops.ooblen    = 0;
11162306a36Sopenharmony_ci	ops.oobretlen = 0;
11262306a36Sopenharmony_ci	ops.ooboffs   = 0;
11362306a36Sopenharmony_ci	ops.datbuf    = wbuffer;
11462306a36Sopenharmony_ci	ops.oobbuf    = NULL;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	err = mtd_write_oob(mtd, offset, &ops);
11762306a36Sopenharmony_ci	if (err || ops.retlen != mtd->writesize) {
11862306a36Sopenharmony_ci		pr_err("error: write_oob failed (%d)\n", err);
11962306a36Sopenharmony_ci		if (!err)
12062306a36Sopenharmony_ci			err = -EIO;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return err;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/* Reads page into rbuffer. Returns number of corrected bit errors (>=0)
12762306a36Sopenharmony_ci * or error (<0) */
12862306a36Sopenharmony_cistatic int read_page(int log)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	int err = 0;
13162306a36Sopenharmony_ci	size_t read;
13262306a36Sopenharmony_ci	struct mtd_ecc_stats oldstats;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (log)
13562306a36Sopenharmony_ci		pr_info("read_page\n");
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* Saving last mtd stats */
13862306a36Sopenharmony_ci	memcpy(&oldstats, &mtd->ecc_stats, sizeof(oldstats));
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	err = mtd_read(mtd, offset, mtd->writesize, &read, rbuffer);
14162306a36Sopenharmony_ci	if (!err || err == -EUCLEAN)
14262306a36Sopenharmony_ci		err = mtd->ecc_stats.corrected - oldstats.corrected;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (err < 0 || read != mtd->writesize) {
14562306a36Sopenharmony_ci		pr_err("error: read failed at %#llx\n", (long long)offset);
14662306a36Sopenharmony_ci		if (err >= 0)
14762306a36Sopenharmony_ci			err = -EIO;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return err;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/* Verifies rbuffer against random sequence */
15462306a36Sopenharmony_cistatic int verify_page(int log)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	unsigned i, errs = 0;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (log)
15962306a36Sopenharmony_ci		pr_info("verify_page\n");
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	for (i = 0; i < mtd->writesize; i++) {
16262306a36Sopenharmony_ci		if (rbuffer[i] != hash(i+seed)) {
16362306a36Sopenharmony_ci			pr_err("Error: page offset %u, expected %02x, got %02x\n",
16462306a36Sopenharmony_ci				i, hash(i+seed), rbuffer[i]);
16562306a36Sopenharmony_ci			errs++;
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (errs)
17062306a36Sopenharmony_ci		return -EIO;
17162306a36Sopenharmony_ci	else
17262306a36Sopenharmony_ci		return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci#define CBIT(v, n) ((v) & (1 << (n)))
17662306a36Sopenharmony_ci#define BCLR(v, n) ((v) = (v) & ~(1 << (n)))
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/* Finds the first '1' bit in wbuffer starting at offset 'byte'
17962306a36Sopenharmony_ci * and sets it to '0'. */
18062306a36Sopenharmony_cistatic int insert_biterror(unsigned byte)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	int bit;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	while (byte < mtd->writesize) {
18562306a36Sopenharmony_ci		for (bit = 7; bit >= 0; bit--) {
18662306a36Sopenharmony_ci			if (CBIT(wbuffer[byte], bit)) {
18762306a36Sopenharmony_ci				BCLR(wbuffer[byte], bit);
18862306a36Sopenharmony_ci				pr_info("Inserted biterror @ %u/%u\n", byte, bit);
18962306a36Sopenharmony_ci				return 0;
19062306a36Sopenharmony_ci			}
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci		byte++;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci	pr_err("biterror: Failed to find a '1' bit\n");
19562306a36Sopenharmony_ci	return -EIO;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/* Writes 'random' data to page and then introduces deliberate bit
19962306a36Sopenharmony_ci * errors into the page, while verifying each step. */
20062306a36Sopenharmony_cistatic int incremental_errors_test(void)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	int err = 0;
20362306a36Sopenharmony_ci	unsigned i;
20462306a36Sopenharmony_ci	unsigned errs_per_subpage = 0;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	pr_info("incremental biterrors test\n");
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	for (i = 0; i < mtd->writesize; i++)
20962306a36Sopenharmony_ci		wbuffer[i] = hash(i+seed);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	err = write_page(1);
21262306a36Sopenharmony_ci	if (err)
21362306a36Sopenharmony_ci		goto exit;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	while (1) {
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		err = rewrite_page(1);
21862306a36Sopenharmony_ci		if (err)
21962306a36Sopenharmony_ci			goto exit;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		err = read_page(1);
22262306a36Sopenharmony_ci		if (err > 0)
22362306a36Sopenharmony_ci			pr_info("Read reported %d corrected bit errors\n", err);
22462306a36Sopenharmony_ci		if (err < 0) {
22562306a36Sopenharmony_ci			pr_err("After %d biterrors per subpage, read reported error %d\n",
22662306a36Sopenharmony_ci				errs_per_subpage, err);
22762306a36Sopenharmony_ci			err = 0;
22862306a36Sopenharmony_ci			goto exit;
22962306a36Sopenharmony_ci		}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		err = verify_page(1);
23262306a36Sopenharmony_ci		if (err) {
23362306a36Sopenharmony_ci			pr_err("ECC failure, read data is incorrect despite read success\n");
23462306a36Sopenharmony_ci			goto exit;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		pr_info("Successfully corrected %d bit errors per subpage\n",
23862306a36Sopenharmony_ci			errs_per_subpage);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		for (i = 0; i < subcount; i++) {
24162306a36Sopenharmony_ci			err = insert_biterror(i * subsize);
24262306a36Sopenharmony_ci			if (err < 0)
24362306a36Sopenharmony_ci				goto exit;
24462306a36Sopenharmony_ci		}
24562306a36Sopenharmony_ci		errs_per_subpage++;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ciexit:
24962306a36Sopenharmony_ci	return err;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/* Writes 'random' data to page and then re-writes that same data repeatedly.
25462306a36Sopenharmony_ci   This eventually develops bit errors (bits written as '1' will slowly become
25562306a36Sopenharmony_ci   '0'), which are corrected as far as the ECC is capable of. */
25662306a36Sopenharmony_cistatic int overwrite_test(void)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	int err = 0;
25962306a36Sopenharmony_ci	unsigned i;
26062306a36Sopenharmony_ci	unsigned max_corrected = 0;
26162306a36Sopenharmony_ci	unsigned opno = 0;
26262306a36Sopenharmony_ci	/* We don't expect more than this many correctable bit errors per
26362306a36Sopenharmony_ci	 * page. */
26462306a36Sopenharmony_ci	#define MAXBITS 512
26562306a36Sopenharmony_ci	static unsigned bitstats[MAXBITS]; /* bit error histogram. */
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	memset(bitstats, 0, sizeof(bitstats));
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	pr_info("overwrite biterrors test\n");
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	for (i = 0; i < mtd->writesize; i++)
27262306a36Sopenharmony_ci		wbuffer[i] = hash(i+seed);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	err = write_page(1);
27562306a36Sopenharmony_ci	if (err)
27662306a36Sopenharmony_ci		goto exit;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	while (opno < max_overwrite) {
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci		err = write_page(0);
28162306a36Sopenharmony_ci		if (err)
28262306a36Sopenharmony_ci			break;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		err = read_page(0);
28562306a36Sopenharmony_ci		if (err >= 0) {
28662306a36Sopenharmony_ci			if (err >= MAXBITS) {
28762306a36Sopenharmony_ci				pr_info("Implausible number of bit errors corrected\n");
28862306a36Sopenharmony_ci				err = -EIO;
28962306a36Sopenharmony_ci				break;
29062306a36Sopenharmony_ci			}
29162306a36Sopenharmony_ci			bitstats[err]++;
29262306a36Sopenharmony_ci			if (err > max_corrected) {
29362306a36Sopenharmony_ci				max_corrected = err;
29462306a36Sopenharmony_ci				pr_info("Read reported %d corrected bit errors\n",
29562306a36Sopenharmony_ci					err);
29662306a36Sopenharmony_ci			}
29762306a36Sopenharmony_ci		} else { /* err < 0 */
29862306a36Sopenharmony_ci			pr_info("Read reported error %d\n", err);
29962306a36Sopenharmony_ci			err = 0;
30062306a36Sopenharmony_ci			break;
30162306a36Sopenharmony_ci		}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		err = verify_page(0);
30462306a36Sopenharmony_ci		if (err) {
30562306a36Sopenharmony_ci			bitstats[max_corrected] = opno;
30662306a36Sopenharmony_ci			pr_info("ECC failure, read data is incorrect despite read success\n");
30762306a36Sopenharmony_ci			break;
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		err = mtdtest_relax();
31162306a36Sopenharmony_ci		if (err)
31262306a36Sopenharmony_ci			break;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		opno++;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* At this point bitstats[0] contains the number of ops with no bit
31862306a36Sopenharmony_ci	 * errors, bitstats[1] the number of ops with 1 bit error, etc. */
31962306a36Sopenharmony_ci	pr_info("Bit error histogram (%d operations total):\n", opno);
32062306a36Sopenharmony_ci	for (i = 0; i < max_corrected; i++)
32162306a36Sopenharmony_ci		pr_info("Page reads with %3d corrected bit errors: %d\n",
32262306a36Sopenharmony_ci			i, bitstats[i]);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ciexit:
32562306a36Sopenharmony_ci	return err;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic int __init mtd_nandbiterrs_init(void)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	int err = 0;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	printk("\n");
33362306a36Sopenharmony_ci	printk(KERN_INFO "==================================================\n");
33462306a36Sopenharmony_ci	pr_info("MTD device: %d\n", dev);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	mtd = get_mtd_device(NULL, dev);
33762306a36Sopenharmony_ci	if (IS_ERR(mtd)) {
33862306a36Sopenharmony_ci		err = PTR_ERR(mtd);
33962306a36Sopenharmony_ci		pr_err("error: cannot get MTD device\n");
34062306a36Sopenharmony_ci		goto exit_mtddev;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (!mtd_type_is_nand(mtd)) {
34462306a36Sopenharmony_ci		pr_info("this test requires NAND flash\n");
34562306a36Sopenharmony_ci		err = -ENODEV;
34662306a36Sopenharmony_ci		goto exit_nand;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	pr_info("MTD device size %llu, eraseblock=%u, page=%u, oob=%u\n",
35062306a36Sopenharmony_ci		(unsigned long long)mtd->size, mtd->erasesize,
35162306a36Sopenharmony_ci		mtd->writesize, mtd->oobsize);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	subsize  = mtd->writesize >> mtd->subpage_sft;
35462306a36Sopenharmony_ci	subcount = mtd->writesize / subsize;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	pr_info("Device uses %d subpages of %d bytes\n", subcount, subsize);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	offset     = (loff_t)page_offset * mtd->writesize;
35962306a36Sopenharmony_ci	eraseblock = mtd_div_by_eb(offset, mtd);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	pr_info("Using page=%u, offset=%llu, eraseblock=%u\n",
36262306a36Sopenharmony_ci		page_offset, offset, eraseblock);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	wbuffer = kmalloc(mtd->writesize, GFP_KERNEL);
36562306a36Sopenharmony_ci	if (!wbuffer) {
36662306a36Sopenharmony_ci		err = -ENOMEM;
36762306a36Sopenharmony_ci		goto exit_wbuffer;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	rbuffer = kmalloc(mtd->writesize, GFP_KERNEL);
37162306a36Sopenharmony_ci	if (!rbuffer) {
37262306a36Sopenharmony_ci		err = -ENOMEM;
37362306a36Sopenharmony_ci		goto exit_rbuffer;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	err = mtdtest_erase_eraseblock(mtd, eraseblock);
37762306a36Sopenharmony_ci	if (err)
37862306a36Sopenharmony_ci		goto exit_error;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (mode == 0)
38162306a36Sopenharmony_ci		err = incremental_errors_test();
38262306a36Sopenharmony_ci	else
38362306a36Sopenharmony_ci		err = overwrite_test();
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (err)
38662306a36Sopenharmony_ci		goto exit_error;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* We leave the block un-erased in case of test failure. */
38962306a36Sopenharmony_ci	err = mtdtest_erase_eraseblock(mtd, eraseblock);
39062306a36Sopenharmony_ci	if (err)
39162306a36Sopenharmony_ci		goto exit_error;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	err = -EIO;
39462306a36Sopenharmony_ci	pr_info("finished successfully.\n");
39562306a36Sopenharmony_ci	printk(KERN_INFO "==================================================\n");
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ciexit_error:
39862306a36Sopenharmony_ci	kfree(rbuffer);
39962306a36Sopenharmony_ciexit_rbuffer:
40062306a36Sopenharmony_ci	kfree(wbuffer);
40162306a36Sopenharmony_ciexit_wbuffer:
40262306a36Sopenharmony_ci	/* Nothing */
40362306a36Sopenharmony_ciexit_nand:
40462306a36Sopenharmony_ci	put_mtd_device(mtd);
40562306a36Sopenharmony_ciexit_mtddev:
40662306a36Sopenharmony_ci	return err;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic void __exit mtd_nandbiterrs_exit(void)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	return;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cimodule_init(mtd_nandbiterrs_init);
41562306a36Sopenharmony_cimodule_exit(mtd_nandbiterrs_exit);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ciMODULE_DESCRIPTION("NAND bit error recovery test");
41862306a36Sopenharmony_ciMODULE_AUTHOR("Iwo Mergler");
41962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
420