18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * mtdram - a test mtd device 38c2ecf20Sopenharmony_ci * Author: Alexander Larsson <alex@cendio.se> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 1999 Alexander Larsson <alex@cendio.se> 68c2ecf20Sopenharmony_ci * Copyright (c) 2005 Joern Engel <joern@wh.fh-wedel.de> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This code is GPL 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/ioport.h> 158c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 168c2ecf20Sopenharmony_ci#include <linux/mm.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 198c2ecf20Sopenharmony_ci#include <linux/mtd/mtdram.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE; 228c2ecf20Sopenharmony_cistatic unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE; 238c2ecf20Sopenharmony_cistatic unsigned long writebuf_size = 64; 248c2ecf20Sopenharmony_ci#define MTDRAM_TOTAL_SIZE (total_size * 1024) 258c2ecf20Sopenharmony_ci#define MTDRAM_ERASE_SIZE (erase_size * 1024) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cimodule_param(total_size, ulong, 0); 288c2ecf20Sopenharmony_ciMODULE_PARM_DESC(total_size, "Total device size in KiB"); 298c2ecf20Sopenharmony_cimodule_param(erase_size, ulong, 0); 308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(erase_size, "Device erase block size in KiB"); 318c2ecf20Sopenharmony_cimodule_param(writebuf_size, ulong, 0); 328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(writebuf_size, "Device write buf size in Bytes (Default: 64)"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci// We could store these in the mtd structure, but we only support 1 device.. 358c2ecf20Sopenharmony_cistatic struct mtd_info *mtd_info; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci int ret = 0; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* Start address must align on block boundary */ 428c2ecf20Sopenharmony_ci if (mtd_mod_by_eb(ofs, mtd)) { 438c2ecf20Sopenharmony_ci pr_debug("%s: unaligned address\n", __func__); 448c2ecf20Sopenharmony_ci ret = -EINVAL; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* Length must align on block boundary */ 488c2ecf20Sopenharmony_ci if (mtd_mod_by_eb(len, mtd)) { 498c2ecf20Sopenharmony_ci pr_debug("%s: length not block aligned\n", __func__); 508c2ecf20Sopenharmony_ci ret = -EINVAL; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return ret; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int ram_erase(struct mtd_info *mtd, struct erase_info *instr) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci if (check_offs_len(mtd, instr->addr, instr->len)) 598c2ecf20Sopenharmony_ci return -EINVAL; 608c2ecf20Sopenharmony_ci memset((char *)mtd->priv + instr->addr, 0xff, instr->len); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int ram_point(struct mtd_info *mtd, loff_t from, size_t len, 668c2ecf20Sopenharmony_ci size_t *retlen, void **virt, resource_size_t *phys) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci *virt = mtd->priv + from; 698c2ecf20Sopenharmony_ci *retlen = len; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (phys) { 728c2ecf20Sopenharmony_ci /* limit retlen to the number of contiguous physical pages */ 738c2ecf20Sopenharmony_ci unsigned long page_ofs = offset_in_page(*virt); 748c2ecf20Sopenharmony_ci void *addr = *virt - page_ofs; 758c2ecf20Sopenharmony_ci unsigned long pfn1, pfn0 = vmalloc_to_pfn(addr); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci *phys = __pfn_to_phys(pfn0) + page_ofs; 788c2ecf20Sopenharmony_ci len += page_ofs; 798c2ecf20Sopenharmony_ci while (len > PAGE_SIZE) { 808c2ecf20Sopenharmony_ci len -= PAGE_SIZE; 818c2ecf20Sopenharmony_ci addr += PAGE_SIZE; 828c2ecf20Sopenharmony_ci pfn0++; 838c2ecf20Sopenharmony_ci pfn1 = vmalloc_to_pfn(addr); 848c2ecf20Sopenharmony_ci if (pfn1 != pfn0) { 858c2ecf20Sopenharmony_ci *retlen = addr - *virt; 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int ram_read(struct mtd_info *mtd, loff_t from, size_t len, 1008c2ecf20Sopenharmony_ci size_t *retlen, u_char *buf) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci memcpy(buf, mtd->priv + from, len); 1038c2ecf20Sopenharmony_ci *retlen = len; 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int ram_write(struct mtd_info *mtd, loff_t to, size_t len, 1088c2ecf20Sopenharmony_ci size_t *retlen, const u_char *buf) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci memcpy((char *)mtd->priv + to, buf, len); 1118c2ecf20Sopenharmony_ci *retlen = len; 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void __exit cleanup_mtdram(void) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci if (mtd_info) { 1188c2ecf20Sopenharmony_ci mtd_device_unregister(mtd_info); 1198c2ecf20Sopenharmony_ci vfree(mtd_info->priv); 1208c2ecf20Sopenharmony_ci kfree(mtd_info); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ciint mtdram_init_device(struct mtd_info *mtd, void *mapped_address, 1258c2ecf20Sopenharmony_ci unsigned long size, const char *name) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci memset(mtd, 0, sizeof(*mtd)); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Setup the MTD structure */ 1308c2ecf20Sopenharmony_ci mtd->name = name; 1318c2ecf20Sopenharmony_ci mtd->type = MTD_RAM; 1328c2ecf20Sopenharmony_ci mtd->flags = MTD_CAP_RAM; 1338c2ecf20Sopenharmony_ci mtd->size = size; 1348c2ecf20Sopenharmony_ci mtd->writesize = 1; 1358c2ecf20Sopenharmony_ci mtd->writebufsize = writebuf_size; 1368c2ecf20Sopenharmony_ci mtd->erasesize = MTDRAM_ERASE_SIZE; 1378c2ecf20Sopenharmony_ci mtd->priv = mapped_address; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci mtd->owner = THIS_MODULE; 1408c2ecf20Sopenharmony_ci mtd->_erase = ram_erase; 1418c2ecf20Sopenharmony_ci mtd->_point = ram_point; 1428c2ecf20Sopenharmony_ci mtd->_unpoint = ram_unpoint; 1438c2ecf20Sopenharmony_ci mtd->_read = ram_read; 1448c2ecf20Sopenharmony_ci mtd->_write = ram_write; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (mtd_device_register(mtd, NULL, 0)) 1478c2ecf20Sopenharmony_ci return -EIO; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int __init init_mtdram(void) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci void *addr; 1558c2ecf20Sopenharmony_ci int err; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!total_size) 1588c2ecf20Sopenharmony_ci return -EINVAL; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Allocate some memory */ 1618c2ecf20Sopenharmony_ci mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); 1628c2ecf20Sopenharmony_ci if (!mtd_info) 1638c2ecf20Sopenharmony_ci return -ENOMEM; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci addr = vmalloc(MTDRAM_TOTAL_SIZE); 1668c2ecf20Sopenharmony_ci if (!addr) { 1678c2ecf20Sopenharmony_ci kfree(mtd_info); 1688c2ecf20Sopenharmony_ci mtd_info = NULL; 1698c2ecf20Sopenharmony_ci return -ENOMEM; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci err = mtdram_init_device(mtd_info, addr, MTDRAM_TOTAL_SIZE, "mtdram test device"); 1728c2ecf20Sopenharmony_ci if (err) { 1738c2ecf20Sopenharmony_ci vfree(addr); 1748c2ecf20Sopenharmony_ci kfree(mtd_info); 1758c2ecf20Sopenharmony_ci mtd_info = NULL; 1768c2ecf20Sopenharmony_ci return err; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE); 1798c2ecf20Sopenharmony_ci return err; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cimodule_init(init_mtdram); 1838c2ecf20Sopenharmony_cimodule_exit(cleanup_mtdram); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexander Larsson <alexl@redhat.com>"); 1878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Simulated MTD driver for testing"); 188