diff mbox

[-next,v2,02/10] mtd: tests: rename sources in order to link a helper object

Message ID 1375523536-32671-3-git-send-email-akinobu.mita@gmail.com
State Accepted
Commit a995c792280db558f994c253c1c3c3a55977d529
Headers show

Commit Message

Akinobu Mita Aug. 3, 2013, 9:52 a.m. UTC
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

Comments

David Woodhouse Aug. 30, 2013, 3:53 p.m. UTC | #1
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?
Brian Norris Aug. 30, 2013, 4:24 p.m. UTC | #2
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
David Woodhouse Aug. 30, 2013, 5:17 p.m. UTC | #3
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 :)
Akinobu Mita Sept. 1, 2013, 12:51 a.m. UTC | #4
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.
diff mbox

Patch

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");