mtd: tests: rename sources in order to link a helper object
authorAkinobu Mita <akinobu.mita@gmail.com>
Sat, 3 Aug 2013 09:52:08 +0000 (18:52 +0900)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Fri, 30 Aug 2013 20:34:06 +0000 (21:34 +0100)
Each mtd test module have a single source whose name is the same as
the module name.  In order to link a single object including helper
functions to every test module, this rename these sources to the
different names.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Brian Norris <computersforpeace@gmail.com>
Cc: Vikram Narayanan <vikram186@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
17 files changed:
drivers/mtd/tests/Makefile
drivers/mtd/tests/mtd_nandbiterrs.c [deleted file]
drivers/mtd/tests/mtd_oobtest.c [deleted file]
drivers/mtd/tests/mtd_pagetest.c [deleted file]
drivers/mtd/tests/mtd_readtest.c [deleted file]
drivers/mtd/tests/mtd_speedtest.c [deleted file]
drivers/mtd/tests/mtd_stresstest.c [deleted file]
drivers/mtd/tests/mtd_subpagetest.c [deleted file]
drivers/mtd/tests/mtd_torturetest.c [deleted file]
drivers/mtd/tests/nandbiterrs.c [new file with mode: 0644]
drivers/mtd/tests/oobtest.c [new file with mode: 0644]
drivers/mtd/tests/pagetest.c [new file with mode: 0644]
drivers/mtd/tests/readtest.c [new file with mode: 0644]
drivers/mtd/tests/speedtest.c [new file with mode: 0644]
drivers/mtd/tests/stresstest.c [new file with mode: 0644]
drivers/mtd/tests/subpagetest.c [new file with mode: 0644]
drivers/mtd/tests/torturetest.c [new file with mode: 0644]

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