1 /* 2 * Copyright (C) 2006-2008 Nokia Corporation 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 as published by 6 * the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * this program; see the file COPYING. If not, write to the Free Software 15 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 16 * 17 * Check MTD device read. 18 * 19 * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> 20 */ 21 22 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 23 24 #include <linux/init.h> 25 #include <linux/module.h> 26 #include <linux/moduleparam.h> 27 #include <linux/err.h> 28 #include <linux/mtd/mtd.h> 29 #include <linux/slab.h> 30 #include <linux/sched.h> 31 32 #include "mtd_test.h" 33 34 static int dev = -EINVAL; 35 module_param(dev, int, S_IRUGO); 36 MODULE_PARM_DESC(dev, "MTD device number to use"); 37 38 static struct mtd_info *mtd; 39 static unsigned char *iobuf; 40 static unsigned char *iobuf1; 41 static unsigned char *bbt; 42 43 static int pgsize; 44 static int ebcnt; 45 static int pgcnt; 46 47 static int read_eraseblock_by_page(int ebnum) 48 { 49 int i, ret, err = 0; 50 loff_t addr = ebnum * mtd->erasesize; 51 void *buf = iobuf; 52 void *oobbuf = iobuf1; 53 54 for (i = 0; i < pgcnt; i++) { 55 memset(buf, 0 , pgsize); 56 ret = mtdtest_read(mtd, addr, pgsize, buf); 57 if (ret) { 58 if (!err) 59 err = ret; 60 } 61 if (mtd->oobsize) { 62 struct mtd_oob_ops ops; 63 64 ops.mode = MTD_OPS_PLACE_OOB; 65 ops.len = 0; 66 ops.retlen = 0; 67 ops.ooblen = mtd->oobsize; 68 ops.oobretlen = 0; 69 ops.ooboffs = 0; 70 ops.datbuf = NULL; 71 ops.oobbuf = oobbuf; 72 ret = mtd_read_oob(mtd, addr, &ops); 73 if ((ret && !mtd_is_bitflip(ret)) || 74 ops.oobretlen != mtd->oobsize) { 75 pr_err("error: read oob failed at " 76 "%#llx\n", (long long)addr); 77 if (!err) 78 err = ret; 79 if (!err) 80 err = -EINVAL; 81 } 82 oobbuf += mtd->oobsize; 83 } 84 addr += pgsize; 85 buf += pgsize; 86 } 87 88 return err; 89 } 90 91 static void dump_eraseblock(int ebnum) 92 { 93 int i, j, n; 94 char line[128]; 95 int pg, oob; 96 97 pr_info("dumping eraseblock %d\n", ebnum); 98 n = mtd->erasesize; 99 for (i = 0; i < n;) { 100 char *p = line; 101 102 p += sprintf(p, "%05x: ", i); 103 for (j = 0; j < 32 && i < n; j++, i++) 104 p += sprintf(p, "%02x", (unsigned int)iobuf[i]); 105 printk(KERN_CRIT "%s\n", line); 106 cond_resched(); 107 } 108 if (!mtd->oobsize) 109 return; 110 pr_info("dumping oob from eraseblock %d\n", ebnum); 111 n = mtd->oobsize; 112 for (pg = 0, i = 0; pg < pgcnt; pg++) 113 for (oob = 0; oob < n;) { 114 char *p = line; 115 116 p += sprintf(p, "%05x: ", i); 117 for (j = 0; j < 32 && oob < n; j++, oob++, i++) 118 p += sprintf(p, "%02x", 119 (unsigned int)iobuf1[i]); 120 printk(KERN_CRIT "%s\n", line); 121 cond_resched(); 122 } 123 } 124 125 static int __init mtd_readtest_init(void) 126 { 127 uint64_t tmp; 128 int err, i; 129 130 printk(KERN_INFO "\n"); 131 printk(KERN_INFO "=================================================\n"); 132 133 if (dev < 0) { 134 pr_info("Please specify a valid mtd-device via module parameter\n"); 135 return -EINVAL; 136 } 137 138 pr_info("MTD device: %d\n", dev); 139 140 mtd = get_mtd_device(NULL, dev); 141 if (IS_ERR(mtd)) { 142 err = PTR_ERR(mtd); 143 pr_err("error: Cannot get MTD device\n"); 144 return err; 145 } 146 147 if (mtd->writesize == 1) { 148 pr_info("not NAND flash, assume page size is 512 " 149 "bytes.\n"); 150 pgsize = 512; 151 } else 152 pgsize = mtd->writesize; 153 154 tmp = mtd->size; 155 do_div(tmp, mtd->erasesize); 156 ebcnt = tmp; 157 pgcnt = mtd->erasesize / pgsize; 158 159 pr_info("MTD device size %llu, eraseblock size %u, " 160 "page size %u, count of eraseblocks %u, pages per " 161 "eraseblock %u, OOB size %u\n", 162 (unsigned long long)mtd->size, mtd->erasesize, 163 pgsize, ebcnt, pgcnt, mtd->oobsize); 164 165 err = -ENOMEM; 166 iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); 167 if (!iobuf) 168 goto out; 169 iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL); 170 if (!iobuf1) 171 goto out; 172 173 bbt = kzalloc(ebcnt, GFP_KERNEL); 174 if (!bbt) 175 goto out; 176 err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); 177 if (err) 178 goto out; 179 180 /* Read all eraseblocks 1 page at a time */ 181 pr_info("testing page read\n"); 182 for (i = 0; i < ebcnt; ++i) { 183 int ret; 184 185 if (bbt[i]) 186 continue; 187 ret = read_eraseblock_by_page(i); 188 if (ret) { 189 dump_eraseblock(i); 190 if (!err) 191 err = ret; 192 } 193 cond_resched(); 194 } 195 196 if (err) 197 pr_info("finished with errors\n"); 198 else 199 pr_info("finished\n"); 200 201 out: 202 203 kfree(iobuf); 204 kfree(iobuf1); 205 kfree(bbt); 206 put_mtd_device(mtd); 207 if (err) 208 pr_info("error %d occurred\n", err); 209 printk(KERN_INFO "=================================================\n"); 210 return err; 211 } 212 module_init(mtd_readtest_init); 213 214 static void __exit mtd_readtest_exit(void) 215 { 216 return; 217 } 218 module_exit(mtd_readtest_exit); 219 220 MODULE_DESCRIPTION("Read test module"); 221 MODULE_AUTHOR("Adrian Hunter"); 222 MODULE_LICENSE("GPL"); 223