mtd: tests: rename sources in order to link a helper object

Linux-MTD Mailing List linux-mtd at lists.infradead.org
Fri Aug 30 16:59:01 EDT 2013


Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=a995c792280db558f994c253c1c3c3a55977d529
Commit:     a995c792280db558f994c253c1c3c3a55977d529
Parent:     084db4b020c7bb7f4169ae74fb9a685c30a2939d
Author:     Akinobu Mita <akinobu.mita at gmail.com>
AuthorDate: Sat Aug 3 18:52:08 2013 +0900
Committer:  David Woodhouse <David.Woodhouse at intel.com>
CommitDate: Fri Aug 30 21:34:06 2013 +0100

    mtd: tests: rename sources in order to link a helper object
    
    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 at gmail.com>
    Cc: Brian Norris <computersforpeace at gmail.com>
    Cc: Vikram Narayanan <vikram186 at gmail.com>
    Cc: Adrian Hunter <adrian.hunter at intel.com>
    Signed-off-by: Artem Bityutskiy <artem.bityutskiy at linux.intel.com>
    Signed-off-by: David Woodhouse <David.Woodhouse at intel.com>
---
 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(-)

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



More information about the linux-mtd-cvs mailing list