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