📄 nandsim.c
字号:
/* * NAND flash simulator. * * Author: Artem B. Bityuckiy <dedekind@oktetlabs.ru>, <dedekind@infradead.org> * * Copyright (C) 2004 Nokia Corporation * * Note: NS means "NAND Simulator". * Note: Input means input TO flash chip, output means output FROM chip. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA */#include <linux/init.h>#include <linux/types.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/vmalloc.h>#include <asm/div64.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/partitions.h>#include <linux/delay.h>#include <linux/list.h>#include <linux/random.h>/* Default simulator parameters values */#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \ !defined(CONFIG_NANDSIM_THIRD_ID_BYTE) || \ !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE)#define CONFIG_NANDSIM_FIRST_ID_BYTE 0x98#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39#define CONFIG_NANDSIM_THIRD_ID_BYTE 0xFF /* No byte */#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */#endif#ifndef CONFIG_NANDSIM_ACCESS_DELAY#define CONFIG_NANDSIM_ACCESS_DELAY 25#endif#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY#define CONFIG_NANDSIM_PROGRAMM_DELAY 200#endif#ifndef CONFIG_NANDSIM_ERASE_DELAY#define CONFIG_NANDSIM_ERASE_DELAY 2#endif#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE#define CONFIG_NANDSIM_OUTPUT_CYCLE 40#endif#ifndef CONFIG_NANDSIM_INPUT_CYCLE#define CONFIG_NANDSIM_INPUT_CYCLE 50#endif#ifndef CONFIG_NANDSIM_BUS_WIDTH#define CONFIG_NANDSIM_BUS_WIDTH 8#endif#ifndef CONFIG_NANDSIM_DO_DELAYS#define CONFIG_NANDSIM_DO_DELAYS 0#endif#ifndef CONFIG_NANDSIM_LOG#define CONFIG_NANDSIM_LOG 0#endif#ifndef CONFIG_NANDSIM_DBG#define CONFIG_NANDSIM_DBG 0#endifstatic uint first_id_byte = CONFIG_NANDSIM_FIRST_ID_BYTE;static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE;static uint third_id_byte = CONFIG_NANDSIM_THIRD_ID_BYTE;static uint fourth_id_byte = CONFIG_NANDSIM_FOURTH_ID_BYTE;static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY;static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY;static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY;static uint output_cycle = CONFIG_NANDSIM_OUTPUT_CYCLE;static uint input_cycle = CONFIG_NANDSIM_INPUT_CYCLE;static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH;static uint do_delays = CONFIG_NANDSIM_DO_DELAYS;static uint log = CONFIG_NANDSIM_LOG;static uint dbg = CONFIG_NANDSIM_DBG;static unsigned long parts[MAX_MTD_DEVICES];static unsigned int parts_num;static char *badblocks = NULL;static char *weakblocks = NULL;static char *weakpages = NULL;static unsigned int bitflips = 0;static char *gravepages = NULL;static unsigned int rptwear = 0;static unsigned int overridesize = 0;module_param(first_id_byte, uint, 0400);module_param(second_id_byte, uint, 0400);module_param(third_id_byte, uint, 0400);module_param(fourth_id_byte, uint, 0400);module_param(access_delay, uint, 0400);module_param(programm_delay, uint, 0400);module_param(erase_delay, uint, 0400);module_param(output_cycle, uint, 0400);module_param(input_cycle, uint, 0400);module_param(bus_width, uint, 0400);module_param(do_delays, uint, 0400);module_param(log, uint, 0400);module_param(dbg, uint, 0400);module_param_array(parts, ulong, &parts_num, 0400);module_param(badblocks, charp, 0400);module_param(weakblocks, charp, 0400);module_param(weakpages, charp, 0400);module_param(bitflips, uint, 0400);module_param(gravepages, charp, 0400);module_param(rptwear, uint, 0400);module_param(overridesize, uint, 0400);MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command");MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");MODULE_PARM_DESC(access_delay, "Initial page access delay (microiseconds)");MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds");MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)");MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)");MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanodeconds)");MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)");MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero");MODULE_PARM_DESC(log, "Perform logging if not zero");MODULE_PARM_DESC(dbg, "Output debug information if not zero");MODULE_PARM_DESC(parts, "Partition sizes (in erase blocks) separated by commas");/* Page and erase block positions for the following parameters are independent of any partitions */MODULE_PARM_DESC(badblocks, "Erase blocks that are initially marked bad, separated by commas");MODULE_PARM_DESC(weakblocks, "Weak erase blocks [: remaining erase cycles (defaults to 3)]" " separated by commas e.g. 113:2 means eb 113" " can be erased only twice before failing");MODULE_PARM_DESC(weakpages, "Weak pages [: maximum writes (defaults to 3)]" " separated by commas e.g. 1401:2 means page 1401" " can be written only twice before failing");MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (zero by default)");MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]" " separated by commas e.g. 1401:2 means page 1401" " can be read only twice before failing");MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if not zero");MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " "The size is specified in erase blocks and as the exponent of a power of two" " e.g. 5 means a size of 32 erase blocks");/* The largest possible page size */#define NS_LARGEST_PAGE_SIZE 2048/* The prefix for simulator output */#define NS_OUTPUT_PREFIX "[nandsim]"/* Simulator's output macros (logging, debugging, warning, error) */#define NS_LOG(args...) \ do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0)#define NS_DBG(args...) \ do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0)#define NS_WARN(args...) \ do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0)#define NS_ERR(args...) \ do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0)#define NS_INFO(args...) \ do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0)/* Busy-wait delay macros (microseconds, milliseconds) */#define NS_UDELAY(us) \ do { if (do_delays) udelay(us); } while(0)#define NS_MDELAY(us) \ do { if (do_delays) mdelay(us); } while(0)/* Is the nandsim structure initialized ? */#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0)/* Good operation completion status */#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0)))/* Operation failed completion status */#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns))/* Calculate the page offset in flash RAM image by (row, column) address */#define NS_RAW_OFFSET(ns) \ (((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column)/* Calculate the OOB offset in flash RAM image by (row, column) address */#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)/* After a command is input, the simulator goes to one of the following states */#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */#define STATE_CMD_PAGEPROG 0x00000004 /* start page programm */#define STATE_CMD_READOOB 0x00000005 /* read OOB area */#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */#define STATE_CMD_STATUS 0x00000007 /* read status */#define STATE_CMD_STATUS_M 0x00000008 /* read multi-plane status (isn't implemented) */#define STATE_CMD_SEQIN 0x00000009 /* sequential data imput */#define STATE_CMD_READID 0x0000000A /* read ID */#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */#define STATE_CMD_RESET 0x0000000C /* reset */#define STATE_CMD_RNDOUT 0x0000000D /* random output command */#define STATE_CMD_RNDOUTSTART 0x0000000E /* random output start command */#define STATE_CMD_MASK 0x0000000F /* command states mask *//* After an address is input, the simulator goes to one of these states */#define STATE_ADDR_PAGE 0x00000010 /* full (row, column) address is accepted */#define STATE_ADDR_SEC 0x00000020 /* sector address was accepted */#define STATE_ADDR_COLUMN 0x00000030 /* column address was accepted */#define STATE_ADDR_ZERO 0x00000040 /* one byte zero address was accepted */#define STATE_ADDR_MASK 0x00000070 /* address states mask *//* Durind data input/output the simulator is in these states */#define STATE_DATAIN 0x00000100 /* waiting for data input */#define STATE_DATAIN_MASK 0x00000100 /* data input states mask */#define STATE_DATAOUT 0x00001000 /* waiting for page data output */#define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */#define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask *//* Previous operation is done, ready to accept new requests */#define STATE_READY 0x00000000/* This state is used to mark that the next state isn't known yet */#define STATE_UNKNOWN 0x10000000/* Simulator's actions bit masks */#define ACTION_CPY 0x00100000 /* copy page/OOB to the internal buffer */#define ACTION_PRGPAGE 0x00200000 /* programm the internal buffer to flash */#define ACTION_SECERASE 0x00300000 /* erase sector */#define ACTION_ZEROOFF 0x00400000 /* don't add any offset to address */#define ACTION_HALFOFF 0x00500000 /* add to address half of page */#define ACTION_OOBOFF 0x00600000 /* add to address OOB offset */#define ACTION_MASK 0x00700000 /* action mask */#define NS_OPER_NUM 13 /* Number of operations supported by the simulator */#define NS_OPER_STATES 6 /* Maximum number of states in operation */#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */#define OPT_PAGE256 0x00000001 /* 256-byte page chips */#define OPT_PAGE512 0x00000002 /* 512-byte page chips */#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */#define OPT_AUTOINCR 0x00000020 /* page number auto inctimentation is possible */#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */#define OPT_LARGEPAGE (OPT_PAGE2048) /* 2048-byte page chips */#define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips *//* Remove action bits ftom state */#define NS_STATE(x) ((x) & ~ACTION_MASK)/* * Maximum previous states which need to be saved. Currently saving is * only needed for page programm operation with preceeded read command * (which is only valid for 512-byte pages). */#define NS_MAX_PREVSTATES 1/* * A union to represent flash memory contents and flash buffer. */union ns_mem { u_char *byte; /* for byte access */ uint16_t *word; /* for 16-bit word access */};/* * The structure which describes all the internal simulator data. */struct nandsim { struct mtd_partition partitions[MAX_MTD_DEVICES]; unsigned int nbparts; uint busw; /* flash chip bus width (8 or 16) */ u_char ids[4]; /* chip's ID bytes */ uint32_t options; /* chip's characteristic bits */ uint32_t state; /* current chip state */ uint32_t nxstate; /* next expected state */ uint32_t *op; /* current operation, NULL operations isn't known yet */ uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */ uint16_t npstates; /* number of previous states saved */ uint16_t stateidx; /* current state index */ /* The simulated NAND flash pages array */ union ns_mem *pages; /* Internal buffer of page + OOB size bytes */ union ns_mem buf; /* NAND flash "geometry" */ struct nandsin_geometry { uint64_t totsz; /* total flash size, bytes */ uint32_t secsz; /* flash sector (erase block) size, bytes */ uint pgsz; /* NAND flash page size, bytes */ uint oobsz; /* page OOB area size, bytes */ uint64_t totszoob; /* total flash size including OOB, bytes */ uint pgszoob; /* page size including OOB , bytes*/ uint secszoob; /* sector size including OOB, bytes */ uint pgnum; /* total number of pages */ uint pgsec; /* number of pages per sector */ uint secshift; /* bits number in sector size */ uint pgshift; /* bits number in page size */ uint oobshift; /* bits number in OOB size */ uint pgaddrbytes; /* bytes per page address */ uint secaddrbytes; /* bytes per sector address */ uint idbytes; /* the number ID bytes that this chip outputs */ } geom; /* NAND flash internal registers */ struct nandsim_regs { unsigned command; /* the command register */ u_char status; /* the status register */ uint row; /* the page number */ uint column; /* the offset within page */ uint count; /* internal counter */ uint num; /* number of bytes which must be processed */ uint off; /* fixed page offset */ } regs; /* NAND flash lines state */ struct ns_lines_status { int ce; /* chip Enable */ int cle; /* command Latch Enable */ int ale; /* address Latch Enable */ int wp; /* write Protect */ } lines;};/* * Operations array. To perform any operation the simulator must pass * through the correspondent states chain. */static struct nandsim_operations { uint32_t reqopts; /* options which are required to perform the operation */ uint32_t states[NS_OPER_STATES]; /* operation's states */} ops[NS_OPER_NUM] = { /* Read page + OOB from the beginning */ {OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY, STATE_DATAOUT, STATE_READY}}, /* Read page + OOB from the second half */ {OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY, STATE_DATAOUT, STATE_READY}}, /* Read OOB */ {OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY, STATE_DATAOUT, STATE_READY}}, /* Programm page starting from the beginning */ {OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, /* Programm page starting from the beginning */ {OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE, STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, /* Programm page starting from the second half */ {OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE, STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, /* Programm OOB */ {OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE, STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, /* Erase sector */ {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}}, /* Read status */ {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}}, /* Read multi-plane status */ {OPT_SMARTMEDIA, {STATE_CMD_STATUS_M, STATE_DATAOUT_STATUS_M, STATE_READY}}, /* Read ID */ {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}}, /* Large page devices read page */ {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY, STATE_DATAOUT, STATE_READY}}, /* Large page devices random page read */ {OPT_LARGEPAGE, {STATE_CMD_RNDOUT, STATE_ADDR_COLUMN, STATE_CMD_RNDOUTSTART | ACTION_CPY, STATE_DATAOUT, STATE_READY}},};struct weak_block { struct list_head list; unsigned int erase_block_no; unsigned int max_erases; unsigned int erases_done;};static LIST_HEAD(weak_blocks);struct weak_page { struct list_head list; unsigned int page_no; unsigned int max_writes; unsigned int writes_done;};static LIST_HEAD(weak_pages);struct grave_page { struct list_head list; unsigned int page_no; unsigned int max_reads; unsigned int reads_done;};static LIST_HEAD(grave_pages);static unsigned long *erase_block_wear = NULL;static unsigned int wear_eb_count = 0;static unsigned long total_wear = 0;static unsigned int rptwear_cnt = 0;/* MTD structure for NAND controller */static struct mtd_info *nsmtd;static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];/* * Allocate array of page pointers and initialize the array to NULL * pointers. * * RETURNS: 0 if success, -ENOMEM if memory alloc fails. */static int alloc_device(struct nandsim *ns){ int i; ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem)); if (!ns->pages) { NS_ERR("alloc_map: unable to allocate page array\n"); return -ENOMEM; } for (i = 0; i < ns->geom.pgnum; i++) { ns->pages[i].byte = NULL; } return 0;}/* * Free any allocated pages, and free the array of page pointers. */static void free_device(struct nandsim *ns){ int i; if (ns->pages) { for (i = 0; i < ns->geom.pgnum; i++) { if (ns->pages[i].byte) kfree(ns->pages[i].byte); } vfree(ns->pages); }}static char *get_partition_name(int i){ char buf[64]; sprintf(buf, "NAND simulator partition %d", i); return kstrdup(buf, GFP_KERNEL);}static u_int64_t divide(u_int64_t n, u_int32_t d){ do_div(n, d); return n;}/* * Initialize the nandsim structure. * * RETURNS: 0 if success, -ERRNO if failure. */static int init_nandsim(struct mtd_info *mtd){ struct nand_chip *chip = (struct nand_chip *)mtd->priv; struct nandsim *ns = (struct nandsim *)(chip->priv); int i, ret = 0; u_int64_t remains; u_int64_t next_offset; if (NS_IS_INITIALIZED(ns)) { NS_ERR("init_nandsim: nandsim is already initialized\n"); return -EIO; } /* Force mtd to not do delays */ chip->chip_delay = 0; /* Initialize the NAND flash parameters */ ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8; ns->geom.totsz = mtd->size; ns->geom.pgsz = mtd->writesize; ns->geom.oobsz = mtd->oobsize; ns->geom.secsz = mtd->erasesize; ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; ns->geom.pgnum = divide(ns->geom.totsz, ns->geom.pgsz); ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz; ns->geom.secshift = ffs(ns->geom.secsz) - 1; ns->geom.pgshift = chip->page_shift; ns->geom.oobshift = ffs(ns->geom.oobsz) - 1; ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz; ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec; ns->options = 0; if (ns->geom.pgsz == 256) { ns->options |= OPT_PAGE256; } else if (ns->geom.pgsz == 512) { ns->options |= (OPT_PAGE512 | OPT_AUTOINCR); if (ns->busw == 8) ns->options |= OPT_PAGE512_8BIT; } else if (ns->geom.pgsz == 2048) { ns->options |= OPT_PAGE2048; } else { NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz); return -EIO; } if (ns->options & OPT_SMALLPAGE) { if (ns->geom.totsz <= (32 << 20)) { ns->geom.pgaddrbytes = 3; ns->geom.secaddrbytes = 2; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -