diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/mtd/tests/sprd_mtdtest/nandflash_ecctest | |
Diffstat (limited to 'drivers/mtd/tests/sprd_mtdtest/nandflash_ecctest')
3 files changed, 282 insertions, 0 deletions
diff --git a/drivers/mtd/tests/sprd_mtdtest/nandflash_ecctest/Makefile b/drivers/mtd/tests/sprd_mtdtest/nandflash_ecctest/Makefile new file mode 100755 index 00000000..3af72abe --- /dev/null +++ b/drivers/mtd/tests/sprd_mtdtest/nandflash_ecctest/Makefile @@ -0,0 +1 @@ +obj-m += nandflash_ecctest.o diff --git a/drivers/mtd/tests/sprd_mtdtest/nandflash_ecctest/nandflash_ecctest.c b/drivers/mtd/tests/sprd_mtdtest/nandflash_ecctest/nandflash_ecctest.c new file mode 100755 index 00000000..fcaf449c --- /dev/null +++ b/drivers/mtd/tests/sprd_mtdtest/nandflash_ecctest/nandflash_ecctest.c @@ -0,0 +1,273 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/random.h> +#include <linux/mtd/mtd.h> +#include <linux/moduleparam.h> + +struct mtd_part { + struct mtd_info mtd; + struct mtd_info *master; + loff_t offset; + struct list_head list; +}; + +#define CBIT(v, n) ((v) & (1 << (n))) +#define BCLR(v, n) ((v) = (v) & ~(1 << (n))) + +static struct mtd_info *mtd = NULL; +static int dev = 4; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static int insert_biterror(unsigned char *buf) +{ + unsigned int bit = 0; + unsigned int byte = 0; + + while (byte < mtd->writesize) { + for (bit = 0; bit <= 7; bit++) { + if (CBIT(buf[byte], bit)) { + BCLR(buf[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; +} + +static int 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 write_oob(loff_t offset, unsigned char *databuf, unsigned int len, unsigned char *oobbuf, unsigned int ooblen, unsigned int mode) +{ + struct mtd_oob_ops ops = {0}; + int err = 0; + + ops.mode = mode; + ops.len = len; + ops.retlen = 0; + ops.ooblen = ooblen; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = databuf; + ops.oobbuf = oobbuf; + err = mtd->_write_oob(mtd, offset, &ops); + if (err || ops.oobretlen != ops.ooblen || ops.retlen != ops.len) { + pr_err("write address: 0x%.8x's failed, err:%d!", (unsigned int)offset, err); + if(err == 0) { + pr_err("expect:%d , %d real:%d, %d\n", ops.len, ops.ooblen, ops.retlen, ops.oobretlen); + err = -1; + } + } + + return err; +} + +static int read_oob(loff_t offset, unsigned char *databuf, unsigned int len, unsigned char *oobbuf, unsigned int ooblen, unsigned int mode) +{ + struct mtd_oob_ops ops = {0}; + int err = 0; + + ops.mode = mode; + ops.len = len; + ops.retlen = 0; + ops.ooblen = ooblen; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = databuf; + ops.oobbuf = oobbuf; + err = mtd->_read_oob(mtd, offset, &ops); + if (err || ops.oobretlen != ops.ooblen || ops.retlen != ops.len) { + pr_err("read address: 0x%.8x failed, err:%d!\n", (unsigned int)offset, err); + if(err == 0) { + pr_err("expect:%d , %d real:%d, %d\n", ops.len, ops.ooblen, ops.retlen, ops.oobretlen); + err = -1; + } else if(err == -EUCLEAN) { + err = 0; + } + } + + return err; +} + +static int __init nandflash_ecctest_init(void) +{ + unsigned int read_len = 0; + int err = 0; + loff_t offset = 0; + unsigned int j = 0; + struct rnd_state rnd_state; + unsigned int corrected_num = 0; + unsigned char *oobbuf_w = NULL; + unsigned char *oobbuf_r = NULL; + unsigned char *pagebuf_r = NULL; + unsigned char *pagebuf_w = NULL; + unsigned char *pagebuf_v = NULL; + struct mtd_ecc_stats stats = {0}; + struct mtd_part *mtd_part = NULL; + + printk("\n"); + pr_info("--------------------------------------------------------------------------\n"); + /*prepare work*/ + mtd = get_mtd_device(NULL, 4); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + pr_err("Cannot get MTD device\n"); + return err; + } + + mtd_part = (struct mtd_part *)mtd; + + if(!mtd->_block_isbad || !mtd->_block_markbad || !mtd->_read_oob || !mtd->_write_oob || + !mtd->_read || !mtd->_write) { + pr_err("Some mtd's method may be NULL!"); + goto out; + } + + pagebuf_r = vmalloc(mtd->writesize); + oobbuf_r = vmalloc(mtd->oobsize); + pagebuf_w = vmalloc(mtd->writesize); + pagebuf_v = vmalloc(mtd->writesize); + oobbuf_w = vmalloc(mtd->oobsize); + if(!pagebuf_r || !oobbuf_r || !pagebuf_w || !pagebuf_v || !oobbuf_w) { + pr_err("Alloc buf failed!"); + goto out; + } + + //find a vailid block + while(offset < mtd->size) { + if(mtd->_block_isbad(mtd, offset)) { + offset += mtd->erasesize; + } else { + break; + } + } + + if(offset >= mtd->size) { + pr_err("The whole mtd_partion are bad!\n"); + goto out; + } + + err = erase_eraseblock(((long)offset)/mtd->erasesize); + if(err) { + pr_err("Erase failed at EB: %d\n", ((unsigned int)offset)/mtd->erasesize); + goto out; + } + pr_info("Erase block %d successfully!\n", ((unsigned int)offset)/mtd->erasesize); + + //prepare value + prandom_seed_state(&rnd_state, 1); + prandom_bytes_state(&rnd_state, oobbuf_w, mtd->oobsize); + prandom_bytes_state(&rnd_state, pagebuf_w, mtd->writesize); + memcpy(pagebuf_v, pagebuf_w, mtd->writesize); + + err = write_oob(offset, pagebuf_w, mtd->writesize, oobbuf_w, mtd->oobsize, MTD_OPS_PLACE_OOB); + if(err != 0) { + pr_err("1.1 write failed!\n"); + goto erase; + } + + err = read_oob(offset, pagebuf_r, mtd->writesize, oobbuf_r, mtd->oobsize, MTD_OPS_PLACE_OOB); + if(err != 0) { + pr_err("1.1 read failed!\n"); + goto erase; + } + + if(memcmp(pagebuf_v, pagebuf_r, mtd->writesize) != 0) { + pr_err("1.1 compare page failed!\n"); + goto erase; + } + memcpy(oobbuf_w, oobbuf_r, mtd->oobsize); + + for(j = 0; j < mtd->writesize * 8; j ++) + { + offset += mtd->writesize; + err = insert_biterror(pagebuf_w); + if(err) { + pr_err("Insert biterror failed!\n"); + goto erase; + } + pr_info("Insert %d bit-flip sucessfully!\n", j + 1); + + err = write_oob(offset, pagebuf_w, mtd->writesize, oobbuf_w, mtd->oobsize, MTD_OPS_RAW); + if(err != 0) { + pr_err("2.1 write failed!\n"); + goto erase; + } + + stats = mtd_part->master->ecc_stats; + err = read_oob(offset, pagebuf_r, mtd->writesize, oobbuf_r, mtd->oobsize, MTD_OPS_PLACE_OOB); + if(err != 0) { + pr_err("2.1 read failed!\n"); + break; + } + + if(memcmp(pagebuf_v, pagebuf_r, mtd->writesize) != 0) { + pr_err("2.2 compare page failed!\n"); + goto erase; + } + + corrected_num = mtd_part->master->ecc_stats.corrected - stats.corrected; + if(corrected_num != (j + 1)) { + pr_err("Bit-flip num is not match, expected:%d, really:%d\n", j + 1, corrected_num); + goto erase; + } + } + + pr_info("Test successfully, this system can corrected %d bit-flip at most!\n", j); +erase: + err = erase_eraseblock(((unsigned int)offset)/mtd->erasesize); + if(err) { + pr_err("1.3 erase failed at EB: %d\n", ((unsigned int)offset)/mtd->erasesize); + goto out; + } +out: + vfree(pagebuf_r); + vfree(oobbuf_r); + vfree(pagebuf_w); + vfree(oobbuf_w); + pr_info("--------------------------------------------------------------------------\n"); + return -1; +} + +static void __exit nandflash_ecctest_exit(void) +{ + put_mtd_device(mtd); + return; +} + +module_init(nandflash_ecctest_init); +module_exit(nandflash_ecctest_exit); + +MODULE_DESCRIPTION("Test ecc strength"); +MODULE_AUTHOR("ming.tang"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/sprd_mtdtest/nandflash_ecctest/readme.txt b/drivers/mtd/tests/sprd_mtdtest/nandflash_ecctest/readme.txt new file mode 100755 index 00000000..ee9ba97f --- /dev/null +++ b/drivers/mtd/tests/sprd_mtdtest/nandflash_ecctest/readme.txt @@ -0,0 +1,8 @@ +ecctest——纠错能力测试,用于获取当前系统实际纠错能力。
+
+参数说明:
+dev:用于指定测试使用的mtd分区,默认值为4
+
+使用方法:
+root后加载测试模块并传入参数,测试结果通过kernel log输出;
+cmd:insmod nandflash_ecctest.ko dev=4
\ No newline at end of file |
