All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules
@ 2013-08-03  9:52 Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 01/10] mtd: tests: introduce helper functions Akinobu Mita
                   ` (10 more replies)
  0 siblings, 11 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-08-03  9:52 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, Iwo Mergler, Adrian Hunter, Akinobu Mita,
	Vikram Narayanan, Brian Norris, David Woodhouse

This patch set reduces code duplication among mtd/tests modules by
moving common helper functions into mtd_test.c. 

* Changes from v1
- link a signle object including helper functions to the test modules,
  instead of providing a separate module.
- extend mtdtest_scan_for_bad_eraseblocks() so that it can specify any
  contiguous erase blocks.
- rename mtdtest_erase_whole_device() to mtdtest_erase_good_eraseblocks()
  and extend it so that it can specify any contiguous erase blocks.
- fix accidentally changed argument of memset() in mtd_readtest module
- convert mtd_torturetest module to use mtd_test helpers
- convert mtd_nandbiterrs module to use mtd_test helpers

Akinobu Mita (10):
  mtd: tests: introduce helper functions
  mtd: tests: rename sources in order to link a helper object
  mtd: mtd_oobtest: use mtd_test helpers
  mtd: mtd_pagetest: use mtd_test helpers
  mtd: mtd_readtest: use mtd_test helpers
  mtd: mtd_speedtest: use mtd_test helpers
  mtd: mtd_stresstest: use mtd_test helpers
  mtd: mtd_subpagetest: use mtd_test helpers
  mtd: mtd_torturetest: use mtd_test helpers
  mtd: mtd_nandbiterrs: use mtd_test helpers

 drivers/mtd/tests/Makefile          |   9 +
 drivers/mtd/tests/mtd_nandbiterrs.c | 461 -----------------------
 drivers/mtd/tests/mtd_oobtest.c     | 714 ------------------------------------
 drivers/mtd/tests/mtd_pagetest.c    | 605 ------------------------------
 drivers/mtd/tests/mtd_readtest.c    | 257 -------------
 drivers/mtd/tests/mtd_speedtest.c   | 556 ----------------------------
 drivers/mtd/tests/mtd_stresstest.c  | 321 ----------------
 drivers/mtd/tests/mtd_subpagetest.c | 504 -------------------------
 drivers/mtd/tests/mtd_test.c        | 110 ++++++
 drivers/mtd/tests/mtd_test.h        |  11 +
 drivers/mtd/tests/mtd_torturetest.c | 535 ---------------------------
 drivers/mtd/tests/nandbiterrs.c     | 434 ++++++++++++++++++++++
 drivers/mtd/tests/oobtest.c         | 646 ++++++++++++++++++++++++++++++++
 drivers/mtd/tests/pagetest.c        | 516 ++++++++++++++++++++++++++
 drivers/mtd/tests/readtest.c        | 224 +++++++++++
 drivers/mtd/tests/speedtest.c       | 448 ++++++++++++++++++++++
 drivers/mtd/tests/stresstest.c      | 259 +++++++++++++
 drivers/mtd/tests/subpagetest.c     | 435 ++++++++++++++++++++++
 drivers/mtd/tests/torturetest.c     | 483 ++++++++++++++++++++++++
 19 files changed, 3575 insertions(+), 3953 deletions(-)
 delete mode 100644 drivers/mtd/tests/mtd_nandbiterrs.c
 delete mode 100644 drivers/mtd/tests/mtd_oobtest.c
 delete mode 100644 drivers/mtd/tests/mtd_pagetest.c
 delete mode 100644 drivers/mtd/tests/mtd_readtest.c
 delete mode 100644 drivers/mtd/tests/mtd_speedtest.c
 delete mode 100644 drivers/mtd/tests/mtd_stresstest.c
 delete mode 100644 drivers/mtd/tests/mtd_subpagetest.c
 create mode 100644 drivers/mtd/tests/mtd_test.c
 create mode 100644 drivers/mtd/tests/mtd_test.h
 delete mode 100644 drivers/mtd/tests/mtd_torturetest.c
 create mode 100644 drivers/mtd/tests/nandbiterrs.c
 create mode 100644 drivers/mtd/tests/oobtest.c
 create mode 100644 drivers/mtd/tests/pagetest.c
 create mode 100644 drivers/mtd/tests/readtest.c
 create mode 100644 drivers/mtd/tests/speedtest.c
 create mode 100644 drivers/mtd/tests/stresstest.c
 create mode 100644 drivers/mtd/tests/subpagetest.c
 create mode 100644 drivers/mtd/tests/torturetest.c

Cc: Iwo Mergler <Iwo.Mergler@netcommwireless.com.au>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org

-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH -next v2 01/10] mtd: tests: introduce helper functions
  2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
@ 2013-08-03  9:52 ` Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 02/10] mtd: tests: rename sources in order to link a helper object Akinobu Mita
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-08-03  9:52 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, Vikram Narayanan, Akinobu Mita, Adrian Hunter,
	Brian Norris, David Woodhouse

This introduces the helper functions which can be used by several
mtd/tests modules.

The following three functions are used all over the test modules.

- mtdtest_erase_eraseblock()
- mtdtest_scan_for_bad_eraseblocks()
- mtdtest_erase_good_eraseblocks()

The following are wrapper functions for mtd_read() and mtd_write()
which can simplify the return value check.

- mtdtest_read()
- mtdtest_write()

All helpers are put into a single .c file and it will be linked to
every test module later.  The code will actually be copied to every
test module, but it is fine for our small test infrastructure.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
---
* Changes from v1
- link a signle object including helper functions to the test modules,
  instead of providing a separate module.
- extend mtdtest_scan_for_bad_eraseblocks() so that it can specify any
  contiguous erase blocks.
- rename mtdtest_erase_whole_device() to mtdtest_erase_good_eraseblocks()
  and extend it so that it can specify any contiguous erase blocks.

 drivers/mtd/tests/mtd_test.c | 110 +++++++++++++++++++++++++++++++++++++++++++
 drivers/mtd/tests/mtd_test.h |  11 +++++
 2 files changed, 121 insertions(+)
 create mode 100644 drivers/mtd/tests/mtd_test.c
 create mode 100644 drivers/mtd/tests/mtd_test.h

diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c
new file mode 100644
index 0000000..9e63896
--- /dev/null
+++ b/drivers/mtd/tests/mtd_test.c
@@ -0,0 +1,110 @@
+#define pr_fmt(fmt) "mtd_test: " fmt
+
+#include <linux/init.h>
+#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 = ebnum * mtd->erasesize;
+
+	memset(&ei, 0, sizeof(struct erase_info));
+	ei.mtd  = mtd;
+	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;
+	}
+
+	if (ei.state == MTD_ERASE_FAILED) {
+		pr_info("some erase error occurred at EB %d\n", ebnum);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int is_block_bad(struct mtd_info *mtd, unsigned int ebnum)
+{
+	int ret;
+	loff_t addr = 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 = -EINVAL;
+
+	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;
+
+	return err;
+}
diff --git a/drivers/mtd/tests/mtd_test.h b/drivers/mtd/tests/mtd_test.h
new file mode 100644
index 0000000..f437c77
--- /dev/null
+++ b/drivers/mtd/tests/mtd_test.h
@@ -0,0 +1,11 @@
+#include <linux/mtd/mtd.h>
+
+int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum);
+int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
+					unsigned int eb, int ebcnt);
+int mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
+				unsigned int eb, int ebcnt);
+
+int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf);
+int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size,
+		const void *buf);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH -next v2 02/10] mtd: tests: rename sources in order to link a helper object
  2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 01/10] mtd: tests: introduce helper functions Akinobu Mita
@ 2013-08-03  9:52 ` Akinobu Mita
  2013-08-30 15:53   ` David Woodhouse
  2013-08-03  9:52 ` [PATCH -next v2 03/10] mtd: mtd_oobtest: use mtd_test helpers Akinobu Mita
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 17+ messages in thread
From: Akinobu Mita @ 2013-08-03  9:52 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, Vikram Narayanan, Akinobu Mita, Adrian Hunter,
	Brian Norris, David Woodhouse

Each mtd test module have a single source whose name is the same as
the module name.  In order to link a single object including helper
functions to every test module, this rename these sources to the
different names.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
---
* Newly added patch from v2

 drivers/mtd/tests/Makefile          |   9 +
 drivers/mtd/tests/mtd_nandbiterrs.c | 461 -----------------------
 drivers/mtd/tests/mtd_oobtest.c     | 714 ------------------------------------
 drivers/mtd/tests/mtd_pagetest.c    | 605 ------------------------------
 drivers/mtd/tests/mtd_readtest.c    | 257 -------------
 drivers/mtd/tests/mtd_speedtest.c   | 556 ----------------------------
 drivers/mtd/tests/mtd_stresstest.c  | 321 ----------------
 drivers/mtd/tests/mtd_subpagetest.c | 504 -------------------------
 drivers/mtd/tests/mtd_torturetest.c | 535 ---------------------------
 drivers/mtd/tests/nandbiterrs.c     | 461 +++++++++++++++++++++++
 drivers/mtd/tests/oobtest.c         | 714 ++++++++++++++++++++++++++++++++++++
 drivers/mtd/tests/pagetest.c        | 605 ++++++++++++++++++++++++++++++
 drivers/mtd/tests/readtest.c        | 257 +++++++++++++
 drivers/mtd/tests/speedtest.c       | 556 ++++++++++++++++++++++++++++
 drivers/mtd/tests/stresstest.c      | 321 ++++++++++++++++
 drivers/mtd/tests/subpagetest.c     | 504 +++++++++++++++++++++++++
 drivers/mtd/tests/torturetest.c     | 535 +++++++++++++++++++++++++++
 17 files changed, 3962 insertions(+), 3953 deletions(-)
 delete mode 100644 drivers/mtd/tests/mtd_nandbiterrs.c
 delete mode 100644 drivers/mtd/tests/mtd_oobtest.c
 delete mode 100644 drivers/mtd/tests/mtd_pagetest.c
 delete mode 100644 drivers/mtd/tests/mtd_readtest.c
 delete mode 100644 drivers/mtd/tests/mtd_speedtest.c
 delete mode 100644 drivers/mtd/tests/mtd_stresstest.c
 delete mode 100644 drivers/mtd/tests/mtd_subpagetest.c
 delete mode 100644 drivers/mtd/tests/mtd_torturetest.c
 create mode 100644 drivers/mtd/tests/nandbiterrs.c
 create mode 100644 drivers/mtd/tests/oobtest.c
 create mode 100644 drivers/mtd/tests/pagetest.c
 create mode 100644 drivers/mtd/tests/readtest.c
 create mode 100644 drivers/mtd/tests/speedtest.c
 create mode 100644 drivers/mtd/tests/stresstest.c
 create mode 100644 drivers/mtd/tests/subpagetest.c
 create mode 100644 drivers/mtd/tests/torturetest.c

diff --git a/drivers/mtd/tests/Makefile b/drivers/mtd/tests/Makefile
index bd0065c..937a829 100644
--- a/drivers/mtd/tests/Makefile
+++ b/drivers/mtd/tests/Makefile
@@ -7,3 +7,12 @@ obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o
 obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o
 obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o
 obj-$(CONFIG_MTD_TESTS) += mtd_nandbiterrs.o
+
+mtd_oobtest-objs := oobtest.o mtd_test.o
+mtd_pagetest-objs := pagetest.o mtd_test.o
+mtd_readtest-objs := readtest.o mtd_test.o
+mtd_speedtest-objs := speedtest.o mtd_test.o
+mtd_stresstest-objs := stresstest.o mtd_test.o
+mtd_subpagetest-objs := subpagetest.o mtd_test.o
+mtd_torturetest-objs := torturetest.o mtd_test.o
+mtd_nandbiterrs-objs := nandbiterrs.o mtd_test.o
diff --git a/drivers/mtd/tests/mtd_nandbiterrs.c b/drivers/mtd/tests/mtd_nandbiterrs.c
deleted file mode 100644
index 207bf9a..0000000
--- a/drivers/mtd/tests/mtd_nandbiterrs.c
+++ /dev/null
@@ -1,461 +0,0 @@
-/*
- * Copyright © 2012 NetCommWireless
- * Iwo Mergler <Iwo.Mergler@netcommwireless.com.au>
- *
- * Test for multi-bit error recovery on a NAND page This mostly tests the
- * ECC controller / driver.
- *
- * There are two test modes:
- *
- *	0 - artificially inserting bit errors until the ECC fails
- *	    This is the default method and fairly quick. It should
- *	    be independent of the quality of the FLASH.
- *
- *	1 - re-writing the same pattern repeatedly until the ECC fails.
- *	    This method relies on the physics of NAND FLASH to eventually
- *	    generate '0' bits if '1' has been written sufficient times.
- *	    Depending on the NAND, the first bit errors will appear after
- *	    1000 or more writes and then will usually snowball, reaching the
- *	    limits of the ECC quickly.
- *
- *	    The test stops after 10000 cycles, should your FLASH be
- *	    exceptionally good and not generate bit errors before that. Try
- *	    a different page in that case.
- *
- * Please note that neither of these tests will significantly 'use up' any
- * FLASH endurance. Only a maximum of two erase operations will be performed.
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; see the file COPYING. If not, write to the Free Software
- * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/mtd/mtd.h>
-#include <linux/err.h>
-#include <linux/mtd/nand.h>
-#include <linux/slab.h>
-
-static int dev;
-module_param(dev, int, S_IRUGO);
-MODULE_PARM_DESC(dev, "MTD device number to use");
-
-static unsigned page_offset;
-module_param(page_offset, uint, S_IRUGO);
-MODULE_PARM_DESC(page_offset, "Page number relative to dev start");
-
-static unsigned seed;
-module_param(seed, uint, S_IRUGO);
-MODULE_PARM_DESC(seed, "Random seed");
-
-static int mode;
-module_param(mode, int, S_IRUGO);
-MODULE_PARM_DESC(mode, "0=incremental errors, 1=overwrite test");
-
-static unsigned max_overwrite = 10000;
-
-static loff_t   offset;     /* Offset of the page we're using. */
-static unsigned eraseblock; /* Eraseblock number for our page. */
-
-/* We assume that the ECC can correct up to a certain number
- * of biterrors per subpage. */
-static unsigned subsize;  /* Size of subpages */
-static unsigned subcount; /* Number of subpages per page */
-
-static struct mtd_info *mtd;   /* MTD device */
-
-static uint8_t *wbuffer; /* One page write / compare buffer */
-static uint8_t *rbuffer; /* One page read buffer */
-
-/* 'random' bytes from known offsets */
-static uint8_t hash(unsigned offset)
-{
-	unsigned v = offset;
-	unsigned char c;
-	v ^= 0x7f7edfd3;
-	v = v ^ (v >> 3);
-	v = v ^ (v >> 5);
-	v = v ^ (v >> 13);
-	c = v & 0xFF;
-	/* Reverse bits of result. */
-	c = (c & 0x0F) << 4 | (c & 0xF0) >> 4;
-	c = (c & 0x33) << 2 | (c & 0xCC) >> 2;
-	c = (c & 0x55) << 1 | (c & 0xAA) >> 1;
-	return c;
-}
-
-static int erase_block(void)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = eraseblock * mtd->erasesize;
-
-	pr_info("erase_block\n");
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err || ei.state == MTD_ERASE_FAILED) {
-		pr_err("error %d while erasing\n", err);
-		if (!err)
-			err = -EIO;
-		return err;
-	}
-
-	return 0;
-}
-
-/* Writes wbuffer to page */
-static int write_page(int log)
-{
-	int err = 0;
-	size_t written;
-
-	if (log)
-		pr_info("write_page\n");
-
-	err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer);
-	if (err || written != mtd->writesize) {
-		pr_err("error: write failed at %#llx\n", (long long)offset);
-		if (!err)
-			err = -EIO;
-	}
-
-	return err;
-}
-
-/* Re-writes the data area while leaving the OOB alone. */
-static int rewrite_page(int log)
-{
-	int err = 0;
-	struct mtd_oob_ops ops;
-
-	if (log)
-		pr_info("rewrite page\n");
-
-	ops.mode      = MTD_OPS_RAW; /* No ECC */
-	ops.len       = mtd->writesize;
-	ops.retlen    = 0;
-	ops.ooblen    = 0;
-	ops.oobretlen = 0;
-	ops.ooboffs   = 0;
-	ops.datbuf    = wbuffer;
-	ops.oobbuf    = NULL;
-
-	err = mtd_write_oob(mtd, offset, &ops);
-	if (err || ops.retlen != mtd->writesize) {
-		pr_err("error: write_oob failed (%d)\n", err);
-		if (!err)
-			err = -EIO;
-	}
-
-	return err;
-}
-
-/* Reads page into rbuffer. Returns number of corrected bit errors (>=0)
- * or error (<0) */
-static int read_page(int log)
-{
-	int err = 0;
-	size_t read;
-	struct mtd_ecc_stats oldstats;
-
-	if (log)
-		pr_info("read_page\n");
-
-	/* Saving last mtd stats */
-	memcpy(&oldstats, &mtd->ecc_stats, sizeof(oldstats));
-
-	err = mtd_read(mtd, offset, mtd->writesize, &read, rbuffer);
-	if (err == -EUCLEAN)
-		err = mtd->ecc_stats.corrected - oldstats.corrected;
-
-	if (err < 0 || read != mtd->writesize) {
-		pr_err("error: read failed at %#llx\n", (long long)offset);
-		if (err >= 0)
-			err = -EIO;
-	}
-
-	return err;
-}
-
-/* Verifies rbuffer against random sequence */
-static int verify_page(int log)
-{
-	unsigned i, errs = 0;
-
-	if (log)
-		pr_info("verify_page\n");
-
-	for (i = 0; i < mtd->writesize; i++) {
-		if (rbuffer[i] != hash(i+seed)) {
-			pr_err("Error: page offset %u, expected %02x, got %02x\n",
-				i, hash(i+seed), rbuffer[i]);
-			errs++;
-		}
-	}
-
-	if (errs)
-		return -EIO;
-	else
-		return 0;
-}
-
-#define CBIT(v, n) ((v) & (1 << (n)))
-#define BCLR(v, n) ((v) = (v) & ~(1 << (n)))
-
-/* Finds the first '1' bit in wbuffer starting at offset 'byte'
- * and sets it to '0'. */
-static int insert_biterror(unsigned byte)
-{
-	int bit;
-
-	while (byte < mtd->writesize) {
-		for (bit = 7; bit >= 0; bit--) {
-			if (CBIT(wbuffer[byte], bit)) {
-				BCLR(wbuffer[byte], bit);
-				pr_info("Inserted biterror @ %u/%u\n", byte, bit);
-				return 0;
-			}
-		}
-		byte++;
-	}
-	pr_err("biterror: Failed to find a '1' bit\n");
-	return -EIO;
-}
-
-/* Writes 'random' data to page and then introduces deliberate bit
- * errors into the page, while verifying each step. */
-static int incremental_errors_test(void)
-{
-	int err = 0;
-	unsigned i;
-	unsigned errs_per_subpage = 0;
-
-	pr_info("incremental biterrors test\n");
-
-	for (i = 0; i < mtd->writesize; i++)
-		wbuffer[i] = hash(i+seed);
-
-	err = write_page(1);
-	if (err)
-		goto exit;
-
-	while (1) {
-
-		err = rewrite_page(1);
-		if (err)
-			goto exit;
-
-		err = read_page(1);
-		if (err > 0)
-			pr_info("Read reported %d corrected bit errors\n", err);
-		if (err < 0) {
-			pr_err("After %d biterrors per subpage, read reported error %d\n",
-				errs_per_subpage, err);
-			err = 0;
-			goto exit;
-		}
-
-		err = verify_page(1);
-		if (err) {
-			pr_err("ECC failure, read data is incorrect despite read success\n");
-			goto exit;
-		}
-
-		pr_info("Successfully corrected %d bit errors per subpage\n",
-			errs_per_subpage);
-
-		for (i = 0; i < subcount; i++) {
-			err = insert_biterror(i * subsize);
-			if (err < 0)
-				goto exit;
-		}
-		errs_per_subpage++;
-	}
-
-exit:
-	return err;
-}
-
-
-/* Writes 'random' data to page and then re-writes that same data repeatedly.
-   This eventually develops bit errors (bits written as '1' will slowly become
-   '0'), which are corrected as far as the ECC is capable of. */
-static int overwrite_test(void)
-{
-	int err = 0;
-	unsigned i;
-	unsigned max_corrected = 0;
-	unsigned opno = 0;
-	/* We don't expect more than this many correctable bit errors per
-	 * page. */
-	#define MAXBITS 512
-	static unsigned bitstats[MAXBITS]; /* bit error histogram. */
-
-	memset(bitstats, 0, sizeof(bitstats));
-
-	pr_info("overwrite biterrors test\n");
-
-	for (i = 0; i < mtd->writesize; i++)
-		wbuffer[i] = hash(i+seed);
-
-	err = write_page(1);
-	if (err)
-		goto exit;
-
-	while (opno < max_overwrite) {
-
-		err = rewrite_page(0);
-		if (err)
-			break;
-
-		err = read_page(0);
-		if (err >= 0) {
-			if (err >= MAXBITS) {
-				pr_info("Implausible number of bit errors corrected\n");
-				err = -EIO;
-				break;
-			}
-			bitstats[err]++;
-			if (err > max_corrected) {
-				max_corrected = err;
-				pr_info("Read reported %d corrected bit errors\n",
-					err);
-			}
-		} else { /* err < 0 */
-			pr_info("Read reported error %d\n", err);
-			err = 0;
-			break;
-		}
-
-		err = verify_page(0);
-		if (err) {
-			bitstats[max_corrected] = opno;
-			pr_info("ECC failure, read data is incorrect despite read success\n");
-			break;
-		}
-
-		opno++;
-	}
-
-	/* At this point bitstats[0] contains the number of ops with no bit
-	 * errors, bitstats[1] the number of ops with 1 bit error, etc. */
-	pr_info("Bit error histogram (%d operations total):\n", opno);
-	for (i = 0; i < max_corrected; i++)
-		pr_info("Page reads with %3d corrected bit errors: %d\n",
-			i, bitstats[i]);
-
-exit:
-	return err;
-}
-
-static int __init mtd_nandbiterrs_init(void)
-{
-	int err = 0;
-
-	printk("\n");
-	printk(KERN_INFO "==================================================\n");
-	pr_info("MTD device: %d\n", dev);
-
-	mtd = get_mtd_device(NULL, dev);
-	if (IS_ERR(mtd)) {
-		err = PTR_ERR(mtd);
-		pr_err("error: cannot get MTD device\n");
-		goto exit_mtddev;
-	}
-
-	if (mtd->type != MTD_NANDFLASH) {
-		pr_info("this test requires NAND flash\n");
-		err = -ENODEV;
-		goto exit_nand;
-	}
-
-	pr_info("MTD device size %llu, eraseblock=%u, page=%u, oob=%u\n",
-		(unsigned long long)mtd->size, mtd->erasesize,
-		mtd->writesize, mtd->oobsize);
-
-	subsize  = mtd->writesize >> mtd->subpage_sft;
-	subcount = mtd->writesize / subsize;
-
-	pr_info("Device uses %d subpages of %d bytes\n", subcount, subsize);
-
-	offset     = page_offset * mtd->writesize;
-	eraseblock = mtd_div_by_eb(offset, mtd);
-
-	pr_info("Using page=%u, offset=%llu, eraseblock=%u\n",
-		page_offset, offset, eraseblock);
-
-	wbuffer = kmalloc(mtd->writesize, GFP_KERNEL);
-	if (!wbuffer) {
-		err = -ENOMEM;
-		goto exit_wbuffer;
-	}
-
-	rbuffer = kmalloc(mtd->writesize, GFP_KERNEL);
-	if (!rbuffer) {
-		err = -ENOMEM;
-		goto exit_rbuffer;
-	}
-
-	err = erase_block();
-	if (err)
-		goto exit_error;
-
-	if (mode == 0)
-		err = incremental_errors_test();
-	else
-		err = overwrite_test();
-
-	if (err)
-		goto exit_error;
-
-	/* We leave the block un-erased in case of test failure. */
-	err = erase_block();
-	if (err)
-		goto exit_error;
-
-	err = -EIO;
-	pr_info("finished successfully.\n");
-	printk(KERN_INFO "==================================================\n");
-
-exit_error:
-	kfree(rbuffer);
-exit_rbuffer:
-	kfree(wbuffer);
-exit_wbuffer:
-	/* Nothing */
-exit_nand:
-	put_mtd_device(mtd);
-exit_mtddev:
-	return err;
-}
-
-static void __exit mtd_nandbiterrs_exit(void)
-{
-	return;
-}
-
-module_init(mtd_nandbiterrs_init);
-module_exit(mtd_nandbiterrs_exit);
-
-MODULE_DESCRIPTION("NAND bit error recovery test");
-MODULE_AUTHOR("Iwo Mergler");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c
deleted file mode 100644
index ab81e9a..0000000
--- a/drivers/mtd/tests/mtd_oobtest.c
+++ /dev/null
@@ -1,714 +0,0 @@
-/*
- * Copyright (C) 2006-2008 Nokia Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; see the file COPYING. If not, write to the Free Software
- * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Test OOB read and write on MTD device.
- *
- * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <asm/div64.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/err.h>
-#include <linux/mtd/mtd.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/random.h>
-
-static int dev = -EINVAL;
-module_param(dev, int, S_IRUGO);
-MODULE_PARM_DESC(dev, "MTD device number to use");
-
-static struct mtd_info *mtd;
-static unsigned char *readbuf;
-static unsigned char *writebuf;
-static unsigned char *bbt;
-
-static int ebcnt;
-static int pgcnt;
-static int errcnt;
-static int use_offset;
-static int use_len;
-static int use_len_max;
-static int vary_offset;
-static struct rnd_state rnd_state;
-
-static int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (ei.state == MTD_ERASE_FAILED) {
-		pr_err("some erase error occurred at EB %d\n", ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int erase_whole_device(void)
-{
-	int err;
-	unsigned int i;
-
-	pr_info("erasing whole device\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = erase_eraseblock(i);
-		if (err)
-			return err;
-		cond_resched();
-	}
-	pr_info("erased %u eraseblocks\n", i);
-	return 0;
-}
-
-static void do_vary_offset(void)
-{
-	use_len -= 1;
-	if (use_len < 1) {
-		use_offset += 1;
-		if (use_offset >= use_len_max)
-			use_offset = 0;
-		use_len = use_len_max - use_offset;
-	}
-}
-
-static int write_eraseblock(int ebnum)
-{
-	int i;
-	struct mtd_oob_ops ops;
-	int err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
-		prandom_bytes_state(&rnd_state, writebuf, use_len);
-		ops.mode      = MTD_OPS_AUTO_OOB;
-		ops.len       = 0;
-		ops.retlen    = 0;
-		ops.ooblen    = use_len;
-		ops.oobretlen = 0;
-		ops.ooboffs   = use_offset;
-		ops.datbuf    = NULL;
-		ops.oobbuf    = writebuf;
-		err = mtd_write_oob(mtd, addr, &ops);
-		if (err || ops.oobretlen != use_len) {
-			pr_err("error: writeoob failed at %#llx\n",
-			       (long long)addr);
-			pr_err("error: use_len %d, use_offset %d\n",
-			       use_len, use_offset);
-			errcnt += 1;
-			return err ? err : -1;
-		}
-		if (vary_offset)
-			do_vary_offset();
-	}
-
-	return err;
-}
-
-static int write_whole_device(void)
-{
-	int err;
-	unsigned int i;
-
-	pr_info("writing OOBs of whole device\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = write_eraseblock(i);
-		if (err)
-			return err;
-		if (i % 256 == 0)
-			pr_info("written up to eraseblock %u\n", i);
-		cond_resched();
-	}
-	pr_info("written %u eraseblocks\n", i);
-	return 0;
-}
-
-static int verify_eraseblock(int ebnum)
-{
-	int i;
-	struct mtd_oob_ops ops;
-	int err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
-		prandom_bytes_state(&rnd_state, writebuf, use_len);
-		ops.mode      = MTD_OPS_AUTO_OOB;
-		ops.len       = 0;
-		ops.retlen    = 0;
-		ops.ooblen    = use_len;
-		ops.oobretlen = 0;
-		ops.ooboffs   = use_offset;
-		ops.datbuf    = NULL;
-		ops.oobbuf    = readbuf;
-		err = mtd_read_oob(mtd, addr, &ops);
-		if (err || ops.oobretlen != use_len) {
-			pr_err("error: readoob failed at %#llx\n",
-			       (long long)addr);
-			errcnt += 1;
-			return err ? err : -1;
-		}
-		if (memcmp(readbuf, writebuf, use_len)) {
-			pr_err("error: verify failed at %#llx\n",
-			       (long long)addr);
-			errcnt += 1;
-			if (errcnt > 1000) {
-				pr_err("error: too many errors\n");
-				return -1;
-			}
-		}
-		if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
-			int k;
-
-			ops.mode      = MTD_OPS_AUTO_OOB;
-			ops.len       = 0;
-			ops.retlen    = 0;
-			ops.ooblen    = mtd->ecclayout->oobavail;
-			ops.oobretlen = 0;
-			ops.ooboffs   = 0;
-			ops.datbuf    = NULL;
-			ops.oobbuf    = readbuf;
-			err = mtd_read_oob(mtd, addr, &ops);
-			if (err || ops.oobretlen != mtd->ecclayout->oobavail) {
-				pr_err("error: readoob failed at %#llx\n",
-						(long long)addr);
-				errcnt += 1;
-				return err ? err : -1;
-			}
-			if (memcmp(readbuf + use_offset, writebuf, use_len)) {
-				pr_err("error: verify failed at %#llx\n",
-						(long long)addr);
-				errcnt += 1;
-				if (errcnt > 1000) {
-					pr_err("error: too many errors\n");
-					return -1;
-				}
-			}
-			for (k = 0; k < use_offset; ++k)
-				if (readbuf[k] != 0xff) {
-					pr_err("error: verify 0xff "
-					       "failed at %#llx\n",
-					       (long long)addr);
-					errcnt += 1;
-					if (errcnt > 1000) {
-						pr_err("error: too "
-						       "many errors\n");
-						return -1;
-					}
-				}
-			for (k = use_offset + use_len;
-			     k < mtd->ecclayout->oobavail; ++k)
-				if (readbuf[k] != 0xff) {
-					pr_err("error: verify 0xff "
-					       "failed at %#llx\n",
-					       (long long)addr);
-					errcnt += 1;
-					if (errcnt > 1000) {
-						pr_err("error: too "
-						       "many errors\n");
-						return -1;
-					}
-				}
-		}
-		if (vary_offset)
-			do_vary_offset();
-	}
-	return err;
-}
-
-static int verify_eraseblock_in_one_go(int ebnum)
-{
-	struct mtd_oob_ops ops;
-	int err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-	size_t len = mtd->ecclayout->oobavail * pgcnt;
-
-	prandom_bytes_state(&rnd_state, writebuf, len);
-	ops.mode      = MTD_OPS_AUTO_OOB;
-	ops.len       = 0;
-	ops.retlen    = 0;
-	ops.ooblen    = len;
-	ops.oobretlen = 0;
-	ops.ooboffs   = 0;
-	ops.datbuf    = NULL;
-	ops.oobbuf    = readbuf;
-	err = mtd_read_oob(mtd, addr, &ops);
-	if (err || ops.oobretlen != len) {
-		pr_err("error: readoob failed at %#llx\n",
-		       (long long)addr);
-		errcnt += 1;
-		return err ? err : -1;
-	}
-	if (memcmp(readbuf, writebuf, len)) {
-		pr_err("error: verify failed at %#llx\n",
-		       (long long)addr);
-		errcnt += 1;
-		if (errcnt > 1000) {
-			pr_err("error: too many errors\n");
-			return -1;
-		}
-	}
-
-	return err;
-}
-
-static int verify_all_eraseblocks(void)
-{
-	int err;
-	unsigned int i;
-
-	pr_info("verifying all eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = verify_eraseblock(i);
-		if (err)
-			return err;
-		if (i % 256 == 0)
-			pr_info("verified up to eraseblock %u\n", i);
-		cond_resched();
-	}
-	pr_info("verified %u eraseblocks\n", i);
-	return 0;
-}
-
-static int is_block_bad(int ebnum)
-{
-	int ret;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kmalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	pr_info("scanning for bad eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		bbt[i] = is_block_bad(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-	return 0;
-}
-
-static int __init mtd_oobtest_init(void)
-{
-	int err = 0;
-	unsigned int i;
-	uint64_t tmp;
-	struct mtd_oob_ops ops;
-	loff_t addr = 0, addr0;
-
-	printk(KERN_INFO "\n");
-	printk(KERN_INFO "=================================================\n");
-
-	if (dev < 0) {
-		pr_info("Please specify a valid mtd-device via module parameter\n");
-		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
-		return -EINVAL;
-	}
-
-	pr_info("MTD device: %d\n", dev);
-
-	mtd = get_mtd_device(NULL, dev);
-	if (IS_ERR(mtd)) {
-		err = PTR_ERR(mtd);
-		pr_err("error: cannot get MTD device\n");
-		return err;
-	}
-
-	if (mtd->type != MTD_NANDFLASH) {
-		pr_info("this test requires NAND flash\n");
-		goto out;
-	}
-
-	tmp = mtd->size;
-	do_div(tmp, mtd->erasesize);
-	ebcnt = tmp;
-	pgcnt = mtd->erasesize / mtd->writesize;
-
-	pr_info("MTD device size %llu, eraseblock size %u, "
-	       "page size %u, count of eraseblocks %u, pages per "
-	       "eraseblock %u, OOB size %u\n",
-	       (unsigned long long)mtd->size, mtd->erasesize,
-	       mtd->writesize, ebcnt, pgcnt, mtd->oobsize);
-
-	err = -ENOMEM;
-	readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!readbuf)
-		goto out;
-	writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!writebuf)
-		goto out;
-
-	err = scan_for_bad_eraseblocks();
-	if (err)
-		goto out;
-
-	use_offset = 0;
-	use_len = mtd->ecclayout->oobavail;
-	use_len_max = mtd->ecclayout->oobavail;
-	vary_offset = 0;
-
-	/* First test: write all OOB, read it back and verify */
-	pr_info("test 1 of 5\n");
-
-	err = erase_whole_device();
-	if (err)
-		goto out;
-
-	prandom_seed_state(&rnd_state, 1);
-	err = write_whole_device();
-	if (err)
-		goto out;
-
-	prandom_seed_state(&rnd_state, 1);
-	err = verify_all_eraseblocks();
-	if (err)
-		goto out;
-
-	/*
-	 * Second test: write all OOB, a block at a time, read it back and
-	 * verify.
-	 */
-	pr_info("test 2 of 5\n");
-
-	err = erase_whole_device();
-	if (err)
-		goto out;
-
-	prandom_seed_state(&rnd_state, 3);
-	err = write_whole_device();
-	if (err)
-		goto out;
-
-	/* Check all eraseblocks */
-	prandom_seed_state(&rnd_state, 3);
-	pr_info("verifying all eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = verify_eraseblock_in_one_go(i);
-		if (err)
-			goto out;
-		if (i % 256 == 0)
-			pr_info("verified up to eraseblock %u\n", i);
-		cond_resched();
-	}
-	pr_info("verified %u eraseblocks\n", i);
-
-	/*
-	 * Third test: write OOB at varying offsets and lengths, read it back
-	 * and verify.
-	 */
-	pr_info("test 3 of 5\n");
-
-	err = erase_whole_device();
-	if (err)
-		goto out;
-
-	/* Write all eraseblocks */
-	use_offset = 0;
-	use_len = mtd->ecclayout->oobavail;
-	use_len_max = mtd->ecclayout->oobavail;
-	vary_offset = 1;
-	prandom_seed_state(&rnd_state, 5);
-
-	err = write_whole_device();
-	if (err)
-		goto out;
-
-	/* Check all eraseblocks */
-	use_offset = 0;
-	use_len = mtd->ecclayout->oobavail;
-	use_len_max = mtd->ecclayout->oobavail;
-	vary_offset = 1;
-	prandom_seed_state(&rnd_state, 5);
-	err = verify_all_eraseblocks();
-	if (err)
-		goto out;
-
-	use_offset = 0;
-	use_len = mtd->ecclayout->oobavail;
-	use_len_max = mtd->ecclayout->oobavail;
-	vary_offset = 0;
-
-	/* Fourth test: try to write off end of device */
-	pr_info("test 4 of 5\n");
-
-	err = erase_whole_device();
-	if (err)
-		goto out;
-
-	addr0 = 0;
-	for (i = 0; i < ebcnt && bbt[i]; ++i)
-		addr0 += mtd->erasesize;
-
-	/* Attempt to write off end of OOB */
-	ops.mode      = MTD_OPS_AUTO_OOB;
-	ops.len       = 0;
-	ops.retlen    = 0;
-	ops.ooblen    = 1;
-	ops.oobretlen = 0;
-	ops.ooboffs   = mtd->ecclayout->oobavail;
-	ops.datbuf    = NULL;
-	ops.oobbuf    = writebuf;
-	pr_info("attempting to start write past end of OOB\n");
-	pr_info("an error is expected...\n");
-	err = mtd_write_oob(mtd, addr0, &ops);
-	if (err) {
-		pr_info("error occurred as expected\n");
-		err = 0;
-	} else {
-		pr_err("error: can write past end of OOB\n");
-		errcnt += 1;
-	}
-
-	/* Attempt to read off end of OOB */
-	ops.mode      = MTD_OPS_AUTO_OOB;
-	ops.len       = 0;
-	ops.retlen    = 0;
-	ops.ooblen    = 1;
-	ops.oobretlen = 0;
-	ops.ooboffs   = mtd->ecclayout->oobavail;
-	ops.datbuf    = NULL;
-	ops.oobbuf    = readbuf;
-	pr_info("attempting to start read past end of OOB\n");
-	pr_info("an error is expected...\n");
-	err = mtd_read_oob(mtd, addr0, &ops);
-	if (err) {
-		pr_info("error occurred as expected\n");
-		err = 0;
-	} else {
-		pr_err("error: can read past end of OOB\n");
-		errcnt += 1;
-	}
-
-	if (bbt[ebcnt - 1])
-		pr_info("skipping end of device tests because last "
-		       "block is bad\n");
-	else {
-		/* Attempt to write off end of device */
-		ops.mode      = MTD_OPS_AUTO_OOB;
-		ops.len       = 0;
-		ops.retlen    = 0;
-		ops.ooblen    = mtd->ecclayout->oobavail + 1;
-		ops.oobretlen = 0;
-		ops.ooboffs   = 0;
-		ops.datbuf    = NULL;
-		ops.oobbuf    = writebuf;
-		pr_info("attempting to write past end of device\n");
-		pr_info("an error is expected...\n");
-		err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
-		if (err) {
-			pr_info("error occurred as expected\n");
-			err = 0;
-		} else {
-			pr_err("error: wrote past end of device\n");
-			errcnt += 1;
-		}
-
-		/* Attempt to read off end of device */
-		ops.mode      = MTD_OPS_AUTO_OOB;
-		ops.len       = 0;
-		ops.retlen    = 0;
-		ops.ooblen    = mtd->ecclayout->oobavail + 1;
-		ops.oobretlen = 0;
-		ops.ooboffs   = 0;
-		ops.datbuf    = NULL;
-		ops.oobbuf    = readbuf;
-		pr_info("attempting to read past end of device\n");
-		pr_info("an error is expected...\n");
-		err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
-		if (err) {
-			pr_info("error occurred as expected\n");
-			err = 0;
-		} else {
-			pr_err("error: read past end of device\n");
-			errcnt += 1;
-		}
-
-		err = erase_eraseblock(ebcnt - 1);
-		if (err)
-			goto out;
-
-		/* Attempt to write off end of device */
-		ops.mode      = MTD_OPS_AUTO_OOB;
-		ops.len       = 0;
-		ops.retlen    = 0;
-		ops.ooblen    = mtd->ecclayout->oobavail;
-		ops.oobretlen = 0;
-		ops.ooboffs   = 1;
-		ops.datbuf    = NULL;
-		ops.oobbuf    = writebuf;
-		pr_info("attempting to write past end of device\n");
-		pr_info("an error is expected...\n");
-		err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
-		if (err) {
-			pr_info("error occurred as expected\n");
-			err = 0;
-		} else {
-			pr_err("error: wrote past end of device\n");
-			errcnt += 1;
-		}
-
-		/* Attempt to read off end of device */
-		ops.mode      = MTD_OPS_AUTO_OOB;
-		ops.len       = 0;
-		ops.retlen    = 0;
-		ops.ooblen    = mtd->ecclayout->oobavail;
-		ops.oobretlen = 0;
-		ops.ooboffs   = 1;
-		ops.datbuf    = NULL;
-		ops.oobbuf    = readbuf;
-		pr_info("attempting to read past end of device\n");
-		pr_info("an error is expected...\n");
-		err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
-		if (err) {
-			pr_info("error occurred as expected\n");
-			err = 0;
-		} else {
-			pr_err("error: read past end of device\n");
-			errcnt += 1;
-		}
-	}
-
-	/* Fifth test: write / read across block boundaries */
-	pr_info("test 5 of 5\n");
-
-	/* Erase all eraseblocks */
-	err = erase_whole_device();
-	if (err)
-		goto out;
-
-	/* Write all eraseblocks */
-	prandom_seed_state(&rnd_state, 11);
-	pr_info("writing OOBs of whole device\n");
-	for (i = 0; i < ebcnt - 1; ++i) {
-		int cnt = 2;
-		int pg;
-		size_t sz = mtd->ecclayout->oobavail;
-		if (bbt[i] || bbt[i + 1])
-			continue;
-		addr = (i + 1) * mtd->erasesize - mtd->writesize;
-		for (pg = 0; pg < cnt; ++pg) {
-			prandom_bytes_state(&rnd_state, writebuf, sz);
-			ops.mode      = MTD_OPS_AUTO_OOB;
-			ops.len       = 0;
-			ops.retlen    = 0;
-			ops.ooblen    = sz;
-			ops.oobretlen = 0;
-			ops.ooboffs   = 0;
-			ops.datbuf    = NULL;
-			ops.oobbuf    = writebuf;
-			err = mtd_write_oob(mtd, addr, &ops);
-			if (err)
-				goto out;
-			if (i % 256 == 0)
-				pr_info("written up to eraseblock %u\n", i);
-			cond_resched();
-			addr += mtd->writesize;
-		}
-	}
-	pr_info("written %u eraseblocks\n", i);
-
-	/* Check all eraseblocks */
-	prandom_seed_state(&rnd_state, 11);
-	pr_info("verifying all eraseblocks\n");
-	for (i = 0; i < ebcnt - 1; ++i) {
-		if (bbt[i] || bbt[i + 1])
-			continue;
-		prandom_bytes_state(&rnd_state, writebuf,
-					mtd->ecclayout->oobavail * 2);
-		addr = (i + 1) * mtd->erasesize - mtd->writesize;
-		ops.mode      = MTD_OPS_AUTO_OOB;
-		ops.len       = 0;
-		ops.retlen    = 0;
-		ops.ooblen    = mtd->ecclayout->oobavail * 2;
-		ops.oobretlen = 0;
-		ops.ooboffs   = 0;
-		ops.datbuf    = NULL;
-		ops.oobbuf    = readbuf;
-		err = mtd_read_oob(mtd, addr, &ops);
-		if (err)
-			goto out;
-		if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) {
-			pr_err("error: verify failed at %#llx\n",
-			       (long long)addr);
-			errcnt += 1;
-			if (errcnt > 1000) {
-				pr_err("error: too many errors\n");
-				goto out;
-			}
-		}
-		if (i % 256 == 0)
-			pr_info("verified up to eraseblock %u\n", i);
-		cond_resched();
-	}
-	pr_info("verified %u eraseblocks\n", i);
-
-	pr_info("finished with %d errors\n", errcnt);
-out:
-	kfree(bbt);
-	kfree(writebuf);
-	kfree(readbuf);
-	put_mtd_device(mtd);
-	if (err)
-		pr_info("error %d occurred\n", err);
-	printk(KERN_INFO "=================================================\n");
-	return err;
-}
-module_init(mtd_oobtest_init);
-
-static void __exit mtd_oobtest_exit(void)
-{
-	return;
-}
-module_exit(mtd_oobtest_exit);
-
-MODULE_DESCRIPTION("Out-of-band test module");
-MODULE_AUTHOR("Adrian Hunter");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c
deleted file mode 100644
index acd991f..0000000
--- a/drivers/mtd/tests/mtd_pagetest.c
+++ /dev/null
@@ -1,605 +0,0 @@
-/*
- * Copyright (C) 2006-2008 Nokia Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; see the file COPYING. If not, write to the Free Software
- * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Test page read and write on MTD device.
- *
- * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <asm/div64.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/err.h>
-#include <linux/mtd/mtd.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/random.h>
-
-static int dev = -EINVAL;
-module_param(dev, int, S_IRUGO);
-MODULE_PARM_DESC(dev, "MTD device number to use");
-
-static struct mtd_info *mtd;
-static unsigned char *twopages;
-static unsigned char *writebuf;
-static unsigned char *boundary;
-static unsigned char *bbt;
-
-static int pgsize;
-static int bufsize;
-static int ebcnt;
-static int pgcnt;
-static int errcnt;
-static struct rnd_state rnd_state;
-
-static int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (ei.state == MTD_ERASE_FAILED) {
-		pr_err("some erase error occurred at EB %d\n",
-		       ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int write_eraseblock(int ebnum)
-{
-	int err = 0;
-	size_t written;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
-	cond_resched();
-	err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf);
-	if (err || written != mtd->erasesize)
-		pr_err("error: write failed at %#llx\n",
-		       (long long)addr);
-
-	return err;
-}
-
-static int verify_eraseblock(int ebnum)
-{
-	uint32_t j;
-	size_t read;
-	int err = 0, i;
-	loff_t addr0, addrn;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	addr0 = 0;
-	for (i = 0; i < ebcnt && bbt[i]; ++i)
-		addr0 += mtd->erasesize;
-
-	addrn = mtd->size;
-	for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
-		addrn -= mtd->erasesize;
-
-	prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
-	for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
-		/* Do a read to set the internal dataRAMs to different data */
-		err = mtd_read(mtd, addr0, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
-			pr_err("error: read failed at %#llx\n",
-			       (long long)addr0);
-			return err;
-		}
-		err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
-			pr_err("error: read failed at %#llx\n",
-			       (long long)(addrn - bufsize));
-			return err;
-		}
-		memset(twopages, 0, bufsize);
-		err = mtd_read(mtd, addr, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
-			pr_err("error: read failed at %#llx\n",
-			       (long long)addr);
-			break;
-		}
-		if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) {
-			pr_err("error: verify failed at %#llx\n",
-			       (long long)addr);
-			errcnt += 1;
-		}
-	}
-	/* Check boundary between eraseblocks */
-	if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) {
-		struct rnd_state old_state = rnd_state;
-
-		/* Do a read to set the internal dataRAMs to different data */
-		err = mtd_read(mtd, addr0, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
-			pr_err("error: read failed at %#llx\n",
-			       (long long)addr0);
-			return err;
-		}
-		err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
-			pr_err("error: read failed at %#llx\n",
-			       (long long)(addrn - bufsize));
-			return err;
-		}
-		memset(twopages, 0, bufsize);
-		err = mtd_read(mtd, addr, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
-			pr_err("error: read failed at %#llx\n",
-			       (long long)addr);
-			return err;
-		}
-		memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize);
-		prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize);
-		if (memcmp(twopages, boundary, bufsize)) {
-			pr_err("error: verify failed at %#llx\n",
-			       (long long)addr);
-			errcnt += 1;
-		}
-		rnd_state = old_state;
-	}
-	return err;
-}
-
-static int crosstest(void)
-{
-	size_t read;
-	int err = 0, i;
-	loff_t addr, addr0, addrn;
-	unsigned char *pp1, *pp2, *pp3, *pp4;
-
-	pr_info("crosstest\n");
-	pp1 = kmalloc(pgsize * 4, GFP_KERNEL);
-	if (!pp1)
-		return -ENOMEM;
-	pp2 = pp1 + pgsize;
-	pp3 = pp2 + pgsize;
-	pp4 = pp3 + pgsize;
-	memset(pp1, 0, pgsize * 4);
-
-	addr0 = 0;
-	for (i = 0; i < ebcnt && bbt[i]; ++i)
-		addr0 += mtd->erasesize;
-
-	addrn = mtd->size;
-	for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
-		addrn -= mtd->erasesize;
-
-	/* Read 2nd-to-last page to pp1 */
-	addr = addrn - pgsize - pgsize;
-	err = mtd_read(mtd, addr, pgsize, &read, pp1);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
-		pr_err("error: read failed at %#llx\n",
-		       (long long)addr);
-		kfree(pp1);
-		return err;
-	}
-
-	/* Read 3rd-to-last page to pp1 */
-	addr = addrn - pgsize - pgsize - pgsize;
-	err = mtd_read(mtd, addr, pgsize, &read, pp1);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
-		pr_err("error: read failed at %#llx\n",
-		       (long long)addr);
-		kfree(pp1);
-		return err;
-	}
-
-	/* Read first page to pp2 */
-	addr = addr0;
-	pr_info("reading page at %#llx\n", (long long)addr);
-	err = mtd_read(mtd, addr, pgsize, &read, pp2);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
-		pr_err("error: read failed at %#llx\n",
-		       (long long)addr);
-		kfree(pp1);
-		return err;
-	}
-
-	/* Read last page to pp3 */
-	addr = addrn - pgsize;
-	pr_info("reading page at %#llx\n", (long long)addr);
-	err = mtd_read(mtd, addr, pgsize, &read, pp3);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
-		pr_err("error: read failed at %#llx\n",
-		       (long long)addr);
-		kfree(pp1);
-		return err;
-	}
-
-	/* Read first page again to pp4 */
-	addr = addr0;
-	pr_info("reading page at %#llx\n", (long long)addr);
-	err = mtd_read(mtd, addr, pgsize, &read, pp4);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
-		pr_err("error: read failed at %#llx\n",
-		       (long long)addr);
-		kfree(pp1);
-		return err;
-	}
-
-	/* pp2 and pp4 should be the same */
-	pr_info("verifying pages read at %#llx match\n",
-	       (long long)addr0);
-	if (memcmp(pp2, pp4, pgsize)) {
-		pr_err("verify failed!\n");
-		errcnt += 1;
-	} else if (!err)
-		pr_info("crosstest ok\n");
-	kfree(pp1);
-	return err;
-}
-
-static int erasecrosstest(void)
-{
-	size_t read, written;
-	int err = 0, i, ebnum, ebnum2;
-	loff_t addr0;
-	char *readbuf = twopages;
-
-	pr_info("erasecrosstest\n");
-
-	ebnum = 0;
-	addr0 = 0;
-	for (i = 0; i < ebcnt && bbt[i]; ++i) {
-		addr0 += mtd->erasesize;
-		ebnum += 1;
-	}
-
-	ebnum2 = ebcnt - 1;
-	while (ebnum2 && bbt[ebnum2])
-		ebnum2 -= 1;
-
-	pr_info("erasing block %d\n", ebnum);
-	err = erase_eraseblock(ebnum);
-	if (err)
-		return err;
-
-	pr_info("writing 1st page of block %d\n", ebnum);
-	prandom_bytes_state(&rnd_state, writebuf, pgsize);
-	strcpy(writebuf, "There is no data like this!");
-	err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
-	if (err || written != pgsize) {
-		pr_info("error: write failed at %#llx\n",
-		       (long long)addr0);
-		return err ? err : -1;
-	}
-
-	pr_info("reading 1st page of block %d\n", ebnum);
-	memset(readbuf, 0, pgsize);
-	err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
-		pr_err("error: read failed at %#llx\n",
-		       (long long)addr0);
-		return err ? err : -1;
-	}
-
-	pr_info("verifying 1st page of block %d\n", ebnum);
-	if (memcmp(writebuf, readbuf, pgsize)) {
-		pr_err("verify failed!\n");
-		errcnt += 1;
-		return -1;
-	}
-
-	pr_info("erasing block %d\n", ebnum);
-	err = erase_eraseblock(ebnum);
-	if (err)
-		return err;
-
-	pr_info("writing 1st page of block %d\n", ebnum);
-	prandom_bytes_state(&rnd_state, writebuf, pgsize);
-	strcpy(writebuf, "There is no data like this!");
-	err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
-	if (err || written != pgsize) {
-		pr_err("error: write failed at %#llx\n",
-		       (long long)addr0);
-		return err ? err : -1;
-	}
-
-	pr_info("erasing block %d\n", ebnum2);
-	err = erase_eraseblock(ebnum2);
-	if (err)
-		return err;
-
-	pr_info("reading 1st page of block %d\n", ebnum);
-	memset(readbuf, 0, pgsize);
-	err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
-		pr_err("error: read failed at %#llx\n",
-		       (long long)addr0);
-		return err ? err : -1;
-	}
-
-	pr_info("verifying 1st page of block %d\n", ebnum);
-	if (memcmp(writebuf, readbuf, pgsize)) {
-		pr_err("verify failed!\n");
-		errcnt += 1;
-		return -1;
-	}
-
-	if (!err)
-		pr_info("erasecrosstest ok\n");
-	return err;
-}
-
-static int erasetest(void)
-{
-	size_t read, written;
-	int err = 0, i, ebnum, ok = 1;
-	loff_t addr0;
-
-	pr_info("erasetest\n");
-
-	ebnum = 0;
-	addr0 = 0;
-	for (i = 0; i < ebcnt && bbt[i]; ++i) {
-		addr0 += mtd->erasesize;
-		ebnum += 1;
-	}
-
-	pr_info("erasing block %d\n", ebnum);
-	err = erase_eraseblock(ebnum);
-	if (err)
-		return err;
-
-	pr_info("writing 1st page of block %d\n", ebnum);
-	prandom_bytes_state(&rnd_state, writebuf, pgsize);
-	err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
-	if (err || written != pgsize) {
-		pr_err("error: write failed at %#llx\n",
-		       (long long)addr0);
-		return err ? err : -1;
-	}
-
-	pr_info("erasing block %d\n", ebnum);
-	err = erase_eraseblock(ebnum);
-	if (err)
-		return err;
-
-	pr_info("reading 1st page of block %d\n", ebnum);
-	err = mtd_read(mtd, addr0, pgsize, &read, twopages);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
-		pr_err("error: read failed at %#llx\n",
-		       (long long)addr0);
-		return err ? err : -1;
-	}
-
-	pr_info("verifying 1st page of block %d is all 0xff\n",
-	       ebnum);
-	for (i = 0; i < pgsize; ++i)
-		if (twopages[i] != 0xff) {
-			pr_err("verifying all 0xff failed at %d\n",
-			       i);
-			errcnt += 1;
-			ok = 0;
-			break;
-		}
-
-	if (ok && !err)
-		pr_info("erasetest ok\n");
-
-	return err;
-}
-
-static int is_block_bad(int ebnum)
-{
-	loff_t addr = ebnum * mtd->erasesize;
-	int ret;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kzalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	pr_info("scanning for bad eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		bbt[i] = is_block_bad(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-	return 0;
-}
-
-static int __init mtd_pagetest_init(void)
-{
-	int err = 0;
-	uint64_t tmp;
-	uint32_t i;
-
-	printk(KERN_INFO "\n");
-	printk(KERN_INFO "=================================================\n");
-
-	if (dev < 0) {
-		pr_info("Please specify a valid mtd-device via module parameter\n");
-		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
-		return -EINVAL;
-	}
-
-	pr_info("MTD device: %d\n", dev);
-
-	mtd = get_mtd_device(NULL, dev);
-	if (IS_ERR(mtd)) {
-		err = PTR_ERR(mtd);
-		pr_err("error: cannot get MTD device\n");
-		return err;
-	}
-
-	if (mtd->type != MTD_NANDFLASH) {
-		pr_info("this test requires NAND flash\n");
-		goto out;
-	}
-
-	tmp = mtd->size;
-	do_div(tmp, mtd->erasesize);
-	ebcnt = tmp;
-	pgcnt = mtd->erasesize / mtd->writesize;
-	pgsize = mtd->writesize;
-
-	pr_info("MTD device size %llu, eraseblock size %u, "
-	       "page size %u, count of eraseblocks %u, pages per "
-	       "eraseblock %u, OOB size %u\n",
-	       (unsigned long long)mtd->size, mtd->erasesize,
-	       pgsize, ebcnt, pgcnt, mtd->oobsize);
-
-	err = -ENOMEM;
-	bufsize = pgsize * 2;
-	writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!writebuf)
-		goto out;
-	twopages = kmalloc(bufsize, GFP_KERNEL);
-	if (!twopages)
-		goto out;
-	boundary = kmalloc(bufsize, GFP_KERNEL);
-	if (!boundary)
-		goto out;
-
-	err = scan_for_bad_eraseblocks();
-	if (err)
-		goto out;
-
-	/* Erase all eraseblocks */
-	pr_info("erasing whole device\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = erase_eraseblock(i);
-		if (err)
-			goto out;
-		cond_resched();
-	}
-	pr_info("erased %u eraseblocks\n", i);
-
-	/* Write all eraseblocks */
-	prandom_seed_state(&rnd_state, 1);
-	pr_info("writing whole device\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = write_eraseblock(i);
-		if (err)
-			goto out;
-		if (i % 256 == 0)
-			pr_info("written up to eraseblock %u\n", i);
-		cond_resched();
-	}
-	pr_info("written %u eraseblocks\n", i);
-
-	/* Check all eraseblocks */
-	prandom_seed_state(&rnd_state, 1);
-	pr_info("verifying all eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = verify_eraseblock(i);
-		if (err)
-			goto out;
-		if (i % 256 == 0)
-			pr_info("verified up to eraseblock %u\n", i);
-		cond_resched();
-	}
-	pr_info("verified %u eraseblocks\n", i);
-
-	err = crosstest();
-	if (err)
-		goto out;
-
-	err = erasecrosstest();
-	if (err)
-		goto out;
-
-	err = erasetest();
-	if (err)
-		goto out;
-
-	pr_info("finished with %d errors\n", errcnt);
-out:
-
-	kfree(bbt);
-	kfree(boundary);
-	kfree(twopages);
-	kfree(writebuf);
-	put_mtd_device(mtd);
-	if (err)
-		pr_info("error %d occurred\n", err);
-	printk(KERN_INFO "=================================================\n");
-	return err;
-}
-module_init(mtd_pagetest_init);
-
-static void __exit mtd_pagetest_exit(void)
-{
-	return;
-}
-module_exit(mtd_pagetest_exit);
-
-MODULE_DESCRIPTION("NAND page test");
-MODULE_AUTHOR("Adrian Hunter");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c
deleted file mode 100644
index 2cdd0c4..0000000
--- a/drivers/mtd/tests/mtd_readtest.c
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2006-2008 Nokia Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; see the file COPYING. If not, write to the Free Software
- * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Check MTD device read.
- *
- * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/err.h>
-#include <linux/mtd/mtd.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-
-static int dev = -EINVAL;
-module_param(dev, int, S_IRUGO);
-MODULE_PARM_DESC(dev, "MTD device number to use");
-
-static struct mtd_info *mtd;
-static unsigned char *iobuf;
-static unsigned char *iobuf1;
-static unsigned char *bbt;
-
-static int pgsize;
-static int ebcnt;
-static int pgcnt;
-
-static int read_eraseblock_by_page(int ebnum)
-{
-	size_t read;
-	int i, ret, err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-	void *buf = iobuf;
-	void *oobbuf = iobuf1;
-
-	for (i = 0; i < pgcnt; i++) {
-		memset(buf, 0 , pgsize);
-		ret = mtd_read(mtd, addr, pgsize, &read, buf);
-		if (ret == -EUCLEAN)
-			ret = 0;
-		if (ret || read != pgsize) {
-			pr_err("error: read failed at %#llx\n",
-			       (long long)addr);
-			if (!err)
-				err = ret;
-			if (!err)
-				err = -EINVAL;
-		}
-		if (mtd->oobsize) {
-			struct mtd_oob_ops ops;
-
-			ops.mode      = MTD_OPS_PLACE_OOB;
-			ops.len       = 0;
-			ops.retlen    = 0;
-			ops.ooblen    = mtd->oobsize;
-			ops.oobretlen = 0;
-			ops.ooboffs   = 0;
-			ops.datbuf    = NULL;
-			ops.oobbuf    = oobbuf;
-			ret = mtd_read_oob(mtd, addr, &ops);
-			if ((ret && !mtd_is_bitflip(ret)) ||
-					ops.oobretlen != mtd->oobsize) {
-				pr_err("error: read oob failed at "
-						  "%#llx\n", (long long)addr);
-				if (!err)
-					err = ret;
-				if (!err)
-					err = -EINVAL;
-			}
-			oobbuf += mtd->oobsize;
-		}
-		addr += pgsize;
-		buf += pgsize;
-	}
-
-	return err;
-}
-
-static void dump_eraseblock(int ebnum)
-{
-	int i, j, n;
-	char line[128];
-	int pg, oob;
-
-	pr_info("dumping eraseblock %d\n", ebnum);
-	n = mtd->erasesize;
-	for (i = 0; i < n;) {
-		char *p = line;
-
-		p += sprintf(p, "%05x: ", i);
-		for (j = 0; j < 32 && i < n; j++, i++)
-			p += sprintf(p, "%02x", (unsigned int)iobuf[i]);
-		printk(KERN_CRIT "%s\n", line);
-		cond_resched();
-	}
-	if (!mtd->oobsize)
-		return;
-	pr_info("dumping oob from eraseblock %d\n", ebnum);
-	n = mtd->oobsize;
-	for (pg = 0, i = 0; pg < pgcnt; pg++)
-		for (oob = 0; oob < n;) {
-			char *p = line;
-
-			p += sprintf(p, "%05x: ", i);
-			for (j = 0; j < 32 && oob < n; j++, oob++, i++)
-				p += sprintf(p, "%02x",
-					     (unsigned int)iobuf1[i]);
-			printk(KERN_CRIT "%s\n", line);
-			cond_resched();
-		}
-}
-
-static int is_block_bad(int ebnum)
-{
-	loff_t addr = ebnum * mtd->erasesize;
-	int ret;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kzalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	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(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-	return 0;
-}
-
-static int __init mtd_readtest_init(void)
-{
-	uint64_t tmp;
-	int err, i;
-
-	printk(KERN_INFO "\n");
-	printk(KERN_INFO "=================================================\n");
-
-	if (dev < 0) {
-		pr_info("Please specify a valid mtd-device via module parameter\n");
-		return -EINVAL;
-	}
-
-	pr_info("MTD device: %d\n", dev);
-
-	mtd = get_mtd_device(NULL, dev);
-	if (IS_ERR(mtd)) {
-		err = PTR_ERR(mtd);
-		pr_err("error: Cannot get MTD device\n");
-		return err;
-	}
-
-	if (mtd->writesize == 1) {
-		pr_info("not NAND flash, assume page size is 512 "
-		       "bytes.\n");
-		pgsize = 512;
-	} else
-		pgsize = mtd->writesize;
-
-	tmp = mtd->size;
-	do_div(tmp, mtd->erasesize);
-	ebcnt = tmp;
-	pgcnt = mtd->erasesize / pgsize;
-
-	pr_info("MTD device size %llu, eraseblock size %u, "
-	       "page size %u, count of eraseblocks %u, pages per "
-	       "eraseblock %u, OOB size %u\n",
-	       (unsigned long long)mtd->size, mtd->erasesize,
-	       pgsize, ebcnt, pgcnt, mtd->oobsize);
-
-	err = -ENOMEM;
-	iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!iobuf)
-		goto out;
-	iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!iobuf1)
-		goto out;
-
-	err = scan_for_bad_eraseblocks();
-	if (err)
-		goto out;
-
-	/* Read all eraseblocks 1 page at a time */
-	pr_info("testing page read\n");
-	for (i = 0; i < ebcnt; ++i) {
-		int ret;
-
-		if (bbt[i])
-			continue;
-		ret = read_eraseblock_by_page(i);
-		if (ret) {
-			dump_eraseblock(i);
-			if (!err)
-				err = ret;
-		}
-		cond_resched();
-	}
-
-	if (err)
-		pr_info("finished with errors\n");
-	else
-		pr_info("finished\n");
-
-out:
-
-	kfree(iobuf);
-	kfree(iobuf1);
-	kfree(bbt);
-	put_mtd_device(mtd);
-	if (err)
-		pr_info("error %d occurred\n", err);
-	printk(KERN_INFO "=================================================\n");
-	return err;
-}
-module_init(mtd_readtest_init);
-
-static void __exit mtd_readtest_exit(void)
-{
-	return;
-}
-module_exit(mtd_readtest_exit);
-
-MODULE_DESCRIPTION("Read test module");
-MODULE_AUTHOR("Adrian Hunter");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c
deleted file mode 100644
index 20b63d1..0000000
--- a/drivers/mtd/tests/mtd_speedtest.c
+++ /dev/null
@@ -1,556 +0,0 @@
-/*
- * Copyright (C) 2007 Nokia Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; see the file COPYING. If not, write to the Free Software
- * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Test read and write speed of a MTD device.
- *
- * Author: Adrian Hunter <adrian.hunter@nokia.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/err.h>
-#include <linux/mtd/mtd.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/random.h>
-
-static int dev = -EINVAL;
-module_param(dev, int, S_IRUGO);
-MODULE_PARM_DESC(dev, "MTD device number to use");
-
-static int count;
-module_param(count, int, S_IRUGO);
-MODULE_PARM_DESC(count, "Maximum number of eraseblocks to use "
-			"(0 means use all)");
-
-static struct mtd_info *mtd;
-static unsigned char *iobuf;
-static unsigned char *bbt;
-
-static int pgsize;
-static int ebcnt;
-static int pgcnt;
-static int goodebcnt;
-static struct timeval start, finish;
-
-
-static int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (ei.state == MTD_ERASE_FAILED) {
-		pr_err("some erase error occurred at EB %d\n",
-		       ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int multiblock_erase(int ebnum, int blocks)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize * blocks;
-
-	err = mtd_erase(mtd, &ei);
-	if (err) {
-		pr_err("error %d while erasing EB %d, blocks %d\n",
-		       err, ebnum, blocks);
-		return err;
-	}
-
-	if (ei.state == MTD_ERASE_FAILED) {
-		pr_err("some erase error occurred at EB %d,"
-		       "blocks %d\n", ebnum, blocks);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int erase_whole_device(void)
-{
-	int err;
-	unsigned int i;
-
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = erase_eraseblock(i);
-		if (err)
-			return err;
-		cond_resched();
-	}
-	return 0;
-}
-
-static int write_eraseblock(int ebnum)
-{
-	size_t written;
-	int err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	err = mtd_write(mtd, addr, mtd->erasesize, &written, iobuf);
-	if (err || written != mtd->erasesize) {
-		pr_err("error: write failed at %#llx\n", addr);
-		if (!err)
-			err = -EINVAL;
-	}
-
-	return err;
-}
-
-static int write_eraseblock_by_page(int ebnum)
-{
-	size_t written;
-	int i, err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-	void *buf = iobuf;
-
-	for (i = 0; i < pgcnt; i++) {
-		err = mtd_write(mtd, addr, pgsize, &written, buf);
-		if (err || written != pgsize) {
-			pr_err("error: write failed at %#llx\n",
-			       addr);
-			if (!err)
-				err = -EINVAL;
-			break;
-		}
-		addr += pgsize;
-		buf += pgsize;
-	}
-
-	return err;
-}
-
-static int write_eraseblock_by_2pages(int ebnum)
-{
-	size_t written, sz = pgsize * 2;
-	int i, n = pgcnt / 2, err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-	void *buf = iobuf;
-
-	for (i = 0; i < n; i++) {
-		err = mtd_write(mtd, addr, sz, &written, buf);
-		if (err || written != sz) {
-			pr_err("error: write failed at %#llx\n",
-			       addr);
-			if (!err)
-				err = -EINVAL;
-			return err;
-		}
-		addr += sz;
-		buf += sz;
-	}
-	if (pgcnt % 2) {
-		err = mtd_write(mtd, addr, pgsize, &written, buf);
-		if (err || written != pgsize) {
-			pr_err("error: write failed at %#llx\n",
-			       addr);
-			if (!err)
-				err = -EINVAL;
-		}
-	}
-
-	return err;
-}
-
-static int read_eraseblock(int ebnum)
-{
-	size_t read;
-	int err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	err = mtd_read(mtd, addr, mtd->erasesize, &read, iobuf);
-	/* Ignore corrected ECC errors */
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != mtd->erasesize) {
-		pr_err("error: read failed at %#llx\n", addr);
-		if (!err)
-			err = -EINVAL;
-	}
-
-	return err;
-}
-
-static int read_eraseblock_by_page(int ebnum)
-{
-	size_t read;
-	int i, err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-	void *buf = iobuf;
-
-	for (i = 0; i < pgcnt; i++) {
-		err = mtd_read(mtd, addr, pgsize, &read, buf);
-		/* Ignore corrected ECC errors */
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != pgsize) {
-			pr_err("error: read failed at %#llx\n",
-			       addr);
-			if (!err)
-				err = -EINVAL;
-			break;
-		}
-		addr += pgsize;
-		buf += pgsize;
-	}
-
-	return err;
-}
-
-static int read_eraseblock_by_2pages(int ebnum)
-{
-	size_t read, sz = pgsize * 2;
-	int i, n = pgcnt / 2, err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-	void *buf = iobuf;
-
-	for (i = 0; i < n; i++) {
-		err = mtd_read(mtd, addr, sz, &read, buf);
-		/* Ignore corrected ECC errors */
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != sz) {
-			pr_err("error: read failed at %#llx\n",
-			       addr);
-			if (!err)
-				err = -EINVAL;
-			return err;
-		}
-		addr += sz;
-		buf += sz;
-	}
-	if (pgcnt % 2) {
-		err = mtd_read(mtd, addr, pgsize, &read, buf);
-		/* Ignore corrected ECC errors */
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != pgsize) {
-			pr_err("error: read failed at %#llx\n",
-			       addr);
-			if (!err)
-				err = -EINVAL;
-		}
-	}
-
-	return err;
-}
-
-static int is_block_bad(int ebnum)
-{
-	loff_t addr = ebnum * mtd->erasesize;
-	int ret;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
-static inline void start_timing(void)
-{
-	do_gettimeofday(&start);
-}
-
-static inline void stop_timing(void)
-{
-	do_gettimeofday(&finish);
-}
-
-static long calc_speed(void)
-{
-	uint64_t k;
-	long ms;
-
-	ms = (finish.tv_sec - start.tv_sec) * 1000 +
-	     (finish.tv_usec - start.tv_usec) / 1000;
-	if (ms == 0)
-		return 0;
-	k = goodebcnt * (mtd->erasesize / 1024) * 1000;
-	do_div(k, ms);
-	return k;
-}
-
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kzalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	if (!mtd_can_have_bb(mtd))
-		goto out;
-
-	pr_info("scanning for bad eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		bbt[i] = is_block_bad(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-out:
-	goodebcnt = ebcnt - bad;
-	return 0;
-}
-
-static int __init mtd_speedtest_init(void)
-{
-	int err, i, blocks, j, k;
-	long speed;
-	uint64_t tmp;
-
-	printk(KERN_INFO "\n");
-	printk(KERN_INFO "=================================================\n");
-
-	if (dev < 0) {
-		pr_info("Please specify a valid mtd-device via module parameter\n");
-		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
-		return -EINVAL;
-	}
-
-	if (count)
-		pr_info("MTD device: %d    count: %d\n", dev, count);
-	else
-		pr_info("MTD device: %d\n", dev);
-
-	mtd = get_mtd_device(NULL, dev);
-	if (IS_ERR(mtd)) {
-		err = PTR_ERR(mtd);
-		pr_err("error: cannot get MTD device\n");
-		return err;
-	}
-
-	if (mtd->writesize == 1) {
-		pr_info("not NAND flash, assume page size is 512 "
-		       "bytes.\n");
-		pgsize = 512;
-	} else
-		pgsize = mtd->writesize;
-
-	tmp = mtd->size;
-	do_div(tmp, mtd->erasesize);
-	ebcnt = tmp;
-	pgcnt = mtd->erasesize / pgsize;
-
-	pr_info("MTD device size %llu, eraseblock size %u, "
-	       "page size %u, count of eraseblocks %u, pages per "
-	       "eraseblock %u, OOB size %u\n",
-	       (unsigned long long)mtd->size, mtd->erasesize,
-	       pgsize, ebcnt, pgcnt, mtd->oobsize);
-
-	if (count > 0 && count < ebcnt)
-		ebcnt = count;
-
-	err = -ENOMEM;
-	iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!iobuf)
-		goto out;
-
-	prandom_bytes(iobuf, mtd->erasesize);
-
-	err = scan_for_bad_eraseblocks();
-	if (err)
-		goto out;
-
-	err = erase_whole_device();
-	if (err)
-		goto out;
-
-	/* Write all eraseblocks, 1 eraseblock at a time */
-	pr_info("testing eraseblock write speed\n");
-	start_timing();
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = write_eraseblock(i);
-		if (err)
-			goto out;
-		cond_resched();
-	}
-	stop_timing();
-	speed = calc_speed();
-	pr_info("eraseblock write speed is %ld KiB/s\n", speed);
-
-	/* Read all eraseblocks, 1 eraseblock at a time */
-	pr_info("testing eraseblock read speed\n");
-	start_timing();
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = read_eraseblock(i);
-		if (err)
-			goto out;
-		cond_resched();
-	}
-	stop_timing();
-	speed = calc_speed();
-	pr_info("eraseblock read speed is %ld KiB/s\n", speed);
-
-	err = erase_whole_device();
-	if (err)
-		goto out;
-
-	/* Write all eraseblocks, 1 page at a time */
-	pr_info("testing page write speed\n");
-	start_timing();
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = write_eraseblock_by_page(i);
-		if (err)
-			goto out;
-		cond_resched();
-	}
-	stop_timing();
-	speed = calc_speed();
-	pr_info("page write speed is %ld KiB/s\n", speed);
-
-	/* Read all eraseblocks, 1 page at a time */
-	pr_info("testing page read speed\n");
-	start_timing();
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = read_eraseblock_by_page(i);
-		if (err)
-			goto out;
-		cond_resched();
-	}
-	stop_timing();
-	speed = calc_speed();
-	pr_info("page read speed is %ld KiB/s\n", speed);
-
-	err = erase_whole_device();
-	if (err)
-		goto out;
-
-	/* Write all eraseblocks, 2 pages at a time */
-	pr_info("testing 2 page write speed\n");
-	start_timing();
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = write_eraseblock_by_2pages(i);
-		if (err)
-			goto out;
-		cond_resched();
-	}
-	stop_timing();
-	speed = calc_speed();
-	pr_info("2 page write speed is %ld KiB/s\n", speed);
-
-	/* Read all eraseblocks, 2 pages at a time */
-	pr_info("testing 2 page read speed\n");
-	start_timing();
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = read_eraseblock_by_2pages(i);
-		if (err)
-			goto out;
-		cond_resched();
-	}
-	stop_timing();
-	speed = calc_speed();
-	pr_info("2 page read speed is %ld KiB/s\n", speed);
-
-	/* Erase all eraseblocks */
-	pr_info("Testing erase speed\n");
-	start_timing();
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = erase_eraseblock(i);
-		if (err)
-			goto out;
-		cond_resched();
-	}
-	stop_timing();
-	speed = calc_speed();
-	pr_info("erase speed is %ld KiB/s\n", speed);
-
-	/* Multi-block erase all eraseblocks */
-	for (k = 1; k < 7; k++) {
-		blocks = 1 << k;
-		pr_info("Testing %dx multi-block erase speed\n",
-		       blocks);
-		start_timing();
-		for (i = 0; i < ebcnt; ) {
-			for (j = 0; j < blocks && (i + j) < ebcnt; j++)
-				if (bbt[i + j])
-					break;
-			if (j < 1) {
-				i++;
-				continue;
-			}
-			err = multiblock_erase(i, j);
-			if (err)
-				goto out;
-			cond_resched();
-			i += j;
-		}
-		stop_timing();
-		speed = calc_speed();
-		pr_info("%dx multi-block erase speed is %ld KiB/s\n",
-		       blocks, speed);
-	}
-	pr_info("finished\n");
-out:
-	kfree(iobuf);
-	kfree(bbt);
-	put_mtd_device(mtd);
-	if (err)
-		pr_info("error %d occurred\n", err);
-	printk(KERN_INFO "=================================================\n");
-	return err;
-}
-module_init(mtd_speedtest_init);
-
-static void __exit mtd_speedtest_exit(void)
-{
-	return;
-}
-module_exit(mtd_speedtest_exit);
-
-MODULE_DESCRIPTION("Speed test module");
-MODULE_AUTHOR("Adrian Hunter");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c
deleted file mode 100644
index 3a95e61..0000000
--- a/drivers/mtd/tests/mtd_stresstest.c
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2006-2008 Nokia Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; see the file COPYING. If not, write to the Free Software
- * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Test random reads, writes and erases on MTD device.
- *
- * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/err.h>
-#include <linux/mtd/mtd.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/vmalloc.h>
-#include <linux/random.h>
-
-static int dev = -EINVAL;
-module_param(dev, int, S_IRUGO);
-MODULE_PARM_DESC(dev, "MTD device number to use");
-
-static int count = 10000;
-module_param(count, int, S_IRUGO);
-MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)");
-
-static struct mtd_info *mtd;
-static unsigned char *writebuf;
-static unsigned char *readbuf;
-static unsigned char *bbt;
-static int *offsets;
-
-static int pgsize;
-static int bufsize;
-static int ebcnt;
-static int pgcnt;
-
-static int rand_eb(void)
-{
-	unsigned int eb;
-
-again:
-	eb = prandom_u32();
-	/* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */
-	eb %= (ebcnt - 1);
-	if (bbt[eb])
-		goto again;
-	return eb;
-}
-
-static int rand_offs(void)
-{
-	unsigned int offs;
-
-	offs = prandom_u32();
-	offs %= bufsize;
-	return offs;
-}
-
-static int rand_len(int offs)
-{
-	unsigned int len;
-
-	len = prandom_u32();
-	len %= (bufsize - offs);
-	return len;
-}
-
-static int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (unlikely(err)) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (unlikely(ei.state == MTD_ERASE_FAILED)) {
-		pr_err("some erase error occurred at EB %d\n",
-		       ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int is_block_bad(int ebnum)
-{
-	loff_t addr = ebnum * mtd->erasesize;
-	int ret;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
-static int do_read(void)
-{
-	size_t read;
-	int eb = rand_eb();
-	int offs = rand_offs();
-	int len = rand_len(offs), err;
-	loff_t addr;
-
-	if (bbt[eb + 1]) {
-		if (offs >= mtd->erasesize)
-			offs -= mtd->erasesize;
-		if (offs + len > mtd->erasesize)
-			len = mtd->erasesize - offs;
-	}
-	addr = eb * mtd->erasesize + offs;
-	err = mtd_read(mtd, addr, len, &read, readbuf);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (unlikely(err || read != len)) {
-		pr_err("error: read failed at 0x%llx\n",
-		       (long long)addr);
-		if (!err)
-			err = -EINVAL;
-		return err;
-	}
-	return 0;
-}
-
-static int do_write(void)
-{
-	int eb = rand_eb(), offs, err, len;
-	size_t written;
-	loff_t addr;
-
-	offs = offsets[eb];
-	if (offs >= mtd->erasesize) {
-		err = erase_eraseblock(eb);
-		if (err)
-			return err;
-		offs = offsets[eb] = 0;
-	}
-	len = rand_len(offs);
-	len = ((len + pgsize - 1) / pgsize) * pgsize;
-	if (offs + len > mtd->erasesize) {
-		if (bbt[eb + 1])
-			len = mtd->erasesize - offs;
-		else {
-			err = erase_eraseblock(eb + 1);
-			if (err)
-				return err;
-			offsets[eb + 1] = 0;
-		}
-	}
-	addr = eb * mtd->erasesize + offs;
-	err = mtd_write(mtd, addr, len, &written, writebuf);
-	if (unlikely(err || written != len)) {
-		pr_err("error: write failed at 0x%llx\n",
-		       (long long)addr);
-		if (!err)
-			err = -EINVAL;
-		return err;
-	}
-	offs += len;
-	while (offs > mtd->erasesize) {
-		offsets[eb++] = mtd->erasesize;
-		offs -= mtd->erasesize;
-	}
-	offsets[eb] = offs;
-	return 0;
-}
-
-static int do_operation(void)
-{
-	if (prandom_u32() & 1)
-		return do_read();
-	else
-		return do_write();
-}
-
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kzalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	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(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-	return 0;
-}
-
-static int __init mtd_stresstest_init(void)
-{
-	int err;
-	int i, op;
-	uint64_t tmp;
-
-	printk(KERN_INFO "\n");
-	printk(KERN_INFO "=================================================\n");
-
-	if (dev < 0) {
-		pr_info("Please specify a valid mtd-device via module parameter\n");
-		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
-		return -EINVAL;
-	}
-
-	pr_info("MTD device: %d\n", dev);
-
-	mtd = get_mtd_device(NULL, dev);
-	if (IS_ERR(mtd)) {
-		err = PTR_ERR(mtd);
-		pr_err("error: cannot get MTD device\n");
-		return err;
-	}
-
-	if (mtd->writesize == 1) {
-		pr_info("not NAND flash, assume page size is 512 "
-		       "bytes.\n");
-		pgsize = 512;
-	} else
-		pgsize = mtd->writesize;
-
-	tmp = mtd->size;
-	do_div(tmp, mtd->erasesize);
-	ebcnt = tmp;
-	pgcnt = mtd->erasesize / pgsize;
-
-	pr_info("MTD device size %llu, eraseblock size %u, "
-	       "page size %u, count of eraseblocks %u, pages per "
-	       "eraseblock %u, OOB size %u\n",
-	       (unsigned long long)mtd->size, mtd->erasesize,
-	       pgsize, ebcnt, pgcnt, mtd->oobsize);
-
-	if (ebcnt < 2) {
-		pr_err("error: need at least 2 eraseblocks\n");
-		err = -ENOSPC;
-		goto out_put_mtd;
-	}
-
-	/* Read or write up 2 eraseblocks at a time */
-	bufsize = mtd->erasesize * 2;
-
-	err = -ENOMEM;
-	readbuf = vmalloc(bufsize);
-	writebuf = vmalloc(bufsize);
-	offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL);
-	if (!readbuf || !writebuf || !offsets)
-		goto out;
-	for (i = 0; i < ebcnt; i++)
-		offsets[i] = mtd->erasesize;
-	prandom_bytes(writebuf, bufsize);
-
-	err = scan_for_bad_eraseblocks();
-	if (err)
-		goto out;
-
-	/* Do operations */
-	pr_info("doing operations\n");
-	for (op = 0; op < count; op++) {
-		if ((op & 1023) == 0)
-			pr_info("%d operations done\n", op);
-		err = do_operation();
-		if (err)
-			goto out;
-		cond_resched();
-	}
-	pr_info("finished, %d operations done\n", op);
-
-out:
-	kfree(offsets);
-	kfree(bbt);
-	vfree(writebuf);
-	vfree(readbuf);
-out_put_mtd:
-	put_mtd_device(mtd);
-	if (err)
-		pr_info("error %d occurred\n", err);
-	printk(KERN_INFO "=================================================\n");
-	return err;
-}
-module_init(mtd_stresstest_init);
-
-static void __exit mtd_stresstest_exit(void)
-{
-	return;
-}
-module_exit(mtd_stresstest_exit);
-
-MODULE_DESCRIPTION("Stress test module");
-MODULE_AUTHOR("Adrian Hunter");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c
deleted file mode 100644
index e41a04f..0000000
--- a/drivers/mtd/tests/mtd_subpagetest.c
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright (C) 2006-2007 Nokia Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; see the file COPYING. If not, write to the Free Software
- * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Test sub-page read and write on MTD device.
- * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/err.h>
-#include <linux/mtd/mtd.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/random.h>
-
-static int dev = -EINVAL;
-module_param(dev, int, S_IRUGO);
-MODULE_PARM_DESC(dev, "MTD device number to use");
-
-static struct mtd_info *mtd;
-static unsigned char *writebuf;
-static unsigned char *readbuf;
-static unsigned char *bbt;
-
-static int subpgsize;
-static int bufsize;
-static int ebcnt;
-static int pgcnt;
-static int errcnt;
-static struct rnd_state rnd_state;
-
-static inline void clear_data(unsigned char *buf, size_t len)
-{
-	memset(buf, 0, len);
-}
-
-static int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (ei.state == MTD_ERASE_FAILED) {
-		pr_err("some erase error occurred at EB %d\n",
-		       ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int erase_whole_device(void)
-{
-	int err;
-	unsigned int i;
-
-	pr_info("erasing whole device\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = erase_eraseblock(i);
-		if (err)
-			return err;
-		cond_resched();
-	}
-	pr_info("erased %u eraseblocks\n", i);
-	return 0;
-}
-
-static int write_eraseblock(int ebnum)
-{
-	size_t written;
-	int err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
-	err = mtd_write(mtd, addr, subpgsize, &written, writebuf);
-	if (unlikely(err || written != subpgsize)) {
-		pr_err("error: write failed at %#llx\n",
-		       (long long)addr);
-		if (written != subpgsize) {
-			pr_err("  write size: %#x\n", subpgsize);
-			pr_err("  written: %#zx\n", written);
-		}
-		return err ? err : -1;
-	}
-
-	addr += subpgsize;
-
-	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
-	err = mtd_write(mtd, addr, subpgsize, &written, writebuf);
-	if (unlikely(err || written != subpgsize)) {
-		pr_err("error: write failed at %#llx\n",
-		       (long long)addr);
-		if (written != subpgsize) {
-			pr_err("  write size: %#x\n", subpgsize);
-			pr_err("  written: %#zx\n", written);
-		}
-		return err ? err : -1;
-	}
-
-	return err;
-}
-
-static int write_eraseblock2(int ebnum)
-{
-	size_t written;
-	int err = 0, k;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	for (k = 1; k < 33; ++k) {
-		if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
-			break;
-		prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
-		err = mtd_write(mtd, addr, subpgsize * k, &written, writebuf);
-		if (unlikely(err || written != subpgsize * k)) {
-			pr_err("error: write failed at %#llx\n",
-			       (long long)addr);
-			if (written != subpgsize) {
-				pr_err("  write size: %#x\n",
-				       subpgsize * k);
-				pr_err("  written: %#08zx\n",
-				       written);
-			}
-			return err ? err : -1;
-		}
-		addr += subpgsize * k;
-	}
-
-	return err;
-}
-
-static void print_subpage(unsigned char *p)
-{
-	int i, j;
-
-	for (i = 0; i < subpgsize; ) {
-		for (j = 0; i < subpgsize && j < 32; ++i, ++j)
-			printk("%02x", *p++);
-		printk("\n");
-	}
-}
-
-static int verify_eraseblock(int ebnum)
-{
-	size_t read;
-	int err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
-	clear_data(readbuf, subpgsize);
-	err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
-	if (unlikely(err || read != subpgsize)) {
-		if (mtd_is_bitflip(err) && read == subpgsize) {
-			pr_info("ECC correction at %#llx\n",
-			       (long long)addr);
-			err = 0;
-		} else {
-			pr_err("error: read failed at %#llx\n",
-			       (long long)addr);
-			return err ? err : -1;
-		}
-	}
-	if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
-		pr_err("error: verify failed at %#llx\n",
-		       (long long)addr);
-		pr_info("------------- written----------------\n");
-		print_subpage(writebuf);
-		pr_info("------------- read ------------------\n");
-		print_subpage(readbuf);
-		pr_info("-------------------------------------\n");
-		errcnt += 1;
-	}
-
-	addr += subpgsize;
-
-	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
-	clear_data(readbuf, subpgsize);
-	err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
-	if (unlikely(err || read != subpgsize)) {
-		if (mtd_is_bitflip(err) && read == subpgsize) {
-			pr_info("ECC correction at %#llx\n",
-			       (long long)addr);
-			err = 0;
-		} else {
-			pr_err("error: read failed at %#llx\n",
-			       (long long)addr);
-			return err ? err : -1;
-		}
-	}
-	if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
-		pr_info("error: verify failed at %#llx\n",
-		       (long long)addr);
-		pr_info("------------- written----------------\n");
-		print_subpage(writebuf);
-		pr_info("------------- read ------------------\n");
-		print_subpage(readbuf);
-		pr_info("-------------------------------------\n");
-		errcnt += 1;
-	}
-
-	return err;
-}
-
-static int verify_eraseblock2(int ebnum)
-{
-	size_t read;
-	int err = 0, k;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	for (k = 1; k < 33; ++k) {
-		if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
-			break;
-		prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
-		clear_data(readbuf, subpgsize * k);
-		err = mtd_read(mtd, addr, subpgsize * k, &read, readbuf);
-		if (unlikely(err || read != subpgsize * k)) {
-			if (mtd_is_bitflip(err) && read == subpgsize * k) {
-				pr_info("ECC correction at %#llx\n",
-				       (long long)addr);
-				err = 0;
-			} else {
-				pr_err("error: read failed at "
-				       "%#llx\n", (long long)addr);
-				return err ? err : -1;
-			}
-		}
-		if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) {
-			pr_err("error: verify failed at %#llx\n",
-			       (long long)addr);
-			errcnt += 1;
-		}
-		addr += subpgsize * k;
-	}
-
-	return err;
-}
-
-static int verify_eraseblock_ff(int ebnum)
-{
-	uint32_t j;
-	size_t read;
-	int err = 0;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(writebuf, 0xff, subpgsize);
-	for (j = 0; j < mtd->erasesize / subpgsize; ++j) {
-		clear_data(readbuf, subpgsize);
-		err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
-		if (unlikely(err || read != subpgsize)) {
-			if (mtd_is_bitflip(err) && read == subpgsize) {
-				pr_info("ECC correction at %#llx\n",
-				       (long long)addr);
-				err = 0;
-			} else {
-				pr_err("error: read failed at "
-				       "%#llx\n", (long long)addr);
-				return err ? err : -1;
-			}
-		}
-		if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
-			pr_err("error: verify 0xff failed at "
-			       "%#llx\n", (long long)addr);
-			errcnt += 1;
-		}
-		addr += subpgsize;
-	}
-
-	return err;
-}
-
-static int verify_all_eraseblocks_ff(void)
-{
-	int err;
-	unsigned int i;
-
-	pr_info("verifying all eraseblocks for 0xff\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = verify_eraseblock_ff(i);
-		if (err)
-			return err;
-		if (i % 256 == 0)
-			pr_info("verified up to eraseblock %u\n", i);
-		cond_resched();
-	}
-	pr_info("verified %u eraseblocks\n", i);
-	return 0;
-}
-
-static int is_block_bad(int ebnum)
-{
-	loff_t addr = ebnum * mtd->erasesize;
-	int ret;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kzalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	pr_info("scanning for bad eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		bbt[i] = is_block_bad(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-	return 0;
-}
-
-static int __init mtd_subpagetest_init(void)
-{
-	int err = 0;
-	uint32_t i;
-	uint64_t tmp;
-
-	printk(KERN_INFO "\n");
-	printk(KERN_INFO "=================================================\n");
-
-	if (dev < 0) {
-		pr_info("Please specify a valid mtd-device via module parameter\n");
-		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
-		return -EINVAL;
-	}
-
-	pr_info("MTD device: %d\n", dev);
-
-	mtd = get_mtd_device(NULL, dev);
-	if (IS_ERR(mtd)) {
-		err = PTR_ERR(mtd);
-		pr_err("error: cannot get MTD device\n");
-		return err;
-	}
-
-	if (mtd->type != MTD_NANDFLASH) {
-		pr_info("this test requires NAND flash\n");
-		goto out;
-	}
-
-	subpgsize = mtd->writesize >> mtd->subpage_sft;
-	tmp = mtd->size;
-	do_div(tmp, mtd->erasesize);
-	ebcnt = tmp;
-	pgcnt = mtd->erasesize / mtd->writesize;
-
-	pr_info("MTD device size %llu, eraseblock size %u, "
-	       "page size %u, subpage size %u, count of eraseblocks %u, "
-	       "pages per eraseblock %u, OOB size %u\n",
-	       (unsigned long long)mtd->size, mtd->erasesize,
-	       mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize);
-
-	err = -ENOMEM;
-	bufsize = subpgsize * 32;
-	writebuf = kmalloc(bufsize, GFP_KERNEL);
-	if (!writebuf)
-		goto out;
-	readbuf = kmalloc(bufsize, GFP_KERNEL);
-	if (!readbuf)
-		goto out;
-
-	err = scan_for_bad_eraseblocks();
-	if (err)
-		goto out;
-
-	err = erase_whole_device();
-	if (err)
-		goto out;
-
-	pr_info("writing whole device\n");
-	prandom_seed_state(&rnd_state, 1);
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = write_eraseblock(i);
-		if (unlikely(err))
-			goto out;
-		if (i % 256 == 0)
-			pr_info("written up to eraseblock %u\n", i);
-		cond_resched();
-	}
-	pr_info("written %u eraseblocks\n", i);
-
-	prandom_seed_state(&rnd_state, 1);
-	pr_info("verifying all eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = verify_eraseblock(i);
-		if (unlikely(err))
-			goto out;
-		if (i % 256 == 0)
-			pr_info("verified up to eraseblock %u\n", i);
-		cond_resched();
-	}
-	pr_info("verified %u eraseblocks\n", i);
-
-	err = erase_whole_device();
-	if (err)
-		goto out;
-
-	err = verify_all_eraseblocks_ff();
-	if (err)
-		goto out;
-
-	/* Write all eraseblocks */
-	prandom_seed_state(&rnd_state, 3);
-	pr_info("writing whole device\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = write_eraseblock2(i);
-		if (unlikely(err))
-			goto out;
-		if (i % 256 == 0)
-			pr_info("written up to eraseblock %u\n", i);
-		cond_resched();
-	}
-	pr_info("written %u eraseblocks\n", i);
-
-	/* Check all eraseblocks */
-	prandom_seed_state(&rnd_state, 3);
-	pr_info("verifying all eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = verify_eraseblock2(i);
-		if (unlikely(err))
-			goto out;
-		if (i % 256 == 0)
-			pr_info("verified up to eraseblock %u\n", i);
-		cond_resched();
-	}
-	pr_info("verified %u eraseblocks\n", i);
-
-	err = erase_whole_device();
-	if (err)
-		goto out;
-
-	err = verify_all_eraseblocks_ff();
-	if (err)
-		goto out;
-
-	pr_info("finished with %d errors\n", errcnt);
-
-out:
-	kfree(bbt);
-	kfree(readbuf);
-	kfree(writebuf);
-	put_mtd_device(mtd);
-	if (err)
-		pr_info("error %d occurred\n", err);
-	printk(KERN_INFO "=================================================\n");
-	return err;
-}
-module_init(mtd_subpagetest_init);
-
-static void __exit mtd_subpagetest_exit(void)
-{
-	return;
-}
-module_exit(mtd_subpagetest_exit);
-
-MODULE_DESCRIPTION("Subpage test module");
-MODULE_AUTHOR("Adrian Hunter");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/mtd_torturetest.c
deleted file mode 100644
index 3a9f6a6..0000000
--- a/drivers/mtd/tests/mtd_torturetest.c
+++ /dev/null
@@ -1,535 +0,0 @@
-/*
- * Copyright (C) 2006-2008 Artem Bityutskiy
- * Copyright (C) 2006-2008 Jarkko Lavinen
- * Copyright (C) 2006-2008 Adrian Hunter
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; see the file COPYING. If not, write to the Free Software
- * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Authors: Artem Bityutskiy, Jarkko Lavinen, Adria Hunter
- *
- * WARNING: this test program may kill your flash and your device. Do not
- * use it unless you know what you do. Authors are not responsible for any
- * damage caused by this program.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/err.h>
-#include <linux/mtd/mtd.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-
-#define RETRIES 3
-
-static int eb = 8;
-module_param(eb, int, S_IRUGO);
-MODULE_PARM_DESC(eb, "eraseblock number within the selected MTD device");
-
-static int ebcnt = 32;
-module_param(ebcnt, int, S_IRUGO);
-MODULE_PARM_DESC(ebcnt, "number of consecutive eraseblocks to torture");
-
-static int pgcnt;
-module_param(pgcnt, int, S_IRUGO);
-MODULE_PARM_DESC(pgcnt, "number of pages per eraseblock to torture (0 => all)");
-
-static int dev = -EINVAL;
-module_param(dev, int, S_IRUGO);
-MODULE_PARM_DESC(dev, "MTD device number to use");
-
-static int gran = 512;
-module_param(gran, int, S_IRUGO);
-MODULE_PARM_DESC(gran, "how often the status information should be printed");
-
-static int check = 1;
-module_param(check, int, S_IRUGO);
-MODULE_PARM_DESC(check, "if the written data should be checked");
-
-static unsigned int cycles_count;
-module_param(cycles_count, uint, S_IRUGO);
-MODULE_PARM_DESC(cycles_count, "how many erase cycles to do "
-			       "(infinite by default)");
-
-static struct mtd_info *mtd;
-
-/* This buffer contains 0x555555...0xAAAAAA... pattern */
-static unsigned char *patt_5A5;
-/* This buffer contains 0xAAAAAA...0x555555... pattern */
-static unsigned char *patt_A5A;
-/* This buffer contains all 0xFF bytes */
-static unsigned char *patt_FF;
-/* This a temporary buffer is use when checking data */
-static unsigned char *check_buf;
-/* How many erase cycles were done */
-static unsigned int erase_cycles;
-
-static int pgsize;
-static struct timeval start, finish;
-
-static void report_corrupt(unsigned char *read, unsigned char *written);
-
-static inline void start_timing(void)
-{
-	do_gettimeofday(&start);
-}
-
-static inline void stop_timing(void)
-{
-	do_gettimeofday(&finish);
-}
-
-/*
- * Erase eraseblock number @ebnum.
- */
-static inline int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (ei.state == MTD_ERASE_FAILED) {
-		pr_err("some erase error occurred at EB %d\n",
-		       ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-/*
- * Check that the contents of eraseblock number @enbum is equivalent to the
- * @buf buffer.
- */
-static inline int check_eraseblock(int ebnum, unsigned char *buf)
-{
-	int err, retries = 0;
-	size_t read;
-	loff_t addr = ebnum * mtd->erasesize;
-	size_t len = mtd->erasesize;
-
-	if (pgcnt) {
-		addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize;
-		len = pgcnt * pgsize;
-	}
-
-retry:
-	err = mtd_read(mtd, addr, len, &read, check_buf);
-	if (mtd_is_bitflip(err))
-		pr_err("single bit flip occurred at EB %d "
-		       "MTD reported that it was fixed.\n", ebnum);
-	else if (err) {
-		pr_err("error %d while reading EB %d, "
-		       "read %zd\n", err, ebnum, read);
-		return err;
-	}
-
-	if (read != len) {
-		pr_err("failed to read %zd bytes from EB %d, "
-		       "read only %zd, but no error reported\n",
-		       len, ebnum, read);
-		return -EIO;
-	}
-
-	if (memcmp(buf, check_buf, len)) {
-		pr_err("read wrong data from EB %d\n", ebnum);
-		report_corrupt(check_buf, buf);
-
-		if (retries++ < RETRIES) {
-			/* Try read again */
-			yield();
-			pr_info("re-try reading data from EB %d\n",
-			       ebnum);
-			goto retry;
-		} else {
-			pr_info("retried %d times, still errors, "
-			       "give-up\n", RETRIES);
-			return -EINVAL;
-		}
-	}
-
-	if (retries != 0)
-		pr_info("only attempt number %d was OK (!!!)\n",
-		       retries);
-
-	return 0;
-}
-
-static inline int write_pattern(int ebnum, void *buf)
-{
-	int err;
-	size_t written;
-	loff_t addr = ebnum * mtd->erasesize;
-	size_t len = mtd->erasesize;
-
-	if (pgcnt) {
-		addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize;
-		len = pgcnt * pgsize;
-	}
-	err = mtd_write(mtd, addr, len, &written, buf);
-	if (err) {
-		pr_err("error %d while writing EB %d, written %zd"
-		      " bytes\n", err, ebnum, written);
-		return err;
-	}
-	if (written != len) {
-		pr_info("written only %zd bytes of %zd, but no error"
-		       " reported\n", written, len);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int __init tort_init(void)
-{
-	int err = 0, i, infinite = !cycles_count;
-	int *bad_ebs;
-
-	printk(KERN_INFO "\n");
-	printk(KERN_INFO "=================================================\n");
-	pr_info("Warning: this program is trying to wear out your "
-	       "flash, stop it if this is not wanted.\n");
-
-	if (dev < 0) {
-		pr_info("Please specify a valid mtd-device via module parameter\n");
-		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
-		return -EINVAL;
-	}
-
-	pr_info("MTD device: %d\n", dev);
-	pr_info("torture %d eraseblocks (%d-%d) of mtd%d\n",
-	       ebcnt, eb, eb + ebcnt - 1, dev);
-	if (pgcnt)
-		pr_info("torturing just %d pages per eraseblock\n",
-			pgcnt);
-	pr_info("write verify %s\n", check ? "enabled" : "disabled");
-
-	mtd = get_mtd_device(NULL, dev);
-	if (IS_ERR(mtd)) {
-		err = PTR_ERR(mtd);
-		pr_err("error: cannot get MTD device\n");
-		return err;
-	}
-
-	if (mtd->writesize == 1) {
-		pr_info("not NAND flash, assume page size is 512 "
-		       "bytes.\n");
-		pgsize = 512;
-	} else
-		pgsize = mtd->writesize;
-
-	if (pgcnt && (pgcnt > mtd->erasesize / pgsize || pgcnt < 0)) {
-		pr_err("error: invalid pgcnt value %d\n", pgcnt);
-		goto out_mtd;
-	}
-
-	err = -ENOMEM;
-	patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!patt_5A5)
-		goto out_mtd;
-
-	patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!patt_A5A)
-		goto out_patt_5A5;
-
-	patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!patt_FF)
-		goto out_patt_A5A;
-
-	check_buf = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!check_buf)
-		goto out_patt_FF;
-
-	bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL);
-	if (!bad_ebs)
-		goto out_check_buf;
-
-	err = 0;
-
-	/* Initialize patterns */
-	memset(patt_FF, 0xFF, mtd->erasesize);
-	for (i = 0; i < mtd->erasesize / pgsize; i++) {
-		if (!(i & 1)) {
-			memset(patt_5A5 + i * pgsize, 0x55, pgsize);
-			memset(patt_A5A + i * pgsize, 0xAA, pgsize);
-		} else {
-			memset(patt_5A5 + i * pgsize, 0xAA, pgsize);
-			memset(patt_A5A + i * pgsize, 0x55, pgsize);
-		}
-	}
-
-	/*
-	 * Check if there is a bad eraseblock among those we are going to test.
-	 */
-	if (mtd_can_have_bb(mtd)) {
-		for (i = eb; i < eb + ebcnt; i++) {
-			err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize);
-
-			if (err < 0) {
-				pr_info("block_isbad() returned %d "
-				       "for EB %d\n", err, i);
-				goto out;
-			}
-
-			if (err) {
-				pr_err("EB %d is bad. Skip it.\n", i);
-				bad_ebs[i - eb] = 1;
-			}
-		}
-	}
-
-	start_timing();
-	while (1) {
-		int i;
-		void *patt;
-
-		/* Erase all eraseblocks */
-		for (i = eb; i < eb + ebcnt; i++) {
-			if (bad_ebs[i - eb])
-				continue;
-			err = erase_eraseblock(i);
-			if (err)
-				goto out;
-			cond_resched();
-		}
-
-		/* Check if the eraseblocks contain only 0xFF bytes */
-		if (check) {
-			for (i = eb; i < eb + ebcnt; i++) {
-				if (bad_ebs[i - eb])
-					continue;
-				err = check_eraseblock(i, patt_FF);
-				if (err) {
-					pr_info("verify failed"
-					       " for 0xFF... pattern\n");
-					goto out;
-				}
-				cond_resched();
-			}
-		}
-
-		/* Write the pattern */
-		for (i = eb; i < eb + ebcnt; i++) {
-			if (bad_ebs[i - eb])
-				continue;
-			if ((eb + erase_cycles) & 1)
-				patt = patt_5A5;
-			else
-				patt = patt_A5A;
-			err = write_pattern(i, patt);
-			if (err)
-				goto out;
-			cond_resched();
-		}
-
-		/* Verify what we wrote */
-		if (check) {
-			for (i = eb; i < eb + ebcnt; i++) {
-				if (bad_ebs[i - eb])
-					continue;
-				if ((eb + erase_cycles) & 1)
-					patt = patt_5A5;
-				else
-					patt = patt_A5A;
-				err = check_eraseblock(i, patt);
-				if (err) {
-					pr_info("verify failed for %s"
-					       " pattern\n",
-					       ((eb + erase_cycles) & 1) ?
-					       "0x55AA55..." : "0xAA55AA...");
-					goto out;
-				}
-				cond_resched();
-			}
-		}
-
-		erase_cycles += 1;
-
-		if (erase_cycles % gran == 0) {
-			long ms;
-
-			stop_timing();
-			ms = (finish.tv_sec - start.tv_sec) * 1000 +
-			     (finish.tv_usec - start.tv_usec) / 1000;
-			pr_info("%08u erase cycles done, took %lu "
-			       "milliseconds (%lu seconds)\n",
-			       erase_cycles, ms, ms / 1000);
-			start_timing();
-		}
-
-		if (!infinite && --cycles_count == 0)
-			break;
-	}
-out:
-
-	pr_info("finished after %u erase cycles\n",
-	       erase_cycles);
-	kfree(bad_ebs);
-out_check_buf:
-	kfree(check_buf);
-out_patt_FF:
-	kfree(patt_FF);
-out_patt_A5A:
-	kfree(patt_A5A);
-out_patt_5A5:
-	kfree(patt_5A5);
-out_mtd:
-	put_mtd_device(mtd);
-	if (err)
-		pr_info("error %d occurred during torturing\n", err);
-	printk(KERN_INFO "=================================================\n");
-	return err;
-}
-module_init(tort_init);
-
-static void __exit tort_exit(void)
-{
-	return;
-}
-module_exit(tort_exit);
-
-static int countdiffs(unsigned char *buf, unsigned char *check_buf,
-		      unsigned offset, unsigned len, unsigned *bytesp,
-		      unsigned *bitsp);
-static void print_bufs(unsigned char *read, unsigned char *written, int start,
-		       int len);
-
-/*
- * Report the detailed information about how the read EB differs from what was
- * written.
- */
-static void report_corrupt(unsigned char *read, unsigned char *written)
-{
-	int i;
-	int bytes, bits, pages, first;
-	int offset, len;
-	size_t check_len = mtd->erasesize;
-
-	if (pgcnt)
-		check_len = pgcnt * pgsize;
-
-	bytes = bits = pages = 0;
-	for (i = 0; i < check_len; i += pgsize)
-		if (countdiffs(written, read, i, pgsize, &bytes,
-			       &bits) >= 0)
-			pages++;
-
-	pr_info("verify fails on %d pages, %d bytes/%d bits\n",
-	       pages, bytes, bits);
-	pr_info("The following is a list of all differences between"
-	       " what was read from flash and what was expected\n");
-
-	for (i = 0; i < check_len; i += pgsize) {
-		cond_resched();
-		bytes = bits = 0;
-		first = countdiffs(written, read, i, pgsize, &bytes,
-				   &bits);
-		if (first < 0)
-			continue;
-
-		printk("-------------------------------------------------------"
-		       "----------------------------------\n");
-
-		pr_info("Page %zd has %d bytes/%d bits failing verify,"
-		       " starting at offset 0x%x\n",
-		       (mtd->erasesize - check_len + i) / pgsize,
-		       bytes, bits, first);
-
-		offset = first & ~0x7;
-		len = ((first + bytes) | 0x7) + 1 - offset;
-
-		print_bufs(read, written, offset, len);
-	}
-}
-
-static void print_bufs(unsigned char *read, unsigned char *written, int start,
-		       int len)
-{
-	int i = 0, j1, j2;
-	char *diff;
-
-	printk("Offset       Read                          Written\n");
-	while (i < len) {
-		printk("0x%08x: ", start + i);
-		diff = "   ";
-		for (j1 = 0; j1 < 8 && i + j1 < len; j1++) {
-			printk(" %02x", read[start + i + j1]);
-			if (read[start + i + j1] != written[start + i + j1])
-				diff = "***";
-		}
-
-		while (j1 < 8) {
-			printk(" ");
-			j1 += 1;
-		}
-
-		printk("  %s ", diff);
-
-		for (j2 = 0; j2 < 8 && i + j2 < len; j2++)
-			printk(" %02x", written[start + i + j2]);
-		printk("\n");
-		i += 8;
-	}
-}
-
-/*
- * Count the number of differing bytes and bits and return the first differing
- * offset.
- */
-static int countdiffs(unsigned char *buf, unsigned char *check_buf,
-		      unsigned offset, unsigned len, unsigned *bytesp,
-		      unsigned *bitsp)
-{
-	unsigned i, bit;
-	int first = -1;
-
-	for (i = offset; i < offset + len; i++)
-		if (buf[i] != check_buf[i]) {
-			first = i;
-			break;
-		}
-
-	while (i < offset + len) {
-		if (buf[i] != check_buf[i]) {
-			(*bytesp)++;
-			bit = 1;
-			while (bit < 256) {
-				if ((buf[i] & bit) != (check_buf[i] & bit))
-					(*bitsp)++;
-				bit <<= 1;
-			}
-		}
-		i++;
-	}
-
-	return first;
-}
-
-MODULE_DESCRIPTION("Eraseblock torturing module");
-MODULE_AUTHOR("Artem Bityutskiy, Jarkko Lavinen, Adrian Hunter");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/nandbiterrs.c b/drivers/mtd/tests/nandbiterrs.c
new file mode 100644
index 0000000..207bf9a
--- /dev/null
+++ b/drivers/mtd/tests/nandbiterrs.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright © 2012 NetCommWireless
+ * Iwo Mergler <Iwo.Mergler@netcommwireless.com.au>
+ *
+ * Test for multi-bit error recovery on a NAND page This mostly tests the
+ * ECC controller / driver.
+ *
+ * There are two test modes:
+ *
+ *	0 - artificially inserting bit errors until the ECC fails
+ *	    This is the default method and fairly quick. It should
+ *	    be independent of the quality of the FLASH.
+ *
+ *	1 - re-writing the same pattern repeatedly until the ECC fails.
+ *	    This method relies on the physics of NAND FLASH to eventually
+ *	    generate '0' bits if '1' has been written sufficient times.
+ *	    Depending on the NAND, the first bit errors will appear after
+ *	    1000 or more writes and then will usually snowball, reaching the
+ *	    limits of the ECC quickly.
+ *
+ *	    The test stops after 10000 cycles, should your FLASH be
+ *	    exceptionally good and not generate bit errors before that. Try
+ *	    a different page in that case.
+ *
+ * Please note that neither of these tests will significantly 'use up' any
+ * FLASH endurance. Only a maximum of two erase operations will be performed.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mtd/mtd.h>
+#include <linux/err.h>
+#include <linux/mtd/nand.h>
+#include <linux/slab.h>
+
+static int dev;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static unsigned page_offset;
+module_param(page_offset, uint, S_IRUGO);
+MODULE_PARM_DESC(page_offset, "Page number relative to dev start");
+
+static unsigned seed;
+module_param(seed, uint, S_IRUGO);
+MODULE_PARM_DESC(seed, "Random seed");
+
+static int mode;
+module_param(mode, int, S_IRUGO);
+MODULE_PARM_DESC(mode, "0=incremental errors, 1=overwrite test");
+
+static unsigned max_overwrite = 10000;
+
+static loff_t   offset;     /* Offset of the page we're using. */
+static unsigned eraseblock; /* Eraseblock number for our page. */
+
+/* We assume that the ECC can correct up to a certain number
+ * of biterrors per subpage. */
+static unsigned subsize;  /* Size of subpages */
+static unsigned subcount; /* Number of subpages per page */
+
+static struct mtd_info *mtd;   /* MTD device */
+
+static uint8_t *wbuffer; /* One page write / compare buffer */
+static uint8_t *rbuffer; /* One page read buffer */
+
+/* 'random' bytes from known offsets */
+static uint8_t hash(unsigned offset)
+{
+	unsigned v = offset;
+	unsigned char c;
+	v ^= 0x7f7edfd3;
+	v = v ^ (v >> 3);
+	v = v ^ (v >> 5);
+	v = v ^ (v >> 13);
+	c = v & 0xFF;
+	/* Reverse bits of result. */
+	c = (c & 0x0F) << 4 | (c & 0xF0) >> 4;
+	c = (c & 0x33) << 2 | (c & 0xCC) >> 2;
+	c = (c & 0x55) << 1 | (c & 0xAA) >> 1;
+	return c;
+}
+
+static int erase_block(void)
+{
+	int err;
+	struct erase_info ei;
+	loff_t addr = eraseblock * mtd->erasesize;
+
+	pr_info("erase_block\n");
+
+	memset(&ei, 0, sizeof(struct erase_info));
+	ei.mtd  = mtd;
+	ei.addr = addr;
+	ei.len  = mtd->erasesize;
+
+	err = mtd_erase(mtd, &ei);
+	if (err || ei.state == MTD_ERASE_FAILED) {
+		pr_err("error %d while erasing\n", err);
+		if (!err)
+			err = -EIO;
+		return err;
+	}
+
+	return 0;
+}
+
+/* Writes wbuffer to page */
+static int write_page(int log)
+{
+	int err = 0;
+	size_t written;
+
+	if (log)
+		pr_info("write_page\n");
+
+	err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer);
+	if (err || written != mtd->writesize) {
+		pr_err("error: write failed at %#llx\n", (long long)offset);
+		if (!err)
+			err = -EIO;
+	}
+
+	return err;
+}
+
+/* Re-writes the data area while leaving the OOB alone. */
+static int rewrite_page(int log)
+{
+	int err = 0;
+	struct mtd_oob_ops ops;
+
+	if (log)
+		pr_info("rewrite page\n");
+
+	ops.mode      = MTD_OPS_RAW; /* No ECC */
+	ops.len       = mtd->writesize;
+	ops.retlen    = 0;
+	ops.ooblen    = 0;
+	ops.oobretlen = 0;
+	ops.ooboffs   = 0;
+	ops.datbuf    = wbuffer;
+	ops.oobbuf    = NULL;
+
+	err = mtd_write_oob(mtd, offset, &ops);
+	if (err || ops.retlen != mtd->writesize) {
+		pr_err("error: write_oob failed (%d)\n", err);
+		if (!err)
+			err = -EIO;
+	}
+
+	return err;
+}
+
+/* Reads page into rbuffer. Returns number of corrected bit errors (>=0)
+ * or error (<0) */
+static int read_page(int log)
+{
+	int err = 0;
+	size_t read;
+	struct mtd_ecc_stats oldstats;
+
+	if (log)
+		pr_info("read_page\n");
+
+	/* Saving last mtd stats */
+	memcpy(&oldstats, &mtd->ecc_stats, sizeof(oldstats));
+
+	err = mtd_read(mtd, offset, mtd->writesize, &read, rbuffer);
+	if (err == -EUCLEAN)
+		err = mtd->ecc_stats.corrected - oldstats.corrected;
+
+	if (err < 0 || read != mtd->writesize) {
+		pr_err("error: read failed at %#llx\n", (long long)offset);
+		if (err >= 0)
+			err = -EIO;
+	}
+
+	return err;
+}
+
+/* Verifies rbuffer against random sequence */
+static int verify_page(int log)
+{
+	unsigned i, errs = 0;
+
+	if (log)
+		pr_info("verify_page\n");
+
+	for (i = 0; i < mtd->writesize; i++) {
+		if (rbuffer[i] != hash(i+seed)) {
+			pr_err("Error: page offset %u, expected %02x, got %02x\n",
+				i, hash(i+seed), rbuffer[i]);
+			errs++;
+		}
+	}
+
+	if (errs)
+		return -EIO;
+	else
+		return 0;
+}
+
+#define CBIT(v, n) ((v) & (1 << (n)))
+#define BCLR(v, n) ((v) = (v) & ~(1 << (n)))
+
+/* Finds the first '1' bit in wbuffer starting at offset 'byte'
+ * and sets it to '0'. */
+static int insert_biterror(unsigned byte)
+{
+	int bit;
+
+	while (byte < mtd->writesize) {
+		for (bit = 7; bit >= 0; bit--) {
+			if (CBIT(wbuffer[byte], bit)) {
+				BCLR(wbuffer[byte], bit);
+				pr_info("Inserted biterror @ %u/%u\n", byte, bit);
+				return 0;
+			}
+		}
+		byte++;
+	}
+	pr_err("biterror: Failed to find a '1' bit\n");
+	return -EIO;
+}
+
+/* Writes 'random' data to page and then introduces deliberate bit
+ * errors into the page, while verifying each step. */
+static int incremental_errors_test(void)
+{
+	int err = 0;
+	unsigned i;
+	unsigned errs_per_subpage = 0;
+
+	pr_info("incremental biterrors test\n");
+
+	for (i = 0; i < mtd->writesize; i++)
+		wbuffer[i] = hash(i+seed);
+
+	err = write_page(1);
+	if (err)
+		goto exit;
+
+	while (1) {
+
+		err = rewrite_page(1);
+		if (err)
+			goto exit;
+
+		err = read_page(1);
+		if (err > 0)
+			pr_info("Read reported %d corrected bit errors\n", err);
+		if (err < 0) {
+			pr_err("After %d biterrors per subpage, read reported error %d\n",
+				errs_per_subpage, err);
+			err = 0;
+			goto exit;
+		}
+
+		err = verify_page(1);
+		if (err) {
+			pr_err("ECC failure, read data is incorrect despite read success\n");
+			goto exit;
+		}
+
+		pr_info("Successfully corrected %d bit errors per subpage\n",
+			errs_per_subpage);
+
+		for (i = 0; i < subcount; i++) {
+			err = insert_biterror(i * subsize);
+			if (err < 0)
+				goto exit;
+		}
+		errs_per_subpage++;
+	}
+
+exit:
+	return err;
+}
+
+
+/* Writes 'random' data to page and then re-writes that same data repeatedly.
+   This eventually develops bit errors (bits written as '1' will slowly become
+   '0'), which are corrected as far as the ECC is capable of. */
+static int overwrite_test(void)
+{
+	int err = 0;
+	unsigned i;
+	unsigned max_corrected = 0;
+	unsigned opno = 0;
+	/* We don't expect more than this many correctable bit errors per
+	 * page. */
+	#define MAXBITS 512
+	static unsigned bitstats[MAXBITS]; /* bit error histogram. */
+
+	memset(bitstats, 0, sizeof(bitstats));
+
+	pr_info("overwrite biterrors test\n");
+
+	for (i = 0; i < mtd->writesize; i++)
+		wbuffer[i] = hash(i+seed);
+
+	err = write_page(1);
+	if (err)
+		goto exit;
+
+	while (opno < max_overwrite) {
+
+		err = rewrite_page(0);
+		if (err)
+			break;
+
+		err = read_page(0);
+		if (err >= 0) {
+			if (err >= MAXBITS) {
+				pr_info("Implausible number of bit errors corrected\n");
+				err = -EIO;
+				break;
+			}
+			bitstats[err]++;
+			if (err > max_corrected) {
+				max_corrected = err;
+				pr_info("Read reported %d corrected bit errors\n",
+					err);
+			}
+		} else { /* err < 0 */
+			pr_info("Read reported error %d\n", err);
+			err = 0;
+			break;
+		}
+
+		err = verify_page(0);
+		if (err) {
+			bitstats[max_corrected] = opno;
+			pr_info("ECC failure, read data is incorrect despite read success\n");
+			break;
+		}
+
+		opno++;
+	}
+
+	/* At this point bitstats[0] contains the number of ops with no bit
+	 * errors, bitstats[1] the number of ops with 1 bit error, etc. */
+	pr_info("Bit error histogram (%d operations total):\n", opno);
+	for (i = 0; i < max_corrected; i++)
+		pr_info("Page reads with %3d corrected bit errors: %d\n",
+			i, bitstats[i]);
+
+exit:
+	return err;
+}
+
+static int __init mtd_nandbiterrs_init(void)
+{
+	int err = 0;
+
+	printk("\n");
+	printk(KERN_INFO "==================================================\n");
+	pr_info("MTD device: %d\n", dev);
+
+	mtd = get_mtd_device(NULL, dev);
+	if (IS_ERR(mtd)) {
+		err = PTR_ERR(mtd);
+		pr_err("error: cannot get MTD device\n");
+		goto exit_mtddev;
+	}
+
+	if (mtd->type != MTD_NANDFLASH) {
+		pr_info("this test requires NAND flash\n");
+		err = -ENODEV;
+		goto exit_nand;
+	}
+
+	pr_info("MTD device size %llu, eraseblock=%u, page=%u, oob=%u\n",
+		(unsigned long long)mtd->size, mtd->erasesize,
+		mtd->writesize, mtd->oobsize);
+
+	subsize  = mtd->writesize >> mtd->subpage_sft;
+	subcount = mtd->writesize / subsize;
+
+	pr_info("Device uses %d subpages of %d bytes\n", subcount, subsize);
+
+	offset     = page_offset * mtd->writesize;
+	eraseblock = mtd_div_by_eb(offset, mtd);
+
+	pr_info("Using page=%u, offset=%llu, eraseblock=%u\n",
+		page_offset, offset, eraseblock);
+
+	wbuffer = kmalloc(mtd->writesize, GFP_KERNEL);
+	if (!wbuffer) {
+		err = -ENOMEM;
+		goto exit_wbuffer;
+	}
+
+	rbuffer = kmalloc(mtd->writesize, GFP_KERNEL);
+	if (!rbuffer) {
+		err = -ENOMEM;
+		goto exit_rbuffer;
+	}
+
+	err = erase_block();
+	if (err)
+		goto exit_error;
+
+	if (mode == 0)
+		err = incremental_errors_test();
+	else
+		err = overwrite_test();
+
+	if (err)
+		goto exit_error;
+
+	/* We leave the block un-erased in case of test failure. */
+	err = erase_block();
+	if (err)
+		goto exit_error;
+
+	err = -EIO;
+	pr_info("finished successfully.\n");
+	printk(KERN_INFO "==================================================\n");
+
+exit_error:
+	kfree(rbuffer);
+exit_rbuffer:
+	kfree(wbuffer);
+exit_wbuffer:
+	/* Nothing */
+exit_nand:
+	put_mtd_device(mtd);
+exit_mtddev:
+	return err;
+}
+
+static void __exit mtd_nandbiterrs_exit(void)
+{
+	return;
+}
+
+module_init(mtd_nandbiterrs_init);
+module_exit(mtd_nandbiterrs_exit);
+
+MODULE_DESCRIPTION("NAND bit error recovery test");
+MODULE_AUTHOR("Iwo Mergler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/oobtest.c b/drivers/mtd/tests/oobtest.c
new file mode 100644
index 0000000..ab81e9a
--- /dev/null
+++ b/drivers/mtd/tests/oobtest.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Test OOB read and write on MTD device.
+ *
+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <asm/div64.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+
+static int dev = -EINVAL;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static struct mtd_info *mtd;
+static unsigned char *readbuf;
+static unsigned char *writebuf;
+static unsigned char *bbt;
+
+static int ebcnt;
+static int pgcnt;
+static int errcnt;
+static int use_offset;
+static int use_len;
+static int use_len_max;
+static int vary_offset;
+static struct rnd_state rnd_state;
+
+static int erase_eraseblock(int ebnum)
+{
+	int err;
+	struct erase_info ei;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	memset(&ei, 0, sizeof(struct erase_info));
+	ei.mtd  = mtd;
+	ei.addr = addr;
+	ei.len  = mtd->erasesize;
+
+	err = mtd_erase(mtd, &ei);
+	if (err) {
+		pr_err("error %d while erasing EB %d\n", err, ebnum);
+		return err;
+	}
+
+	if (ei.state == MTD_ERASE_FAILED) {
+		pr_err("some erase error occurred at EB %d\n", ebnum);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int erase_whole_device(void)
+{
+	int err;
+	unsigned int i;
+
+	pr_info("erasing whole device\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = erase_eraseblock(i);
+		if (err)
+			return err;
+		cond_resched();
+	}
+	pr_info("erased %u eraseblocks\n", i);
+	return 0;
+}
+
+static void do_vary_offset(void)
+{
+	use_len -= 1;
+	if (use_len < 1) {
+		use_offset += 1;
+		if (use_offset >= use_len_max)
+			use_offset = 0;
+		use_len = use_len_max - use_offset;
+	}
+}
+
+static int write_eraseblock(int ebnum)
+{
+	int i;
+	struct mtd_oob_ops ops;
+	int err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
+		prandom_bytes_state(&rnd_state, writebuf, use_len);
+		ops.mode      = MTD_OPS_AUTO_OOB;
+		ops.len       = 0;
+		ops.retlen    = 0;
+		ops.ooblen    = use_len;
+		ops.oobretlen = 0;
+		ops.ooboffs   = use_offset;
+		ops.datbuf    = NULL;
+		ops.oobbuf    = writebuf;
+		err = mtd_write_oob(mtd, addr, &ops);
+		if (err || ops.oobretlen != use_len) {
+			pr_err("error: writeoob failed at %#llx\n",
+			       (long long)addr);
+			pr_err("error: use_len %d, use_offset %d\n",
+			       use_len, use_offset);
+			errcnt += 1;
+			return err ? err : -1;
+		}
+		if (vary_offset)
+			do_vary_offset();
+	}
+
+	return err;
+}
+
+static int write_whole_device(void)
+{
+	int err;
+	unsigned int i;
+
+	pr_info("writing OOBs of whole device\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = write_eraseblock(i);
+		if (err)
+			return err;
+		if (i % 256 == 0)
+			pr_info("written up to eraseblock %u\n", i);
+		cond_resched();
+	}
+	pr_info("written %u eraseblocks\n", i);
+	return 0;
+}
+
+static int verify_eraseblock(int ebnum)
+{
+	int i;
+	struct mtd_oob_ops ops;
+	int err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
+		prandom_bytes_state(&rnd_state, writebuf, use_len);
+		ops.mode      = MTD_OPS_AUTO_OOB;
+		ops.len       = 0;
+		ops.retlen    = 0;
+		ops.ooblen    = use_len;
+		ops.oobretlen = 0;
+		ops.ooboffs   = use_offset;
+		ops.datbuf    = NULL;
+		ops.oobbuf    = readbuf;
+		err = mtd_read_oob(mtd, addr, &ops);
+		if (err || ops.oobretlen != use_len) {
+			pr_err("error: readoob failed at %#llx\n",
+			       (long long)addr);
+			errcnt += 1;
+			return err ? err : -1;
+		}
+		if (memcmp(readbuf, writebuf, use_len)) {
+			pr_err("error: verify failed at %#llx\n",
+			       (long long)addr);
+			errcnt += 1;
+			if (errcnt > 1000) {
+				pr_err("error: too many errors\n");
+				return -1;
+			}
+		}
+		if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
+			int k;
+
+			ops.mode      = MTD_OPS_AUTO_OOB;
+			ops.len       = 0;
+			ops.retlen    = 0;
+			ops.ooblen    = mtd->ecclayout->oobavail;
+			ops.oobretlen = 0;
+			ops.ooboffs   = 0;
+			ops.datbuf    = NULL;
+			ops.oobbuf    = readbuf;
+			err = mtd_read_oob(mtd, addr, &ops);
+			if (err || ops.oobretlen != mtd->ecclayout->oobavail) {
+				pr_err("error: readoob failed at %#llx\n",
+						(long long)addr);
+				errcnt += 1;
+				return err ? err : -1;
+			}
+			if (memcmp(readbuf + use_offset, writebuf, use_len)) {
+				pr_err("error: verify failed at %#llx\n",
+						(long long)addr);
+				errcnt += 1;
+				if (errcnt > 1000) {
+					pr_err("error: too many errors\n");
+					return -1;
+				}
+			}
+			for (k = 0; k < use_offset; ++k)
+				if (readbuf[k] != 0xff) {
+					pr_err("error: verify 0xff "
+					       "failed at %#llx\n",
+					       (long long)addr);
+					errcnt += 1;
+					if (errcnt > 1000) {
+						pr_err("error: too "
+						       "many errors\n");
+						return -1;
+					}
+				}
+			for (k = use_offset + use_len;
+			     k < mtd->ecclayout->oobavail; ++k)
+				if (readbuf[k] != 0xff) {
+					pr_err("error: verify 0xff "
+					       "failed at %#llx\n",
+					       (long long)addr);
+					errcnt += 1;
+					if (errcnt > 1000) {
+						pr_err("error: too "
+						       "many errors\n");
+						return -1;
+					}
+				}
+		}
+		if (vary_offset)
+			do_vary_offset();
+	}
+	return err;
+}
+
+static int verify_eraseblock_in_one_go(int ebnum)
+{
+	struct mtd_oob_ops ops;
+	int err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+	size_t len = mtd->ecclayout->oobavail * pgcnt;
+
+	prandom_bytes_state(&rnd_state, writebuf, len);
+	ops.mode      = MTD_OPS_AUTO_OOB;
+	ops.len       = 0;
+	ops.retlen    = 0;
+	ops.ooblen    = len;
+	ops.oobretlen = 0;
+	ops.ooboffs   = 0;
+	ops.datbuf    = NULL;
+	ops.oobbuf    = readbuf;
+	err = mtd_read_oob(mtd, addr, &ops);
+	if (err || ops.oobretlen != len) {
+		pr_err("error: readoob failed at %#llx\n",
+		       (long long)addr);
+		errcnt += 1;
+		return err ? err : -1;
+	}
+	if (memcmp(readbuf, writebuf, len)) {
+		pr_err("error: verify failed at %#llx\n",
+		       (long long)addr);
+		errcnt += 1;
+		if (errcnt > 1000) {
+			pr_err("error: too many errors\n");
+			return -1;
+		}
+	}
+
+	return err;
+}
+
+static int verify_all_eraseblocks(void)
+{
+	int err;
+	unsigned int i;
+
+	pr_info("verifying all eraseblocks\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = verify_eraseblock(i);
+		if (err)
+			return err;
+		if (i % 256 == 0)
+			pr_info("verified up to eraseblock %u\n", i);
+		cond_resched();
+	}
+	pr_info("verified %u eraseblocks\n", i);
+	return 0;
+}
+
+static int is_block_bad(int ebnum)
+{
+	int ret;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	ret = mtd_block_isbad(mtd, addr);
+	if (ret)
+		pr_info("block %d is bad\n", ebnum);
+	return ret;
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+	int i, bad = 0;
+
+	bbt = kmalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		return -ENOMEM;
+
+	pr_info("scanning for bad eraseblocks\n");
+	for (i = 0; i < ebcnt; ++i) {
+		bbt[i] = is_block_bad(i) ? 1 : 0;
+		if (bbt[i])
+			bad += 1;
+		cond_resched();
+	}
+	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
+	return 0;
+}
+
+static int __init mtd_oobtest_init(void)
+{
+	int err = 0;
+	unsigned int i;
+	uint64_t tmp;
+	struct mtd_oob_ops ops;
+	loff_t addr = 0, addr0;
+
+	printk(KERN_INFO "\n");
+	printk(KERN_INFO "=================================================\n");
+
+	if (dev < 0) {
+		pr_info("Please specify a valid mtd-device via module parameter\n");
+		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
+		return -EINVAL;
+	}
+
+	pr_info("MTD device: %d\n", dev);
+
+	mtd = get_mtd_device(NULL, dev);
+	if (IS_ERR(mtd)) {
+		err = PTR_ERR(mtd);
+		pr_err("error: cannot get MTD device\n");
+		return err;
+	}
+
+	if (mtd->type != MTD_NANDFLASH) {
+		pr_info("this test requires NAND flash\n");
+		goto out;
+	}
+
+	tmp = mtd->size;
+	do_div(tmp, mtd->erasesize);
+	ebcnt = tmp;
+	pgcnt = mtd->erasesize / mtd->writesize;
+
+	pr_info("MTD device size %llu, eraseblock size %u, "
+	       "page size %u, count of eraseblocks %u, pages per "
+	       "eraseblock %u, OOB size %u\n",
+	       (unsigned long long)mtd->size, mtd->erasesize,
+	       mtd->writesize, ebcnt, pgcnt, mtd->oobsize);
+
+	err = -ENOMEM;
+	readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
+	if (!readbuf)
+		goto out;
+	writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
+	if (!writebuf)
+		goto out;
+
+	err = scan_for_bad_eraseblocks();
+	if (err)
+		goto out;
+
+	use_offset = 0;
+	use_len = mtd->ecclayout->oobavail;
+	use_len_max = mtd->ecclayout->oobavail;
+	vary_offset = 0;
+
+	/* First test: write all OOB, read it back and verify */
+	pr_info("test 1 of 5\n");
+
+	err = erase_whole_device();
+	if (err)
+		goto out;
+
+	prandom_seed_state(&rnd_state, 1);
+	err = write_whole_device();
+	if (err)
+		goto out;
+
+	prandom_seed_state(&rnd_state, 1);
+	err = verify_all_eraseblocks();
+	if (err)
+		goto out;
+
+	/*
+	 * Second test: write all OOB, a block at a time, read it back and
+	 * verify.
+	 */
+	pr_info("test 2 of 5\n");
+
+	err = erase_whole_device();
+	if (err)
+		goto out;
+
+	prandom_seed_state(&rnd_state, 3);
+	err = write_whole_device();
+	if (err)
+		goto out;
+
+	/* Check all eraseblocks */
+	prandom_seed_state(&rnd_state, 3);
+	pr_info("verifying all eraseblocks\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = verify_eraseblock_in_one_go(i);
+		if (err)
+			goto out;
+		if (i % 256 == 0)
+			pr_info("verified up to eraseblock %u\n", i);
+		cond_resched();
+	}
+	pr_info("verified %u eraseblocks\n", i);
+
+	/*
+	 * Third test: write OOB at varying offsets and lengths, read it back
+	 * and verify.
+	 */
+	pr_info("test 3 of 5\n");
+
+	err = erase_whole_device();
+	if (err)
+		goto out;
+
+	/* Write all eraseblocks */
+	use_offset = 0;
+	use_len = mtd->ecclayout->oobavail;
+	use_len_max = mtd->ecclayout->oobavail;
+	vary_offset = 1;
+	prandom_seed_state(&rnd_state, 5);
+
+	err = write_whole_device();
+	if (err)
+		goto out;
+
+	/* Check all eraseblocks */
+	use_offset = 0;
+	use_len = mtd->ecclayout->oobavail;
+	use_len_max = mtd->ecclayout->oobavail;
+	vary_offset = 1;
+	prandom_seed_state(&rnd_state, 5);
+	err = verify_all_eraseblocks();
+	if (err)
+		goto out;
+
+	use_offset = 0;
+	use_len = mtd->ecclayout->oobavail;
+	use_len_max = mtd->ecclayout->oobavail;
+	vary_offset = 0;
+
+	/* Fourth test: try to write off end of device */
+	pr_info("test 4 of 5\n");
+
+	err = erase_whole_device();
+	if (err)
+		goto out;
+
+	addr0 = 0;
+	for (i = 0; i < ebcnt && bbt[i]; ++i)
+		addr0 += mtd->erasesize;
+
+	/* Attempt to write off end of OOB */
+	ops.mode      = MTD_OPS_AUTO_OOB;
+	ops.len       = 0;
+	ops.retlen    = 0;
+	ops.ooblen    = 1;
+	ops.oobretlen = 0;
+	ops.ooboffs   = mtd->ecclayout->oobavail;
+	ops.datbuf    = NULL;
+	ops.oobbuf    = writebuf;
+	pr_info("attempting to start write past end of OOB\n");
+	pr_info("an error is expected...\n");
+	err = mtd_write_oob(mtd, addr0, &ops);
+	if (err) {
+		pr_info("error occurred as expected\n");
+		err = 0;
+	} else {
+		pr_err("error: can write past end of OOB\n");
+		errcnt += 1;
+	}
+
+	/* Attempt to read off end of OOB */
+	ops.mode      = MTD_OPS_AUTO_OOB;
+	ops.len       = 0;
+	ops.retlen    = 0;
+	ops.ooblen    = 1;
+	ops.oobretlen = 0;
+	ops.ooboffs   = mtd->ecclayout->oobavail;
+	ops.datbuf    = NULL;
+	ops.oobbuf    = readbuf;
+	pr_info("attempting to start read past end of OOB\n");
+	pr_info("an error is expected...\n");
+	err = mtd_read_oob(mtd, addr0, &ops);
+	if (err) {
+		pr_info("error occurred as expected\n");
+		err = 0;
+	} else {
+		pr_err("error: can read past end of OOB\n");
+		errcnt += 1;
+	}
+
+	if (bbt[ebcnt - 1])
+		pr_info("skipping end of device tests because last "
+		       "block is bad\n");
+	else {
+		/* Attempt to write off end of device */
+		ops.mode      = MTD_OPS_AUTO_OOB;
+		ops.len       = 0;
+		ops.retlen    = 0;
+		ops.ooblen    = mtd->ecclayout->oobavail + 1;
+		ops.oobretlen = 0;
+		ops.ooboffs   = 0;
+		ops.datbuf    = NULL;
+		ops.oobbuf    = writebuf;
+		pr_info("attempting to write past end of device\n");
+		pr_info("an error is expected...\n");
+		err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
+		if (err) {
+			pr_info("error occurred as expected\n");
+			err = 0;
+		} else {
+			pr_err("error: wrote past end of device\n");
+			errcnt += 1;
+		}
+
+		/* Attempt to read off end of device */
+		ops.mode      = MTD_OPS_AUTO_OOB;
+		ops.len       = 0;
+		ops.retlen    = 0;
+		ops.ooblen    = mtd->ecclayout->oobavail + 1;
+		ops.oobretlen = 0;
+		ops.ooboffs   = 0;
+		ops.datbuf    = NULL;
+		ops.oobbuf    = readbuf;
+		pr_info("attempting to read past end of device\n");
+		pr_info("an error is expected...\n");
+		err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
+		if (err) {
+			pr_info("error occurred as expected\n");
+			err = 0;
+		} else {
+			pr_err("error: read past end of device\n");
+			errcnt += 1;
+		}
+
+		err = erase_eraseblock(ebcnt - 1);
+		if (err)
+			goto out;
+
+		/* Attempt to write off end of device */
+		ops.mode      = MTD_OPS_AUTO_OOB;
+		ops.len       = 0;
+		ops.retlen    = 0;
+		ops.ooblen    = mtd->ecclayout->oobavail;
+		ops.oobretlen = 0;
+		ops.ooboffs   = 1;
+		ops.datbuf    = NULL;
+		ops.oobbuf    = writebuf;
+		pr_info("attempting to write past end of device\n");
+		pr_info("an error is expected...\n");
+		err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
+		if (err) {
+			pr_info("error occurred as expected\n");
+			err = 0;
+		} else {
+			pr_err("error: wrote past end of device\n");
+			errcnt += 1;
+		}
+
+		/* Attempt to read off end of device */
+		ops.mode      = MTD_OPS_AUTO_OOB;
+		ops.len       = 0;
+		ops.retlen    = 0;
+		ops.ooblen    = mtd->ecclayout->oobavail;
+		ops.oobretlen = 0;
+		ops.ooboffs   = 1;
+		ops.datbuf    = NULL;
+		ops.oobbuf    = readbuf;
+		pr_info("attempting to read past end of device\n");
+		pr_info("an error is expected...\n");
+		err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
+		if (err) {
+			pr_info("error occurred as expected\n");
+			err = 0;
+		} else {
+			pr_err("error: read past end of device\n");
+			errcnt += 1;
+		}
+	}
+
+	/* Fifth test: write / read across block boundaries */
+	pr_info("test 5 of 5\n");
+
+	/* Erase all eraseblocks */
+	err = erase_whole_device();
+	if (err)
+		goto out;
+
+	/* Write all eraseblocks */
+	prandom_seed_state(&rnd_state, 11);
+	pr_info("writing OOBs of whole device\n");
+	for (i = 0; i < ebcnt - 1; ++i) {
+		int cnt = 2;
+		int pg;
+		size_t sz = mtd->ecclayout->oobavail;
+		if (bbt[i] || bbt[i + 1])
+			continue;
+		addr = (i + 1) * mtd->erasesize - mtd->writesize;
+		for (pg = 0; pg < cnt; ++pg) {
+			prandom_bytes_state(&rnd_state, writebuf, sz);
+			ops.mode      = MTD_OPS_AUTO_OOB;
+			ops.len       = 0;
+			ops.retlen    = 0;
+			ops.ooblen    = sz;
+			ops.oobretlen = 0;
+			ops.ooboffs   = 0;
+			ops.datbuf    = NULL;
+			ops.oobbuf    = writebuf;
+			err = mtd_write_oob(mtd, addr, &ops);
+			if (err)
+				goto out;
+			if (i % 256 == 0)
+				pr_info("written up to eraseblock %u\n", i);
+			cond_resched();
+			addr += mtd->writesize;
+		}
+	}
+	pr_info("written %u eraseblocks\n", i);
+
+	/* Check all eraseblocks */
+	prandom_seed_state(&rnd_state, 11);
+	pr_info("verifying all eraseblocks\n");
+	for (i = 0; i < ebcnt - 1; ++i) {
+		if (bbt[i] || bbt[i + 1])
+			continue;
+		prandom_bytes_state(&rnd_state, writebuf,
+					mtd->ecclayout->oobavail * 2);
+		addr = (i + 1) * mtd->erasesize - mtd->writesize;
+		ops.mode      = MTD_OPS_AUTO_OOB;
+		ops.len       = 0;
+		ops.retlen    = 0;
+		ops.ooblen    = mtd->ecclayout->oobavail * 2;
+		ops.oobretlen = 0;
+		ops.ooboffs   = 0;
+		ops.datbuf    = NULL;
+		ops.oobbuf    = readbuf;
+		err = mtd_read_oob(mtd, addr, &ops);
+		if (err)
+			goto out;
+		if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) {
+			pr_err("error: verify failed at %#llx\n",
+			       (long long)addr);
+			errcnt += 1;
+			if (errcnt > 1000) {
+				pr_err("error: too many errors\n");
+				goto out;
+			}
+		}
+		if (i % 256 == 0)
+			pr_info("verified up to eraseblock %u\n", i);
+		cond_resched();
+	}
+	pr_info("verified %u eraseblocks\n", i);
+
+	pr_info("finished with %d errors\n", errcnt);
+out:
+	kfree(bbt);
+	kfree(writebuf);
+	kfree(readbuf);
+	put_mtd_device(mtd);
+	if (err)
+		pr_info("error %d occurred\n", err);
+	printk(KERN_INFO "=================================================\n");
+	return err;
+}
+module_init(mtd_oobtest_init);
+
+static void __exit mtd_oobtest_exit(void)
+{
+	return;
+}
+module_exit(mtd_oobtest_exit);
+
+MODULE_DESCRIPTION("Out-of-band test module");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c
new file mode 100644
index 0000000..acd991f
--- /dev/null
+++ b/drivers/mtd/tests/pagetest.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Test page read and write on MTD device.
+ *
+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <asm/div64.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+
+static int dev = -EINVAL;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static struct mtd_info *mtd;
+static unsigned char *twopages;
+static unsigned char *writebuf;
+static unsigned char *boundary;
+static unsigned char *bbt;
+
+static int pgsize;
+static int bufsize;
+static int ebcnt;
+static int pgcnt;
+static int errcnt;
+static struct rnd_state rnd_state;
+
+static int erase_eraseblock(int ebnum)
+{
+	int err;
+	struct erase_info ei;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	memset(&ei, 0, sizeof(struct erase_info));
+	ei.mtd  = mtd;
+	ei.addr = addr;
+	ei.len  = mtd->erasesize;
+
+	err = mtd_erase(mtd, &ei);
+	if (err) {
+		pr_err("error %d while erasing EB %d\n", err, ebnum);
+		return err;
+	}
+
+	if (ei.state == MTD_ERASE_FAILED) {
+		pr_err("some erase error occurred at EB %d\n",
+		       ebnum);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int write_eraseblock(int ebnum)
+{
+	int err = 0;
+	size_t written;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
+	cond_resched();
+	err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf);
+	if (err || written != mtd->erasesize)
+		pr_err("error: write failed at %#llx\n",
+		       (long long)addr);
+
+	return err;
+}
+
+static int verify_eraseblock(int ebnum)
+{
+	uint32_t j;
+	size_t read;
+	int err = 0, i;
+	loff_t addr0, addrn;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	addr0 = 0;
+	for (i = 0; i < ebcnt && bbt[i]; ++i)
+		addr0 += mtd->erasesize;
+
+	addrn = mtd->size;
+	for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
+		addrn -= mtd->erasesize;
+
+	prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
+	for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
+		/* Do a read to set the internal dataRAMs to different data */
+		err = mtd_read(mtd, addr0, bufsize, &read, twopages);
+		if (mtd_is_bitflip(err))
+			err = 0;
+		if (err || read != bufsize) {
+			pr_err("error: read failed at %#llx\n",
+			       (long long)addr0);
+			return err;
+		}
+		err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
+		if (mtd_is_bitflip(err))
+			err = 0;
+		if (err || read != bufsize) {
+			pr_err("error: read failed at %#llx\n",
+			       (long long)(addrn - bufsize));
+			return err;
+		}
+		memset(twopages, 0, bufsize);
+		err = mtd_read(mtd, addr, bufsize, &read, twopages);
+		if (mtd_is_bitflip(err))
+			err = 0;
+		if (err || read != bufsize) {
+			pr_err("error: read failed at %#llx\n",
+			       (long long)addr);
+			break;
+		}
+		if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) {
+			pr_err("error: verify failed at %#llx\n",
+			       (long long)addr);
+			errcnt += 1;
+		}
+	}
+	/* Check boundary between eraseblocks */
+	if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) {
+		struct rnd_state old_state = rnd_state;
+
+		/* Do a read to set the internal dataRAMs to different data */
+		err = mtd_read(mtd, addr0, bufsize, &read, twopages);
+		if (mtd_is_bitflip(err))
+			err = 0;
+		if (err || read != bufsize) {
+			pr_err("error: read failed at %#llx\n",
+			       (long long)addr0);
+			return err;
+		}
+		err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
+		if (mtd_is_bitflip(err))
+			err = 0;
+		if (err || read != bufsize) {
+			pr_err("error: read failed at %#llx\n",
+			       (long long)(addrn - bufsize));
+			return err;
+		}
+		memset(twopages, 0, bufsize);
+		err = mtd_read(mtd, addr, bufsize, &read, twopages);
+		if (mtd_is_bitflip(err))
+			err = 0;
+		if (err || read != bufsize) {
+			pr_err("error: read failed at %#llx\n",
+			       (long long)addr);
+			return err;
+		}
+		memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize);
+		prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize);
+		if (memcmp(twopages, boundary, bufsize)) {
+			pr_err("error: verify failed at %#llx\n",
+			       (long long)addr);
+			errcnt += 1;
+		}
+		rnd_state = old_state;
+	}
+	return err;
+}
+
+static int crosstest(void)
+{
+	size_t read;
+	int err = 0, i;
+	loff_t addr, addr0, addrn;
+	unsigned char *pp1, *pp2, *pp3, *pp4;
+
+	pr_info("crosstest\n");
+	pp1 = kmalloc(pgsize * 4, GFP_KERNEL);
+	if (!pp1)
+		return -ENOMEM;
+	pp2 = pp1 + pgsize;
+	pp3 = pp2 + pgsize;
+	pp4 = pp3 + pgsize;
+	memset(pp1, 0, pgsize * 4);
+
+	addr0 = 0;
+	for (i = 0; i < ebcnt && bbt[i]; ++i)
+		addr0 += mtd->erasesize;
+
+	addrn = mtd->size;
+	for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
+		addrn -= mtd->erasesize;
+
+	/* Read 2nd-to-last page to pp1 */
+	addr = addrn - pgsize - pgsize;
+	err = mtd_read(mtd, addr, pgsize, &read, pp1);
+	if (mtd_is_bitflip(err))
+		err = 0;
+	if (err || read != pgsize) {
+		pr_err("error: read failed at %#llx\n",
+		       (long long)addr);
+		kfree(pp1);
+		return err;
+	}
+
+	/* Read 3rd-to-last page to pp1 */
+	addr = addrn - pgsize - pgsize - pgsize;
+	err = mtd_read(mtd, addr, pgsize, &read, pp1);
+	if (mtd_is_bitflip(err))
+		err = 0;
+	if (err || read != pgsize) {
+		pr_err("error: read failed at %#llx\n",
+		       (long long)addr);
+		kfree(pp1);
+		return err;
+	}
+
+	/* Read first page to pp2 */
+	addr = addr0;
+	pr_info("reading page at %#llx\n", (long long)addr);
+	err = mtd_read(mtd, addr, pgsize, &read, pp2);
+	if (mtd_is_bitflip(err))
+		err = 0;
+	if (err || read != pgsize) {
+		pr_err("error: read failed at %#llx\n",
+		       (long long)addr);
+		kfree(pp1);
+		return err;
+	}
+
+	/* Read last page to pp3 */
+	addr = addrn - pgsize;
+	pr_info("reading page at %#llx\n", (long long)addr);
+	err = mtd_read(mtd, addr, pgsize, &read, pp3);
+	if (mtd_is_bitflip(err))
+		err = 0;
+	if (err || read != pgsize) {
+		pr_err("error: read failed at %#llx\n",
+		       (long long)addr);
+		kfree(pp1);
+		return err;
+	}
+
+	/* Read first page again to pp4 */
+	addr = addr0;
+	pr_info("reading page at %#llx\n", (long long)addr);
+	err = mtd_read(mtd, addr, pgsize, &read, pp4);
+	if (mtd_is_bitflip(err))
+		err = 0;
+	if (err || read != pgsize) {
+		pr_err("error: read failed at %#llx\n",
+		       (long long)addr);
+		kfree(pp1);
+		return err;
+	}
+
+	/* pp2 and pp4 should be the same */
+	pr_info("verifying pages read at %#llx match\n",
+	       (long long)addr0);
+	if (memcmp(pp2, pp4, pgsize)) {
+		pr_err("verify failed!\n");
+		errcnt += 1;
+	} else if (!err)
+		pr_info("crosstest ok\n");
+	kfree(pp1);
+	return err;
+}
+
+static int erasecrosstest(void)
+{
+	size_t read, written;
+	int err = 0, i, ebnum, ebnum2;
+	loff_t addr0;
+	char *readbuf = twopages;
+
+	pr_info("erasecrosstest\n");
+
+	ebnum = 0;
+	addr0 = 0;
+	for (i = 0; i < ebcnt && bbt[i]; ++i) {
+		addr0 += mtd->erasesize;
+		ebnum += 1;
+	}
+
+	ebnum2 = ebcnt - 1;
+	while (ebnum2 && bbt[ebnum2])
+		ebnum2 -= 1;
+
+	pr_info("erasing block %d\n", ebnum);
+	err = erase_eraseblock(ebnum);
+	if (err)
+		return err;
+
+	pr_info("writing 1st page of block %d\n", ebnum);
+	prandom_bytes_state(&rnd_state, writebuf, pgsize);
+	strcpy(writebuf, "There is no data like this!");
+	err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
+	if (err || written != pgsize) {
+		pr_info("error: write failed at %#llx\n",
+		       (long long)addr0);
+		return err ? err : -1;
+	}
+
+	pr_info("reading 1st page of block %d\n", ebnum);
+	memset(readbuf, 0, pgsize);
+	err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
+	if (mtd_is_bitflip(err))
+		err = 0;
+	if (err || read != pgsize) {
+		pr_err("error: read failed at %#llx\n",
+		       (long long)addr0);
+		return err ? err : -1;
+	}
+
+	pr_info("verifying 1st page of block %d\n", ebnum);
+	if (memcmp(writebuf, readbuf, pgsize)) {
+		pr_err("verify failed!\n");
+		errcnt += 1;
+		return -1;
+	}
+
+	pr_info("erasing block %d\n", ebnum);
+	err = erase_eraseblock(ebnum);
+	if (err)
+		return err;
+
+	pr_info("writing 1st page of block %d\n", ebnum);
+	prandom_bytes_state(&rnd_state, writebuf, pgsize);
+	strcpy(writebuf, "There is no data like this!");
+	err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
+	if (err || written != pgsize) {
+		pr_err("error: write failed at %#llx\n",
+		       (long long)addr0);
+		return err ? err : -1;
+	}
+
+	pr_info("erasing block %d\n", ebnum2);
+	err = erase_eraseblock(ebnum2);
+	if (err)
+		return err;
+
+	pr_info("reading 1st page of block %d\n", ebnum);
+	memset(readbuf, 0, pgsize);
+	err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
+	if (mtd_is_bitflip(err))
+		err = 0;
+	if (err || read != pgsize) {
+		pr_err("error: read failed at %#llx\n",
+		       (long long)addr0);
+		return err ? err : -1;
+	}
+
+	pr_info("verifying 1st page of block %d\n", ebnum);
+	if (memcmp(writebuf, readbuf, pgsize)) {
+		pr_err("verify failed!\n");
+		errcnt += 1;
+		return -1;
+	}
+
+	if (!err)
+		pr_info("erasecrosstest ok\n");
+	return err;
+}
+
+static int erasetest(void)
+{
+	size_t read, written;
+	int err = 0, i, ebnum, ok = 1;
+	loff_t addr0;
+
+	pr_info("erasetest\n");
+
+	ebnum = 0;
+	addr0 = 0;
+	for (i = 0; i < ebcnt && bbt[i]; ++i) {
+		addr0 += mtd->erasesize;
+		ebnum += 1;
+	}
+
+	pr_info("erasing block %d\n", ebnum);
+	err = erase_eraseblock(ebnum);
+	if (err)
+		return err;
+
+	pr_info("writing 1st page of block %d\n", ebnum);
+	prandom_bytes_state(&rnd_state, writebuf, pgsize);
+	err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
+	if (err || written != pgsize) {
+		pr_err("error: write failed at %#llx\n",
+		       (long long)addr0);
+		return err ? err : -1;
+	}
+
+	pr_info("erasing block %d\n", ebnum);
+	err = erase_eraseblock(ebnum);
+	if (err)
+		return err;
+
+	pr_info("reading 1st page of block %d\n", ebnum);
+	err = mtd_read(mtd, addr0, pgsize, &read, twopages);
+	if (mtd_is_bitflip(err))
+		err = 0;
+	if (err || read != pgsize) {
+		pr_err("error: read failed at %#llx\n",
+		       (long long)addr0);
+		return err ? err : -1;
+	}
+
+	pr_info("verifying 1st page of block %d is all 0xff\n",
+	       ebnum);
+	for (i = 0; i < pgsize; ++i)
+		if (twopages[i] != 0xff) {
+			pr_err("verifying all 0xff failed at %d\n",
+			       i);
+			errcnt += 1;
+			ok = 0;
+			break;
+		}
+
+	if (ok && !err)
+		pr_info("erasetest ok\n");
+
+	return err;
+}
+
+static int is_block_bad(int ebnum)
+{
+	loff_t addr = ebnum * mtd->erasesize;
+	int ret;
+
+	ret = mtd_block_isbad(mtd, addr);
+	if (ret)
+		pr_info("block %d is bad\n", ebnum);
+	return ret;
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+	int i, bad = 0;
+
+	bbt = kzalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		return -ENOMEM;
+
+	pr_info("scanning for bad eraseblocks\n");
+	for (i = 0; i < ebcnt; ++i) {
+		bbt[i] = is_block_bad(i) ? 1 : 0;
+		if (bbt[i])
+			bad += 1;
+		cond_resched();
+	}
+	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
+	return 0;
+}
+
+static int __init mtd_pagetest_init(void)
+{
+	int err = 0;
+	uint64_t tmp;
+	uint32_t i;
+
+	printk(KERN_INFO "\n");
+	printk(KERN_INFO "=================================================\n");
+
+	if (dev < 0) {
+		pr_info("Please specify a valid mtd-device via module parameter\n");
+		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
+		return -EINVAL;
+	}
+
+	pr_info("MTD device: %d\n", dev);
+
+	mtd = get_mtd_device(NULL, dev);
+	if (IS_ERR(mtd)) {
+		err = PTR_ERR(mtd);
+		pr_err("error: cannot get MTD device\n");
+		return err;
+	}
+
+	if (mtd->type != MTD_NANDFLASH) {
+		pr_info("this test requires NAND flash\n");
+		goto out;
+	}
+
+	tmp = mtd->size;
+	do_div(tmp, mtd->erasesize);
+	ebcnt = tmp;
+	pgcnt = mtd->erasesize / mtd->writesize;
+	pgsize = mtd->writesize;
+
+	pr_info("MTD device size %llu, eraseblock size %u, "
+	       "page size %u, count of eraseblocks %u, pages per "
+	       "eraseblock %u, OOB size %u\n",
+	       (unsigned long long)mtd->size, mtd->erasesize,
+	       pgsize, ebcnt, pgcnt, mtd->oobsize);
+
+	err = -ENOMEM;
+	bufsize = pgsize * 2;
+	writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
+	if (!writebuf)
+		goto out;
+	twopages = kmalloc(bufsize, GFP_KERNEL);
+	if (!twopages)
+		goto out;
+	boundary = kmalloc(bufsize, GFP_KERNEL);
+	if (!boundary)
+		goto out;
+
+	err = scan_for_bad_eraseblocks();
+	if (err)
+		goto out;
+
+	/* Erase all eraseblocks */
+	pr_info("erasing whole device\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = erase_eraseblock(i);
+		if (err)
+			goto out;
+		cond_resched();
+	}
+	pr_info("erased %u eraseblocks\n", i);
+
+	/* Write all eraseblocks */
+	prandom_seed_state(&rnd_state, 1);
+	pr_info("writing whole device\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = write_eraseblock(i);
+		if (err)
+			goto out;
+		if (i % 256 == 0)
+			pr_info("written up to eraseblock %u\n", i);
+		cond_resched();
+	}
+	pr_info("written %u eraseblocks\n", i);
+
+	/* Check all eraseblocks */
+	prandom_seed_state(&rnd_state, 1);
+	pr_info("verifying all eraseblocks\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = verify_eraseblock(i);
+		if (err)
+			goto out;
+		if (i % 256 == 0)
+			pr_info("verified up to eraseblock %u\n", i);
+		cond_resched();
+	}
+	pr_info("verified %u eraseblocks\n", i);
+
+	err = crosstest();
+	if (err)
+		goto out;
+
+	err = erasecrosstest();
+	if (err)
+		goto out;
+
+	err = erasetest();
+	if (err)
+		goto out;
+
+	pr_info("finished with %d errors\n", errcnt);
+out:
+
+	kfree(bbt);
+	kfree(boundary);
+	kfree(twopages);
+	kfree(writebuf);
+	put_mtd_device(mtd);
+	if (err)
+		pr_info("error %d occurred\n", err);
+	printk(KERN_INFO "=================================================\n");
+	return err;
+}
+module_init(mtd_pagetest_init);
+
+static void __exit mtd_pagetest_exit(void)
+{
+	return;
+}
+module_exit(mtd_pagetest_exit);
+
+MODULE_DESCRIPTION("NAND page test");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/readtest.c b/drivers/mtd/tests/readtest.c
new file mode 100644
index 0000000..2cdd0c4
--- /dev/null
+++ b/drivers/mtd/tests/readtest.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Check MTD device read.
+ *
+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+static int dev = -EINVAL;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static struct mtd_info *mtd;
+static unsigned char *iobuf;
+static unsigned char *iobuf1;
+static unsigned char *bbt;
+
+static int pgsize;
+static int ebcnt;
+static int pgcnt;
+
+static int read_eraseblock_by_page(int ebnum)
+{
+	size_t read;
+	int i, ret, err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+	void *buf = iobuf;
+	void *oobbuf = iobuf1;
+
+	for (i = 0; i < pgcnt; i++) {
+		memset(buf, 0 , pgsize);
+		ret = mtd_read(mtd, addr, pgsize, &read, buf);
+		if (ret == -EUCLEAN)
+			ret = 0;
+		if (ret || read != pgsize) {
+			pr_err("error: read failed at %#llx\n",
+			       (long long)addr);
+			if (!err)
+				err = ret;
+			if (!err)
+				err = -EINVAL;
+		}
+		if (mtd->oobsize) {
+			struct mtd_oob_ops ops;
+
+			ops.mode      = MTD_OPS_PLACE_OOB;
+			ops.len       = 0;
+			ops.retlen    = 0;
+			ops.ooblen    = mtd->oobsize;
+			ops.oobretlen = 0;
+			ops.ooboffs   = 0;
+			ops.datbuf    = NULL;
+			ops.oobbuf    = oobbuf;
+			ret = mtd_read_oob(mtd, addr, &ops);
+			if ((ret && !mtd_is_bitflip(ret)) ||
+					ops.oobretlen != mtd->oobsize) {
+				pr_err("error: read oob failed at "
+						  "%#llx\n", (long long)addr);
+				if (!err)
+					err = ret;
+				if (!err)
+					err = -EINVAL;
+			}
+			oobbuf += mtd->oobsize;
+		}
+		addr += pgsize;
+		buf += pgsize;
+	}
+
+	return err;
+}
+
+static void dump_eraseblock(int ebnum)
+{
+	int i, j, n;
+	char line[128];
+	int pg, oob;
+
+	pr_info("dumping eraseblock %d\n", ebnum);
+	n = mtd->erasesize;
+	for (i = 0; i < n;) {
+		char *p = line;
+
+		p += sprintf(p, "%05x: ", i);
+		for (j = 0; j < 32 && i < n; j++, i++)
+			p += sprintf(p, "%02x", (unsigned int)iobuf[i]);
+		printk(KERN_CRIT "%s\n", line);
+		cond_resched();
+	}
+	if (!mtd->oobsize)
+		return;
+	pr_info("dumping oob from eraseblock %d\n", ebnum);
+	n = mtd->oobsize;
+	for (pg = 0, i = 0; pg < pgcnt; pg++)
+		for (oob = 0; oob < n;) {
+			char *p = line;
+
+			p += sprintf(p, "%05x: ", i);
+			for (j = 0; j < 32 && oob < n; j++, oob++, i++)
+				p += sprintf(p, "%02x",
+					     (unsigned int)iobuf1[i]);
+			printk(KERN_CRIT "%s\n", line);
+			cond_resched();
+		}
+}
+
+static int is_block_bad(int ebnum)
+{
+	loff_t addr = ebnum * mtd->erasesize;
+	int ret;
+
+	ret = mtd_block_isbad(mtd, addr);
+	if (ret)
+		pr_info("block %d is bad\n", ebnum);
+	return ret;
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+	int i, bad = 0;
+
+	bbt = kzalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		return -ENOMEM;
+
+	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(i) ? 1 : 0;
+		if (bbt[i])
+			bad += 1;
+		cond_resched();
+	}
+	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
+	return 0;
+}
+
+static int __init mtd_readtest_init(void)
+{
+	uint64_t tmp;
+	int err, i;
+
+	printk(KERN_INFO "\n");
+	printk(KERN_INFO "=================================================\n");
+
+	if (dev < 0) {
+		pr_info("Please specify a valid mtd-device via module parameter\n");
+		return -EINVAL;
+	}
+
+	pr_info("MTD device: %d\n", dev);
+
+	mtd = get_mtd_device(NULL, dev);
+	if (IS_ERR(mtd)) {
+		err = PTR_ERR(mtd);
+		pr_err("error: Cannot get MTD device\n");
+		return err;
+	}
+
+	if (mtd->writesize == 1) {
+		pr_info("not NAND flash, assume page size is 512 "
+		       "bytes.\n");
+		pgsize = 512;
+	} else
+		pgsize = mtd->writesize;
+
+	tmp = mtd->size;
+	do_div(tmp, mtd->erasesize);
+	ebcnt = tmp;
+	pgcnt = mtd->erasesize / pgsize;
+
+	pr_info("MTD device size %llu, eraseblock size %u, "
+	       "page size %u, count of eraseblocks %u, pages per "
+	       "eraseblock %u, OOB size %u\n",
+	       (unsigned long long)mtd->size, mtd->erasesize,
+	       pgsize, ebcnt, pgcnt, mtd->oobsize);
+
+	err = -ENOMEM;
+	iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
+	if (!iobuf)
+		goto out;
+	iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL);
+	if (!iobuf1)
+		goto out;
+
+	err = scan_for_bad_eraseblocks();
+	if (err)
+		goto out;
+
+	/* Read all eraseblocks 1 page at a time */
+	pr_info("testing page read\n");
+	for (i = 0; i < ebcnt; ++i) {
+		int ret;
+
+		if (bbt[i])
+			continue;
+		ret = read_eraseblock_by_page(i);
+		if (ret) {
+			dump_eraseblock(i);
+			if (!err)
+				err = ret;
+		}
+		cond_resched();
+	}
+
+	if (err)
+		pr_info("finished with errors\n");
+	else
+		pr_info("finished\n");
+
+out:
+
+	kfree(iobuf);
+	kfree(iobuf1);
+	kfree(bbt);
+	put_mtd_device(mtd);
+	if (err)
+		pr_info("error %d occurred\n", err);
+	printk(KERN_INFO "=================================================\n");
+	return err;
+}
+module_init(mtd_readtest_init);
+
+static void __exit mtd_readtest_exit(void)
+{
+	return;
+}
+module_exit(mtd_readtest_exit);
+
+MODULE_DESCRIPTION("Read test module");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c
new file mode 100644
index 0000000..20b63d1
--- /dev/null
+++ b/drivers/mtd/tests/speedtest.c
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Test read and write speed of a MTD device.
+ *
+ * Author: Adrian Hunter <adrian.hunter@nokia.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+
+static int dev = -EINVAL;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static int count;
+module_param(count, int, S_IRUGO);
+MODULE_PARM_DESC(count, "Maximum number of eraseblocks to use "
+			"(0 means use all)");
+
+static struct mtd_info *mtd;
+static unsigned char *iobuf;
+static unsigned char *bbt;
+
+static int pgsize;
+static int ebcnt;
+static int pgcnt;
+static int goodebcnt;
+static struct timeval start, finish;
+
+
+static int erase_eraseblock(int ebnum)
+{
+	int err;
+	struct erase_info ei;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	memset(&ei, 0, sizeof(struct erase_info));
+	ei.mtd  = mtd;
+	ei.addr = addr;
+	ei.len  = mtd->erasesize;
+
+	err = mtd_erase(mtd, &ei);
+	if (err) {
+		pr_err("error %d while erasing EB %d\n", err, ebnum);
+		return err;
+	}
+
+	if (ei.state == MTD_ERASE_FAILED) {
+		pr_err("some erase error occurred at EB %d\n",
+		       ebnum);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int multiblock_erase(int ebnum, int blocks)
+{
+	int err;
+	struct erase_info ei;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	memset(&ei, 0, sizeof(struct erase_info));
+	ei.mtd  = mtd;
+	ei.addr = addr;
+	ei.len  = mtd->erasesize * blocks;
+
+	err = mtd_erase(mtd, &ei);
+	if (err) {
+		pr_err("error %d while erasing EB %d, blocks %d\n",
+		       err, ebnum, blocks);
+		return err;
+	}
+
+	if (ei.state == MTD_ERASE_FAILED) {
+		pr_err("some erase error occurred at EB %d,"
+		       "blocks %d\n", ebnum, blocks);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int erase_whole_device(void)
+{
+	int err;
+	unsigned int i;
+
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = erase_eraseblock(i);
+		if (err)
+			return err;
+		cond_resched();
+	}
+	return 0;
+}
+
+static int write_eraseblock(int ebnum)
+{
+	size_t written;
+	int err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	err = mtd_write(mtd, addr, mtd->erasesize, &written, iobuf);
+	if (err || written != mtd->erasesize) {
+		pr_err("error: write failed at %#llx\n", addr);
+		if (!err)
+			err = -EINVAL;
+	}
+
+	return err;
+}
+
+static int write_eraseblock_by_page(int ebnum)
+{
+	size_t written;
+	int i, err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+	void *buf = iobuf;
+
+	for (i = 0; i < pgcnt; i++) {
+		err = mtd_write(mtd, addr, pgsize, &written, buf);
+		if (err || written != pgsize) {
+			pr_err("error: write failed at %#llx\n",
+			       addr);
+			if (!err)
+				err = -EINVAL;
+			break;
+		}
+		addr += pgsize;
+		buf += pgsize;
+	}
+
+	return err;
+}
+
+static int write_eraseblock_by_2pages(int ebnum)
+{
+	size_t written, sz = pgsize * 2;
+	int i, n = pgcnt / 2, err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+	void *buf = iobuf;
+
+	for (i = 0; i < n; i++) {
+		err = mtd_write(mtd, addr, sz, &written, buf);
+		if (err || written != sz) {
+			pr_err("error: write failed at %#llx\n",
+			       addr);
+			if (!err)
+				err = -EINVAL;
+			return err;
+		}
+		addr += sz;
+		buf += sz;
+	}
+	if (pgcnt % 2) {
+		err = mtd_write(mtd, addr, pgsize, &written, buf);
+		if (err || written != pgsize) {
+			pr_err("error: write failed at %#llx\n",
+			       addr);
+			if (!err)
+				err = -EINVAL;
+		}
+	}
+
+	return err;
+}
+
+static int read_eraseblock(int ebnum)
+{
+	size_t read;
+	int err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	err = mtd_read(mtd, addr, mtd->erasesize, &read, iobuf);
+	/* Ignore corrected ECC errors */
+	if (mtd_is_bitflip(err))
+		err = 0;
+	if (err || read != mtd->erasesize) {
+		pr_err("error: read failed at %#llx\n", addr);
+		if (!err)
+			err = -EINVAL;
+	}
+
+	return err;
+}
+
+static int read_eraseblock_by_page(int ebnum)
+{
+	size_t read;
+	int i, err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+	void *buf = iobuf;
+
+	for (i = 0; i < pgcnt; i++) {
+		err = mtd_read(mtd, addr, pgsize, &read, buf);
+		/* Ignore corrected ECC errors */
+		if (mtd_is_bitflip(err))
+			err = 0;
+		if (err || read != pgsize) {
+			pr_err("error: read failed at %#llx\n",
+			       addr);
+			if (!err)
+				err = -EINVAL;
+			break;
+		}
+		addr += pgsize;
+		buf += pgsize;
+	}
+
+	return err;
+}
+
+static int read_eraseblock_by_2pages(int ebnum)
+{
+	size_t read, sz = pgsize * 2;
+	int i, n = pgcnt / 2, err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+	void *buf = iobuf;
+
+	for (i = 0; i < n; i++) {
+		err = mtd_read(mtd, addr, sz, &read, buf);
+		/* Ignore corrected ECC errors */
+		if (mtd_is_bitflip(err))
+			err = 0;
+		if (err || read != sz) {
+			pr_err("error: read failed at %#llx\n",
+			       addr);
+			if (!err)
+				err = -EINVAL;
+			return err;
+		}
+		addr += sz;
+		buf += sz;
+	}
+	if (pgcnt % 2) {
+		err = mtd_read(mtd, addr, pgsize, &read, buf);
+		/* Ignore corrected ECC errors */
+		if (mtd_is_bitflip(err))
+			err = 0;
+		if (err || read != pgsize) {
+			pr_err("error: read failed at %#llx\n",
+			       addr);
+			if (!err)
+				err = -EINVAL;
+		}
+	}
+
+	return err;
+}
+
+static int is_block_bad(int ebnum)
+{
+	loff_t addr = ebnum * mtd->erasesize;
+	int ret;
+
+	ret = mtd_block_isbad(mtd, addr);
+	if (ret)
+		pr_info("block %d is bad\n", ebnum);
+	return ret;
+}
+
+static inline void start_timing(void)
+{
+	do_gettimeofday(&start);
+}
+
+static inline void stop_timing(void)
+{
+	do_gettimeofday(&finish);
+}
+
+static long calc_speed(void)
+{
+	uint64_t k;
+	long ms;
+
+	ms = (finish.tv_sec - start.tv_sec) * 1000 +
+	     (finish.tv_usec - start.tv_usec) / 1000;
+	if (ms == 0)
+		return 0;
+	k = goodebcnt * (mtd->erasesize / 1024) * 1000;
+	do_div(k, ms);
+	return k;
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+	int i, bad = 0;
+
+	bbt = kzalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		return -ENOMEM;
+
+	if (!mtd_can_have_bb(mtd))
+		goto out;
+
+	pr_info("scanning for bad eraseblocks\n");
+	for (i = 0; i < ebcnt; ++i) {
+		bbt[i] = is_block_bad(i) ? 1 : 0;
+		if (bbt[i])
+			bad += 1;
+		cond_resched();
+	}
+	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
+out:
+	goodebcnt = ebcnt - bad;
+	return 0;
+}
+
+static int __init mtd_speedtest_init(void)
+{
+	int err, i, blocks, j, k;
+	long speed;
+	uint64_t tmp;
+
+	printk(KERN_INFO "\n");
+	printk(KERN_INFO "=================================================\n");
+
+	if (dev < 0) {
+		pr_info("Please specify a valid mtd-device via module parameter\n");
+		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
+		return -EINVAL;
+	}
+
+	if (count)
+		pr_info("MTD device: %d    count: %d\n", dev, count);
+	else
+		pr_info("MTD device: %d\n", dev);
+
+	mtd = get_mtd_device(NULL, dev);
+	if (IS_ERR(mtd)) {
+		err = PTR_ERR(mtd);
+		pr_err("error: cannot get MTD device\n");
+		return err;
+	}
+
+	if (mtd->writesize == 1) {
+		pr_info("not NAND flash, assume page size is 512 "
+		       "bytes.\n");
+		pgsize = 512;
+	} else
+		pgsize = mtd->writesize;
+
+	tmp = mtd->size;
+	do_div(tmp, mtd->erasesize);
+	ebcnt = tmp;
+	pgcnt = mtd->erasesize / pgsize;
+
+	pr_info("MTD device size %llu, eraseblock size %u, "
+	       "page size %u, count of eraseblocks %u, pages per "
+	       "eraseblock %u, OOB size %u\n",
+	       (unsigned long long)mtd->size, mtd->erasesize,
+	       pgsize, ebcnt, pgcnt, mtd->oobsize);
+
+	if (count > 0 && count < ebcnt)
+		ebcnt = count;
+
+	err = -ENOMEM;
+	iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
+	if (!iobuf)
+		goto out;
+
+	prandom_bytes(iobuf, mtd->erasesize);
+
+	err = scan_for_bad_eraseblocks();
+	if (err)
+		goto out;
+
+	err = erase_whole_device();
+	if (err)
+		goto out;
+
+	/* Write all eraseblocks, 1 eraseblock at a time */
+	pr_info("testing eraseblock write speed\n");
+	start_timing();
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = write_eraseblock(i);
+		if (err)
+			goto out;
+		cond_resched();
+	}
+	stop_timing();
+	speed = calc_speed();
+	pr_info("eraseblock write speed is %ld KiB/s\n", speed);
+
+	/* Read all eraseblocks, 1 eraseblock at a time */
+	pr_info("testing eraseblock read speed\n");
+	start_timing();
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = read_eraseblock(i);
+		if (err)
+			goto out;
+		cond_resched();
+	}
+	stop_timing();
+	speed = calc_speed();
+	pr_info("eraseblock read speed is %ld KiB/s\n", speed);
+
+	err = erase_whole_device();
+	if (err)
+		goto out;
+
+	/* Write all eraseblocks, 1 page at a time */
+	pr_info("testing page write speed\n");
+	start_timing();
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = write_eraseblock_by_page(i);
+		if (err)
+			goto out;
+		cond_resched();
+	}
+	stop_timing();
+	speed = calc_speed();
+	pr_info("page write speed is %ld KiB/s\n", speed);
+
+	/* Read all eraseblocks, 1 page at a time */
+	pr_info("testing page read speed\n");
+	start_timing();
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = read_eraseblock_by_page(i);
+		if (err)
+			goto out;
+		cond_resched();
+	}
+	stop_timing();
+	speed = calc_speed();
+	pr_info("page read speed is %ld KiB/s\n", speed);
+
+	err = erase_whole_device();
+	if (err)
+		goto out;
+
+	/* Write all eraseblocks, 2 pages at a time */
+	pr_info("testing 2 page write speed\n");
+	start_timing();
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = write_eraseblock_by_2pages(i);
+		if (err)
+			goto out;
+		cond_resched();
+	}
+	stop_timing();
+	speed = calc_speed();
+	pr_info("2 page write speed is %ld KiB/s\n", speed);
+
+	/* Read all eraseblocks, 2 pages at a time */
+	pr_info("testing 2 page read speed\n");
+	start_timing();
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = read_eraseblock_by_2pages(i);
+		if (err)
+			goto out;
+		cond_resched();
+	}
+	stop_timing();
+	speed = calc_speed();
+	pr_info("2 page read speed is %ld KiB/s\n", speed);
+
+	/* Erase all eraseblocks */
+	pr_info("Testing erase speed\n");
+	start_timing();
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = erase_eraseblock(i);
+		if (err)
+			goto out;
+		cond_resched();
+	}
+	stop_timing();
+	speed = calc_speed();
+	pr_info("erase speed is %ld KiB/s\n", speed);
+
+	/* Multi-block erase all eraseblocks */
+	for (k = 1; k < 7; k++) {
+		blocks = 1 << k;
+		pr_info("Testing %dx multi-block erase speed\n",
+		       blocks);
+		start_timing();
+		for (i = 0; i < ebcnt; ) {
+			for (j = 0; j < blocks && (i + j) < ebcnt; j++)
+				if (bbt[i + j])
+					break;
+			if (j < 1) {
+				i++;
+				continue;
+			}
+			err = multiblock_erase(i, j);
+			if (err)
+				goto out;
+			cond_resched();
+			i += j;
+		}
+		stop_timing();
+		speed = calc_speed();
+		pr_info("%dx multi-block erase speed is %ld KiB/s\n",
+		       blocks, speed);
+	}
+	pr_info("finished\n");
+out:
+	kfree(iobuf);
+	kfree(bbt);
+	put_mtd_device(mtd);
+	if (err)
+		pr_info("error %d occurred\n", err);
+	printk(KERN_INFO "=================================================\n");
+	return err;
+}
+module_init(mtd_speedtest_init);
+
+static void __exit mtd_speedtest_exit(void)
+{
+	return;
+}
+module_exit(mtd_speedtest_exit);
+
+MODULE_DESCRIPTION("Speed test module");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/stresstest.c b/drivers/mtd/tests/stresstest.c
new file mode 100644
index 0000000..3a95e61
--- /dev/null
+++ b/drivers/mtd/tests/stresstest.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Test random reads, writes and erases on MTD device.
+ *
+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+
+static int dev = -EINVAL;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static int count = 10000;
+module_param(count, int, S_IRUGO);
+MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)");
+
+static struct mtd_info *mtd;
+static unsigned char *writebuf;
+static unsigned char *readbuf;
+static unsigned char *bbt;
+static int *offsets;
+
+static int pgsize;
+static int bufsize;
+static int ebcnt;
+static int pgcnt;
+
+static int rand_eb(void)
+{
+	unsigned int eb;
+
+again:
+	eb = prandom_u32();
+	/* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */
+	eb %= (ebcnt - 1);
+	if (bbt[eb])
+		goto again;
+	return eb;
+}
+
+static int rand_offs(void)
+{
+	unsigned int offs;
+
+	offs = prandom_u32();
+	offs %= bufsize;
+	return offs;
+}
+
+static int rand_len(int offs)
+{
+	unsigned int len;
+
+	len = prandom_u32();
+	len %= (bufsize - offs);
+	return len;
+}
+
+static int erase_eraseblock(int ebnum)
+{
+	int err;
+	struct erase_info ei;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	memset(&ei, 0, sizeof(struct erase_info));
+	ei.mtd  = mtd;
+	ei.addr = addr;
+	ei.len  = mtd->erasesize;
+
+	err = mtd_erase(mtd, &ei);
+	if (unlikely(err)) {
+		pr_err("error %d while erasing EB %d\n", err, ebnum);
+		return err;
+	}
+
+	if (unlikely(ei.state == MTD_ERASE_FAILED)) {
+		pr_err("some erase error occurred at EB %d\n",
+		       ebnum);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int is_block_bad(int ebnum)
+{
+	loff_t addr = ebnum * mtd->erasesize;
+	int ret;
+
+	ret = mtd_block_isbad(mtd, addr);
+	if (ret)
+		pr_info("block %d is bad\n", ebnum);
+	return ret;
+}
+
+static int do_read(void)
+{
+	size_t read;
+	int eb = rand_eb();
+	int offs = rand_offs();
+	int len = rand_len(offs), err;
+	loff_t addr;
+
+	if (bbt[eb + 1]) {
+		if (offs >= mtd->erasesize)
+			offs -= mtd->erasesize;
+		if (offs + len > mtd->erasesize)
+			len = mtd->erasesize - offs;
+	}
+	addr = eb * mtd->erasesize + offs;
+	err = mtd_read(mtd, addr, len, &read, readbuf);
+	if (mtd_is_bitflip(err))
+		err = 0;
+	if (unlikely(err || read != len)) {
+		pr_err("error: read failed at 0x%llx\n",
+		       (long long)addr);
+		if (!err)
+			err = -EINVAL;
+		return err;
+	}
+	return 0;
+}
+
+static int do_write(void)
+{
+	int eb = rand_eb(), offs, err, len;
+	size_t written;
+	loff_t addr;
+
+	offs = offsets[eb];
+	if (offs >= mtd->erasesize) {
+		err = erase_eraseblock(eb);
+		if (err)
+			return err;
+		offs = offsets[eb] = 0;
+	}
+	len = rand_len(offs);
+	len = ((len + pgsize - 1) / pgsize) * pgsize;
+	if (offs + len > mtd->erasesize) {
+		if (bbt[eb + 1])
+			len = mtd->erasesize - offs;
+		else {
+			err = erase_eraseblock(eb + 1);
+			if (err)
+				return err;
+			offsets[eb + 1] = 0;
+		}
+	}
+	addr = eb * mtd->erasesize + offs;
+	err = mtd_write(mtd, addr, len, &written, writebuf);
+	if (unlikely(err || written != len)) {
+		pr_err("error: write failed at 0x%llx\n",
+		       (long long)addr);
+		if (!err)
+			err = -EINVAL;
+		return err;
+	}
+	offs += len;
+	while (offs > mtd->erasesize) {
+		offsets[eb++] = mtd->erasesize;
+		offs -= mtd->erasesize;
+	}
+	offsets[eb] = offs;
+	return 0;
+}
+
+static int do_operation(void)
+{
+	if (prandom_u32() & 1)
+		return do_read();
+	else
+		return do_write();
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+	int i, bad = 0;
+
+	bbt = kzalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		return -ENOMEM;
+
+	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(i) ? 1 : 0;
+		if (bbt[i])
+			bad += 1;
+		cond_resched();
+	}
+	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
+	return 0;
+}
+
+static int __init mtd_stresstest_init(void)
+{
+	int err;
+	int i, op;
+	uint64_t tmp;
+
+	printk(KERN_INFO "\n");
+	printk(KERN_INFO "=================================================\n");
+
+	if (dev < 0) {
+		pr_info("Please specify a valid mtd-device via module parameter\n");
+		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
+		return -EINVAL;
+	}
+
+	pr_info("MTD device: %d\n", dev);
+
+	mtd = get_mtd_device(NULL, dev);
+	if (IS_ERR(mtd)) {
+		err = PTR_ERR(mtd);
+		pr_err("error: cannot get MTD device\n");
+		return err;
+	}
+
+	if (mtd->writesize == 1) {
+		pr_info("not NAND flash, assume page size is 512 "
+		       "bytes.\n");
+		pgsize = 512;
+	} else
+		pgsize = mtd->writesize;
+
+	tmp = mtd->size;
+	do_div(tmp, mtd->erasesize);
+	ebcnt = tmp;
+	pgcnt = mtd->erasesize / pgsize;
+
+	pr_info("MTD device size %llu, eraseblock size %u, "
+	       "page size %u, count of eraseblocks %u, pages per "
+	       "eraseblock %u, OOB size %u\n",
+	       (unsigned long long)mtd->size, mtd->erasesize,
+	       pgsize, ebcnt, pgcnt, mtd->oobsize);
+
+	if (ebcnt < 2) {
+		pr_err("error: need at least 2 eraseblocks\n");
+		err = -ENOSPC;
+		goto out_put_mtd;
+	}
+
+	/* Read or write up 2 eraseblocks at a time */
+	bufsize = mtd->erasesize * 2;
+
+	err = -ENOMEM;
+	readbuf = vmalloc(bufsize);
+	writebuf = vmalloc(bufsize);
+	offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL);
+	if (!readbuf || !writebuf || !offsets)
+		goto out;
+	for (i = 0; i < ebcnt; i++)
+		offsets[i] = mtd->erasesize;
+	prandom_bytes(writebuf, bufsize);
+
+	err = scan_for_bad_eraseblocks();
+	if (err)
+		goto out;
+
+	/* Do operations */
+	pr_info("doing operations\n");
+	for (op = 0; op < count; op++) {
+		if ((op & 1023) == 0)
+			pr_info("%d operations done\n", op);
+		err = do_operation();
+		if (err)
+			goto out;
+		cond_resched();
+	}
+	pr_info("finished, %d operations done\n", op);
+
+out:
+	kfree(offsets);
+	kfree(bbt);
+	vfree(writebuf);
+	vfree(readbuf);
+out_put_mtd:
+	put_mtd_device(mtd);
+	if (err)
+		pr_info("error %d occurred\n", err);
+	printk(KERN_INFO "=================================================\n");
+	return err;
+}
+module_init(mtd_stresstest_init);
+
+static void __exit mtd_stresstest_exit(void)
+{
+	return;
+}
+module_exit(mtd_stresstest_exit);
+
+MODULE_DESCRIPTION("Stress test module");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/subpagetest.c b/drivers/mtd/tests/subpagetest.c
new file mode 100644
index 0000000..e41a04f
--- /dev/null
+++ b/drivers/mtd/tests/subpagetest.c
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2006-2007 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Test sub-page read and write on MTD device.
+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+
+static int dev = -EINVAL;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static struct mtd_info *mtd;
+static unsigned char *writebuf;
+static unsigned char *readbuf;
+static unsigned char *bbt;
+
+static int subpgsize;
+static int bufsize;
+static int ebcnt;
+static int pgcnt;
+static int errcnt;
+static struct rnd_state rnd_state;
+
+static inline void clear_data(unsigned char *buf, size_t len)
+{
+	memset(buf, 0, len);
+}
+
+static int erase_eraseblock(int ebnum)
+{
+	int err;
+	struct erase_info ei;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	memset(&ei, 0, sizeof(struct erase_info));
+	ei.mtd  = mtd;
+	ei.addr = addr;
+	ei.len  = mtd->erasesize;
+
+	err = mtd_erase(mtd, &ei);
+	if (err) {
+		pr_err("error %d while erasing EB %d\n", err, ebnum);
+		return err;
+	}
+
+	if (ei.state == MTD_ERASE_FAILED) {
+		pr_err("some erase error occurred at EB %d\n",
+		       ebnum);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int erase_whole_device(void)
+{
+	int err;
+	unsigned int i;
+
+	pr_info("erasing whole device\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = erase_eraseblock(i);
+		if (err)
+			return err;
+		cond_resched();
+	}
+	pr_info("erased %u eraseblocks\n", i);
+	return 0;
+}
+
+static int write_eraseblock(int ebnum)
+{
+	size_t written;
+	int err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
+	err = mtd_write(mtd, addr, subpgsize, &written, writebuf);
+	if (unlikely(err || written != subpgsize)) {
+		pr_err("error: write failed at %#llx\n",
+		       (long long)addr);
+		if (written != subpgsize) {
+			pr_err("  write size: %#x\n", subpgsize);
+			pr_err("  written: %#zx\n", written);
+		}
+		return err ? err : -1;
+	}
+
+	addr += subpgsize;
+
+	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
+	err = mtd_write(mtd, addr, subpgsize, &written, writebuf);
+	if (unlikely(err || written != subpgsize)) {
+		pr_err("error: write failed at %#llx\n",
+		       (long long)addr);
+		if (written != subpgsize) {
+			pr_err("  write size: %#x\n", subpgsize);
+			pr_err("  written: %#zx\n", written);
+		}
+		return err ? err : -1;
+	}
+
+	return err;
+}
+
+static int write_eraseblock2(int ebnum)
+{
+	size_t written;
+	int err = 0, k;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	for (k = 1; k < 33; ++k) {
+		if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
+			break;
+		prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
+		err = mtd_write(mtd, addr, subpgsize * k, &written, writebuf);
+		if (unlikely(err || written != subpgsize * k)) {
+			pr_err("error: write failed at %#llx\n",
+			       (long long)addr);
+			if (written != subpgsize) {
+				pr_err("  write size: %#x\n",
+				       subpgsize * k);
+				pr_err("  written: %#08zx\n",
+				       written);
+			}
+			return err ? err : -1;
+		}
+		addr += subpgsize * k;
+	}
+
+	return err;
+}
+
+static void print_subpage(unsigned char *p)
+{
+	int i, j;
+
+	for (i = 0; i < subpgsize; ) {
+		for (j = 0; i < subpgsize && j < 32; ++i, ++j)
+			printk("%02x", *p++);
+		printk("\n");
+	}
+}
+
+static int verify_eraseblock(int ebnum)
+{
+	size_t read;
+	int err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
+	clear_data(readbuf, subpgsize);
+	err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
+	if (unlikely(err || read != subpgsize)) {
+		if (mtd_is_bitflip(err) && read == subpgsize) {
+			pr_info("ECC correction at %#llx\n",
+			       (long long)addr);
+			err = 0;
+		} else {
+			pr_err("error: read failed at %#llx\n",
+			       (long long)addr);
+			return err ? err : -1;
+		}
+	}
+	if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
+		pr_err("error: verify failed at %#llx\n",
+		       (long long)addr);
+		pr_info("------------- written----------------\n");
+		print_subpage(writebuf);
+		pr_info("------------- read ------------------\n");
+		print_subpage(readbuf);
+		pr_info("-------------------------------------\n");
+		errcnt += 1;
+	}
+
+	addr += subpgsize;
+
+	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
+	clear_data(readbuf, subpgsize);
+	err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
+	if (unlikely(err || read != subpgsize)) {
+		if (mtd_is_bitflip(err) && read == subpgsize) {
+			pr_info("ECC correction at %#llx\n",
+			       (long long)addr);
+			err = 0;
+		} else {
+			pr_err("error: read failed at %#llx\n",
+			       (long long)addr);
+			return err ? err : -1;
+		}
+	}
+	if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
+		pr_info("error: verify failed at %#llx\n",
+		       (long long)addr);
+		pr_info("------------- written----------------\n");
+		print_subpage(writebuf);
+		pr_info("------------- read ------------------\n");
+		print_subpage(readbuf);
+		pr_info("-------------------------------------\n");
+		errcnt += 1;
+	}
+
+	return err;
+}
+
+static int verify_eraseblock2(int ebnum)
+{
+	size_t read;
+	int err = 0, k;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	for (k = 1; k < 33; ++k) {
+		if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
+			break;
+		prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
+		clear_data(readbuf, subpgsize * k);
+		err = mtd_read(mtd, addr, subpgsize * k, &read, readbuf);
+		if (unlikely(err || read != subpgsize * k)) {
+			if (mtd_is_bitflip(err) && read == subpgsize * k) {
+				pr_info("ECC correction at %#llx\n",
+				       (long long)addr);
+				err = 0;
+			} else {
+				pr_err("error: read failed at "
+				       "%#llx\n", (long long)addr);
+				return err ? err : -1;
+			}
+		}
+		if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) {
+			pr_err("error: verify failed at %#llx\n",
+			       (long long)addr);
+			errcnt += 1;
+		}
+		addr += subpgsize * k;
+	}
+
+	return err;
+}
+
+static int verify_eraseblock_ff(int ebnum)
+{
+	uint32_t j;
+	size_t read;
+	int err = 0;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	memset(writebuf, 0xff, subpgsize);
+	for (j = 0; j < mtd->erasesize / subpgsize; ++j) {
+		clear_data(readbuf, subpgsize);
+		err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
+		if (unlikely(err || read != subpgsize)) {
+			if (mtd_is_bitflip(err) && read == subpgsize) {
+				pr_info("ECC correction at %#llx\n",
+				       (long long)addr);
+				err = 0;
+			} else {
+				pr_err("error: read failed at "
+				       "%#llx\n", (long long)addr);
+				return err ? err : -1;
+			}
+		}
+		if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
+			pr_err("error: verify 0xff failed at "
+			       "%#llx\n", (long long)addr);
+			errcnt += 1;
+		}
+		addr += subpgsize;
+	}
+
+	return err;
+}
+
+static int verify_all_eraseblocks_ff(void)
+{
+	int err;
+	unsigned int i;
+
+	pr_info("verifying all eraseblocks for 0xff\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = verify_eraseblock_ff(i);
+		if (err)
+			return err;
+		if (i % 256 == 0)
+			pr_info("verified up to eraseblock %u\n", i);
+		cond_resched();
+	}
+	pr_info("verified %u eraseblocks\n", i);
+	return 0;
+}
+
+static int is_block_bad(int ebnum)
+{
+	loff_t addr = ebnum * mtd->erasesize;
+	int ret;
+
+	ret = mtd_block_isbad(mtd, addr);
+	if (ret)
+		pr_info("block %d is bad\n", ebnum);
+	return ret;
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+	int i, bad = 0;
+
+	bbt = kzalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		return -ENOMEM;
+
+	pr_info("scanning for bad eraseblocks\n");
+	for (i = 0; i < ebcnt; ++i) {
+		bbt[i] = is_block_bad(i) ? 1 : 0;
+		if (bbt[i])
+			bad += 1;
+		cond_resched();
+	}
+	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
+	return 0;
+}
+
+static int __init mtd_subpagetest_init(void)
+{
+	int err = 0;
+	uint32_t i;
+	uint64_t tmp;
+
+	printk(KERN_INFO "\n");
+	printk(KERN_INFO "=================================================\n");
+
+	if (dev < 0) {
+		pr_info("Please specify a valid mtd-device via module parameter\n");
+		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
+		return -EINVAL;
+	}
+
+	pr_info("MTD device: %d\n", dev);
+
+	mtd = get_mtd_device(NULL, dev);
+	if (IS_ERR(mtd)) {
+		err = PTR_ERR(mtd);
+		pr_err("error: cannot get MTD device\n");
+		return err;
+	}
+
+	if (mtd->type != MTD_NANDFLASH) {
+		pr_info("this test requires NAND flash\n");
+		goto out;
+	}
+
+	subpgsize = mtd->writesize >> mtd->subpage_sft;
+	tmp = mtd->size;
+	do_div(tmp, mtd->erasesize);
+	ebcnt = tmp;
+	pgcnt = mtd->erasesize / mtd->writesize;
+
+	pr_info("MTD device size %llu, eraseblock size %u, "
+	       "page size %u, subpage size %u, count of eraseblocks %u, "
+	       "pages per eraseblock %u, OOB size %u\n",
+	       (unsigned long long)mtd->size, mtd->erasesize,
+	       mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize);
+
+	err = -ENOMEM;
+	bufsize = subpgsize * 32;
+	writebuf = kmalloc(bufsize, GFP_KERNEL);
+	if (!writebuf)
+		goto out;
+	readbuf = kmalloc(bufsize, GFP_KERNEL);
+	if (!readbuf)
+		goto out;
+
+	err = scan_for_bad_eraseblocks();
+	if (err)
+		goto out;
+
+	err = erase_whole_device();
+	if (err)
+		goto out;
+
+	pr_info("writing whole device\n");
+	prandom_seed_state(&rnd_state, 1);
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = write_eraseblock(i);
+		if (unlikely(err))
+			goto out;
+		if (i % 256 == 0)
+			pr_info("written up to eraseblock %u\n", i);
+		cond_resched();
+	}
+	pr_info("written %u eraseblocks\n", i);
+
+	prandom_seed_state(&rnd_state, 1);
+	pr_info("verifying all eraseblocks\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = verify_eraseblock(i);
+		if (unlikely(err))
+			goto out;
+		if (i % 256 == 0)
+			pr_info("verified up to eraseblock %u\n", i);
+		cond_resched();
+	}
+	pr_info("verified %u eraseblocks\n", i);
+
+	err = erase_whole_device();
+	if (err)
+		goto out;
+
+	err = verify_all_eraseblocks_ff();
+	if (err)
+		goto out;
+
+	/* Write all eraseblocks */
+	prandom_seed_state(&rnd_state, 3);
+	pr_info("writing whole device\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = write_eraseblock2(i);
+		if (unlikely(err))
+			goto out;
+		if (i % 256 == 0)
+			pr_info("written up to eraseblock %u\n", i);
+		cond_resched();
+	}
+	pr_info("written %u eraseblocks\n", i);
+
+	/* Check all eraseblocks */
+	prandom_seed_state(&rnd_state, 3);
+	pr_info("verifying all eraseblocks\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = verify_eraseblock2(i);
+		if (unlikely(err))
+			goto out;
+		if (i % 256 == 0)
+			pr_info("verified up to eraseblock %u\n", i);
+		cond_resched();
+	}
+	pr_info("verified %u eraseblocks\n", i);
+
+	err = erase_whole_device();
+	if (err)
+		goto out;
+
+	err = verify_all_eraseblocks_ff();
+	if (err)
+		goto out;
+
+	pr_info("finished with %d errors\n", errcnt);
+
+out:
+	kfree(bbt);
+	kfree(readbuf);
+	kfree(writebuf);
+	put_mtd_device(mtd);
+	if (err)
+		pr_info("error %d occurred\n", err);
+	printk(KERN_INFO "=================================================\n");
+	return err;
+}
+module_init(mtd_subpagetest_init);
+
+static void __exit mtd_subpagetest_exit(void)
+{
+	return;
+}
+module_exit(mtd_subpagetest_exit);
+
+MODULE_DESCRIPTION("Subpage test module");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/torturetest.c b/drivers/mtd/tests/torturetest.c
new file mode 100644
index 0000000..3a9f6a6
--- /dev/null
+++ b/drivers/mtd/tests/torturetest.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2006-2008 Artem Bityutskiy
+ * Copyright (C) 2006-2008 Jarkko Lavinen
+ * Copyright (C) 2006-2008 Adrian Hunter
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: Artem Bityutskiy, Jarkko Lavinen, Adria Hunter
+ *
+ * WARNING: this test program may kill your flash and your device. Do not
+ * use it unless you know what you do. Authors are not responsible for any
+ * damage caused by this program.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#define RETRIES 3
+
+static int eb = 8;
+module_param(eb, int, S_IRUGO);
+MODULE_PARM_DESC(eb, "eraseblock number within the selected MTD device");
+
+static int ebcnt = 32;
+module_param(ebcnt, int, S_IRUGO);
+MODULE_PARM_DESC(ebcnt, "number of consecutive eraseblocks to torture");
+
+static int pgcnt;
+module_param(pgcnt, int, S_IRUGO);
+MODULE_PARM_DESC(pgcnt, "number of pages per eraseblock to torture (0 => all)");
+
+static int dev = -EINVAL;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static int gran = 512;
+module_param(gran, int, S_IRUGO);
+MODULE_PARM_DESC(gran, "how often the status information should be printed");
+
+static int check = 1;
+module_param(check, int, S_IRUGO);
+MODULE_PARM_DESC(check, "if the written data should be checked");
+
+static unsigned int cycles_count;
+module_param(cycles_count, uint, S_IRUGO);
+MODULE_PARM_DESC(cycles_count, "how many erase cycles to do "
+			       "(infinite by default)");
+
+static struct mtd_info *mtd;
+
+/* This buffer contains 0x555555...0xAAAAAA... pattern */
+static unsigned char *patt_5A5;
+/* This buffer contains 0xAAAAAA...0x555555... pattern */
+static unsigned char *patt_A5A;
+/* This buffer contains all 0xFF bytes */
+static unsigned char *patt_FF;
+/* This a temporary buffer is use when checking data */
+static unsigned char *check_buf;
+/* How many erase cycles were done */
+static unsigned int erase_cycles;
+
+static int pgsize;
+static struct timeval start, finish;
+
+static void report_corrupt(unsigned char *read, unsigned char *written);
+
+static inline void start_timing(void)
+{
+	do_gettimeofday(&start);
+}
+
+static inline void stop_timing(void)
+{
+	do_gettimeofday(&finish);
+}
+
+/*
+ * Erase eraseblock number @ebnum.
+ */
+static inline int erase_eraseblock(int ebnum)
+{
+	int err;
+	struct erase_info ei;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	memset(&ei, 0, sizeof(struct erase_info));
+	ei.mtd  = mtd;
+	ei.addr = addr;
+	ei.len  = mtd->erasesize;
+
+	err = mtd_erase(mtd, &ei);
+	if (err) {
+		pr_err("error %d while erasing EB %d\n", err, ebnum);
+		return err;
+	}
+
+	if (ei.state == MTD_ERASE_FAILED) {
+		pr_err("some erase error occurred at EB %d\n",
+		       ebnum);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * Check that the contents of eraseblock number @enbum is equivalent to the
+ * @buf buffer.
+ */
+static inline int check_eraseblock(int ebnum, unsigned char *buf)
+{
+	int err, retries = 0;
+	size_t read;
+	loff_t addr = ebnum * mtd->erasesize;
+	size_t len = mtd->erasesize;
+
+	if (pgcnt) {
+		addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize;
+		len = pgcnt * pgsize;
+	}
+
+retry:
+	err = mtd_read(mtd, addr, len, &read, check_buf);
+	if (mtd_is_bitflip(err))
+		pr_err("single bit flip occurred at EB %d "
+		       "MTD reported that it was fixed.\n", ebnum);
+	else if (err) {
+		pr_err("error %d while reading EB %d, "
+		       "read %zd\n", err, ebnum, read);
+		return err;
+	}
+
+	if (read != len) {
+		pr_err("failed to read %zd bytes from EB %d, "
+		       "read only %zd, but no error reported\n",
+		       len, ebnum, read);
+		return -EIO;
+	}
+
+	if (memcmp(buf, check_buf, len)) {
+		pr_err("read wrong data from EB %d\n", ebnum);
+		report_corrupt(check_buf, buf);
+
+		if (retries++ < RETRIES) {
+			/* Try read again */
+			yield();
+			pr_info("re-try reading data from EB %d\n",
+			       ebnum);
+			goto retry;
+		} else {
+			pr_info("retried %d times, still errors, "
+			       "give-up\n", RETRIES);
+			return -EINVAL;
+		}
+	}
+
+	if (retries != 0)
+		pr_info("only attempt number %d was OK (!!!)\n",
+		       retries);
+
+	return 0;
+}
+
+static inline int write_pattern(int ebnum, void *buf)
+{
+	int err;
+	size_t written;
+	loff_t addr = ebnum * mtd->erasesize;
+	size_t len = mtd->erasesize;
+
+	if (pgcnt) {
+		addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize;
+		len = pgcnt * pgsize;
+	}
+	err = mtd_write(mtd, addr, len, &written, buf);
+	if (err) {
+		pr_err("error %d while writing EB %d, written %zd"
+		      " bytes\n", err, ebnum, written);
+		return err;
+	}
+	if (written != len) {
+		pr_info("written only %zd bytes of %zd, but no error"
+		       " reported\n", written, len);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int __init tort_init(void)
+{
+	int err = 0, i, infinite = !cycles_count;
+	int *bad_ebs;
+
+	printk(KERN_INFO "\n");
+	printk(KERN_INFO "=================================================\n");
+	pr_info("Warning: this program is trying to wear out your "
+	       "flash, stop it if this is not wanted.\n");
+
+	if (dev < 0) {
+		pr_info("Please specify a valid mtd-device via module parameter\n");
+		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
+		return -EINVAL;
+	}
+
+	pr_info("MTD device: %d\n", dev);
+	pr_info("torture %d eraseblocks (%d-%d) of mtd%d\n",
+	       ebcnt, eb, eb + ebcnt - 1, dev);
+	if (pgcnt)
+		pr_info("torturing just %d pages per eraseblock\n",
+			pgcnt);
+	pr_info("write verify %s\n", check ? "enabled" : "disabled");
+
+	mtd = get_mtd_device(NULL, dev);
+	if (IS_ERR(mtd)) {
+		err = PTR_ERR(mtd);
+		pr_err("error: cannot get MTD device\n");
+		return err;
+	}
+
+	if (mtd->writesize == 1) {
+		pr_info("not NAND flash, assume page size is 512 "
+		       "bytes.\n");
+		pgsize = 512;
+	} else
+		pgsize = mtd->writesize;
+
+	if (pgcnt && (pgcnt > mtd->erasesize / pgsize || pgcnt < 0)) {
+		pr_err("error: invalid pgcnt value %d\n", pgcnt);
+		goto out_mtd;
+	}
+
+	err = -ENOMEM;
+	patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL);
+	if (!patt_5A5)
+		goto out_mtd;
+
+	patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL);
+	if (!patt_A5A)
+		goto out_patt_5A5;
+
+	patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL);
+	if (!patt_FF)
+		goto out_patt_A5A;
+
+	check_buf = kmalloc(mtd->erasesize, GFP_KERNEL);
+	if (!check_buf)
+		goto out_patt_FF;
+
+	bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL);
+	if (!bad_ebs)
+		goto out_check_buf;
+
+	err = 0;
+
+	/* Initialize patterns */
+	memset(patt_FF, 0xFF, mtd->erasesize);
+	for (i = 0; i < mtd->erasesize / pgsize; i++) {
+		if (!(i & 1)) {
+			memset(patt_5A5 + i * pgsize, 0x55, pgsize);
+			memset(patt_A5A + i * pgsize, 0xAA, pgsize);
+		} else {
+			memset(patt_5A5 + i * pgsize, 0xAA, pgsize);
+			memset(patt_A5A + i * pgsize, 0x55, pgsize);
+		}
+	}
+
+	/*
+	 * Check if there is a bad eraseblock among those we are going to test.
+	 */
+	if (mtd_can_have_bb(mtd)) {
+		for (i = eb; i < eb + ebcnt; i++) {
+			err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize);
+
+			if (err < 0) {
+				pr_info("block_isbad() returned %d "
+				       "for EB %d\n", err, i);
+				goto out;
+			}
+
+			if (err) {
+				pr_err("EB %d is bad. Skip it.\n", i);
+				bad_ebs[i - eb] = 1;
+			}
+		}
+	}
+
+	start_timing();
+	while (1) {
+		int i;
+		void *patt;
+
+		/* Erase all eraseblocks */
+		for (i = eb; i < eb + ebcnt; i++) {
+			if (bad_ebs[i - eb])
+				continue;
+			err = erase_eraseblock(i);
+			if (err)
+				goto out;
+			cond_resched();
+		}
+
+		/* Check if the eraseblocks contain only 0xFF bytes */
+		if (check) {
+			for (i = eb; i < eb + ebcnt; i++) {
+				if (bad_ebs[i - eb])
+					continue;
+				err = check_eraseblock(i, patt_FF);
+				if (err) {
+					pr_info("verify failed"
+					       " for 0xFF... pattern\n");
+					goto out;
+				}
+				cond_resched();
+			}
+		}
+
+		/* Write the pattern */
+		for (i = eb; i < eb + ebcnt; i++) {
+			if (bad_ebs[i - eb])
+				continue;
+			if ((eb + erase_cycles) & 1)
+				patt = patt_5A5;
+			else
+				patt = patt_A5A;
+			err = write_pattern(i, patt);
+			if (err)
+				goto out;
+			cond_resched();
+		}
+
+		/* Verify what we wrote */
+		if (check) {
+			for (i = eb; i < eb + ebcnt; i++) {
+				if (bad_ebs[i - eb])
+					continue;
+				if ((eb + erase_cycles) & 1)
+					patt = patt_5A5;
+				else
+					patt = patt_A5A;
+				err = check_eraseblock(i, patt);
+				if (err) {
+					pr_info("verify failed for %s"
+					       " pattern\n",
+					       ((eb + erase_cycles) & 1) ?
+					       "0x55AA55..." : "0xAA55AA...");
+					goto out;
+				}
+				cond_resched();
+			}
+		}
+
+		erase_cycles += 1;
+
+		if (erase_cycles % gran == 0) {
+			long ms;
+
+			stop_timing();
+			ms = (finish.tv_sec - start.tv_sec) * 1000 +
+			     (finish.tv_usec - start.tv_usec) / 1000;
+			pr_info("%08u erase cycles done, took %lu "
+			       "milliseconds (%lu seconds)\n",
+			       erase_cycles, ms, ms / 1000);
+			start_timing();
+		}
+
+		if (!infinite && --cycles_count == 0)
+			break;
+	}
+out:
+
+	pr_info("finished after %u erase cycles\n",
+	       erase_cycles);
+	kfree(bad_ebs);
+out_check_buf:
+	kfree(check_buf);
+out_patt_FF:
+	kfree(patt_FF);
+out_patt_A5A:
+	kfree(patt_A5A);
+out_patt_5A5:
+	kfree(patt_5A5);
+out_mtd:
+	put_mtd_device(mtd);
+	if (err)
+		pr_info("error %d occurred during torturing\n", err);
+	printk(KERN_INFO "=================================================\n");
+	return err;
+}
+module_init(tort_init);
+
+static void __exit tort_exit(void)
+{
+	return;
+}
+module_exit(tort_exit);
+
+static int countdiffs(unsigned char *buf, unsigned char *check_buf,
+		      unsigned offset, unsigned len, unsigned *bytesp,
+		      unsigned *bitsp);
+static void print_bufs(unsigned char *read, unsigned char *written, int start,
+		       int len);
+
+/*
+ * Report the detailed information about how the read EB differs from what was
+ * written.
+ */
+static void report_corrupt(unsigned char *read, unsigned char *written)
+{
+	int i;
+	int bytes, bits, pages, first;
+	int offset, len;
+	size_t check_len = mtd->erasesize;
+
+	if (pgcnt)
+		check_len = pgcnt * pgsize;
+
+	bytes = bits = pages = 0;
+	for (i = 0; i < check_len; i += pgsize)
+		if (countdiffs(written, read, i, pgsize, &bytes,
+			       &bits) >= 0)
+			pages++;
+
+	pr_info("verify fails on %d pages, %d bytes/%d bits\n",
+	       pages, bytes, bits);
+	pr_info("The following is a list of all differences between"
+	       " what was read from flash and what was expected\n");
+
+	for (i = 0; i < check_len; i += pgsize) {
+		cond_resched();
+		bytes = bits = 0;
+		first = countdiffs(written, read, i, pgsize, &bytes,
+				   &bits);
+		if (first < 0)
+			continue;
+
+		printk("-------------------------------------------------------"
+		       "----------------------------------\n");
+
+		pr_info("Page %zd has %d bytes/%d bits failing verify,"
+		       " starting at offset 0x%x\n",
+		       (mtd->erasesize - check_len + i) / pgsize,
+		       bytes, bits, first);
+
+		offset = first & ~0x7;
+		len = ((first + bytes) | 0x7) + 1 - offset;
+
+		print_bufs(read, written, offset, len);
+	}
+}
+
+static void print_bufs(unsigned char *read, unsigned char *written, int start,
+		       int len)
+{
+	int i = 0, j1, j2;
+	char *diff;
+
+	printk("Offset       Read                          Written\n");
+	while (i < len) {
+		printk("0x%08x: ", start + i);
+		diff = "   ";
+		for (j1 = 0; j1 < 8 && i + j1 < len; j1++) {
+			printk(" %02x", read[start + i + j1]);
+			if (read[start + i + j1] != written[start + i + j1])
+				diff = "***";
+		}
+
+		while (j1 < 8) {
+			printk(" ");
+			j1 += 1;
+		}
+
+		printk("  %s ", diff);
+
+		for (j2 = 0; j2 < 8 && i + j2 < len; j2++)
+			printk(" %02x", written[start + i + j2]);
+		printk("\n");
+		i += 8;
+	}
+}
+
+/*
+ * Count the number of differing bytes and bits and return the first differing
+ * offset.
+ */
+static int countdiffs(unsigned char *buf, unsigned char *check_buf,
+		      unsigned offset, unsigned len, unsigned *bytesp,
+		      unsigned *bitsp)
+{
+	unsigned i, bit;
+	int first = -1;
+
+	for (i = offset; i < offset + len; i++)
+		if (buf[i] != check_buf[i]) {
+			first = i;
+			break;
+		}
+
+	while (i < offset + len) {
+		if (buf[i] != check_buf[i]) {
+			(*bytesp)++;
+			bit = 1;
+			while (bit < 256) {
+				if ((buf[i] & bit) != (check_buf[i] & bit))
+					(*bitsp)++;
+				bit <<= 1;
+			}
+		}
+		i++;
+	}
+
+	return first;
+}
+
+MODULE_DESCRIPTION("Eraseblock torturing module");
+MODULE_AUTHOR("Artem Bityutskiy, Jarkko Lavinen, Adrian Hunter");
+MODULE_LICENSE("GPL");
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH -next v2 03/10] mtd: mtd_oobtest: use mtd_test helpers
  2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 01/10] mtd: tests: introduce helper functions Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 02/10] mtd: tests: rename sources in order to link a helper object Akinobu Mita
@ 2013-08-03  9:52 ` Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 04/10] mtd: mtd_pagetest: " Akinobu Mita
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-08-03  9:52 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, Vikram Narayanan, Akinobu Mita, Adrian Hunter,
	Brian Norris, David Woodhouse

Use mtdtest_scan_for_bad_eraseblocks(), mtdtest_erase_good_eraseblocks(),
and mtdtest_erase_eraseblock() in mtd_test helpers.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
---
* Changes from v1
- follow the changes of helper functions

 drivers/mtd/tests/oobtest.c | 92 ++++++---------------------------------------
 1 file changed, 12 insertions(+), 80 deletions(-)

diff --git a/drivers/mtd/tests/oobtest.c b/drivers/mtd/tests/oobtest.c
index ab81e9a..ff35c46 100644
--- a/drivers/mtd/tests/oobtest.c
+++ b/drivers/mtd/tests/oobtest.c
@@ -31,6 +31,8 @@
 #include <linux/sched.h>
 #include <linux/random.h>
 
+#include "mtd_test.h"
+
 static int dev = -EINVAL;
 module_param(dev, int, S_IRUGO);
 MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -49,49 +51,6 @@ static int use_len_max;
 static int vary_offset;
 static struct rnd_state rnd_state;
 
-static int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (ei.state == MTD_ERASE_FAILED) {
-		pr_err("some erase error occurred at EB %d\n", ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int erase_whole_device(void)
-{
-	int err;
-	unsigned int i;
-
-	pr_info("erasing whole device\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = erase_eraseblock(i);
-		if (err)
-			return err;
-		cond_resched();
-	}
-	pr_info("erased %u eraseblocks\n", i);
-	return 0;
-}
-
 static void do_vary_offset(void)
 {
 	use_len -= 1;
@@ -304,36 +263,6 @@ static int verify_all_eraseblocks(void)
 	return 0;
 }
 
-static int is_block_bad(int ebnum)
-{
-	int ret;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kmalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	pr_info("scanning for bad eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		bbt[i] = is_block_bad(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-	return 0;
-}
-
 static int __init mtd_oobtest_init(void)
 {
 	int err = 0;
@@ -383,8 +312,11 @@ static int __init mtd_oobtest_init(void)
 	writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
 	if (!writebuf)
 		goto out;
+	bbt = kzalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		goto out;
 
-	err = scan_for_bad_eraseblocks();
+	err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
@@ -396,7 +328,7 @@ static int __init mtd_oobtest_init(void)
 	/* First test: write all OOB, read it back and verify */
 	pr_info("test 1 of 5\n");
 
-	err = erase_whole_device();
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
@@ -416,7 +348,7 @@ static int __init mtd_oobtest_init(void)
 	 */
 	pr_info("test 2 of 5\n");
 
-	err = erase_whole_device();
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
@@ -446,7 +378,7 @@ static int __init mtd_oobtest_init(void)
 	 */
 	pr_info("test 3 of 5\n");
 
-	err = erase_whole_device();
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
@@ -479,7 +411,7 @@ static int __init mtd_oobtest_init(void)
 	/* Fourth test: try to write off end of device */
 	pr_info("test 4 of 5\n");
 
-	err = erase_whole_device();
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
@@ -571,7 +503,7 @@ static int __init mtd_oobtest_init(void)
 			errcnt += 1;
 		}
 
-		err = erase_eraseblock(ebcnt - 1);
+		err = mtdtest_erase_eraseblock(mtd, ebcnt - 1);
 		if (err)
 			goto out;
 
@@ -620,7 +552,7 @@ static int __init mtd_oobtest_init(void)
 	pr_info("test 5 of 5\n");
 
 	/* Erase all eraseblocks */
-	err = erase_whole_device();
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH -next v2 04/10] mtd: mtd_pagetest: use mtd_test helpers
  2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
                   ` (2 preceding siblings ...)
  2013-08-03  9:52 ` [PATCH -next v2 03/10] mtd: mtd_oobtest: use mtd_test helpers Akinobu Mita
@ 2013-08-03  9:52 ` Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 05/10] mtd: mtd_readtest: " Akinobu Mita
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-08-03  9:52 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, Vikram Narayanan, Akinobu Mita, Adrian Hunter,
	Brian Norris, David Woodhouse

Use mtdtest_write(), mtdtest_read(), mtdtest_erase_eraseblock(),
mtdtest_scan_for_bad_eraseblocks(), and mtdtest_erase_good_eraseblocks()
in mtd_test helpers.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
---
* Changes from v1
- follow the changes of helper functions

 drivers/mtd/tests/pagetest.c | 199 ++++++++++++-------------------------------
 1 file changed, 55 insertions(+), 144 deletions(-)

diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c
index acd991f..22cc38c 100644
--- a/drivers/mtd/tests/pagetest.c
+++ b/drivers/mtd/tests/pagetest.c
@@ -31,6 +31,8 @@
 #include <linux/sched.h>
 #include <linux/random.h>
 
+#include "mtd_test.h"
+
 static int dev = -EINVAL;
 module_param(dev, int, S_IRUGO);
 MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -48,42 +50,15 @@ static int pgcnt;
 static int errcnt;
 static struct rnd_state rnd_state;
 
-static int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (ei.state == MTD_ERASE_FAILED) {
-		pr_err("some erase error occurred at EB %d\n",
-		       ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
 static int write_eraseblock(int ebnum)
 {
-	int err = 0;
-	size_t written;
+	int err;
 	loff_t addr = ebnum * mtd->erasesize;
 
 	prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
 	cond_resched();
-	err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf);
-	if (err || written != mtd->erasesize)
+	err = mtdtest_write(mtd, addr, mtd->erasesize, writebuf);
+	if (err)
 		pr_err("error: write failed at %#llx\n",
 		       (long long)addr);
 
@@ -93,7 +68,6 @@ static int write_eraseblock(int ebnum)
 static int verify_eraseblock(int ebnum)
 {
 	uint32_t j;
-	size_t read;
 	int err = 0, i;
 	loff_t addr0, addrn;
 	loff_t addr = ebnum * mtd->erasesize;
@@ -109,27 +83,21 @@ static int verify_eraseblock(int ebnum)
 	prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
 	for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
 		/* Do a read to set the internal dataRAMs to different data */
-		err = mtd_read(mtd, addr0, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
+		err = mtdtest_read(mtd, addr0, bufsize, twopages);
+		if (err) {
 			pr_err("error: read failed at %#llx\n",
 			       (long long)addr0);
 			return err;
 		}
-		err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
+		err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
+		if (err) {
 			pr_err("error: read failed at %#llx\n",
 			       (long long)(addrn - bufsize));
 			return err;
 		}
 		memset(twopages, 0, bufsize);
-		err = mtd_read(mtd, addr, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
+		err = mtdtest_read(mtd, addr, bufsize, twopages);
+		if (err) {
 			pr_err("error: read failed at %#llx\n",
 			       (long long)addr);
 			break;
@@ -145,27 +113,21 @@ static int verify_eraseblock(int ebnum)
 		struct rnd_state old_state = rnd_state;
 
 		/* Do a read to set the internal dataRAMs to different data */
-		err = mtd_read(mtd, addr0, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
+		err = mtdtest_read(mtd, addr0, bufsize, twopages);
+		if (err) {
 			pr_err("error: read failed at %#llx\n",
 			       (long long)addr0);
 			return err;
 		}
-		err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
+		err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
+		if (err) {
 			pr_err("error: read failed at %#llx\n",
 			       (long long)(addrn - bufsize));
 			return err;
 		}
 		memset(twopages, 0, bufsize);
-		err = mtd_read(mtd, addr, bufsize, &read, twopages);
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != bufsize) {
+		err = mtdtest_read(mtd, addr, bufsize, twopages);
+		if (err) {
 			pr_err("error: read failed at %#llx\n",
 			       (long long)addr);
 			return err;
@@ -184,7 +146,6 @@ static int verify_eraseblock(int ebnum)
 
 static int crosstest(void)
 {
-	size_t read;
 	int err = 0, i;
 	loff_t addr, addr0, addrn;
 	unsigned char *pp1, *pp2, *pp3, *pp4;
@@ -208,10 +169,8 @@ static int crosstest(void)
 
 	/* Read 2nd-to-last page to pp1 */
 	addr = addrn - pgsize - pgsize;
-	err = mtd_read(mtd, addr, pgsize, &read, pp1);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
+	err = mtdtest_read(mtd, addr, pgsize, pp1);
+	if (err) {
 		pr_err("error: read failed at %#llx\n",
 		       (long long)addr);
 		kfree(pp1);
@@ -220,10 +179,8 @@ static int crosstest(void)
 
 	/* Read 3rd-to-last page to pp1 */
 	addr = addrn - pgsize - pgsize - pgsize;
-	err = mtd_read(mtd, addr, pgsize, &read, pp1);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
+	err = mtdtest_read(mtd, addr, pgsize, pp1);
+	if (err) {
 		pr_err("error: read failed at %#llx\n",
 		       (long long)addr);
 		kfree(pp1);
@@ -233,10 +190,8 @@ static int crosstest(void)
 	/* Read first page to pp2 */
 	addr = addr0;
 	pr_info("reading page at %#llx\n", (long long)addr);
-	err = mtd_read(mtd, addr, pgsize, &read, pp2);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
+	err = mtdtest_read(mtd, addr, pgsize, pp2);
+	if (err) {
 		pr_err("error: read failed at %#llx\n",
 		       (long long)addr);
 		kfree(pp1);
@@ -246,10 +201,8 @@ static int crosstest(void)
 	/* Read last page to pp3 */
 	addr = addrn - pgsize;
 	pr_info("reading page at %#llx\n", (long long)addr);
-	err = mtd_read(mtd, addr, pgsize, &read, pp3);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
+	err = mtdtest_read(mtd, addr, pgsize, pp3);
+	if (err) {
 		pr_err("error: read failed at %#llx\n",
 		       (long long)addr);
 		kfree(pp1);
@@ -259,10 +212,8 @@ static int crosstest(void)
 	/* Read first page again to pp4 */
 	addr = addr0;
 	pr_info("reading page at %#llx\n", (long long)addr);
-	err = mtd_read(mtd, addr, pgsize, &read, pp4);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
+	err = mtdtest_read(mtd, addr, pgsize, pp4);
+	if (err) {
 		pr_err("error: read failed at %#llx\n",
 		       (long long)addr);
 		kfree(pp1);
@@ -283,7 +234,6 @@ static int crosstest(void)
 
 static int erasecrosstest(void)
 {
-	size_t read, written;
 	int err = 0, i, ebnum, ebnum2;
 	loff_t addr0;
 	char *readbuf = twopages;
@@ -302,26 +252,24 @@ static int erasecrosstest(void)
 		ebnum2 -= 1;
 
 	pr_info("erasing block %d\n", ebnum);
-	err = erase_eraseblock(ebnum);
+	err = mtdtest_erase_eraseblock(mtd, ebnum);
 	if (err)
 		return err;
 
 	pr_info("writing 1st page of block %d\n", ebnum);
 	prandom_bytes_state(&rnd_state, writebuf, pgsize);
 	strcpy(writebuf, "There is no data like this!");
-	err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
-	if (err || written != pgsize) {
+	err = mtdtest_write(mtd, addr0, pgsize, writebuf);
+	if (err) {
 		pr_info("error: write failed at %#llx\n",
 		       (long long)addr0);
-		return err ? err : -1;
+		return err;
 	}
 
 	pr_info("reading 1st page of block %d\n", ebnum);
 	memset(readbuf, 0, pgsize);
-	err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
+	err = mtdtest_read(mtd, addr0, pgsize, readbuf);
+	if (err) {
 		pr_err("error: read failed at %#llx\n",
 		       (long long)addr0);
 		return err ? err : -1;
@@ -335,31 +283,29 @@ static int erasecrosstest(void)
 	}
 
 	pr_info("erasing block %d\n", ebnum);
-	err = erase_eraseblock(ebnum);
+	err = mtdtest_erase_eraseblock(mtd, ebnum);
 	if (err)
 		return err;
 
 	pr_info("writing 1st page of block %d\n", ebnum);
 	prandom_bytes_state(&rnd_state, writebuf, pgsize);
 	strcpy(writebuf, "There is no data like this!");
-	err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
-	if (err || written != pgsize) {
+	err = mtdtest_write(mtd, addr0, pgsize, writebuf);
+	if (err) {
 		pr_err("error: write failed at %#llx\n",
 		       (long long)addr0);
-		return err ? err : -1;
+		return err;
 	}
 
 	pr_info("erasing block %d\n", ebnum2);
-	err = erase_eraseblock(ebnum2);
+	err = mtdtest_erase_eraseblock(mtd, ebnum2);
 	if (err)
 		return err;
 
 	pr_info("reading 1st page of block %d\n", ebnum);
 	memset(readbuf, 0, pgsize);
-	err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
+	err = mtdtest_read(mtd, addr0, pgsize, readbuf);
+	if (err) {
 		pr_err("error: read failed at %#llx\n",
 		       (long long)addr0);
 		return err ? err : -1;
@@ -379,7 +325,6 @@ static int erasecrosstest(void)
 
 static int erasetest(void)
 {
-	size_t read, written;
 	int err = 0, i, ebnum, ok = 1;
 	loff_t addr0;
 
@@ -393,29 +338,27 @@ static int erasetest(void)
 	}
 
 	pr_info("erasing block %d\n", ebnum);
-	err = erase_eraseblock(ebnum);
+	err = mtdtest_erase_eraseblock(mtd, ebnum);
 	if (err)
 		return err;
 
 	pr_info("writing 1st page of block %d\n", ebnum);
 	prandom_bytes_state(&rnd_state, writebuf, pgsize);
-	err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
-	if (err || written != pgsize) {
+	err = mtdtest_write(mtd, addr0, pgsize, writebuf);
+	if (err) {
 		pr_err("error: write failed at %#llx\n",
 		       (long long)addr0);
-		return err ? err : -1;
+		return err;
 	}
 
 	pr_info("erasing block %d\n", ebnum);
-	err = erase_eraseblock(ebnum);
+	err = mtdtest_erase_eraseblock(mtd, ebnum);
 	if (err)
 		return err;
 
 	pr_info("reading 1st page of block %d\n", ebnum);
-	err = mtd_read(mtd, addr0, pgsize, &read, twopages);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != pgsize) {
+	err = mtdtest_read(mtd, addr0, pgsize, twopages);
+	if (err) {
 		pr_err("error: read failed at %#llx\n",
 		       (long long)addr0);
 		return err ? err : -1;
@@ -438,36 +381,6 @@ static int erasetest(void)
 	return err;
 }
 
-static int is_block_bad(int ebnum)
-{
-	loff_t addr = ebnum * mtd->erasesize;
-	int ret;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kzalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	pr_info("scanning for bad eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		bbt[i] = is_block_bad(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-	return 0;
-}
-
 static int __init mtd_pagetest_init(void)
 {
 	int err = 0;
@@ -521,21 +434,19 @@ static int __init mtd_pagetest_init(void)
 	if (!boundary)
 		goto out;
 
-	err = scan_for_bad_eraseblocks();
+	bbt = kzalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		goto out;
+	err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
 	/* Erase all eraseblocks */
 	pr_info("erasing whole device\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = erase_eraseblock(i);
-		if (err)
-			goto out;
-		cond_resched();
-	}
-	pr_info("erased %u eraseblocks\n", i);
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
+	if (err)
+		goto out;
+	pr_info("erased %u eraseblocks\n", ebcnt);
 
 	/* Write all eraseblocks */
 	prandom_seed_state(&rnd_state, 1);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH -next v2 05/10] mtd: mtd_readtest: use mtd_test helpers
  2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
                   ` (3 preceding siblings ...)
  2013-08-03  9:52 ` [PATCH -next v2 04/10] mtd: mtd_pagetest: " Akinobu Mita
@ 2013-08-03  9:52 ` Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 06/10] mtd: mtd_speedtest: " Akinobu Mita
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-08-03  9:52 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, Vikram Narayanan, Akinobu Mita, Adrian Hunter,
	Brian Norris, David Woodhouse

Use mtdtest_read() and mtdtest_scan_for_bad_eraseblocks() in mtd_test
helpers.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
---
* Changes from v1
- follow the changes of helper functions
- fix accidentally changed argument of memset() in mtd_readtest module

 drivers/mtd/tests/readtest.c | 49 ++++++++------------------------------------
 1 file changed, 8 insertions(+), 41 deletions(-)

diff --git a/drivers/mtd/tests/readtest.c b/drivers/mtd/tests/readtest.c
index 2cdd0c4..ffd50d6 100644
--- a/drivers/mtd/tests/readtest.c
+++ b/drivers/mtd/tests/readtest.c
@@ -29,6 +29,8 @@
 #include <linux/slab.h>
 #include <linux/sched.h>
 
+#include "mtd_test.h"
+
 static int dev = -EINVAL;
 module_param(dev, int, S_IRUGO);
 MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -44,7 +46,6 @@ static int pgcnt;
 
 static int read_eraseblock_by_page(int ebnum)
 {
-	size_t read;
 	int i, ret, err = 0;
 	loff_t addr = ebnum * mtd->erasesize;
 	void *buf = iobuf;
@@ -52,16 +53,12 @@ static int read_eraseblock_by_page(int ebnum)
 
 	for (i = 0; i < pgcnt; i++) {
 		memset(buf, 0 , pgsize);
-		ret = mtd_read(mtd, addr, pgsize, &read, buf);
-		if (ret == -EUCLEAN)
-			ret = 0;
-		if (ret || read != pgsize) {
+		ret = mtdtest_read(mtd, addr, pgsize, buf);
+		if (ret) {
 			pr_err("error: read failed at %#llx\n",
 			       (long long)addr);
 			if (!err)
 				err = ret;
-			if (!err)
-				err = -EINVAL;
 		}
 		if (mtd->oobsize) {
 			struct mtd_oob_ops ops;
@@ -127,39 +124,6 @@ static void dump_eraseblock(int ebnum)
 		}
 }
 
-static int is_block_bad(int ebnum)
-{
-	loff_t addr = ebnum * mtd->erasesize;
-	int ret;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kzalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	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(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-	return 0;
-}
-
 static int __init mtd_readtest_init(void)
 {
 	uint64_t tmp;
@@ -208,7 +172,10 @@ static int __init mtd_readtest_init(void)
 	if (!iobuf1)
 		goto out;
 
-	err = scan_for_bad_eraseblocks();
+	bbt = kzalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		goto out;
+	err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH -next v2 06/10] mtd: mtd_speedtest: use mtd_test helpers
  2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
                   ` (4 preceding siblings ...)
  2013-08-03  9:52 ` [PATCH -next v2 05/10] mtd: mtd_readtest: " Akinobu Mita
@ 2013-08-03  9:52 ` Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 07/10] mtd: mtd_stresstest: " Akinobu Mita
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-08-03  9:52 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, Vikram Narayanan, Akinobu Mita, Adrian Hunter,
	Brian Norris, David Woodhouse

Use mtdtest_write(), mtdtest_read(), mtdtest_scan_for_bad_eraseblocks(),
mtdtest_erase_good_eraseblocks() in mtd_test helpers.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
---
* Changes from v1
- follow the changes of helper functions

 drivers/mtd/tests/speedtest.c | 180 +++++++++---------------------------------
 1 file changed, 36 insertions(+), 144 deletions(-)

diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c
index 20b63d1..457c45c 100644
--- a/drivers/mtd/tests/speedtest.c
+++ b/drivers/mtd/tests/speedtest.c
@@ -30,6 +30,8 @@
 #include <linux/sched.h>
 #include <linux/random.h>
 
+#include "mtd_test.h"
+
 static int dev = -EINVAL;
 module_param(dev, int, S_IRUGO);
 MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -49,33 +51,6 @@ static int pgcnt;
 static int goodebcnt;
 static struct timeval start, finish;
 
-
-static int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (ei.state == MTD_ERASE_FAILED) {
-		pr_err("some erase error occurred at EB %d\n",
-		       ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
 static int multiblock_erase(int ebnum, int blocks)
 {
 	int err;
@@ -103,52 +78,29 @@ static int multiblock_erase(int ebnum, int blocks)
 	return 0;
 }
 
-static int erase_whole_device(void)
-{
-	int err;
-	unsigned int i;
-
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = erase_eraseblock(i);
-		if (err)
-			return err;
-		cond_resched();
-	}
-	return 0;
-}
-
 static int write_eraseblock(int ebnum)
 {
-	size_t written;
-	int err = 0;
+	int err;
 	loff_t addr = ebnum * mtd->erasesize;
 
-	err = mtd_write(mtd, addr, mtd->erasesize, &written, iobuf);
-	if (err || written != mtd->erasesize) {
+	err = mtdtest_write(mtd, addr, mtd->erasesize, iobuf);
+	if (err)
 		pr_err("error: write failed at %#llx\n", addr);
-		if (!err)
-			err = -EINVAL;
-	}
 
 	return err;
 }
 
 static int write_eraseblock_by_page(int ebnum)
 {
-	size_t written;
 	int i, err = 0;
 	loff_t addr = ebnum * mtd->erasesize;
 	void *buf = iobuf;
 
 	for (i = 0; i < pgcnt; i++) {
-		err = mtd_write(mtd, addr, pgsize, &written, buf);
-		if (err || written != pgsize) {
+		err = mtdtest_write(mtd, addr, pgsize, buf);
+		if (err) {
 			pr_err("error: write failed at %#llx\n",
 			       addr);
-			if (!err)
-				err = -EINVAL;
 			break;
 		}
 		addr += pgsize;
@@ -160,30 +112,26 @@ static int write_eraseblock_by_page(int ebnum)
 
 static int write_eraseblock_by_2pages(int ebnum)
 {
-	size_t written, sz = pgsize * 2;
+	size_t sz = pgsize * 2;
 	int i, n = pgcnt / 2, err = 0;
 	loff_t addr = ebnum * mtd->erasesize;
 	void *buf = iobuf;
 
 	for (i = 0; i < n; i++) {
-		err = mtd_write(mtd, addr, sz, &written, buf);
-		if (err || written != sz) {
+		err = mtdtest_write(mtd, addr, sz, buf);
+		if (err) {
 			pr_err("error: write failed at %#llx\n",
 			       addr);
-			if (!err)
-				err = -EINVAL;
 			return err;
 		}
 		addr += sz;
 		buf += sz;
 	}
 	if (pgcnt % 2) {
-		err = mtd_write(mtd, addr, pgsize, &written, buf);
-		if (err || written != pgsize) {
+		err = mtdtest_write(mtd, addr, pgsize, buf);
+		if (err) {
 			pr_err("error: write failed at %#llx\n",
 			       addr);
-			if (!err)
-				err = -EINVAL;
 		}
 	}
 
@@ -192,40 +140,27 @@ static int write_eraseblock_by_2pages(int ebnum)
 
 static int read_eraseblock(int ebnum)
 {
-	size_t read;
-	int err = 0;
+	int err;
 	loff_t addr = ebnum * mtd->erasesize;
 
-	err = mtd_read(mtd, addr, mtd->erasesize, &read, iobuf);
-	/* Ignore corrected ECC errors */
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (err || read != mtd->erasesize) {
+	err = mtdtest_read(mtd, addr, mtd->erasesize, iobuf);
+	if (err)
 		pr_err("error: read failed at %#llx\n", addr);
-		if (!err)
-			err = -EINVAL;
-	}
 
 	return err;
 }
 
 static int read_eraseblock_by_page(int ebnum)
 {
-	size_t read;
 	int i, err = 0;
 	loff_t addr = ebnum * mtd->erasesize;
 	void *buf = iobuf;
 
 	for (i = 0; i < pgcnt; i++) {
-		err = mtd_read(mtd, addr, pgsize, &read, buf);
-		/* Ignore corrected ECC errors */
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != pgsize) {
+		err = mtdtest_read(mtd, addr, pgsize, buf);
+		if (err) {
 			pr_err("error: read failed at %#llx\n",
 			       addr);
-			if (!err)
-				err = -EINVAL;
 			break;
 		}
 		addr += pgsize;
@@ -237,53 +172,32 @@ static int read_eraseblock_by_page(int ebnum)
 
 static int read_eraseblock_by_2pages(int ebnum)
 {
-	size_t read, sz = pgsize * 2;
+	size_t sz = pgsize * 2;
 	int i, n = pgcnt / 2, err = 0;
 	loff_t addr = ebnum * mtd->erasesize;
 	void *buf = iobuf;
 
 	for (i = 0; i < n; i++) {
-		err = mtd_read(mtd, addr, sz, &read, buf);
-		/* Ignore corrected ECC errors */
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != sz) {
+		err = mtdtest_read(mtd, addr, sz, buf);
+		if (err) {
 			pr_err("error: read failed at %#llx\n",
 			       addr);
-			if (!err)
-				err = -EINVAL;
 			return err;
 		}
 		addr += sz;
 		buf += sz;
 	}
 	if (pgcnt % 2) {
-		err = mtd_read(mtd, addr, pgsize, &read, buf);
-		/* Ignore corrected ECC errors */
-		if (mtd_is_bitflip(err))
-			err = 0;
-		if (err || read != pgsize) {
+		err = mtdtest_read(mtd, addr, pgsize, buf);
+		if (err) {
 			pr_err("error: read failed at %#llx\n",
 			       addr);
-			if (!err)
-				err = -EINVAL;
 		}
 	}
 
 	return err;
 }
 
-static int is_block_bad(int ebnum)
-{
-	loff_t addr = ebnum * mtd->erasesize;
-	int ret;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
 static inline void start_timing(void)
 {
 	do_gettimeofday(&start);
@@ -308,30 +222,6 @@ static long calc_speed(void)
 	return k;
 }
 
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kzalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	if (!mtd_can_have_bb(mtd))
-		goto out;
-
-	pr_info("scanning for bad eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		bbt[i] = is_block_bad(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-out:
-	goodebcnt = ebcnt - bad;
-	return 0;
-}
-
 static int __init mtd_speedtest_init(void)
 {
 	int err, i, blocks, j, k;
@@ -387,11 +277,18 @@ static int __init mtd_speedtest_init(void)
 
 	prandom_bytes(iobuf, mtd->erasesize);
 
-	err = scan_for_bad_eraseblocks();
+	bbt = kzalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		goto out;
+	err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
+	for (i = 0; i < ebcnt; i++) {
+		if (!bbt[i])
+			goodebcnt++;
+	}
 
-	err = erase_whole_device();
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
@@ -425,7 +322,7 @@ static int __init mtd_speedtest_init(void)
 	speed = calc_speed();
 	pr_info("eraseblock read speed is %ld KiB/s\n", speed);
 
-	err = erase_whole_device();
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
@@ -459,7 +356,7 @@ static int __init mtd_speedtest_init(void)
 	speed = calc_speed();
 	pr_info("page read speed is %ld KiB/s\n", speed);
 
-	err = erase_whole_device();
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
@@ -496,14 +393,9 @@ static int __init mtd_speedtest_init(void)
 	/* Erase all eraseblocks */
 	pr_info("Testing erase speed\n");
 	start_timing();
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = erase_eraseblock(i);
-		if (err)
-			goto out;
-		cond_resched();
-	}
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
+	if (err)
+		goto out;
 	stop_timing();
 	speed = calc_speed();
 	pr_info("erase speed is %ld KiB/s\n", speed);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH -next v2 07/10] mtd: mtd_stresstest: use mtd_test helpers
  2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
                   ` (5 preceding siblings ...)
  2013-08-03  9:52 ` [PATCH -next v2 06/10] mtd: mtd_speedtest: " Akinobu Mita
@ 2013-08-03  9:52 ` Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 08/10] mtd: mtd_subpagetest: " Akinobu Mita
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-08-03  9:52 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, Vikram Narayanan, Akinobu Mita, Adrian Hunter,
	Brian Norris, David Woodhouse

Use mtdtest_read(), mtdtest_write(), mtdtest_erase_eraseblock(), and
mtdtest_scan_for_bad_eraseblocks() in mtd_test helpers.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
---
* Changes from v1
- follow the changes of helper functions

 drivers/mtd/tests/stresstest.c | 86 ++++++------------------------------------
 1 file changed, 12 insertions(+), 74 deletions(-)

diff --git a/drivers/mtd/tests/stresstest.c b/drivers/mtd/tests/stresstest.c
index 3a95e61..1695248 100644
--- a/drivers/mtd/tests/stresstest.c
+++ b/drivers/mtd/tests/stresstest.c
@@ -31,6 +31,8 @@
 #include <linux/vmalloc.h>
 #include <linux/random.h>
 
+#include "mtd_test.h"
+
 static int dev = -EINVAL;
 module_param(dev, int, S_IRUGO);
 MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -81,46 +83,8 @@ static int rand_len(int offs)
 	return len;
 }
 
-static int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (unlikely(err)) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (unlikely(ei.state == MTD_ERASE_FAILED)) {
-		pr_err("some erase error occurred at EB %d\n",
-		       ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int is_block_bad(int ebnum)
-{
-	loff_t addr = ebnum * mtd->erasesize;
-	int ret;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
 static int do_read(void)
 {
-	size_t read;
 	int eb = rand_eb();
 	int offs = rand_offs();
 	int len = rand_len(offs), err;
@@ -133,14 +97,10 @@ static int do_read(void)
 			len = mtd->erasesize - offs;
 	}
 	addr = eb * mtd->erasesize + offs;
-	err = mtd_read(mtd, addr, len, &read, readbuf);
-	if (mtd_is_bitflip(err))
-		err = 0;
-	if (unlikely(err || read != len)) {
+	err = mtdtest_read(mtd, addr, len, readbuf);
+	if (unlikely(err)) {
 		pr_err("error: read failed at 0x%llx\n",
 		       (long long)addr);
-		if (!err)
-			err = -EINVAL;
 		return err;
 	}
 	return 0;
@@ -149,12 +109,11 @@ static int do_read(void)
 static int do_write(void)
 {
 	int eb = rand_eb(), offs, err, len;
-	size_t written;
 	loff_t addr;
 
 	offs = offsets[eb];
 	if (offs >= mtd->erasesize) {
-		err = erase_eraseblock(eb);
+		err = mtdtest_erase_eraseblock(mtd, eb);
 		if (err)
 			return err;
 		offs = offsets[eb] = 0;
@@ -165,19 +124,17 @@ static int do_write(void)
 		if (bbt[eb + 1])
 			len = mtd->erasesize - offs;
 		else {
-			err = erase_eraseblock(eb + 1);
+			err = mtdtest_erase_eraseblock(mtd, eb + 1);
 			if (err)
 				return err;
 			offsets[eb + 1] = 0;
 		}
 	}
 	addr = eb * mtd->erasesize + offs;
-	err = mtd_write(mtd, addr, len, &written, writebuf);
-	if (unlikely(err || written != len)) {
+	err = mtdtest_write(mtd, addr, len, writebuf);
+	if (unlikely(err)) {
 		pr_err("error: write failed at 0x%llx\n",
 		       (long long)addr);
-		if (!err)
-			err = -EINVAL;
 		return err;
 	}
 	offs += len;
@@ -197,28 +154,6 @@ static int do_operation(void)
 		return do_write();
 }
 
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kzalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	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(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-	return 0;
-}
-
 static int __init mtd_stresstest_init(void)
 {
 	int err;
@@ -280,7 +215,10 @@ static int __init mtd_stresstest_init(void)
 		offsets[i] = mtd->erasesize;
 	prandom_bytes(writebuf, bufsize);
 
-	err = scan_for_bad_eraseblocks();
+	bbt = kzalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		goto out;
+	err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH -next v2 08/10] mtd: mtd_subpagetest: use mtd_test helpers
  2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
                   ` (6 preceding siblings ...)
  2013-08-03  9:52 ` [PATCH -next v2 07/10] mtd: mtd_stresstest: " Akinobu Mita
@ 2013-08-03  9:52 ` Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 09/10] mtd: mtd_torturetest: " Akinobu Mita
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-08-03  9:52 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, Vikram Narayanan, Akinobu Mita, Adrian Hunter,
	Brian Norris, David Woodhouse

Use mtdtest_scan_for_bad_eraseblocks() and mtdtest_erase_good_eraseblocks()
in mtd_test helpers.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
---
* Changes from v1
- follow the changes of helper functions

 drivers/mtd/tests/subpagetest.c | 87 +++++------------------------------------
 1 file changed, 9 insertions(+), 78 deletions(-)

diff --git a/drivers/mtd/tests/subpagetest.c b/drivers/mtd/tests/subpagetest.c
index e41a04f..e2c0adf 100644
--- a/drivers/mtd/tests/subpagetest.c
+++ b/drivers/mtd/tests/subpagetest.c
@@ -30,6 +30,8 @@
 #include <linux/sched.h>
 #include <linux/random.h>
 
+#include "mtd_test.h"
+
 static int dev = -EINVAL;
 module_param(dev, int, S_IRUGO);
 MODULE_PARM_DESC(dev, "MTD device number to use");
@@ -51,50 +53,6 @@ static inline void clear_data(unsigned char *buf, size_t len)
 	memset(buf, 0, len);
 }
 
-static int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (ei.state == MTD_ERASE_FAILED) {
-		pr_err("some erase error occurred at EB %d\n",
-		       ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int erase_whole_device(void)
-{
-	int err;
-	unsigned int i;
-
-	pr_info("erasing whole device\n");
-	for (i = 0; i < ebcnt; ++i) {
-		if (bbt[i])
-			continue;
-		err = erase_eraseblock(i);
-		if (err)
-			return err;
-		cond_resched();
-	}
-	pr_info("erased %u eraseblocks\n", i);
-	return 0;
-}
-
 static int write_eraseblock(int ebnum)
 {
 	size_t written;
@@ -317,36 +275,6 @@ static int verify_all_eraseblocks_ff(void)
 	return 0;
 }
 
-static int is_block_bad(int ebnum)
-{
-	loff_t addr = ebnum * mtd->erasesize;
-	int ret;
-
-	ret = mtd_block_isbad(mtd, addr);
-	if (ret)
-		pr_info("block %d is bad\n", ebnum);
-	return ret;
-}
-
-static int scan_for_bad_eraseblocks(void)
-{
-	int i, bad = 0;
-
-	bbt = kzalloc(ebcnt, GFP_KERNEL);
-	if (!bbt)
-		return -ENOMEM;
-
-	pr_info("scanning for bad eraseblocks\n");
-	for (i = 0; i < ebcnt; ++i) {
-		bbt[i] = is_block_bad(i) ? 1 : 0;
-		if (bbt[i])
-			bad += 1;
-		cond_resched();
-	}
-	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
-	return 0;
-}
-
 static int __init mtd_subpagetest_init(void)
 {
 	int err = 0;
@@ -396,12 +324,15 @@ static int __init mtd_subpagetest_init(void)
 	readbuf = kmalloc(bufsize, GFP_KERNEL);
 	if (!readbuf)
 		goto out;
+	bbt = kzalloc(ebcnt, GFP_KERNEL);
+	if (!bbt)
+		goto out;
 
-	err = scan_for_bad_eraseblocks();
+	err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
-	err = erase_whole_device();
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
@@ -433,7 +364,7 @@ static int __init mtd_subpagetest_init(void)
 	}
 	pr_info("verified %u eraseblocks\n", i);
 
-	err = erase_whole_device();
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
@@ -471,7 +402,7 @@ static int __init mtd_subpagetest_init(void)
 	}
 	pr_info("verified %u eraseblocks\n", i);
 
-	err = erase_whole_device();
+	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 	if (err)
 		goto out;
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH -next v2 09/10] mtd: mtd_torturetest: use mtd_test helpers
  2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
                   ` (7 preceding siblings ...)
  2013-08-03  9:52 ` [PATCH -next v2 08/10] mtd: mtd_subpagetest: " Akinobu Mita
@ 2013-08-03  9:52 ` Akinobu Mita
  2013-08-03  9:52 ` [PATCH -next v2 10/10] mtd: mtd_nandbiterrs: " Akinobu Mita
  2013-08-06 14:07 ` [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Artem Bityutskiy
  10 siblings, 0 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-08-03  9:52 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, Vikram Narayanan, Akinobu Mita, Adrian Hunter,
	Brian Norris, David Woodhouse

Use mtdtest_scan_for_bad_eraseblocks() and mtdtest_erase_good_eraseblocks()
in mtd_test helpers.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
---
* Newly added patch from v2

 drivers/mtd/tests/torturetest.c | 66 +++++------------------------------------
 1 file changed, 7 insertions(+), 59 deletions(-)

diff --git a/drivers/mtd/tests/torturetest.c b/drivers/mtd/tests/torturetest.c
index 3a9f6a6..eeab969 100644
--- a/drivers/mtd/tests/torturetest.c
+++ b/drivers/mtd/tests/torturetest.c
@@ -32,6 +32,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
+#include "mtd_test.h"
 
 #define RETRIES 3
 
@@ -93,35 +94,6 @@ static inline void stop_timing(void)
 }
 
 /*
- * Erase eraseblock number @ebnum.
- */
-static inline int erase_eraseblock(int ebnum)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = ebnum * mtd->erasesize;
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err) {
-		pr_err("error %d while erasing EB %d\n", err, ebnum);
-		return err;
-	}
-
-	if (ei.state == MTD_ERASE_FAILED) {
-		pr_err("some erase error occurred at EB %d\n",
-		       ebnum);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-/*
  * Check that the contents of eraseblock number @enbum is equivalent to the
  * @buf buffer.
  */
@@ -208,7 +180,7 @@ static inline int write_pattern(int ebnum, void *buf)
 static int __init tort_init(void)
 {
 	int err = 0, i, infinite = !cycles_count;
-	int *bad_ebs;
+	unsigned char *bad_ebs;
 
 	printk(KERN_INFO "\n");
 	printk(KERN_INFO "=================================================\n");
@@ -265,7 +237,7 @@ static int __init tort_init(void)
 	if (!check_buf)
 		goto out_patt_FF;
 
-	bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL);
+	bad_ebs = kzalloc(ebcnt, GFP_KERNEL);
 	if (!bad_ebs)
 		goto out_check_buf;
 
@@ -283,40 +255,16 @@ static int __init tort_init(void)
 		}
 	}
 
-	/*
-	 * Check if there is a bad eraseblock among those we are going to test.
-	 */
-	if (mtd_can_have_bb(mtd)) {
-		for (i = eb; i < eb + ebcnt; i++) {
-			err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize);
-
-			if (err < 0) {
-				pr_info("block_isbad() returned %d "
-				       "for EB %d\n", err, i);
-				goto out;
-			}
-
-			if (err) {
-				pr_err("EB %d is bad. Skip it.\n", i);
-				bad_ebs[i - eb] = 1;
-			}
-		}
-	}
+	err = mtdtest_scan_for_bad_eraseblocks(mtd, bad_ebs, eb, ebcnt);
+	if (err)
+		goto out;
 
 	start_timing();
 	while (1) {
 		int i;
 		void *patt;
 
-		/* Erase all eraseblocks */
-		for (i = eb; i < eb + ebcnt; i++) {
-			if (bad_ebs[i - eb])
-				continue;
-			err = erase_eraseblock(i);
-			if (err)
-				goto out;
-			cond_resched();
-		}
+		mtdtest_erase_good_eraseblocks(mtd, bad_ebs, eb, ebcnt);
 
 		/* Check if the eraseblocks contain only 0xFF bytes */
 		if (check) {
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH -next v2 10/10] mtd: mtd_nandbiterrs: use mtd_test helpers
  2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
                   ` (8 preceding siblings ...)
  2013-08-03  9:52 ` [PATCH -next v2 09/10] mtd: mtd_torturetest: " Akinobu Mita
@ 2013-08-03  9:52 ` Akinobu Mita
  2013-08-06 14:07 ` [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Artem Bityutskiy
  10 siblings, 0 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-08-03  9:52 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, Iwo Mergler, Adrian Hunter, Akinobu Mita,
	Vikram Narayanan, Brian Norris, David Woodhouse

Use mtdtest_write() and mtdtest_erase_eraseblock() in mtd_test helpers.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Iwo Mergler <Iwo.Mergler@netcommwireless.com.au>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
---
* Newly added patch from v2

 drivers/mtd/tests/nandbiterrs.c | 39 ++++++---------------------------------
 1 file changed, 6 insertions(+), 33 deletions(-)

diff --git a/drivers/mtd/tests/nandbiterrs.c b/drivers/mtd/tests/nandbiterrs.c
index 207bf9a..5a8c858 100644
--- a/drivers/mtd/tests/nandbiterrs.c
+++ b/drivers/mtd/tests/nandbiterrs.c
@@ -49,6 +49,7 @@
 #include <linux/err.h>
 #include <linux/mtd/nand.h>
 #include <linux/slab.h>
+#include "mtd_test.h"
 
 static int dev;
 module_param(dev, int, S_IRUGO);
@@ -98,45 +99,17 @@ static uint8_t hash(unsigned offset)
 	return c;
 }
 
-static int erase_block(void)
-{
-	int err;
-	struct erase_info ei;
-	loff_t addr = eraseblock * mtd->erasesize;
-
-	pr_info("erase_block\n");
-
-	memset(&ei, 0, sizeof(struct erase_info));
-	ei.mtd  = mtd;
-	ei.addr = addr;
-	ei.len  = mtd->erasesize;
-
-	err = mtd_erase(mtd, &ei);
-	if (err || ei.state == MTD_ERASE_FAILED) {
-		pr_err("error %d while erasing\n", err);
-		if (!err)
-			err = -EIO;
-		return err;
-	}
-
-	return 0;
-}
-
 /* Writes wbuffer to page */
 static int write_page(int log)
 {
-	int err = 0;
-	size_t written;
+	int err;
 
 	if (log)
 		pr_info("write_page\n");
 
-	err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer);
-	if (err || written != mtd->writesize) {
+	err = mtdtest_write(mtd, offset, mtd->writesize, wbuffer);
+	if (err)
 		pr_err("error: write failed at %#llx\n", (long long)offset);
-		if (!err)
-			err = -EIO;
-	}
 
 	return err;
 }
@@ -415,7 +388,7 @@ static int __init mtd_nandbiterrs_init(void)
 		goto exit_rbuffer;
 	}
 
-	err = erase_block();
+	err = mtdtest_erase_eraseblock(mtd, eraseblock);
 	if (err)
 		goto exit_error;
 
@@ -428,7 +401,7 @@ static int __init mtd_nandbiterrs_init(void)
 		goto exit_error;
 
 	/* We leave the block un-erased in case of test failure. */
-	err = erase_block();
+	err = mtdtest_erase_eraseblock(mtd, eraseblock);
 	if (err)
 		goto exit_error;
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules
  2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
                   ` (9 preceding siblings ...)
  2013-08-03  9:52 ` [PATCH -next v2 10/10] mtd: mtd_nandbiterrs: " Akinobu Mita
@ 2013-08-06 14:07 ` Artem Bityutskiy
  2013-08-07 14:11   ` Akinobu Mita
  10 siblings, 1 reply; 17+ messages in thread
From: Artem Bityutskiy @ 2013-08-06 14:07 UTC (permalink / raw)
  To: Akinobu Mita
  Cc: Iwo Mergler, Adrian Hunter, linux-mtd, Vikram Narayanan,
	Brian Norris, David Woodhouse

On Sat, 2013-08-03 at 18:52 +0900, Akinobu Mita wrote:
> This patch set reduces code duplication among mtd/tests modules by
> moving common helper functions into mtd_test.c. 
> 

You could make this nicer by incorporating the error messages to the
common handlers, and removing them from tests - they are almost the same
everywhere.

But this is anyway a good clean-up, so pushed to l2-mtd.git, thanks!

-- 
Best Regards,
Artem Bityutskiy

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules
  2013-08-06 14:07 ` [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Artem Bityutskiy
@ 2013-08-07 14:11   ` Akinobu Mita
  0 siblings, 0 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-08-07 14:11 UTC (permalink / raw)
  To: Artem Bityutskiy
  Cc: Iwo Mergler, Adrian Hunter, linux-mtd, Vikram Narayanan,
	Brian Norris, David Woodhouse

2013/8/6 Artem Bityutskiy <dedekind1@gmail.com>:
> On Sat, 2013-08-03 at 18:52 +0900, Akinobu Mita wrote:
>> This patch set reduces code duplication among mtd/tests modules by
>> moving common helper functions into mtd_test.c.
>>
>
> You could make this nicer by incorporating the error messages to the
> common handlers, and removing them from tests - they are almost the same
> everywhere.

Sounds good.  Especially incorporating the error messages to mtdtest_read()
and mtdtest_write() enables to convert some of remaining usages of
mtd_read() and mtd_write in the test modules.

> But this is anyway a good clean-up, so pushed to l2-mtd.git, thanks!

Thanks.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH -next v2 02/10] mtd: tests: rename sources in order to link a helper object
  2013-08-03  9:52 ` [PATCH -next v2 02/10] mtd: tests: rename sources in order to link a helper object Akinobu Mita
@ 2013-08-30 15:53   ` David Woodhouse
  2013-08-30 16:24     ` Brian Norris
  0 siblings, 1 reply; 17+ messages in thread
From: David Woodhouse @ 2013-08-30 15:53 UTC (permalink / raw)
  To: Akinobu Mita
  Cc: Vikram Narayanan, Brian Norris, linux-mtd, Adrian Hunter,
	Artem Bityutskiy

[-- Attachment #1: Type: text/plain, Size: 630 bytes --]

On Sat, 2013-08-03 at 18:52 +0900, Akinobu Mita wrote:
> Each mtd test module have a single source whose name is the same as
> the module name.  In order to link a single object including helper
> functions to every test module, this rename these sources to the
> different names.

Hrm, why link a separate copy into every test module? Why not just make
a separate mtd_test.ko module and have it export the symbols?

Not that I really mind simplifying the names of the actual C files
anyway, I suppose. So I'm applying the sequence as-is, but perhaps we
could follow up by making it a separate module?

-- 
dwmw2


[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 5745 bytes --]

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH -next v2 02/10] mtd: tests: rename sources in order to link a helper object
  2013-08-30 15:53   ` David Woodhouse
@ 2013-08-30 16:24     ` Brian Norris
  2013-08-30 17:17       ` David Woodhouse
  0 siblings, 1 reply; 17+ messages in thread
From: Brian Norris @ 2013-08-30 16:24 UTC (permalink / raw)
  To: David Woodhouse
  Cc: Vikram Narayanan, Adrian Hunter, linux-mtd, Akinobu Mita,
	Artem Bityutskiy

On 08/30/2013 08:53 AM, David Woodhouse wrote:
> On Sat, 2013-08-03 at 18:52 +0900, Akinobu Mita wrote:
>> Each mtd test module have a single source whose name is the same as
>> the module name.  In order to link a single object including helper
>> functions to every test module, this rename these sources to the
>> different names.
>
> Hrm, why link a separate copy into every test module? Why not just make
> a separate mtd_test.ko module and have it export the symbols?
>
> Not that I really mind simplifying the names of the actual C files
> anyway, I suppose. So I'm applying the sequence as-is, but perhaps we
> could follow up by making it a separate module?

We actually had the discussion (in multiple threads?) that because a 
separate module would complicate the process for running the test 
modules, it is preferable to copy the small helper code into each 
module. That is, we don't want to do this:

insmod mtd_test.ko
insmod mtd_<actualtest>.ko dev=X ...

However, this is still fairly trivial, so if you think there are good 
reasons to provide separate modules, I do not object to separate modules.

Brian

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH -next v2 02/10] mtd: tests: rename sources in order to link a helper object
  2013-08-30 16:24     ` Brian Norris
@ 2013-08-30 17:17       ` David Woodhouse
  2013-09-01  0:51         ` Akinobu Mita
  0 siblings, 1 reply; 17+ messages in thread
From: David Woodhouse @ 2013-08-30 17:17 UTC (permalink / raw)
  To: Brian Norris
  Cc: Vikram Narayanan, Adrian Hunter, linux-mtd, Akinobu Mita,
	Artem Bityutskiy

[-- Attachment #1: Type: text/plain, Size: 1068 bytes --]

On Fri, 2013-08-30 at 09:24 -0700, Brian Norris wrote:
> We actually had the discussion (in multiple threads?) that because a 
> separate module would complicate the process for running the test 
> modules, it is preferable to copy the small helper code into each 
> module. That is, we don't want to do this:
> 
> insmod mtd_test.ko
> insmod mtd_<actualtest>.ko dev=X ...

So use modprobe instead :)

> However, this is still fairly trivial, so if you think there are good 
> reasons to provide separate modules, I do not object to separate modules.

Even if you can't use modprobe, once mtd_test.ko is loaded it's going to
stay loaded and you can just forget about it and do your various tests.

Unless you're actually making changes to the code within mtd_test.ko
itself, of course. In which case you really *don't* want to have eight
different copies of it built in to eight different modules. I know that
making changes to that code is going to be relatively infrequent, but we
probably shouldn't make it suck *this* much :)

-- 
dwmw2


[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 5745 bytes --]

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH -next v2 02/10] mtd: tests: rename sources in order to link a helper object
  2013-08-30 17:17       ` David Woodhouse
@ 2013-09-01  0:51         ` Akinobu Mita
  0 siblings, 0 replies; 17+ messages in thread
From: Akinobu Mita @ 2013-09-01  0:51 UTC (permalink / raw)
  To: David Woodhouse
  Cc: Vikram Narayanan, Brian Norris, linux-mtd, Adrian Hunter,
	Artem Bityutskiy

2013/8/31 David Woodhouse <dwmw2@infradead.org>:
> On Fri, 2013-08-30 at 09:24 -0700, Brian Norris wrote:
>> We actually had the discussion (in multiple threads?) that because a
>> separate module would complicate the process for running the test
>> modules, it is preferable to copy the small helper code into each
>> module. That is, we don't want to do this:
>>
>> insmod mtd_test.ko
>> insmod mtd_<actualtest>.ko dev=X ...
>
> So use modprobe instead :)
>
>> However, this is still fairly trivial, so if you think there are good
>> reasons to provide separate modules, I do not object to separate modules.
>
> Even if you can't use modprobe, once mtd_test.ko is loaded it's going to
> stay loaded and you can just forget about it and do your various tests.
>
> Unless you're actually making changes to the code within mtd_test.ko
> itself, of course. In which case you really *don't* want to have eight
> different copies of it built in to eight different modules. I know that
> making changes to that code is going to be relatively infrequent, but we
> probably shouldn't make it suck *this* much :)

Brian, even if we have a separate mtd_test.ko module and we want to
run a custom mtd test module which may also make some changes within
mtd_test.c, we can just build it as we currently do by adding the
following lines in Makefile.

        obj-$(CONFIG_MTD_TESTS) += mtd_mytest.o
        mtd_mytest-objs := mytest.o mtd_test.o

You need to ignore the warnings like "'mtdtest_write' exported twice",
but you can install a custom test module without the extra step of
inserting another module as far as you don't insert mtd_test.ko.

So I'm going to prepare the patch that makes it a separate module.

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2013-09-01  0:51 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-03  9:52 [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Akinobu Mita
2013-08-03  9:52 ` [PATCH -next v2 01/10] mtd: tests: introduce helper functions Akinobu Mita
2013-08-03  9:52 ` [PATCH -next v2 02/10] mtd: tests: rename sources in order to link a helper object Akinobu Mita
2013-08-30 15:53   ` David Woodhouse
2013-08-30 16:24     ` Brian Norris
2013-08-30 17:17       ` David Woodhouse
2013-09-01  0:51         ` Akinobu Mita
2013-08-03  9:52 ` [PATCH -next v2 03/10] mtd: mtd_oobtest: use mtd_test helpers Akinobu Mita
2013-08-03  9:52 ` [PATCH -next v2 04/10] mtd: mtd_pagetest: " Akinobu Mita
2013-08-03  9:52 ` [PATCH -next v2 05/10] mtd: mtd_readtest: " Akinobu Mita
2013-08-03  9:52 ` [PATCH -next v2 06/10] mtd: mtd_speedtest: " Akinobu Mita
2013-08-03  9:52 ` [PATCH -next v2 07/10] mtd: mtd_stresstest: " Akinobu Mita
2013-08-03  9:52 ` [PATCH -next v2 08/10] mtd: mtd_subpagetest: " Akinobu Mita
2013-08-03  9:52 ` [PATCH -next v2 09/10] mtd: mtd_torturetest: " Akinobu Mita
2013-08-03  9:52 ` [PATCH -next v2 10/10] mtd: mtd_nandbiterrs: " Akinobu Mita
2013-08-06 14:07 ` [PATCH -next v2 00/10] mtd: tests: reduce duplication among mtd tests modules Artem Bityutskiy
2013-08-07 14:11   ` Akinobu Mita

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.