blob: acd991f4bf941a09d016f12cfea8def7e2c04433 [file] [log] [blame]
Artem Bityutskiye73f2172008-12-08 13:34:16 +02001/*
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 * Test page read and write on MTD device.
18 *
19 * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
20 */
21
Vikram Narayananbb998412012-10-10 23:07:40 +053022#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23
Artem Bityutskiye73f2172008-12-08 13:34:16 +020024#include <asm/div64.h>
25#include <linux/init.h>
26#include <linux/module.h>
27#include <linux/moduleparam.h>
28#include <linux/err.h>
29#include <linux/mtd/mtd.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090030#include <linux/slab.h>
Artem Bityutskiye73f2172008-12-08 13:34:16 +020031#include <linux/sched.h>
Akinobu Mita825b8cc2013-02-27 17:05:35 -080032#include <linux/random.h>
Artem Bityutskiye73f2172008-12-08 13:34:16 +020033
Wolfram Sang74060602011-10-30 00:11:53 +020034static int dev = -EINVAL;
Artem Bityutskiye73f2172008-12-08 13:34:16 +020035module_param(dev, int, S_IRUGO);
36MODULE_PARM_DESC(dev, "MTD device number to use");
37
38static struct mtd_info *mtd;
39static unsigned char *twopages;
40static unsigned char *writebuf;
41static unsigned char *boundary;
42static unsigned char *bbt;
43
44static int pgsize;
45static int bufsize;
46static int ebcnt;
47static int pgcnt;
48static int errcnt;
Akinobu Mita825b8cc2013-02-27 17:05:35 -080049static struct rnd_state rnd_state;
Artem Bityutskiye73f2172008-12-08 13:34:16 +020050
51static int erase_eraseblock(int ebnum)
52{
53 int err;
54 struct erase_info ei;
55 loff_t addr = ebnum * mtd->erasesize;
56
57 memset(&ei, 0, sizeof(struct erase_info));
58 ei.mtd = mtd;
59 ei.addr = addr;
60 ei.len = mtd->erasesize;
61
Artem Bityutskiy7e1f0dc2011-12-23 15:25:39 +020062 err = mtd_erase(mtd, &ei);
Artem Bityutskiye73f2172008-12-08 13:34:16 +020063 if (err) {
Vikram Narayananbb998412012-10-10 23:07:40 +053064 pr_err("error %d while erasing EB %d\n", err, ebnum);
Artem Bityutskiye73f2172008-12-08 13:34:16 +020065 return err;
66 }
67
68 if (ei.state == MTD_ERASE_FAILED) {
Vikram Narayananbb998412012-10-10 23:07:40 +053069 pr_err("some erase error occurred at EB %d\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +020070 ebnum);
71 return -EIO;
72 }
73
74 return 0;
75}
76
77static int write_eraseblock(int ebnum)
78{
79 int err = 0;
Artem Bityutskiy30fa9842011-12-29 15:16:28 +020080 size_t written;
Artem Bityutskiye73f2172008-12-08 13:34:16 +020081 loff_t addr = ebnum * mtd->erasesize;
82
Akinobu Mita825b8cc2013-02-27 17:05:35 -080083 prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
Artem Bityutskiye73f2172008-12-08 13:34:16 +020084 cond_resched();
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +020085 err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf);
Artem Bityutskiye73f2172008-12-08 13:34:16 +020086 if (err || written != mtd->erasesize)
Vikram Narayananbb998412012-10-10 23:07:40 +053087 pr_err("error: write failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +020088 (long long)addr);
89
90 return err;
91}
92
93static int verify_eraseblock(int ebnum)
94{
95 uint32_t j;
Artem Bityutskiy30fa9842011-12-29 15:16:28 +020096 size_t read;
Artem Bityutskiye73f2172008-12-08 13:34:16 +020097 int err = 0, i;
98 loff_t addr0, addrn;
99 loff_t addr = ebnum * mtd->erasesize;
100
101 addr0 = 0;
Roel Kluinc6f7e7b2009-07-31 16:21:01 +0200102 for (i = 0; i < ebcnt && bbt[i]; ++i)
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200103 addr0 += mtd->erasesize;
104
105 addrn = mtd->size;
Roel Kluinc6f7e7b2009-07-31 16:21:01 +0200106 for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200107 addrn -= mtd->erasesize;
108
Akinobu Mita825b8cc2013-02-27 17:05:35 -0800109 prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200110 for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
111 /* Do a read to set the internal dataRAMs to different data */
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200112 err = mtd_read(mtd, addr0, bufsize, &read, twopages);
Brian Norrisd57f40542011-09-20 18:34:25 -0700113 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200114 err = 0;
115 if (err || read != bufsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530116 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200117 (long long)addr0);
118 return err;
119 }
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200120 err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
Brian Norrisd57f40542011-09-20 18:34:25 -0700121 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200122 err = 0;
123 if (err || read != bufsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530124 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200125 (long long)(addrn - bufsize));
126 return err;
127 }
128 memset(twopages, 0, bufsize);
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200129 err = mtd_read(mtd, addr, bufsize, &read, twopages);
Brian Norrisd57f40542011-09-20 18:34:25 -0700130 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200131 err = 0;
132 if (err || read != bufsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530133 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200134 (long long)addr);
135 break;
136 }
137 if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530138 pr_err("error: verify failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200139 (long long)addr);
140 errcnt += 1;
141 }
142 }
143 /* Check boundary between eraseblocks */
144 if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) {
Akinobu Mita825b8cc2013-02-27 17:05:35 -0800145 struct rnd_state old_state = rnd_state;
146
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200147 /* Do a read to set the internal dataRAMs to different data */
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200148 err = mtd_read(mtd, addr0, bufsize, &read, twopages);
Brian Norrisd57f40542011-09-20 18:34:25 -0700149 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200150 err = 0;
151 if (err || read != bufsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530152 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200153 (long long)addr0);
154 return err;
155 }
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200156 err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
Brian Norrisd57f40542011-09-20 18:34:25 -0700157 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200158 err = 0;
159 if (err || read != bufsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530160 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200161 (long long)(addrn - bufsize));
162 return err;
163 }
164 memset(twopages, 0, bufsize);
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200165 err = mtd_read(mtd, addr, bufsize, &read, twopages);
Brian Norrisd57f40542011-09-20 18:34:25 -0700166 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200167 err = 0;
168 if (err || read != bufsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530169 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200170 (long long)addr);
171 return err;
172 }
173 memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize);
Akinobu Mita825b8cc2013-02-27 17:05:35 -0800174 prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200175 if (memcmp(twopages, boundary, bufsize)) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530176 pr_err("error: verify failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200177 (long long)addr);
178 errcnt += 1;
179 }
Akinobu Mita825b8cc2013-02-27 17:05:35 -0800180 rnd_state = old_state;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200181 }
182 return err;
183}
184
185static int crosstest(void)
186{
Artem Bityutskiy30fa9842011-12-29 15:16:28 +0200187 size_t read;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200188 int err = 0, i;
189 loff_t addr, addr0, addrn;
190 unsigned char *pp1, *pp2, *pp3, *pp4;
191
Vikram Narayananbb998412012-10-10 23:07:40 +0530192 pr_info("crosstest\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200193 pp1 = kmalloc(pgsize * 4, GFP_KERNEL);
Brian Norris33777e62013-05-02 14:18:51 -0700194 if (!pp1)
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200195 return -ENOMEM;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200196 pp2 = pp1 + pgsize;
197 pp3 = pp2 + pgsize;
198 pp4 = pp3 + pgsize;
199 memset(pp1, 0, pgsize * 4);
200
201 addr0 = 0;
Roel Kluinc6f7e7b2009-07-31 16:21:01 +0200202 for (i = 0; i < ebcnt && bbt[i]; ++i)
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200203 addr0 += mtd->erasesize;
204
205 addrn = mtd->size;
Roel Kluinc6f7e7b2009-07-31 16:21:01 +0200206 for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200207 addrn -= mtd->erasesize;
208
209 /* Read 2nd-to-last page to pp1 */
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200210 addr = addrn - pgsize - pgsize;
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200211 err = mtd_read(mtd, addr, pgsize, &read, pp1);
Brian Norrisd57f40542011-09-20 18:34:25 -0700212 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200213 err = 0;
214 if (err || read != pgsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530215 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200216 (long long)addr);
217 kfree(pp1);
218 return err;
219 }
220
221 /* Read 3rd-to-last page to pp1 */
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200222 addr = addrn - pgsize - pgsize - pgsize;
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200223 err = mtd_read(mtd, addr, pgsize, &read, pp1);
Brian Norrisd57f40542011-09-20 18:34:25 -0700224 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200225 err = 0;
226 if (err || read != pgsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530227 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200228 (long long)addr);
229 kfree(pp1);
230 return err;
231 }
232
233 /* Read first page to pp2 */
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200234 addr = addr0;
Vikram Narayananbb998412012-10-10 23:07:40 +0530235 pr_info("reading page at %#llx\n", (long long)addr);
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200236 err = mtd_read(mtd, addr, pgsize, &read, pp2);
Brian Norrisd57f40542011-09-20 18:34:25 -0700237 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200238 err = 0;
239 if (err || read != pgsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530240 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200241 (long long)addr);
242 kfree(pp1);
243 return err;
244 }
245
246 /* Read last page to pp3 */
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200247 addr = addrn - pgsize;
Vikram Narayananbb998412012-10-10 23:07:40 +0530248 pr_info("reading page at %#llx\n", (long long)addr);
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200249 err = mtd_read(mtd, addr, pgsize, &read, pp3);
Brian Norrisd57f40542011-09-20 18:34:25 -0700250 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200251 err = 0;
252 if (err || read != pgsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530253 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200254 (long long)addr);
255 kfree(pp1);
256 return err;
257 }
258
259 /* Read first page again to pp4 */
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200260 addr = addr0;
Vikram Narayananbb998412012-10-10 23:07:40 +0530261 pr_info("reading page at %#llx\n", (long long)addr);
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200262 err = mtd_read(mtd, addr, pgsize, &read, pp4);
Brian Norrisd57f40542011-09-20 18:34:25 -0700263 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200264 err = 0;
265 if (err || read != pgsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530266 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200267 (long long)addr);
268 kfree(pp1);
269 return err;
270 }
271
272 /* pp2 and pp4 should be the same */
Vikram Narayananbb998412012-10-10 23:07:40 +0530273 pr_info("verifying pages read at %#llx match\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200274 (long long)addr0);
275 if (memcmp(pp2, pp4, pgsize)) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530276 pr_err("verify failed!\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200277 errcnt += 1;
278 } else if (!err)
Vikram Narayananbb998412012-10-10 23:07:40 +0530279 pr_info("crosstest ok\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200280 kfree(pp1);
281 return err;
282}
283
284static int erasecrosstest(void)
285{
Artem Bityutskiy30fa9842011-12-29 15:16:28 +0200286 size_t read, written;
Roel Kluin7fc14bc2010-07-13 13:24:31 +0300287 int err = 0, i, ebnum, ebnum2;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200288 loff_t addr0;
289 char *readbuf = twopages;
290
Vikram Narayananbb998412012-10-10 23:07:40 +0530291 pr_info("erasecrosstest\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200292
293 ebnum = 0;
294 addr0 = 0;
Roel Kluinc6f7e7b2009-07-31 16:21:01 +0200295 for (i = 0; i < ebcnt && bbt[i]; ++i) {
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200296 addr0 += mtd->erasesize;
297 ebnum += 1;
298 }
299
300 ebnum2 = ebcnt - 1;
301 while (ebnum2 && bbt[ebnum2])
302 ebnum2 -= 1;
303
Vikram Narayananbb998412012-10-10 23:07:40 +0530304 pr_info("erasing block %d\n", ebnum);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200305 err = erase_eraseblock(ebnum);
306 if (err)
307 return err;
308
Vikram Narayananbb998412012-10-10 23:07:40 +0530309 pr_info("writing 1st page of block %d\n", ebnum);
Akinobu Mita825b8cc2013-02-27 17:05:35 -0800310 prandom_bytes_state(&rnd_state, writebuf, pgsize);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200311 strcpy(writebuf, "There is no data like this!");
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200312 err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200313 if (err || written != pgsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530314 pr_info("error: write failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200315 (long long)addr0);
316 return err ? err : -1;
317 }
318
Vikram Narayananbb998412012-10-10 23:07:40 +0530319 pr_info("reading 1st page of block %d\n", ebnum);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200320 memset(readbuf, 0, pgsize);
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200321 err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
Brian Norrisd57f40542011-09-20 18:34:25 -0700322 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200323 err = 0;
324 if (err || read != pgsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530325 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200326 (long long)addr0);
327 return err ? err : -1;
328 }
329
Vikram Narayananbb998412012-10-10 23:07:40 +0530330 pr_info("verifying 1st page of block %d\n", ebnum);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200331 if (memcmp(writebuf, readbuf, pgsize)) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530332 pr_err("verify failed!\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200333 errcnt += 1;
Roel Kluin7fc14bc2010-07-13 13:24:31 +0300334 return -1;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200335 }
336
Vikram Narayananbb998412012-10-10 23:07:40 +0530337 pr_info("erasing block %d\n", ebnum);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200338 err = erase_eraseblock(ebnum);
339 if (err)
340 return err;
341
Vikram Narayananbb998412012-10-10 23:07:40 +0530342 pr_info("writing 1st page of block %d\n", ebnum);
Akinobu Mita825b8cc2013-02-27 17:05:35 -0800343 prandom_bytes_state(&rnd_state, writebuf, pgsize);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200344 strcpy(writebuf, "There is no data like this!");
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200345 err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200346 if (err || written != pgsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530347 pr_err("error: write failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200348 (long long)addr0);
349 return err ? err : -1;
350 }
351
Vikram Narayananbb998412012-10-10 23:07:40 +0530352 pr_info("erasing block %d\n", ebnum2);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200353 err = erase_eraseblock(ebnum2);
354 if (err)
355 return err;
356
Vikram Narayananbb998412012-10-10 23:07:40 +0530357 pr_info("reading 1st page of block %d\n", ebnum);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200358 memset(readbuf, 0, pgsize);
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200359 err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
Brian Norrisd57f40542011-09-20 18:34:25 -0700360 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200361 err = 0;
362 if (err || read != pgsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530363 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200364 (long long)addr0);
365 return err ? err : -1;
366 }
367
Vikram Narayananbb998412012-10-10 23:07:40 +0530368 pr_info("verifying 1st page of block %d\n", ebnum);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200369 if (memcmp(writebuf, readbuf, pgsize)) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530370 pr_err("verify failed!\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200371 errcnt += 1;
Roel Kluin7fc14bc2010-07-13 13:24:31 +0300372 return -1;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200373 }
374
Roel Kluin7fc14bc2010-07-13 13:24:31 +0300375 if (!err)
Vikram Narayananbb998412012-10-10 23:07:40 +0530376 pr_info("erasecrosstest ok\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200377 return err;
378}
379
380static int erasetest(void)
381{
Artem Bityutskiy30fa9842011-12-29 15:16:28 +0200382 size_t read, written;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200383 int err = 0, i, ebnum, ok = 1;
384 loff_t addr0;
385
Vikram Narayananbb998412012-10-10 23:07:40 +0530386 pr_info("erasetest\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200387
388 ebnum = 0;
389 addr0 = 0;
Roel Kluinc6f7e7b2009-07-31 16:21:01 +0200390 for (i = 0; i < ebcnt && bbt[i]; ++i) {
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200391 addr0 += mtd->erasesize;
392 ebnum += 1;
393 }
394
Vikram Narayananbb998412012-10-10 23:07:40 +0530395 pr_info("erasing block %d\n", ebnum);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200396 err = erase_eraseblock(ebnum);
397 if (err)
398 return err;
399
Vikram Narayananbb998412012-10-10 23:07:40 +0530400 pr_info("writing 1st page of block %d\n", ebnum);
Akinobu Mita825b8cc2013-02-27 17:05:35 -0800401 prandom_bytes_state(&rnd_state, writebuf, pgsize);
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200402 err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200403 if (err || written != pgsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530404 pr_err("error: write failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200405 (long long)addr0);
406 return err ? err : -1;
407 }
408
Vikram Narayananbb998412012-10-10 23:07:40 +0530409 pr_info("erasing block %d\n", ebnum);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200410 err = erase_eraseblock(ebnum);
411 if (err)
412 return err;
413
Vikram Narayananbb998412012-10-10 23:07:40 +0530414 pr_info("reading 1st page of block %d\n", ebnum);
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200415 err = mtd_read(mtd, addr0, pgsize, &read, twopages);
Brian Norrisd57f40542011-09-20 18:34:25 -0700416 if (mtd_is_bitflip(err))
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200417 err = 0;
418 if (err || read != pgsize) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530419 pr_err("error: read failed at %#llx\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200420 (long long)addr0);
421 return err ? err : -1;
422 }
423
Vikram Narayananbb998412012-10-10 23:07:40 +0530424 pr_info("verifying 1st page of block %d is all 0xff\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200425 ebnum);
426 for (i = 0; i < pgsize; ++i)
427 if (twopages[i] != 0xff) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530428 pr_err("verifying all 0xff failed at %d\n",
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200429 i);
430 errcnt += 1;
431 ok = 0;
432 break;
433 }
434
435 if (ok && !err)
Vikram Narayananbb998412012-10-10 23:07:40 +0530436 pr_info("erasetest ok\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200437
438 return err;
439}
440
441static int is_block_bad(int ebnum)
442{
443 loff_t addr = ebnum * mtd->erasesize;
444 int ret;
445
Artem Bityutskiy7086c192011-12-23 19:35:30 +0200446 ret = mtd_block_isbad(mtd, addr);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200447 if (ret)
Vikram Narayananbb998412012-10-10 23:07:40 +0530448 pr_info("block %d is bad\n", ebnum);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200449 return ret;
450}
451
452static int scan_for_bad_eraseblocks(void)
453{
454 int i, bad = 0;
455
Julia Lawall2bfefa4c2010-05-13 22:03:15 +0200456 bbt = kzalloc(ebcnt, GFP_KERNEL);
Brian Norris33777e62013-05-02 14:18:51 -0700457 if (!bbt)
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200458 return -ENOMEM;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200459
Vikram Narayananbb998412012-10-10 23:07:40 +0530460 pr_info("scanning for bad eraseblocks\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200461 for (i = 0; i < ebcnt; ++i) {
462 bbt[i] = is_block_bad(i) ? 1 : 0;
463 if (bbt[i])
464 bad += 1;
465 cond_resched();
466 }
Vikram Narayananbb998412012-10-10 23:07:40 +0530467 pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200468 return 0;
469}
470
471static int __init mtd_pagetest_init(void)
472{
473 int err = 0;
474 uint64_t tmp;
475 uint32_t i;
476
477 printk(KERN_INFO "\n");
478 printk(KERN_INFO "=================================================\n");
Wolfram Sang74060602011-10-30 00:11:53 +0200479
480 if (dev < 0) {
Masanari Iida064a7692012-11-09 23:20:58 +0900481 pr_info("Please specify a valid mtd-device via module parameter\n");
Vikram Narayananbb998412012-10-10 23:07:40 +0530482 pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
Wolfram Sang74060602011-10-30 00:11:53 +0200483 return -EINVAL;
484 }
485
Vikram Narayananbb998412012-10-10 23:07:40 +0530486 pr_info("MTD device: %d\n", dev);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200487
488 mtd = get_mtd_device(NULL, dev);
489 if (IS_ERR(mtd)) {
490 err = PTR_ERR(mtd);
Vikram Narayananbb998412012-10-10 23:07:40 +0530491 pr_err("error: cannot get MTD device\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200492 return err;
493 }
494
495 if (mtd->type != MTD_NANDFLASH) {
Vikram Narayananbb998412012-10-10 23:07:40 +0530496 pr_info("this test requires NAND flash\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200497 goto out;
498 }
499
500 tmp = mtd->size;
501 do_div(tmp, mtd->erasesize);
502 ebcnt = tmp;
503 pgcnt = mtd->erasesize / mtd->writesize;
Artem Bityutskiy4c2b8a62009-11-27 16:58:08 +0200504 pgsize = mtd->writesize;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200505
Vikram Narayananbb998412012-10-10 23:07:40 +0530506 pr_info("MTD device size %llu, eraseblock size %u, "
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200507 "page size %u, count of eraseblocks %u, pages per "
508 "eraseblock %u, OOB size %u\n",
509 (unsigned long long)mtd->size, mtd->erasesize,
510 pgsize, ebcnt, pgcnt, mtd->oobsize);
511
512 err = -ENOMEM;
513 bufsize = pgsize * 2;
514 writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
Brian Norris33777e62013-05-02 14:18:51 -0700515 if (!writebuf)
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200516 goto out;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200517 twopages = kmalloc(bufsize, GFP_KERNEL);
Brian Norris33777e62013-05-02 14:18:51 -0700518 if (!twopages)
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200519 goto out;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200520 boundary = kmalloc(bufsize, GFP_KERNEL);
Brian Norris33777e62013-05-02 14:18:51 -0700521 if (!boundary)
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200522 goto out;
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200523
524 err = scan_for_bad_eraseblocks();
525 if (err)
526 goto out;
527
528 /* Erase all eraseblocks */
Vikram Narayananbb998412012-10-10 23:07:40 +0530529 pr_info("erasing whole device\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200530 for (i = 0; i < ebcnt; ++i) {
531 if (bbt[i])
532 continue;
533 err = erase_eraseblock(i);
534 if (err)
535 goto out;
536 cond_resched();
537 }
Vikram Narayananbb998412012-10-10 23:07:40 +0530538 pr_info("erased %u eraseblocks\n", i);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200539
540 /* Write all eraseblocks */
Akinobu Mita825b8cc2013-02-27 17:05:35 -0800541 prandom_seed_state(&rnd_state, 1);
Vikram Narayananbb998412012-10-10 23:07:40 +0530542 pr_info("writing whole device\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200543 for (i = 0; i < ebcnt; ++i) {
544 if (bbt[i])
545 continue;
546 err = write_eraseblock(i);
547 if (err)
548 goto out;
549 if (i % 256 == 0)
Vikram Narayananbb998412012-10-10 23:07:40 +0530550 pr_info("written up to eraseblock %u\n", i);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200551 cond_resched();
552 }
Vikram Narayananbb998412012-10-10 23:07:40 +0530553 pr_info("written %u eraseblocks\n", i);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200554
555 /* Check all eraseblocks */
Akinobu Mita825b8cc2013-02-27 17:05:35 -0800556 prandom_seed_state(&rnd_state, 1);
Vikram Narayananbb998412012-10-10 23:07:40 +0530557 pr_info("verifying all eraseblocks\n");
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200558 for (i = 0; i < ebcnt; ++i) {
559 if (bbt[i])
560 continue;
561 err = verify_eraseblock(i);
562 if (err)
563 goto out;
564 if (i % 256 == 0)
Vikram Narayananbb998412012-10-10 23:07:40 +0530565 pr_info("verified up to eraseblock %u\n", i);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200566 cond_resched();
567 }
Vikram Narayananbb998412012-10-10 23:07:40 +0530568 pr_info("verified %u eraseblocks\n", i);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200569
570 err = crosstest();
571 if (err)
572 goto out;
573
574 err = erasecrosstest();
575 if (err)
576 goto out;
577
578 err = erasetest();
579 if (err)
580 goto out;
581
Vikram Narayananbb998412012-10-10 23:07:40 +0530582 pr_info("finished with %d errors\n", errcnt);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200583out:
584
585 kfree(bbt);
586 kfree(boundary);
587 kfree(twopages);
588 kfree(writebuf);
589 put_mtd_device(mtd);
590 if (err)
Vikram Narayananbb998412012-10-10 23:07:40 +0530591 pr_info("error %d occurred\n", err);
Artem Bityutskiye73f2172008-12-08 13:34:16 +0200592 printk(KERN_INFO "=================================================\n");
593 return err;
594}
595module_init(mtd_pagetest_init);
596
597static void __exit mtd_pagetest_exit(void)
598{
599 return;
600}
601module_exit(mtd_pagetest_exit);
602
603MODULE_DESCRIPTION("NAND page test");
604MODULE_AUTHOR("Adrian Hunter");
605MODULE_LICENSE("GPL");