Commit bf60862a58f7cd881cfe86a3b2aceaea4a22b3b0

Authored by Artem Bityutskiy
1 parent 7163cea15f

MTD: tests: add mtd_subpagetest

This tests makes sure sub-pages on NAND MTD device work fine.

Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>

Showing 1 changed file with 525 additions and 0 deletions Side-by-side Diff

drivers/mtd/tests/mtd_subpagetest.c
  1 +/*
  2 + * Copyright (C) 2006-2007 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 sub-page read and write on MTD device.
  18 + * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
  19 + *
  20 + */
  21 +
  22 +#include <linux/init.h>
  23 +#include <linux/module.h>
  24 +#include <linux/moduleparam.h>
  25 +#include <linux/err.h>
  26 +#include <linux/mtd/mtd.h>
  27 +#include <linux/sched.h>
  28 +
  29 +#define PRINT_PREF KERN_INFO "mtd_subpagetest: "
  30 +
  31 +static int dev;
  32 +module_param(dev, int, S_IRUGO);
  33 +MODULE_PARM_DESC(dev, "MTD device number to use");
  34 +
  35 +static struct mtd_info *mtd;
  36 +static unsigned char *writebuf;
  37 +static unsigned char *readbuf;
  38 +static unsigned char *bbt;
  39 +
  40 +static int subpgsize;
  41 +static int bufsize;
  42 +static int ebcnt;
  43 +static int pgcnt;
  44 +static int errcnt;
  45 +static unsigned long next = 1;
  46 +
  47 +static inline unsigned int simple_rand(void)
  48 +{
  49 + next = next * 1103515245 + 12345;
  50 + return (unsigned int)((next / 65536) % 32768);
  51 +}
  52 +
  53 +static inline void simple_srand(unsigned long seed)
  54 +{
  55 + next = seed;
  56 +}
  57 +
  58 +static void set_random_data(unsigned char *buf, size_t len)
  59 +{
  60 + size_t i;
  61 +
  62 + for (i = 0; i < len; ++i)
  63 + buf[i] = simple_rand();
  64 +}
  65 +
  66 +static inline void clear_data(unsigned char *buf, size_t len)
  67 +{
  68 + memset(buf, 0, len);
  69 +}
  70 +
  71 +static int erase_eraseblock(int ebnum)
  72 +{
  73 + int err;
  74 + struct erase_info ei;
  75 + loff_t addr = ebnum * mtd->erasesize;
  76 +
  77 + memset(&ei, 0, sizeof(struct erase_info));
  78 + ei.mtd = mtd;
  79 + ei.addr = addr;
  80 + ei.len = mtd->erasesize;
  81 +
  82 + err = mtd->erase(mtd, &ei);
  83 + if (err) {
  84 + printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum);
  85 + return err;
  86 + }
  87 +
  88 + if (ei.state == MTD_ERASE_FAILED) {
  89 + printk(PRINT_PREF "some erase error occurred at EB %d\n",
  90 + ebnum);
  91 + return -EIO;
  92 + }
  93 +
  94 + return 0;
  95 +}
  96 +
  97 +static int erase_whole_device(void)
  98 +{
  99 + int err;
  100 + unsigned int i;
  101 +
  102 + printk(PRINT_PREF "erasing whole device\n");
  103 + for (i = 0; i < ebcnt; ++i) {
  104 + if (bbt[i])
  105 + continue;
  106 + err = erase_eraseblock(i);
  107 + if (err)
  108 + return err;
  109 + cond_resched();
  110 + }
  111 + printk(PRINT_PREF "erased %u eraseblocks\n", i);
  112 + return 0;
  113 +}
  114 +
  115 +static int write_eraseblock(int ebnum)
  116 +{
  117 + size_t written = 0;
  118 + int err = 0;
  119 + loff_t addr = ebnum * mtd->erasesize;
  120 +
  121 + set_random_data(writebuf, subpgsize);
  122 + err = mtd->write(mtd, addr, subpgsize, &written, writebuf);
  123 + if (unlikely(err || written != subpgsize)) {
  124 + printk(PRINT_PREF "error: write failed at %#llx\n",
  125 + (long long)addr);
  126 + if (written != subpgsize) {
  127 + printk(PRINT_PREF " write size: %#x\n", subpgsize);
  128 + printk(PRINT_PREF " written: %#x\n", written);
  129 + }
  130 + return err ? err : -1;
  131 + }
  132 +
  133 + addr += subpgsize;
  134 +
  135 + set_random_data(writebuf, subpgsize);
  136 + err = mtd->write(mtd, addr, subpgsize, &written, writebuf);
  137 + if (unlikely(err || written != subpgsize)) {
  138 + printk(PRINT_PREF "error: write failed at %#llx\n",
  139 + (long long)addr);
  140 + if (written != subpgsize) {
  141 + printk(PRINT_PREF " write size: %#x\n", subpgsize);
  142 + printk(PRINT_PREF " written: %#x\n", written);
  143 + }
  144 + return err ? err : -1;
  145 + }
  146 +
  147 + return err;
  148 +}
  149 +
  150 +static int write_eraseblock2(int ebnum)
  151 +{
  152 + size_t written = 0;
  153 + int err = 0, k;
  154 + loff_t addr = ebnum * mtd->erasesize;
  155 +
  156 + for (k = 1; k < 33; ++k) {
  157 + if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
  158 + break;
  159 + set_random_data(writebuf, subpgsize * k);
  160 + err = mtd->write(mtd, addr, subpgsize * k, &written, writebuf);
  161 + if (unlikely(err || written != subpgsize * k)) {
  162 + printk(PRINT_PREF "error: write failed at %#llx\n",
  163 + (long long)addr);
  164 + if (written != subpgsize) {
  165 + printk(PRINT_PREF " write size: %#x\n",
  166 + subpgsize * k);
  167 + printk(PRINT_PREF " written: %#08x\n",
  168 + written);
  169 + }
  170 + return err ? err : -1;
  171 + }
  172 + addr += subpgsize * k;
  173 + }
  174 +
  175 + return err;
  176 +}
  177 +
  178 +static void print_subpage(unsigned char *p)
  179 +{
  180 + int i, j;
  181 +
  182 + for (i = 0; i < subpgsize; ) {
  183 + for (j = 0; i < subpgsize && j < 32; ++i, ++j)
  184 + printk("%02x", *p++);
  185 + printk("\n");
  186 + }
  187 +}
  188 +
  189 +static int verify_eraseblock(int ebnum)
  190 +{
  191 + size_t read = 0;
  192 + int err = 0;
  193 + loff_t addr = ebnum * mtd->erasesize;
  194 +
  195 + set_random_data(writebuf, subpgsize);
  196 + clear_data(readbuf, subpgsize);
  197 + read = 0;
  198 + err = mtd->read(mtd, addr, subpgsize, &read, readbuf);
  199 + if (unlikely(err || read != subpgsize)) {
  200 + if (err == -EUCLEAN && read == subpgsize) {
  201 + printk(PRINT_PREF "ECC correction at %#llx\n",
  202 + (long long)addr);
  203 + err = 0;
  204 + } else {
  205 + printk(PRINT_PREF "error: read failed at %#llx\n",
  206 + (long long)addr);
  207 + return err ? err : -1;
  208 + }
  209 + }
  210 + if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
  211 + printk(PRINT_PREF "error: verify failed at %#llx\n",
  212 + (long long)addr);
  213 + printk(PRINT_PREF "------------- written----------------\n");
  214 + print_subpage(writebuf);
  215 + printk(PRINT_PREF "------------- read ------------------\n");
  216 + print_subpage(readbuf);
  217 + printk(PRINT_PREF "-------------------------------------\n");
  218 + errcnt += 1;
  219 + }
  220 +
  221 + addr += subpgsize;
  222 +
  223 + set_random_data(writebuf, subpgsize);
  224 + clear_data(readbuf, subpgsize);
  225 + read = 0;
  226 + err = mtd->read(mtd, addr, subpgsize, &read, readbuf);
  227 + if (unlikely(err || read != subpgsize)) {
  228 + if (err == -EUCLEAN && read == subpgsize) {
  229 + printk(PRINT_PREF "ECC correction at %#llx\n",
  230 + (long long)addr);
  231 + err = 0;
  232 + } else {
  233 + printk(PRINT_PREF "error: read failed at %#llx\n",
  234 + (long long)addr);
  235 + return err ? err : -1;
  236 + }
  237 + }
  238 + if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
  239 + printk(PRINT_PREF "error: verify failed at %#llx\n",
  240 + (long long)addr);
  241 + printk(PRINT_PREF "------------- written----------------\n");
  242 + print_subpage(writebuf);
  243 + printk(PRINT_PREF "------------- read ------------------\n");
  244 + print_subpage(readbuf);
  245 + printk(PRINT_PREF "-------------------------------------\n");
  246 + errcnt += 1;
  247 + }
  248 +
  249 + return err;
  250 +}
  251 +
  252 +static int verify_eraseblock2(int ebnum)
  253 +{
  254 + size_t read = 0;
  255 + int err = 0, k;
  256 + loff_t addr = ebnum * mtd->erasesize;
  257 +
  258 + for (k = 1; k < 33; ++k) {
  259 + if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
  260 + break;
  261 + set_random_data(writebuf, subpgsize * k);
  262 + clear_data(readbuf, subpgsize * k);
  263 + read = 0;
  264 + err = mtd->read(mtd, addr, subpgsize * k, &read, readbuf);
  265 + if (unlikely(err || read != subpgsize * k)) {
  266 + if (err == -EUCLEAN && read == subpgsize * k) {
  267 + printk(PRINT_PREF "ECC correction at %#llx\n",
  268 + (long long)addr);
  269 + err = 0;
  270 + } else {
  271 + printk(PRINT_PREF "error: read failed at "
  272 + "%#llx\n", (long long)addr);
  273 + return err ? err : -1;
  274 + }
  275 + }
  276 + if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) {
  277 + printk(PRINT_PREF "error: verify failed at %#llx\n",
  278 + (long long)addr);
  279 + errcnt += 1;
  280 + }
  281 + addr += subpgsize * k;
  282 + }
  283 +
  284 + return err;
  285 +}
  286 +
  287 +static int verify_eraseblock_ff(int ebnum)
  288 +{
  289 + uint32_t j;
  290 + size_t read = 0;
  291 + int err = 0;
  292 + loff_t addr = ebnum * mtd->erasesize;
  293 +
  294 + memset(writebuf, 0xff, subpgsize);
  295 + for (j = 0; j < mtd->erasesize / subpgsize; ++j) {
  296 + clear_data(readbuf, subpgsize);
  297 + read = 0;
  298 + err = mtd->read(mtd, addr, subpgsize, &read, readbuf);
  299 + if (unlikely(err || read != subpgsize)) {
  300 + if (err == -EUCLEAN && read == subpgsize) {
  301 + printk(PRINT_PREF "ECC correction at %#llx\n",
  302 + (long long)addr);
  303 + err = 0;
  304 + } else {
  305 + printk(PRINT_PREF "error: read failed at "
  306 + "%#llx\n", (long long)addr);
  307 + return err ? err : -1;
  308 + }
  309 + }
  310 + if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
  311 + printk(PRINT_PREF "error: verify 0xff failed at "
  312 + "%#llx\n", (long long)addr);
  313 + errcnt += 1;
  314 + }
  315 + addr += subpgsize;
  316 + }
  317 +
  318 + return err;
  319 +}
  320 +
  321 +static int verify_all_eraseblocks_ff(void)
  322 +{
  323 + int err;
  324 + unsigned int i;
  325 +
  326 + printk(PRINT_PREF "verifying all eraseblocks for 0xff\n");
  327 + for (i = 0; i < ebcnt; ++i) {
  328 + if (bbt[i])
  329 + continue;
  330 + err = verify_eraseblock_ff(i);
  331 + if (err)
  332 + return err;
  333 + if (i % 256 == 0)
  334 + printk(PRINT_PREF "verified up to eraseblock %u\n", i);
  335 + cond_resched();
  336 + }
  337 + printk(PRINT_PREF "verified %u eraseblocks\n", i);
  338 + return 0;
  339 +}
  340 +
  341 +static int is_block_bad(int ebnum)
  342 +{
  343 + loff_t addr = ebnum * mtd->erasesize;
  344 + int ret;
  345 +
  346 + ret = mtd->block_isbad(mtd, addr);
  347 + if (ret)
  348 + printk(PRINT_PREF "block %d is bad\n", ebnum);
  349 + return ret;
  350 +}
  351 +
  352 +static int scan_for_bad_eraseblocks(void)
  353 +{
  354 + int i, bad = 0;
  355 +
  356 + bbt = kmalloc(ebcnt, GFP_KERNEL);
  357 + if (!bbt) {
  358 + printk(PRINT_PREF "error: cannot allocate memory\n");
  359 + return -ENOMEM;
  360 + }
  361 + memset(bbt, 0 , ebcnt);
  362 +
  363 + printk(PRINT_PREF "scanning for bad eraseblocks\n");
  364 + for (i = 0; i < ebcnt; ++i) {
  365 + bbt[i] = is_block_bad(i) ? 1 : 0;
  366 + if (bbt[i])
  367 + bad += 1;
  368 + cond_resched();
  369 + }
  370 + printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad);
  371 + return 0;
  372 +}
  373 +
  374 +static int __init mtd_subpagetest_init(void)
  375 +{
  376 + int err = 0;
  377 + uint32_t i;
  378 + uint64_t tmp;
  379 +
  380 + printk(KERN_INFO "\n");
  381 + printk(KERN_INFO "=================================================\n");
  382 + printk(PRINT_PREF "MTD device: %d\n", dev);
  383 +
  384 + mtd = get_mtd_device(NULL, dev);
  385 + if (IS_ERR(mtd)) {
  386 + err = PTR_ERR(mtd);
  387 + printk(PRINT_PREF "error: cannot get MTD device\n");
  388 + return err;
  389 + }
  390 +
  391 + if (mtd->type != MTD_NANDFLASH) {
  392 + printk(PRINT_PREF "this test requires NAND flash\n");
  393 + goto out;
  394 + }
  395 +
  396 + subpgsize = mtd->writesize >> mtd->subpage_sft;
  397 + printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
  398 + "page size %u, subpage size %u, count of eraseblocks %u, "
  399 + "pages per eraseblock %u, OOB size %u\n",
  400 + (unsigned long long)mtd->size, mtd->erasesize,
  401 + mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize);
  402 +
  403 + err = -ENOMEM;
  404 + bufsize = subpgsize * 32;
  405 + writebuf = kmalloc(bufsize, GFP_KERNEL);
  406 + if (!writebuf) {
  407 + printk(PRINT_PREF "error: cannot allocate memory\n");
  408 + goto out;
  409 + }
  410 + readbuf = kmalloc(bufsize, GFP_KERNEL);
  411 + if (!readbuf) {
  412 + printk(PRINT_PREF "error: cannot allocate memory\n");
  413 + goto out;
  414 + }
  415 +
  416 + tmp = mtd->size;
  417 + do_div(tmp, mtd->erasesize);
  418 + ebcnt = tmp;
  419 + pgcnt = mtd->erasesize / mtd->writesize;
  420 +
  421 + err = scan_for_bad_eraseblocks();
  422 + if (err)
  423 + goto out;
  424 +
  425 + err = erase_whole_device();
  426 + if (err)
  427 + goto out;
  428 +
  429 + printk(PRINT_PREF "writing whole device\n");
  430 + simple_srand(1);
  431 + for (i = 0; i < ebcnt; ++i) {
  432 + if (bbt[i])
  433 + continue;
  434 + err = write_eraseblock(i);
  435 + if (unlikely(err))
  436 + goto out;
  437 + if (i % 256 == 0)
  438 + printk(PRINT_PREF "written up to eraseblock %u\n", i);
  439 + cond_resched();
  440 + }
  441 + printk(PRINT_PREF "written %u eraseblocks\n", i);
  442 +
  443 + simple_srand(1);
  444 + printk(PRINT_PREF "verifying all eraseblocks\n");
  445 + for (i = 0; i < ebcnt; ++i) {
  446 + if (bbt[i])
  447 + continue;
  448 + err = verify_eraseblock(i);
  449 + if (unlikely(err))
  450 + goto out;
  451 + if (i % 256 == 0)
  452 + printk(PRINT_PREF "verified up to eraseblock %u\n", i);
  453 + cond_resched();
  454 + }
  455 + printk(PRINT_PREF "verified %u eraseblocks\n", i);
  456 +
  457 + err = erase_whole_device();
  458 + if (err)
  459 + goto out;
  460 +
  461 + err = verify_all_eraseblocks_ff();
  462 + if (err)
  463 + goto out;
  464 +
  465 + /* Write all eraseblocks */
  466 + simple_srand(3);
  467 + printk(PRINT_PREF "writing whole device\n");
  468 + for (i = 0; i < ebcnt; ++i) {
  469 + if (bbt[i])
  470 + continue;
  471 + err = write_eraseblock2(i);
  472 + if (unlikely(err))
  473 + goto out;
  474 + if (i % 256 == 0)
  475 + printk(PRINT_PREF "written up to eraseblock %u\n", i);
  476 + cond_resched();
  477 + }
  478 + printk(PRINT_PREF "written %u eraseblocks\n", i);
  479 +
  480 + /* Check all eraseblocks */
  481 + simple_srand(3);
  482 + printk(PRINT_PREF "verifying all eraseblocks\n");
  483 + for (i = 0; i < ebcnt; ++i) {
  484 + if (bbt[i])
  485 + continue;
  486 + err = verify_eraseblock2(i);
  487 + if (unlikely(err))
  488 + goto out;
  489 + if (i % 256 == 0)
  490 + printk(PRINT_PREF "verified up to eraseblock %u\n", i);
  491 + cond_resched();
  492 + }
  493 + printk(PRINT_PREF "verified %u eraseblocks\n", i);
  494 +
  495 + err = erase_whole_device();
  496 + if (err)
  497 + goto out;
  498 +
  499 + err = verify_all_eraseblocks_ff();
  500 + if (err)
  501 + goto out;
  502 +
  503 + printk(PRINT_PREF "finished with %d errors\n", errcnt);
  504 +
  505 +out:
  506 + kfree(bbt);
  507 + kfree(readbuf);
  508 + kfree(writebuf);
  509 + put_mtd_device(mtd);
  510 + if (err)
  511 + printk(PRINT_PREF "error %d occurred\n", err);
  512 + printk(KERN_INFO "=================================================\n");
  513 + return err;
  514 +}
  515 +module_init(mtd_subpagetest_init);
  516 +
  517 +static void __exit mtd_subpagetest_exit(void)
  518 +{
  519 + return;
  520 +}
  521 +module_exit(mtd_subpagetest_exit);
  522 +
  523 +MODULE_DESCRIPTION("Subpage test module");
  524 +MODULE_AUTHOR("Adrian Hunter");
  525 +MODULE_LICENSE("GPL");