···11+/*22+ * Copyright (C) 2006-2008 Nokia Corporation33+ *44+ * This program is free software; you can redistribute it and/or modify it55+ * under the terms of the GNU General Public License version 2 as published by66+ * the Free Software Foundation.77+ *88+ * This program is distributed in the hope that it will be useful, but WITHOUT99+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or1010+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for1111+ * more details.1212+ *1313+ * You should have received a copy of the GNU General Public License along with1414+ * this program; see the file COPYING. If not, write to the Free Software1515+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.1616+ *1717+ * Check MTD device read.1818+ *1919+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>2020+ */2121+2222+#include <linux/init.h>2323+#include <linux/module.h>2424+#include <linux/moduleparam.h>2525+#include <linux/err.h>2626+#include <linux/mtd/mtd.h>2727+#include <linux/sched.h>2828+2929+#define PRINT_PREF KERN_INFO "mtd_readtest: "3030+3131+static int dev;3232+module_param(dev, int, S_IRUGO);3333+MODULE_PARM_DESC(dev, "MTD device number to use");3434+3535+static struct mtd_info *mtd;3636+static unsigned char *iobuf;3737+static unsigned char *iobuf1;3838+static unsigned char *bbt;3939+4040+static int pgsize;4141+static int ebcnt;4242+static int pgcnt;4343+4444+static int read_eraseblock_by_page(int ebnum)4545+{4646+ size_t read = 0;4747+ int i, ret, err = 0;4848+ loff_t addr = ebnum * mtd->erasesize;4949+ void *buf = iobuf;5050+ void *oobbuf = iobuf1;5151+5252+ for (i = 0; i < pgcnt; i++) {5353+ memset(buf, 0 , pgcnt);5454+ ret = mtd->read(mtd, addr, pgsize, &read, buf);5555+ if (ret == -EUCLEAN)5656+ ret = 0;5757+ if (ret || read != pgsize) {5858+ printk(PRINT_PREF "error: read failed at %#llx\n",5959+ (long long)addr);6060+ if (!err)6161+ err = ret;6262+ if (!err)6363+ err = -EINVAL;6464+ }6565+ if (mtd->oobsize) {6666+ struct mtd_oob_ops ops;6767+6868+ ops.mode = MTD_OOB_PLACE;6969+ ops.len = 0;7070+ ops.retlen = 0;7171+ ops.ooblen = mtd->oobsize;7272+ ops.oobretlen = 0;7373+ ops.ooboffs = 0;7474+ ops.datbuf = 0;7575+ ops.oobbuf = oobbuf;7676+ ret = mtd->read_oob(mtd, addr, &ops);7777+ if (ret || ops.oobretlen != mtd->oobsize) {7878+ printk(PRINT_PREF "error: read oob failed at "7979+ "%#llx\n", (long long)addr);8080+ if (!err)8181+ err = ret;8282+ if (!err)8383+ err = -EINVAL;8484+ }8585+ oobbuf += mtd->oobsize;8686+ }8787+ addr += pgsize;8888+ buf += pgsize;8989+ }9090+9191+ return err;9292+}9393+9494+static void dump_eraseblock(int ebnum)9595+{9696+ int i, j, n;9797+ char line[128];9898+ int pg, oob;9999+100100+ printk(PRINT_PREF "dumping eraseblock %d\n", ebnum);101101+ n = mtd->erasesize;102102+ for (i = 0; i < n;) {103103+ char *p = line;104104+105105+ p += sprintf(p, "%05x: ", i);106106+ for (j = 0; j < 32 && i < n; j++, i++)107107+ p += sprintf(p, "%02x", (unsigned int)iobuf[i]);108108+ printk(KERN_CRIT "%s\n", line);109109+ cond_resched();110110+ }111111+ if (!mtd->oobsize)112112+ return;113113+ printk(PRINT_PREF "dumping oob from eraseblock %d\n", ebnum);114114+ n = mtd->oobsize;115115+ for (pg = 0, i = 0; pg < pgcnt; pg++)116116+ for (oob = 0; oob < n;) {117117+ char *p = line;118118+119119+ p += sprintf(p, "%05x: ", i);120120+ for (j = 0; j < 32 && oob < n; j++, oob++, i++)121121+ p += sprintf(p, "%02x",122122+ (unsigned int)iobuf1[i]);123123+ printk(KERN_CRIT "%s\n", line);124124+ cond_resched();125125+ }126126+}127127+128128+static int is_block_bad(int ebnum)129129+{130130+ loff_t addr = ebnum * mtd->erasesize;131131+ int ret;132132+133133+ ret = mtd->block_isbad(mtd, addr);134134+ if (ret)135135+ printk(PRINT_PREF "block %d is bad\n", ebnum);136136+ return ret;137137+}138138+139139+static int scan_for_bad_eraseblocks(void)140140+{141141+ int i, bad = 0;142142+143143+ bbt = kmalloc(ebcnt, GFP_KERNEL);144144+ if (!bbt) {145145+ printk(PRINT_PREF "error: cannot allocate memory\n");146146+ return -ENOMEM;147147+ }148148+ memset(bbt, 0 , ebcnt);149149+150150+ printk(PRINT_PREF "scanning for bad eraseblocks\n");151151+ for (i = 0; i < ebcnt; ++i) {152152+ bbt[i] = is_block_bad(i) ? 1 : 0;153153+ if (bbt[i])154154+ bad += 1;155155+ cond_resched();156156+ }157157+ printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad);158158+ return 0;159159+}160160+161161+static int __init mtd_readtest_init(void)162162+{163163+ uint64_t tmp;164164+ int err, i;165165+166166+ printk(KERN_INFO "\n");167167+ printk(KERN_INFO "=================================================\n");168168+ printk(PRINT_PREF "MTD device: %d\n", dev);169169+170170+ mtd = get_mtd_device(NULL, dev);171171+ if (IS_ERR(mtd)) {172172+ err = PTR_ERR(mtd);173173+ printk(PRINT_PREF "error: Cannot get MTD device\n");174174+ return err;175175+ }176176+177177+ if (mtd->writesize == 1) {178178+ printk(PRINT_PREF "not NAND flash, assume page size is 512 "179179+ "bytes.\n");180180+ pgsize = 512;181181+ } else182182+ pgsize = mtd->writesize;183183+184184+ tmp = mtd->size;185185+ do_div(tmp, mtd->erasesize);186186+ ebcnt = tmp;187187+ pgcnt = mtd->erasesize / mtd->writesize;188188+189189+ printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "190190+ "page size %u, count of eraseblocks %u, pages per "191191+ "eraseblock %u, OOB size %u\n",192192+ (unsigned long long)mtd->size, mtd->erasesize,193193+ pgsize, ebcnt, pgcnt, mtd->oobsize);194194+195195+ err = -ENOMEM;196196+ iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);197197+ if (!iobuf) {198198+ printk(PRINT_PREF "error: cannot allocate memory\n");199199+ goto out;200200+ }201201+ iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL);202202+ if (!iobuf1) {203203+ printk(PRINT_PREF "error: cannot allocate memory\n");204204+ goto out;205205+ }206206+207207+ err = scan_for_bad_eraseblocks();208208+ if (err)209209+ goto out;210210+211211+ /* Read all eraseblocks 1 page at a time */212212+ printk(PRINT_PREF "testing page read\n");213213+ for (i = 0; i < ebcnt; ++i) {214214+ int ret;215215+216216+ if (bbt[i])217217+ continue;218218+ ret = read_eraseblock_by_page(i);219219+ if (ret) {220220+ dump_eraseblock(i);221221+ if (!err)222222+ err = ret;223223+ }224224+ cond_resched();225225+ }226226+227227+ if (err)228228+ printk(PRINT_PREF "finished with errors\n");229229+ else230230+ printk(PRINT_PREF "finished\n");231231+232232+out:233233+234234+ kfree(iobuf);235235+ kfree(iobuf1);236236+ kfree(bbt);237237+ put_mtd_device(mtd);238238+ if (err)239239+ printk(PRINT_PREF "error %d occurred\n", err);240240+ printk(KERN_INFO "=================================================\n");241241+ return err;242242+}243243+module_init(mtd_readtest_init);244244+245245+static void __exit mtd_readtest_exit(void)246246+{247247+ return;248248+}249249+module_exit(mtd_readtest_exit);250250+251251+MODULE_DESCRIPTION("Read test module");252252+MODULE_AUTHOR("Adrian Hunter");253253+MODULE_LICENSE("GPL");