18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright © 2012 NetCommWireless 48c2ecf20Sopenharmony_ci * Iwo Mergler <Iwo.Mergler@netcommwireless.com.au> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Test for multi-bit error recovery on a NAND page This mostly tests the 78c2ecf20Sopenharmony_ci * ECC controller / driver. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * There are two test modes: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * 0 - artificially inserting bit errors until the ECC fails 128c2ecf20Sopenharmony_ci * This is the default method and fairly quick. It should 138c2ecf20Sopenharmony_ci * be independent of the quality of the FLASH. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * 1 - re-writing the same pattern repeatedly until the ECC fails. 168c2ecf20Sopenharmony_ci * This method relies on the physics of NAND FLASH to eventually 178c2ecf20Sopenharmony_ci * generate '0' bits if '1' has been written sufficient times. 188c2ecf20Sopenharmony_ci * Depending on the NAND, the first bit errors will appear after 198c2ecf20Sopenharmony_ci * 1000 or more writes and then will usually snowball, reaching the 208c2ecf20Sopenharmony_ci * limits of the ECC quickly. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * The test stops after 10000 cycles, should your FLASH be 238c2ecf20Sopenharmony_ci * exceptionally good and not generate bit errors before that. Try 248c2ecf20Sopenharmony_ci * a different page in that case. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Please note that neither of these tests will significantly 'use up' any 278c2ecf20Sopenharmony_ci * FLASH endurance. Only a maximum of two erase operations will be performed. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <linux/init.h> 338c2ecf20Sopenharmony_ci#include <linux/module.h> 348c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 358c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 368c2ecf20Sopenharmony_ci#include <linux/err.h> 378c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 388c2ecf20Sopenharmony_ci#include <linux/slab.h> 398c2ecf20Sopenharmony_ci#include "mtd_test.h" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int dev; 428c2ecf20Sopenharmony_cimodule_param(dev, int, S_IRUGO); 438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dev, "MTD device number to use"); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic unsigned page_offset; 468c2ecf20Sopenharmony_cimodule_param(page_offset, uint, S_IRUGO); 478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(page_offset, "Page number relative to dev start"); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic unsigned seed; 508c2ecf20Sopenharmony_cimodule_param(seed, uint, S_IRUGO); 518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(seed, "Random seed"); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int mode; 548c2ecf20Sopenharmony_cimodule_param(mode, int, S_IRUGO); 558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode, "0=incremental errors, 1=overwrite test"); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic unsigned max_overwrite = 10000; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic loff_t offset; /* Offset of the page we're using. */ 608c2ecf20Sopenharmony_cistatic unsigned eraseblock; /* Eraseblock number for our page. */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* We assume that the ECC can correct up to a certain number 638c2ecf20Sopenharmony_ci * of biterrors per subpage. */ 648c2ecf20Sopenharmony_cistatic unsigned subsize; /* Size of subpages */ 658c2ecf20Sopenharmony_cistatic unsigned subcount; /* Number of subpages per page */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic struct mtd_info *mtd; /* MTD device */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic uint8_t *wbuffer; /* One page write / compare buffer */ 708c2ecf20Sopenharmony_cistatic uint8_t *rbuffer; /* One page read buffer */ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 'random' bytes from known offsets */ 738c2ecf20Sopenharmony_cistatic uint8_t hash(unsigned offset) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci unsigned v = offset; 768c2ecf20Sopenharmony_ci unsigned char c; 778c2ecf20Sopenharmony_ci v ^= 0x7f7edfd3; 788c2ecf20Sopenharmony_ci v = v ^ (v >> 3); 798c2ecf20Sopenharmony_ci v = v ^ (v >> 5); 808c2ecf20Sopenharmony_ci v = v ^ (v >> 13); 818c2ecf20Sopenharmony_ci c = v & 0xFF; 828c2ecf20Sopenharmony_ci /* Reverse bits of result. */ 838c2ecf20Sopenharmony_ci c = (c & 0x0F) << 4 | (c & 0xF0) >> 4; 848c2ecf20Sopenharmony_ci c = (c & 0x33) << 2 | (c & 0xCC) >> 2; 858c2ecf20Sopenharmony_ci c = (c & 0x55) << 1 | (c & 0xAA) >> 1; 868c2ecf20Sopenharmony_ci return c; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* Writes wbuffer to page */ 908c2ecf20Sopenharmony_cistatic int write_page(int log) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci if (log) 938c2ecf20Sopenharmony_ci pr_info("write_page\n"); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return mtdtest_write(mtd, offset, mtd->writesize, wbuffer); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Re-writes the data area while leaving the OOB alone. */ 998c2ecf20Sopenharmony_cistatic int rewrite_page(int log) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int err = 0; 1028c2ecf20Sopenharmony_ci struct mtd_oob_ops ops; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (log) 1058c2ecf20Sopenharmony_ci pr_info("rewrite page\n"); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ops.mode = MTD_OPS_RAW; /* No ECC */ 1088c2ecf20Sopenharmony_ci ops.len = mtd->writesize; 1098c2ecf20Sopenharmony_ci ops.retlen = 0; 1108c2ecf20Sopenharmony_ci ops.ooblen = 0; 1118c2ecf20Sopenharmony_ci ops.oobretlen = 0; 1128c2ecf20Sopenharmony_ci ops.ooboffs = 0; 1138c2ecf20Sopenharmony_ci ops.datbuf = wbuffer; 1148c2ecf20Sopenharmony_ci ops.oobbuf = NULL; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci err = mtd_write_oob(mtd, offset, &ops); 1178c2ecf20Sopenharmony_ci if (err || ops.retlen != mtd->writesize) { 1188c2ecf20Sopenharmony_ci pr_err("error: write_oob failed (%d)\n", err); 1198c2ecf20Sopenharmony_ci if (!err) 1208c2ecf20Sopenharmony_ci err = -EIO; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return err; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* Reads page into rbuffer. Returns number of corrected bit errors (>=0) 1278c2ecf20Sopenharmony_ci * or error (<0) */ 1288c2ecf20Sopenharmony_cistatic int read_page(int log) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci int err = 0; 1318c2ecf20Sopenharmony_ci size_t read; 1328c2ecf20Sopenharmony_ci struct mtd_ecc_stats oldstats; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (log) 1358c2ecf20Sopenharmony_ci pr_info("read_page\n"); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Saving last mtd stats */ 1388c2ecf20Sopenharmony_ci memcpy(&oldstats, &mtd->ecc_stats, sizeof(oldstats)); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci err = mtd_read(mtd, offset, mtd->writesize, &read, rbuffer); 1418c2ecf20Sopenharmony_ci if (!err || err == -EUCLEAN) 1428c2ecf20Sopenharmony_ci err = mtd->ecc_stats.corrected - oldstats.corrected; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (err < 0 || read != mtd->writesize) { 1458c2ecf20Sopenharmony_ci pr_err("error: read failed at %#llx\n", (long long)offset); 1468c2ecf20Sopenharmony_ci if (err >= 0) 1478c2ecf20Sopenharmony_ci err = -EIO; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return err; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* Verifies rbuffer against random sequence */ 1548c2ecf20Sopenharmony_cistatic int verify_page(int log) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci unsigned i, errs = 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (log) 1598c2ecf20Sopenharmony_ci pr_info("verify_page\n"); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci for (i = 0; i < mtd->writesize; i++) { 1628c2ecf20Sopenharmony_ci if (rbuffer[i] != hash(i+seed)) { 1638c2ecf20Sopenharmony_ci pr_err("Error: page offset %u, expected %02x, got %02x\n", 1648c2ecf20Sopenharmony_ci i, hash(i+seed), rbuffer[i]); 1658c2ecf20Sopenharmony_ci errs++; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (errs) 1708c2ecf20Sopenharmony_ci return -EIO; 1718c2ecf20Sopenharmony_ci else 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci#define CBIT(v, n) ((v) & (1 << (n))) 1768c2ecf20Sopenharmony_ci#define BCLR(v, n) ((v) = (v) & ~(1 << (n))) 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* Finds the first '1' bit in wbuffer starting at offset 'byte' 1798c2ecf20Sopenharmony_ci * and sets it to '0'. */ 1808c2ecf20Sopenharmony_cistatic int insert_biterror(unsigned byte) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci int bit; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci while (byte < mtd->writesize) { 1858c2ecf20Sopenharmony_ci for (bit = 7; bit >= 0; bit--) { 1868c2ecf20Sopenharmony_ci if (CBIT(wbuffer[byte], bit)) { 1878c2ecf20Sopenharmony_ci BCLR(wbuffer[byte], bit); 1888c2ecf20Sopenharmony_ci pr_info("Inserted biterror @ %u/%u\n", byte, bit); 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci byte++; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci pr_err("biterror: Failed to find a '1' bit\n"); 1958c2ecf20Sopenharmony_ci return -EIO; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* Writes 'random' data to page and then introduces deliberate bit 1998c2ecf20Sopenharmony_ci * errors into the page, while verifying each step. */ 2008c2ecf20Sopenharmony_cistatic int incremental_errors_test(void) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int err = 0; 2038c2ecf20Sopenharmony_ci unsigned i; 2048c2ecf20Sopenharmony_ci unsigned errs_per_subpage = 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci pr_info("incremental biterrors test\n"); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci for (i = 0; i < mtd->writesize; i++) 2098c2ecf20Sopenharmony_ci wbuffer[i] = hash(i+seed); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci err = write_page(1); 2128c2ecf20Sopenharmony_ci if (err) 2138c2ecf20Sopenharmony_ci goto exit; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci while (1) { 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci err = rewrite_page(1); 2188c2ecf20Sopenharmony_ci if (err) 2198c2ecf20Sopenharmony_ci goto exit; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci err = read_page(1); 2228c2ecf20Sopenharmony_ci if (err > 0) 2238c2ecf20Sopenharmony_ci pr_info("Read reported %d corrected bit errors\n", err); 2248c2ecf20Sopenharmony_ci if (err < 0) { 2258c2ecf20Sopenharmony_ci pr_err("After %d biterrors per subpage, read reported error %d\n", 2268c2ecf20Sopenharmony_ci errs_per_subpage, err); 2278c2ecf20Sopenharmony_ci err = 0; 2288c2ecf20Sopenharmony_ci goto exit; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci err = verify_page(1); 2328c2ecf20Sopenharmony_ci if (err) { 2338c2ecf20Sopenharmony_ci pr_err("ECC failure, read data is incorrect despite read success\n"); 2348c2ecf20Sopenharmony_ci goto exit; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci pr_info("Successfully corrected %d bit errors per subpage\n", 2388c2ecf20Sopenharmony_ci errs_per_subpage); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci for (i = 0; i < subcount; i++) { 2418c2ecf20Sopenharmony_ci err = insert_biterror(i * subsize); 2428c2ecf20Sopenharmony_ci if (err < 0) 2438c2ecf20Sopenharmony_ci goto exit; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci errs_per_subpage++; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ciexit: 2498c2ecf20Sopenharmony_ci return err; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* Writes 'random' data to page and then re-writes that same data repeatedly. 2548c2ecf20Sopenharmony_ci This eventually develops bit errors (bits written as '1' will slowly become 2558c2ecf20Sopenharmony_ci '0'), which are corrected as far as the ECC is capable of. */ 2568c2ecf20Sopenharmony_cistatic int overwrite_test(void) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci int err = 0; 2598c2ecf20Sopenharmony_ci unsigned i; 2608c2ecf20Sopenharmony_ci unsigned max_corrected = 0; 2618c2ecf20Sopenharmony_ci unsigned opno = 0; 2628c2ecf20Sopenharmony_ci /* We don't expect more than this many correctable bit errors per 2638c2ecf20Sopenharmony_ci * page. */ 2648c2ecf20Sopenharmony_ci #define MAXBITS 512 2658c2ecf20Sopenharmony_ci static unsigned bitstats[MAXBITS]; /* bit error histogram. */ 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci memset(bitstats, 0, sizeof(bitstats)); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci pr_info("overwrite biterrors test\n"); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci for (i = 0; i < mtd->writesize; i++) 2728c2ecf20Sopenharmony_ci wbuffer[i] = hash(i+seed); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci err = write_page(1); 2758c2ecf20Sopenharmony_ci if (err) 2768c2ecf20Sopenharmony_ci goto exit; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci while (opno < max_overwrite) { 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci err = write_page(0); 2818c2ecf20Sopenharmony_ci if (err) 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci err = read_page(0); 2858c2ecf20Sopenharmony_ci if (err >= 0) { 2868c2ecf20Sopenharmony_ci if (err >= MAXBITS) { 2878c2ecf20Sopenharmony_ci pr_info("Implausible number of bit errors corrected\n"); 2888c2ecf20Sopenharmony_ci err = -EIO; 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci bitstats[err]++; 2928c2ecf20Sopenharmony_ci if (err > max_corrected) { 2938c2ecf20Sopenharmony_ci max_corrected = err; 2948c2ecf20Sopenharmony_ci pr_info("Read reported %d corrected bit errors\n", 2958c2ecf20Sopenharmony_ci err); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci } else { /* err < 0 */ 2988c2ecf20Sopenharmony_ci pr_info("Read reported error %d\n", err); 2998c2ecf20Sopenharmony_ci err = 0; 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci err = verify_page(0); 3048c2ecf20Sopenharmony_ci if (err) { 3058c2ecf20Sopenharmony_ci bitstats[max_corrected] = opno; 3068c2ecf20Sopenharmony_ci pr_info("ECC failure, read data is incorrect despite read success\n"); 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci err = mtdtest_relax(); 3118c2ecf20Sopenharmony_ci if (err) 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci opno++; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* At this point bitstats[0] contains the number of ops with no bit 3188c2ecf20Sopenharmony_ci * errors, bitstats[1] the number of ops with 1 bit error, etc. */ 3198c2ecf20Sopenharmony_ci pr_info("Bit error histogram (%d operations total):\n", opno); 3208c2ecf20Sopenharmony_ci for (i = 0; i < max_corrected; i++) 3218c2ecf20Sopenharmony_ci pr_info("Page reads with %3d corrected bit errors: %d\n", 3228c2ecf20Sopenharmony_ci i, bitstats[i]); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ciexit: 3258c2ecf20Sopenharmony_ci return err; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int __init mtd_nandbiterrs_init(void) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci int err = 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci printk("\n"); 3338c2ecf20Sopenharmony_ci printk(KERN_INFO "==================================================\n"); 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 goto exit_mtddev; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (!mtd_type_is_nand(mtd)) { 3448c2ecf20Sopenharmony_ci pr_info("this test requires NAND flash\n"); 3458c2ecf20Sopenharmony_ci err = -ENODEV; 3468c2ecf20Sopenharmony_ci goto exit_nand; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci pr_info("MTD device size %llu, eraseblock=%u, page=%u, oob=%u\n", 3508c2ecf20Sopenharmony_ci (unsigned long long)mtd->size, mtd->erasesize, 3518c2ecf20Sopenharmony_ci mtd->writesize, mtd->oobsize); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci subsize = mtd->writesize >> mtd->subpage_sft; 3548c2ecf20Sopenharmony_ci subcount = mtd->writesize / subsize; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci pr_info("Device uses %d subpages of %d bytes\n", subcount, subsize); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci offset = (loff_t)page_offset * mtd->writesize; 3598c2ecf20Sopenharmony_ci eraseblock = mtd_div_by_eb(offset, mtd); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci pr_info("Using page=%u, offset=%llu, eraseblock=%u\n", 3628c2ecf20Sopenharmony_ci page_offset, offset, eraseblock); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci wbuffer = kmalloc(mtd->writesize, GFP_KERNEL); 3658c2ecf20Sopenharmony_ci if (!wbuffer) { 3668c2ecf20Sopenharmony_ci err = -ENOMEM; 3678c2ecf20Sopenharmony_ci goto exit_wbuffer; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci rbuffer = kmalloc(mtd->writesize, GFP_KERNEL); 3718c2ecf20Sopenharmony_ci if (!rbuffer) { 3728c2ecf20Sopenharmony_ci err = -ENOMEM; 3738c2ecf20Sopenharmony_ci goto exit_rbuffer; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci err = mtdtest_erase_eraseblock(mtd, eraseblock); 3778c2ecf20Sopenharmony_ci if (err) 3788c2ecf20Sopenharmony_ci goto exit_error; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (mode == 0) 3818c2ecf20Sopenharmony_ci err = incremental_errors_test(); 3828c2ecf20Sopenharmony_ci else 3838c2ecf20Sopenharmony_ci err = overwrite_test(); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (err) 3868c2ecf20Sopenharmony_ci goto exit_error; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* We leave the block un-erased in case of test failure. */ 3898c2ecf20Sopenharmony_ci err = mtdtest_erase_eraseblock(mtd, eraseblock); 3908c2ecf20Sopenharmony_ci if (err) 3918c2ecf20Sopenharmony_ci goto exit_error; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci err = -EIO; 3948c2ecf20Sopenharmony_ci pr_info("finished successfully.\n"); 3958c2ecf20Sopenharmony_ci printk(KERN_INFO "==================================================\n"); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ciexit_error: 3988c2ecf20Sopenharmony_ci kfree(rbuffer); 3998c2ecf20Sopenharmony_ciexit_rbuffer: 4008c2ecf20Sopenharmony_ci kfree(wbuffer); 4018c2ecf20Sopenharmony_ciexit_wbuffer: 4028c2ecf20Sopenharmony_ci /* Nothing */ 4038c2ecf20Sopenharmony_ciexit_nand: 4048c2ecf20Sopenharmony_ci put_mtd_device(mtd); 4058c2ecf20Sopenharmony_ciexit_mtddev: 4068c2ecf20Sopenharmony_ci return err; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic void __exit mtd_nandbiterrs_exit(void) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci return; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cimodule_init(mtd_nandbiterrs_init); 4158c2ecf20Sopenharmony_cimodule_exit(mtd_nandbiterrs_exit); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NAND bit error recovery test"); 4188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Iwo Mergler"); 4198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 420