18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2006-2008 Nokia Corporation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Test page read and write on MTD device. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/div64.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/sched.h> 208c2ecf20Sopenharmony_ci#include <linux/random.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "mtd_test.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int dev = -EINVAL; 258c2ecf20Sopenharmony_cimodule_param(dev, int, S_IRUGO); 268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dev, "MTD device number to use"); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic struct mtd_info *mtd; 298c2ecf20Sopenharmony_cistatic unsigned char *twopages; 308c2ecf20Sopenharmony_cistatic unsigned char *writebuf; 318c2ecf20Sopenharmony_cistatic unsigned char *boundary; 328c2ecf20Sopenharmony_cistatic unsigned char *bbt; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int pgsize; 358c2ecf20Sopenharmony_cistatic int bufsize; 368c2ecf20Sopenharmony_cistatic int ebcnt; 378c2ecf20Sopenharmony_cistatic int pgcnt; 388c2ecf20Sopenharmony_cistatic int errcnt; 398c2ecf20Sopenharmony_cistatic struct rnd_state rnd_state; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int write_eraseblock(int ebnum) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); 468c2ecf20Sopenharmony_ci cond_resched(); 478c2ecf20Sopenharmony_ci return mtdtest_write(mtd, addr, mtd->erasesize, writebuf); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int verify_eraseblock(int ebnum) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci uint32_t j; 538c2ecf20Sopenharmony_ci int err = 0, i; 548c2ecf20Sopenharmony_ci loff_t addr0, addrn; 558c2ecf20Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci addr0 = 0; 588c2ecf20Sopenharmony_ci for (i = 0; i < ebcnt && bbt[i]; ++i) 598c2ecf20Sopenharmony_ci addr0 += mtd->erasesize; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci addrn = mtd->size; 628c2ecf20Sopenharmony_ci for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) 638c2ecf20Sopenharmony_ci addrn -= mtd->erasesize; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); 668c2ecf20Sopenharmony_ci for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) { 678c2ecf20Sopenharmony_ci /* Do a read to set the internal dataRAMs to different data */ 688c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr0, bufsize, twopages); 698c2ecf20Sopenharmony_ci if (err) 708c2ecf20Sopenharmony_ci return err; 718c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages); 728c2ecf20Sopenharmony_ci if (err) 738c2ecf20Sopenharmony_ci return err; 748c2ecf20Sopenharmony_ci memset(twopages, 0, bufsize); 758c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr, bufsize, twopages); 768c2ecf20Sopenharmony_ci if (err) 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) { 798c2ecf20Sopenharmony_ci pr_err("error: verify failed at %#llx\n", 808c2ecf20Sopenharmony_ci (long long)addr); 818c2ecf20Sopenharmony_ci errcnt += 1; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci /* Check boundary between eraseblocks */ 858c2ecf20Sopenharmony_ci if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) { 868c2ecf20Sopenharmony_ci struct rnd_state old_state = rnd_state; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* Do a read to set the internal dataRAMs to different data */ 898c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr0, bufsize, twopages); 908c2ecf20Sopenharmony_ci if (err) 918c2ecf20Sopenharmony_ci return err; 928c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages); 938c2ecf20Sopenharmony_ci if (err) 948c2ecf20Sopenharmony_ci return err; 958c2ecf20Sopenharmony_ci memset(twopages, 0, bufsize); 968c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr, bufsize, twopages); 978c2ecf20Sopenharmony_ci if (err) 988c2ecf20Sopenharmony_ci return err; 998c2ecf20Sopenharmony_ci memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize); 1008c2ecf20Sopenharmony_ci prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize); 1018c2ecf20Sopenharmony_ci if (memcmp(twopages, boundary, bufsize)) { 1028c2ecf20Sopenharmony_ci pr_err("error: verify failed at %#llx\n", 1038c2ecf20Sopenharmony_ci (long long)addr); 1048c2ecf20Sopenharmony_ci errcnt += 1; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci rnd_state = old_state; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci return err; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int crosstest(void) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci int err = 0, i; 1148c2ecf20Sopenharmony_ci loff_t addr, addr0, addrn; 1158c2ecf20Sopenharmony_ci unsigned char *pp1, *pp2, *pp3, *pp4; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci pr_info("crosstest\n"); 1188c2ecf20Sopenharmony_ci pp1 = kcalloc(pgsize, 4, GFP_KERNEL); 1198c2ecf20Sopenharmony_ci if (!pp1) 1208c2ecf20Sopenharmony_ci return -ENOMEM; 1218c2ecf20Sopenharmony_ci pp2 = pp1 + pgsize; 1228c2ecf20Sopenharmony_ci pp3 = pp2 + pgsize; 1238c2ecf20Sopenharmony_ci pp4 = pp3 + pgsize; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci addr0 = 0; 1268c2ecf20Sopenharmony_ci for (i = 0; i < ebcnt && bbt[i]; ++i) 1278c2ecf20Sopenharmony_ci addr0 += mtd->erasesize; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci addrn = mtd->size; 1308c2ecf20Sopenharmony_ci for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) 1318c2ecf20Sopenharmony_ci addrn -= mtd->erasesize; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Read 2nd-to-last page to pp1 */ 1348c2ecf20Sopenharmony_ci addr = addrn - pgsize - pgsize; 1358c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr, pgsize, pp1); 1368c2ecf20Sopenharmony_ci if (err) { 1378c2ecf20Sopenharmony_ci kfree(pp1); 1388c2ecf20Sopenharmony_ci return err; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Read 3rd-to-last page to pp1 */ 1428c2ecf20Sopenharmony_ci addr = addrn - pgsize - pgsize - pgsize; 1438c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr, pgsize, pp1); 1448c2ecf20Sopenharmony_ci if (err) { 1458c2ecf20Sopenharmony_ci kfree(pp1); 1468c2ecf20Sopenharmony_ci return err; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* Read first page to pp2 */ 1508c2ecf20Sopenharmony_ci addr = addr0; 1518c2ecf20Sopenharmony_ci pr_info("reading page at %#llx\n", (long long)addr); 1528c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr, pgsize, pp2); 1538c2ecf20Sopenharmony_ci if (err) { 1548c2ecf20Sopenharmony_ci kfree(pp1); 1558c2ecf20Sopenharmony_ci return err; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Read last page to pp3 */ 1598c2ecf20Sopenharmony_ci addr = addrn - pgsize; 1608c2ecf20Sopenharmony_ci pr_info("reading page at %#llx\n", (long long)addr); 1618c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr, pgsize, pp3); 1628c2ecf20Sopenharmony_ci if (err) { 1638c2ecf20Sopenharmony_ci kfree(pp1); 1648c2ecf20Sopenharmony_ci return err; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* Read first page again to pp4 */ 1688c2ecf20Sopenharmony_ci addr = addr0; 1698c2ecf20Sopenharmony_ci pr_info("reading page at %#llx\n", (long long)addr); 1708c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr, pgsize, pp4); 1718c2ecf20Sopenharmony_ci if (err) { 1728c2ecf20Sopenharmony_ci kfree(pp1); 1738c2ecf20Sopenharmony_ci return err; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* pp2 and pp4 should be the same */ 1778c2ecf20Sopenharmony_ci pr_info("verifying pages read at %#llx match\n", 1788c2ecf20Sopenharmony_ci (long long)addr0); 1798c2ecf20Sopenharmony_ci if (memcmp(pp2, pp4, pgsize)) { 1808c2ecf20Sopenharmony_ci pr_err("verify failed!\n"); 1818c2ecf20Sopenharmony_ci errcnt += 1; 1828c2ecf20Sopenharmony_ci } else if (!err) 1838c2ecf20Sopenharmony_ci pr_info("crosstest ok\n"); 1848c2ecf20Sopenharmony_ci kfree(pp1); 1858c2ecf20Sopenharmony_ci return err; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int erasecrosstest(void) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci int err = 0, i, ebnum, ebnum2; 1918c2ecf20Sopenharmony_ci loff_t addr0; 1928c2ecf20Sopenharmony_ci char *readbuf = twopages; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci pr_info("erasecrosstest\n"); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ebnum = 0; 1978c2ecf20Sopenharmony_ci addr0 = 0; 1988c2ecf20Sopenharmony_ci for (i = 0; i < ebcnt && bbt[i]; ++i) { 1998c2ecf20Sopenharmony_ci addr0 += mtd->erasesize; 2008c2ecf20Sopenharmony_ci ebnum += 1; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci ebnum2 = ebcnt - 1; 2048c2ecf20Sopenharmony_ci while (ebnum2 && bbt[ebnum2]) 2058c2ecf20Sopenharmony_ci ebnum2 -= 1; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci pr_info("erasing block %d\n", ebnum); 2088c2ecf20Sopenharmony_ci err = mtdtest_erase_eraseblock(mtd, ebnum); 2098c2ecf20Sopenharmony_ci if (err) 2108c2ecf20Sopenharmony_ci return err; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci pr_info("writing 1st page of block %d\n", ebnum); 2138c2ecf20Sopenharmony_ci prandom_bytes_state(&rnd_state, writebuf, pgsize); 2148c2ecf20Sopenharmony_ci strcpy(writebuf, "There is no data like this!"); 2158c2ecf20Sopenharmony_ci err = mtdtest_write(mtd, addr0, pgsize, writebuf); 2168c2ecf20Sopenharmony_ci if (err) 2178c2ecf20Sopenharmony_ci return err; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci pr_info("reading 1st page of block %d\n", ebnum); 2208c2ecf20Sopenharmony_ci memset(readbuf, 0, pgsize); 2218c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr0, pgsize, readbuf); 2228c2ecf20Sopenharmony_ci if (err) 2238c2ecf20Sopenharmony_ci return err; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci pr_info("verifying 1st page of block %d\n", ebnum); 2268c2ecf20Sopenharmony_ci if (memcmp(writebuf, readbuf, pgsize)) { 2278c2ecf20Sopenharmony_ci pr_err("verify failed!\n"); 2288c2ecf20Sopenharmony_ci errcnt += 1; 2298c2ecf20Sopenharmony_ci return -1; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci pr_info("erasing block %d\n", ebnum); 2338c2ecf20Sopenharmony_ci err = mtdtest_erase_eraseblock(mtd, ebnum); 2348c2ecf20Sopenharmony_ci if (err) 2358c2ecf20Sopenharmony_ci return err; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci pr_info("writing 1st page of block %d\n", ebnum); 2388c2ecf20Sopenharmony_ci prandom_bytes_state(&rnd_state, writebuf, pgsize); 2398c2ecf20Sopenharmony_ci strcpy(writebuf, "There is no data like this!"); 2408c2ecf20Sopenharmony_ci err = mtdtest_write(mtd, addr0, pgsize, writebuf); 2418c2ecf20Sopenharmony_ci if (err) 2428c2ecf20Sopenharmony_ci return err; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci pr_info("erasing block %d\n", ebnum2); 2458c2ecf20Sopenharmony_ci err = mtdtest_erase_eraseblock(mtd, ebnum2); 2468c2ecf20Sopenharmony_ci if (err) 2478c2ecf20Sopenharmony_ci return err; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci pr_info("reading 1st page of block %d\n", ebnum); 2508c2ecf20Sopenharmony_ci memset(readbuf, 0, pgsize); 2518c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr0, pgsize, readbuf); 2528c2ecf20Sopenharmony_ci if (err) 2538c2ecf20Sopenharmony_ci return err; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci pr_info("verifying 1st page of block %d\n", ebnum); 2568c2ecf20Sopenharmony_ci if (memcmp(writebuf, readbuf, pgsize)) { 2578c2ecf20Sopenharmony_ci pr_err("verify failed!\n"); 2588c2ecf20Sopenharmony_ci errcnt += 1; 2598c2ecf20Sopenharmony_ci return -1; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (!err) 2638c2ecf20Sopenharmony_ci pr_info("erasecrosstest ok\n"); 2648c2ecf20Sopenharmony_ci return err; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int erasetest(void) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci int err = 0, i, ebnum, ok = 1; 2708c2ecf20Sopenharmony_ci loff_t addr0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci pr_info("erasetest\n"); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci ebnum = 0; 2758c2ecf20Sopenharmony_ci addr0 = 0; 2768c2ecf20Sopenharmony_ci for (i = 0; i < ebcnt && bbt[i]; ++i) { 2778c2ecf20Sopenharmony_ci addr0 += mtd->erasesize; 2788c2ecf20Sopenharmony_ci ebnum += 1; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci pr_info("erasing block %d\n", ebnum); 2828c2ecf20Sopenharmony_ci err = mtdtest_erase_eraseblock(mtd, ebnum); 2838c2ecf20Sopenharmony_ci if (err) 2848c2ecf20Sopenharmony_ci return err; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci pr_info("writing 1st page of block %d\n", ebnum); 2878c2ecf20Sopenharmony_ci prandom_bytes_state(&rnd_state, writebuf, pgsize); 2888c2ecf20Sopenharmony_ci err = mtdtest_write(mtd, addr0, pgsize, writebuf); 2898c2ecf20Sopenharmony_ci if (err) 2908c2ecf20Sopenharmony_ci return err; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci pr_info("erasing block %d\n", ebnum); 2938c2ecf20Sopenharmony_ci err = mtdtest_erase_eraseblock(mtd, ebnum); 2948c2ecf20Sopenharmony_ci if (err) 2958c2ecf20Sopenharmony_ci return err; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci pr_info("reading 1st page of block %d\n", ebnum); 2988c2ecf20Sopenharmony_ci err = mtdtest_read(mtd, addr0, pgsize, twopages); 2998c2ecf20Sopenharmony_ci if (err) 3008c2ecf20Sopenharmony_ci return err; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci pr_info("verifying 1st page of block %d is all 0xff\n", 3038c2ecf20Sopenharmony_ci ebnum); 3048c2ecf20Sopenharmony_ci for (i = 0; i < pgsize; ++i) 3058c2ecf20Sopenharmony_ci if (twopages[i] != 0xff) { 3068c2ecf20Sopenharmony_ci pr_err("verifying all 0xff failed at %d\n", 3078c2ecf20Sopenharmony_ci i); 3088c2ecf20Sopenharmony_ci errcnt += 1; 3098c2ecf20Sopenharmony_ci ok = 0; 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (ok && !err) 3148c2ecf20Sopenharmony_ci pr_info("erasetest ok\n"); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return err; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int __init mtd_pagetest_init(void) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci int err = 0; 3228c2ecf20Sopenharmony_ci uint64_t tmp; 3238c2ecf20Sopenharmony_ci uint32_t i; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci printk(KERN_INFO "\n"); 3268c2ecf20Sopenharmony_ci printk(KERN_INFO "=================================================\n"); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (dev < 0) { 3298c2ecf20Sopenharmony_ci pr_info("Please specify a valid mtd-device via module parameter\n"); 3308c2ecf20Sopenharmony_ci pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); 3318c2ecf20Sopenharmony_ci return -EINVAL; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci pr_info("MTD device: %d\n", dev); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci mtd = get_mtd_device(NULL, dev); 3378c2ecf20Sopenharmony_ci if (IS_ERR(mtd)) { 3388c2ecf20Sopenharmony_ci err = PTR_ERR(mtd); 3398c2ecf20Sopenharmony_ci pr_err("error: cannot get MTD device\n"); 3408c2ecf20Sopenharmony_ci return err; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (!mtd_type_is_nand(mtd)) { 3448c2ecf20Sopenharmony_ci pr_info("this test requires NAND flash\n"); 3458c2ecf20Sopenharmony_ci goto out; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci tmp = mtd->size; 3498c2ecf20Sopenharmony_ci do_div(tmp, mtd->erasesize); 3508c2ecf20Sopenharmony_ci ebcnt = tmp; 3518c2ecf20Sopenharmony_ci pgcnt = mtd->erasesize / mtd->writesize; 3528c2ecf20Sopenharmony_ci pgsize = mtd->writesize; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci pr_info("MTD device size %llu, eraseblock size %u, " 3558c2ecf20Sopenharmony_ci "page size %u, count of eraseblocks %u, pages per " 3568c2ecf20Sopenharmony_ci "eraseblock %u, OOB size %u\n", 3578c2ecf20Sopenharmony_ci (unsigned long long)mtd->size, mtd->erasesize, 3588c2ecf20Sopenharmony_ci pgsize, ebcnt, pgcnt, mtd->oobsize); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci err = -ENOMEM; 3618c2ecf20Sopenharmony_ci bufsize = pgsize * 2; 3628c2ecf20Sopenharmony_ci writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); 3638c2ecf20Sopenharmony_ci if (!writebuf) 3648c2ecf20Sopenharmony_ci goto out; 3658c2ecf20Sopenharmony_ci twopages = kmalloc(bufsize, GFP_KERNEL); 3668c2ecf20Sopenharmony_ci if (!twopages) 3678c2ecf20Sopenharmony_ci goto out; 3688c2ecf20Sopenharmony_ci boundary = kmalloc(bufsize, GFP_KERNEL); 3698c2ecf20Sopenharmony_ci if (!boundary) 3708c2ecf20Sopenharmony_ci goto out; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci bbt = kzalloc(ebcnt, GFP_KERNEL); 3738c2ecf20Sopenharmony_ci if (!bbt) 3748c2ecf20Sopenharmony_ci goto out; 3758c2ecf20Sopenharmony_ci err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); 3768c2ecf20Sopenharmony_ci if (err) 3778c2ecf20Sopenharmony_ci goto out; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* Erase all eraseblocks */ 3808c2ecf20Sopenharmony_ci pr_info("erasing whole device\n"); 3818c2ecf20Sopenharmony_ci err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); 3828c2ecf20Sopenharmony_ci if (err) 3838c2ecf20Sopenharmony_ci goto out; 3848c2ecf20Sopenharmony_ci pr_info("erased %u eraseblocks\n", ebcnt); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Write all eraseblocks */ 3878c2ecf20Sopenharmony_ci prandom_seed_state(&rnd_state, 1); 3888c2ecf20Sopenharmony_ci pr_info("writing whole device\n"); 3898c2ecf20Sopenharmony_ci for (i = 0; i < ebcnt; ++i) { 3908c2ecf20Sopenharmony_ci if (bbt[i]) 3918c2ecf20Sopenharmony_ci continue; 3928c2ecf20Sopenharmony_ci err = write_eraseblock(i); 3938c2ecf20Sopenharmony_ci if (err) 3948c2ecf20Sopenharmony_ci goto out; 3958c2ecf20Sopenharmony_ci if (i % 256 == 0) 3968c2ecf20Sopenharmony_ci pr_info("written up to eraseblock %u\n", i); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci err = mtdtest_relax(); 3998c2ecf20Sopenharmony_ci if (err) 4008c2ecf20Sopenharmony_ci goto out; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci pr_info("written %u eraseblocks\n", i); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* Check all eraseblocks */ 4058c2ecf20Sopenharmony_ci prandom_seed_state(&rnd_state, 1); 4068c2ecf20Sopenharmony_ci pr_info("verifying all eraseblocks\n"); 4078c2ecf20Sopenharmony_ci for (i = 0; i < ebcnt; ++i) { 4088c2ecf20Sopenharmony_ci if (bbt[i]) 4098c2ecf20Sopenharmony_ci continue; 4108c2ecf20Sopenharmony_ci err = verify_eraseblock(i); 4118c2ecf20Sopenharmony_ci if (err) 4128c2ecf20Sopenharmony_ci goto out; 4138c2ecf20Sopenharmony_ci if (i % 256 == 0) 4148c2ecf20Sopenharmony_ci pr_info("verified up to eraseblock %u\n", i); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci err = mtdtest_relax(); 4178c2ecf20Sopenharmony_ci if (err) 4188c2ecf20Sopenharmony_ci goto out; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci pr_info("verified %u eraseblocks\n", i); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci err = crosstest(); 4238c2ecf20Sopenharmony_ci if (err) 4248c2ecf20Sopenharmony_ci goto out; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (ebcnt > 1) { 4278c2ecf20Sopenharmony_ci err = erasecrosstest(); 4288c2ecf20Sopenharmony_ci if (err) 4298c2ecf20Sopenharmony_ci goto out; 4308c2ecf20Sopenharmony_ci } else { 4318c2ecf20Sopenharmony_ci pr_info("skipping erasecrosstest, 2 erase blocks needed\n"); 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci err = erasetest(); 4358c2ecf20Sopenharmony_ci if (err) 4368c2ecf20Sopenharmony_ci goto out; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci pr_info("finished with %d errors\n", errcnt); 4398c2ecf20Sopenharmony_ciout: 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci kfree(bbt); 4428c2ecf20Sopenharmony_ci kfree(boundary); 4438c2ecf20Sopenharmony_ci kfree(twopages); 4448c2ecf20Sopenharmony_ci kfree(writebuf); 4458c2ecf20Sopenharmony_ci put_mtd_device(mtd); 4468c2ecf20Sopenharmony_ci if (err) 4478c2ecf20Sopenharmony_ci pr_info("error %d occurred\n", err); 4488c2ecf20Sopenharmony_ci printk(KERN_INFO "=================================================\n"); 4498c2ecf20Sopenharmony_ci return err; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_cimodule_init(mtd_pagetest_init); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void __exit mtd_pagetest_exit(void) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci return; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_cimodule_exit(mtd_pagetest_exit); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NAND page test"); 4608c2ecf20Sopenharmony_ciMODULE_AUTHOR("Adrian Hunter"); 4618c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 462