162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007 Nokia Corporation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Test read and write speed of a MTD device. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Adrian Hunter <adrian.hunter@nokia.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/ktime.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/moduleparam.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/sched.h> 2062306a36Sopenharmony_ci#include <linux/random.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "mtd_test.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int dev = -EINVAL; 2562306a36Sopenharmony_cimodule_param(dev, int, S_IRUGO); 2662306a36Sopenharmony_ciMODULE_PARM_DESC(dev, "MTD device number to use"); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int count; 2962306a36Sopenharmony_cimodule_param(count, int, S_IRUGO); 3062306a36Sopenharmony_ciMODULE_PARM_DESC(count, "Maximum number of eraseblocks to use " 3162306a36Sopenharmony_ci "(0 means use all)"); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct mtd_info *mtd; 3462306a36Sopenharmony_cistatic unsigned char *iobuf; 3562306a36Sopenharmony_cistatic unsigned char *bbt; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int pgsize; 3862306a36Sopenharmony_cistatic int ebcnt; 3962306a36Sopenharmony_cistatic int pgcnt; 4062306a36Sopenharmony_cistatic int goodebcnt; 4162306a36Sopenharmony_cistatic ktime_t start, finish; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int multiblock_erase(int ebnum, int blocks) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci int err; 4662306a36Sopenharmony_ci struct erase_info ei; 4762306a36Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci memset(&ei, 0, sizeof(struct erase_info)); 5062306a36Sopenharmony_ci ei.addr = addr; 5162306a36Sopenharmony_ci ei.len = mtd->erasesize * blocks; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci err = mtd_erase(mtd, &ei); 5462306a36Sopenharmony_ci if (err) { 5562306a36Sopenharmony_ci pr_err("error %d while erasing EB %d, blocks %d\n", 5662306a36Sopenharmony_ci err, ebnum, blocks); 5762306a36Sopenharmony_ci return err; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int write_eraseblock(int ebnum) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return mtdtest_write(mtd, addr, mtd->erasesize, iobuf); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int write_eraseblock_by_page(int ebnum) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci int i, err = 0; 7362306a36Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 7462306a36Sopenharmony_ci void *buf = iobuf; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci for (i = 0; i < pgcnt; i++) { 7762306a36Sopenharmony_ci err = mtdtest_write(mtd, addr, pgsize, buf); 7862306a36Sopenharmony_ci if (err) 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci addr += pgsize; 8162306a36Sopenharmony_ci buf += pgsize; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return err; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int write_eraseblock_by_2pages(int ebnum) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci size_t sz = pgsize * 2; 9062306a36Sopenharmony_ci int i, n = pgcnt / 2, err = 0; 9162306a36Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 9262306a36Sopenharmony_ci void *buf = iobuf; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci for (i = 0; i < n; i++) { 9562306a36Sopenharmony_ci err = mtdtest_write(mtd, addr, sz, buf); 9662306a36Sopenharmony_ci if (err) 9762306a36Sopenharmony_ci return err; 9862306a36Sopenharmony_ci addr += sz; 9962306a36Sopenharmony_ci buf += sz; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci if (pgcnt % 2) 10262306a36Sopenharmony_ci err = mtdtest_write(mtd, addr, pgsize, buf); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return err; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int read_eraseblock(int ebnum) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return mtdtest_read(mtd, addr, mtd->erasesize, iobuf); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int read_eraseblock_by_page(int ebnum) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci int i, err = 0; 11762306a36Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 11862306a36Sopenharmony_ci void *buf = iobuf; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci for (i = 0; i < pgcnt; i++) { 12162306a36Sopenharmony_ci err = mtdtest_read(mtd, addr, pgsize, buf); 12262306a36Sopenharmony_ci if (err) 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci addr += pgsize; 12562306a36Sopenharmony_ci buf += pgsize; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return err; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int read_eraseblock_by_2pages(int ebnum) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci size_t sz = pgsize * 2; 13462306a36Sopenharmony_ci int i, n = pgcnt / 2, err = 0; 13562306a36Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 13662306a36Sopenharmony_ci void *buf = iobuf; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci for (i = 0; i < n; i++) { 13962306a36Sopenharmony_ci err = mtdtest_read(mtd, addr, sz, buf); 14062306a36Sopenharmony_ci if (err) 14162306a36Sopenharmony_ci return err; 14262306a36Sopenharmony_ci addr += sz; 14362306a36Sopenharmony_ci buf += sz; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci if (pgcnt % 2) 14662306a36Sopenharmony_ci err = mtdtest_read(mtd, addr, pgsize, buf); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return err; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic inline void start_timing(void) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci start = ktime_get(); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic inline void stop_timing(void) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci finish = ktime_get(); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic long calc_speed(void) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci uint64_t k, us; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci us = ktime_us_delta(finish, start); 16662306a36Sopenharmony_ci if (us == 0) 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci k = (uint64_t)goodebcnt * (mtd->erasesize / 1024) * 1000000; 16962306a36Sopenharmony_ci do_div(k, us); 17062306a36Sopenharmony_ci return k; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int __init mtd_speedtest_init(void) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int err, i, blocks, j, k; 17662306a36Sopenharmony_ci long speed; 17762306a36Sopenharmony_ci uint64_t tmp; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci printk(KERN_INFO "\n"); 18062306a36Sopenharmony_ci printk(KERN_INFO "=================================================\n"); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (dev < 0) { 18362306a36Sopenharmony_ci pr_info("Please specify a valid mtd-device via module parameter\n"); 18462306a36Sopenharmony_ci pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); 18562306a36Sopenharmony_ci return -EINVAL; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (count) 18962306a36Sopenharmony_ci pr_info("MTD device: %d count: %d\n", dev, count); 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci pr_info("MTD device: %d\n", dev); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci mtd = get_mtd_device(NULL, dev); 19462306a36Sopenharmony_ci if (IS_ERR(mtd)) { 19562306a36Sopenharmony_ci err = PTR_ERR(mtd); 19662306a36Sopenharmony_ci pr_err("error: cannot get MTD device\n"); 19762306a36Sopenharmony_ci return err; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (mtd->writesize == 1) { 20162306a36Sopenharmony_ci pr_info("not NAND flash, assume page size is 512 " 20262306a36Sopenharmony_ci "bytes.\n"); 20362306a36Sopenharmony_ci pgsize = 512; 20462306a36Sopenharmony_ci } else 20562306a36Sopenharmony_ci pgsize = mtd->writesize; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci tmp = mtd->size; 20862306a36Sopenharmony_ci do_div(tmp, mtd->erasesize); 20962306a36Sopenharmony_ci ebcnt = tmp; 21062306a36Sopenharmony_ci pgcnt = mtd->erasesize / pgsize; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci pr_info("MTD device size %llu, eraseblock size %u, " 21362306a36Sopenharmony_ci "page size %u, count of eraseblocks %u, pages per " 21462306a36Sopenharmony_ci "eraseblock %u, OOB size %u\n", 21562306a36Sopenharmony_ci (unsigned long long)mtd->size, mtd->erasesize, 21662306a36Sopenharmony_ci pgsize, ebcnt, pgcnt, mtd->oobsize); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (count > 0 && count < ebcnt) 21962306a36Sopenharmony_ci ebcnt = count; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci err = -ENOMEM; 22262306a36Sopenharmony_ci iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); 22362306a36Sopenharmony_ci if (!iobuf) 22462306a36Sopenharmony_ci goto out; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci get_random_bytes(iobuf, mtd->erasesize); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci bbt = kzalloc(ebcnt, GFP_KERNEL); 22962306a36Sopenharmony_ci if (!bbt) 23062306a36Sopenharmony_ci goto out; 23162306a36Sopenharmony_ci err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); 23262306a36Sopenharmony_ci if (err) 23362306a36Sopenharmony_ci goto out; 23462306a36Sopenharmony_ci for (i = 0; i < ebcnt; i++) { 23562306a36Sopenharmony_ci if (!bbt[i]) 23662306a36Sopenharmony_ci goodebcnt++; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); 24062306a36Sopenharmony_ci if (err) 24162306a36Sopenharmony_ci goto out; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* Write all eraseblocks, 1 eraseblock at a time */ 24462306a36Sopenharmony_ci pr_info("testing eraseblock write speed\n"); 24562306a36Sopenharmony_ci start_timing(); 24662306a36Sopenharmony_ci for (i = 0; i < ebcnt; ++i) { 24762306a36Sopenharmony_ci if (bbt[i]) 24862306a36Sopenharmony_ci continue; 24962306a36Sopenharmony_ci err = write_eraseblock(i); 25062306a36Sopenharmony_ci if (err) 25162306a36Sopenharmony_ci goto out; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci err = mtdtest_relax(); 25462306a36Sopenharmony_ci if (err) 25562306a36Sopenharmony_ci goto out; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci stop_timing(); 25862306a36Sopenharmony_ci speed = calc_speed(); 25962306a36Sopenharmony_ci pr_info("eraseblock write speed is %ld KiB/s\n", speed); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Read all eraseblocks, 1 eraseblock at a time */ 26262306a36Sopenharmony_ci pr_info("testing eraseblock read speed\n"); 26362306a36Sopenharmony_ci start_timing(); 26462306a36Sopenharmony_ci for (i = 0; i < ebcnt; ++i) { 26562306a36Sopenharmony_ci if (bbt[i]) 26662306a36Sopenharmony_ci continue; 26762306a36Sopenharmony_ci err = read_eraseblock(i); 26862306a36Sopenharmony_ci if (err) 26962306a36Sopenharmony_ci goto out; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci err = mtdtest_relax(); 27262306a36Sopenharmony_ci if (err) 27362306a36Sopenharmony_ci goto out; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci stop_timing(); 27662306a36Sopenharmony_ci speed = calc_speed(); 27762306a36Sopenharmony_ci pr_info("eraseblock read speed is %ld KiB/s\n", speed); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); 28062306a36Sopenharmony_ci if (err) 28162306a36Sopenharmony_ci goto out; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* Write all eraseblocks, 1 page at a time */ 28462306a36Sopenharmony_ci pr_info("testing page write speed\n"); 28562306a36Sopenharmony_ci start_timing(); 28662306a36Sopenharmony_ci for (i = 0; i < ebcnt; ++i) { 28762306a36Sopenharmony_ci if (bbt[i]) 28862306a36Sopenharmony_ci continue; 28962306a36Sopenharmony_ci err = write_eraseblock_by_page(i); 29062306a36Sopenharmony_ci if (err) 29162306a36Sopenharmony_ci goto out; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci err = mtdtest_relax(); 29462306a36Sopenharmony_ci if (err) 29562306a36Sopenharmony_ci goto out; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci stop_timing(); 29862306a36Sopenharmony_ci speed = calc_speed(); 29962306a36Sopenharmony_ci pr_info("page write speed is %ld KiB/s\n", speed); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Read all eraseblocks, 1 page at a time */ 30262306a36Sopenharmony_ci pr_info("testing page read speed\n"); 30362306a36Sopenharmony_ci start_timing(); 30462306a36Sopenharmony_ci for (i = 0; i < ebcnt; ++i) { 30562306a36Sopenharmony_ci if (bbt[i]) 30662306a36Sopenharmony_ci continue; 30762306a36Sopenharmony_ci err = read_eraseblock_by_page(i); 30862306a36Sopenharmony_ci if (err) 30962306a36Sopenharmony_ci goto out; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci err = mtdtest_relax(); 31262306a36Sopenharmony_ci if (err) 31362306a36Sopenharmony_ci goto out; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci stop_timing(); 31662306a36Sopenharmony_ci speed = calc_speed(); 31762306a36Sopenharmony_ci pr_info("page read speed is %ld KiB/s\n", speed); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); 32062306a36Sopenharmony_ci if (err) 32162306a36Sopenharmony_ci goto out; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Write all eraseblocks, 2 pages at a time */ 32462306a36Sopenharmony_ci pr_info("testing 2 page write speed\n"); 32562306a36Sopenharmony_ci start_timing(); 32662306a36Sopenharmony_ci for (i = 0; i < ebcnt; ++i) { 32762306a36Sopenharmony_ci if (bbt[i]) 32862306a36Sopenharmony_ci continue; 32962306a36Sopenharmony_ci err = write_eraseblock_by_2pages(i); 33062306a36Sopenharmony_ci if (err) 33162306a36Sopenharmony_ci goto out; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci err = mtdtest_relax(); 33462306a36Sopenharmony_ci if (err) 33562306a36Sopenharmony_ci goto out; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci stop_timing(); 33862306a36Sopenharmony_ci speed = calc_speed(); 33962306a36Sopenharmony_ci pr_info("2 page write speed is %ld KiB/s\n", speed); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* Read all eraseblocks, 2 pages at a time */ 34262306a36Sopenharmony_ci pr_info("testing 2 page read speed\n"); 34362306a36Sopenharmony_ci start_timing(); 34462306a36Sopenharmony_ci for (i = 0; i < ebcnt; ++i) { 34562306a36Sopenharmony_ci if (bbt[i]) 34662306a36Sopenharmony_ci continue; 34762306a36Sopenharmony_ci err = read_eraseblock_by_2pages(i); 34862306a36Sopenharmony_ci if (err) 34962306a36Sopenharmony_ci goto out; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci err = mtdtest_relax(); 35262306a36Sopenharmony_ci if (err) 35362306a36Sopenharmony_ci goto out; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci stop_timing(); 35662306a36Sopenharmony_ci speed = calc_speed(); 35762306a36Sopenharmony_ci pr_info("2 page read speed is %ld KiB/s\n", speed); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* Erase all eraseblocks */ 36062306a36Sopenharmony_ci pr_info("Testing erase speed\n"); 36162306a36Sopenharmony_ci start_timing(); 36262306a36Sopenharmony_ci err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); 36362306a36Sopenharmony_ci if (err) 36462306a36Sopenharmony_ci goto out; 36562306a36Sopenharmony_ci stop_timing(); 36662306a36Sopenharmony_ci speed = calc_speed(); 36762306a36Sopenharmony_ci pr_info("erase speed is %ld KiB/s\n", speed); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Multi-block erase all eraseblocks */ 37062306a36Sopenharmony_ci for (k = 1; k < 7; k++) { 37162306a36Sopenharmony_ci blocks = 1 << k; 37262306a36Sopenharmony_ci pr_info("Testing %dx multi-block erase speed\n", 37362306a36Sopenharmony_ci blocks); 37462306a36Sopenharmony_ci start_timing(); 37562306a36Sopenharmony_ci for (i = 0; i < ebcnt; ) { 37662306a36Sopenharmony_ci for (j = 0; j < blocks && (i + j) < ebcnt; j++) 37762306a36Sopenharmony_ci if (bbt[i + j]) 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci if (j < 1) { 38062306a36Sopenharmony_ci i++; 38162306a36Sopenharmony_ci continue; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci err = multiblock_erase(i, j); 38462306a36Sopenharmony_ci if (err) 38562306a36Sopenharmony_ci goto out; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci err = mtdtest_relax(); 38862306a36Sopenharmony_ci if (err) 38962306a36Sopenharmony_ci goto out; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci i += j; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci stop_timing(); 39462306a36Sopenharmony_ci speed = calc_speed(); 39562306a36Sopenharmony_ci pr_info("%dx multi-block erase speed is %ld KiB/s\n", 39662306a36Sopenharmony_ci blocks, speed); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci pr_info("finished\n"); 39962306a36Sopenharmony_ciout: 40062306a36Sopenharmony_ci kfree(iobuf); 40162306a36Sopenharmony_ci kfree(bbt); 40262306a36Sopenharmony_ci put_mtd_device(mtd); 40362306a36Sopenharmony_ci if (err) 40462306a36Sopenharmony_ci pr_info("error %d occurred\n", err); 40562306a36Sopenharmony_ci printk(KERN_INFO "=================================================\n"); 40662306a36Sopenharmony_ci return err; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_cimodule_init(mtd_speedtest_init); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic void __exit mtd_speedtest_exit(void) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci return; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_cimodule_exit(mtd_speedtest_exit); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ciMODULE_DESCRIPTION("Speed test module"); 41762306a36Sopenharmony_ciMODULE_AUTHOR("Adrian Hunter"); 41862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 419