summaryrefslogtreecommitdiff
path: root/drivers/mtd/tests/sprd_mtdtest/nandflash_agetest
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/tests/sprd_mtdtest/nandflash_agetest')
-rwxr-xr-xdrivers/mtd/tests/sprd_mtdtest/nandflash_agetest/Makefile1
-rwxr-xr-xdrivers/mtd/tests/sprd_mtdtest/nandflash_agetest/nandflash_agetest.c250
-rwxr-xr-xdrivers/mtd/tests/sprd_mtdtest/nandflash_agetest/readme.txt15
3 files changed, 266 insertions, 0 deletions
diff --git a/drivers/mtd/tests/sprd_mtdtest/nandflash_agetest/Makefile b/drivers/mtd/tests/sprd_mtdtest/nandflash_agetest/Makefile
new file mode 100755
index 00000000..f18a8597
--- /dev/null
+++ b/drivers/mtd/tests/sprd_mtdtest/nandflash_agetest/Makefile
@@ -0,0 +1 @@
+obj-m += nandflash_agetest.o
diff --git a/drivers/mtd/tests/sprd_mtdtest/nandflash_agetest/nandflash_agetest.c b/drivers/mtd/tests/sprd_mtdtest/nandflash_agetest/nandflash_agetest.c
new file mode 100755
index 00000000..5963a6ba
--- /dev/null
+++ b/drivers/mtd/tests/sprd_mtdtest/nandflash_agetest/nandflash_agetest.c
@@ -0,0 +1,250 @@
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+#include <linux/err.h>
+#include <linux/random.h>
+#include <linux/vmalloc.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+static int dev = 4;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static int ebcnt = 0;
+static int pgcnt = 0;
+static unsigned char *bbt = NULL;
+static struct mtd_info *mtd = NULL;
+static struct rnd_state rnd_state = {0};
+
+static int agetest_erase_eraseblock(unsigned int ebnum)
+{
+ int err = 0;
+ struct erase_info ei = {0};
+
+ ei.mtd = mtd;
+ ei.addr = ebnum * mtd->erasesize;
+ ei.len = mtd->erasesize;
+
+ err = mtd_erase(mtd, &ei);
+ if (err) {
+ pr_err("err %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 agetest_scan_bad_block(void)
+{
+ unsigned int i = 0, bad = 0;
+
+ bbt = (unsigned char *)vmalloc(ebcnt);
+ if (!bbt) {
+ pr_info("error: cannot allocate memory\n");
+ return -ENOMEM;
+ }
+
+ pr_info("scanning for bad eraseblocks first\n");
+ for (i = 0; i < ebcnt; ++i) {
+ bbt[i] = mtd_block_isbad(mtd, i * mtd->erasesize) ? 1 : 0;
+ if (bbt[i])
+ bad += 1;
+ cond_resched();
+ }
+ pr_info("scanned %d eraseblocks, %d are bad\n", ebcnt, bad);
+ return 0;
+}
+
+static int nandflash_agetest(void)
+{
+ int err = 0;
+ loff_t addr = 0;
+ unsigned int test_blk = 10;
+ unsigned int op = 0;
+ unsigned char *readbuf = NULL;
+ unsigned char *writebuf = NULL;
+ unsigned int written = 0, read = 0;
+ unsigned int continue_err = 0;
+ unsigned int erase_time = 0;
+
+ readbuf = vmalloc(mtd->writesize);
+ if(!readbuf) {
+ pr_err("%s, alloc readbuf failed!\n", __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ writebuf = vmalloc(mtd->writesize);
+ if(!writebuf) {
+ pr_err("%s, alloc writebuf failed!\n", __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ while((test_blk < ebcnt) && bbt[test_blk]) {
+ test_blk ++;
+ }
+
+ if(test_blk >= ebcnt) {
+ pr_err("The whole mtd_partion are bad!\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ pr_info("Begin to make block %d as bad!\n", test_blk);
+ addr = test_blk * mtd->erasesize;
+ prandom_seed_state(&rnd_state, 1);
+ prandom_bytes_state(&rnd_state, writebuf, mtd->writesize);
+ err = agetest_erase_eraseblock(test_blk);
+ if(err) {
+ pr_err("the first erase failed at addr: 0x%.8x, err: %d\n", (unsigned int)addr, err);
+ goto out;
+ }
+
+ while(1) {
+ if(signal_pending(current))
+ goto out;
+ err = mtd_write(mtd, addr, mtd->writesize, &written, writebuf);
+ if(err || written != mtd->writesize) {
+ pr_err("write failed at addr: 0x%.8x, err: %d, written: %d, time: %d\n", (unsigned int)addr, err, written, op);
+ goto erase;
+ }
+
+ err = mtd_read(mtd, addr, mtd->writesize, &read, readbuf);
+ if(err || read != mtd->writesize) {
+ pr_err("read failed at addr: 0x%.8x, err: %d, read: %d, time: %d\n", (unsigned int)addr, err, read, op);
+ goto erase;
+ }
+
+ if(memcmp(readbuf, writebuf, mtd->writesize) != 0) {
+ pr_err("verify failed at addr: 0x%.8x!", (unsigned int)addr);
+ goto erase;
+ }
+
+erase:
+ err = agetest_erase_eraseblock(test_blk);
+ if(err) {
+ pr_err("erase failed at addr: 0x%.8x, err: %d, time: %d\n", (unsigned int)addr, err, op);
+ break;
+ }
+ op += 1;
+ if(op % 1024 == 0)
+ pr_info("we have erased %d times!\n", op);
+ }
+
+/*make sure the block has been erased as bad block*/
+ while(1) {
+ if(signal_pending(current))
+ break;
+ err = mtd_write(mtd, addr, mtd->writesize, &written, writebuf);
+ err = agetest_erase_eraseblock(test_blk);
+ if(err) {
+ continue_err ++;
+ if(continue_err >= 0x1000) {
+ pr_info("after %d times continue erase, the block has been erased as bad block!", erase_time);
+ break;
+ }
+ if( continue_err % 100 == 0)
+ pr_info("the continue_err is %d!", continue_err);
+ } else {
+ if(continue_err)
+ pr_info("the continue_err may be clean, old value is %d\n", continue_err);
+ continue_err = 0;
+ }
+ erase_time ++;
+ if( erase_time % 1024 == 0)
+ pr_info("the erase_time is %d!", erase_time);
+ }
+ pr_info("mark block %d as bad!\n", test_blk);
+ mtd->_block_markbad(mtd, addr);
+ err = 0;
+
+out:
+ vfree(readbuf);
+ vfree(writebuf);
+ pr_info("agetest finished, %d(%d) operations done!\n", op, erase_time);
+ return err;
+}
+
+static int __init nandflash_agetest_init(void)
+{
+ int err = 0;
+ uint64_t tmp = 0;
+
+ pr_info(KERN_INFO "\n");
+ pr_info(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_info("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 = agetest_scan_bad_block();
+ if (err)
+ goto out;
+
+ err = nandflash_agetest();
+ if(err) {
+ pr_err("stresstest failed!\n");
+ goto out;
+ }
+
+out:
+ pr_info(KERN_INFO "=================================================\n");
+ if(bbt != NULL)
+ vfree(bbt);
+ put_mtd_device(mtd);
+
+ return -1;
+}
+
+static void __exit nandflash_agetest_exit(void)
+{
+ return;
+}
+
+module_init(nandflash_agetest_init);
+
+module_exit(nandflash_agetest_exit);
+
+MODULE_DESCRIPTION("mtd_age_test");
+MODULE_AUTHOR("ming.tang@spreadtrum.com");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/sprd_mtdtest/nandflash_agetest/readme.txt b/drivers/mtd/tests/sprd_mtdtest/nandflash_agetest/readme.txt
new file mode 100755
index 00000000..91a5977f
--- /dev/null
+++ b/drivers/mtd/tests/sprd_mtdtest/nandflash_agetest/readme.txt
@@ -0,0 +1,15 @@
+agetest, 即老化测试,用于获取单个nandflash 擦除块的寿命。该测试需要的时间较长,一般需要5小时以上,由nandflash的特性决定。为了确保被测试擦除块彻底被擦坏,需要使用强制擦除接口(将/sys/kernel/debug/nfc_base/allowEraseBadBlock置为1)。
+
+参数说明:
+dev:用于指定测试使用的mtd分区,默认值为4
+
+使用方法:
+1 将/sys/kernel/debug/nfc_base/allowEraseBadBlock置为1;
+cmd:echo "1" > /sys/kernel/debug/nfc_base/allowEraseBadBlock
+2 root后加载测试模块并传入参数,测试结果通过kernel log输出,通过erase_time来衡量某款nandflash的特性;
+cmd:insmod nandflash_agetest.ko dev=4
+3 将/sys/kernel/debug/nfc_base/allowEraseBadBlock置为0。
+cmd:echo "0" > /sys/kernel/debug/nfc_base/allowEraseBadBlock
+
+注意:
+每进行一次测试,会增加一个bad block。 \ No newline at end of file