11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * NAND flash simulator.
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Author: Artem B. Bityuckiy <dedekind@oktetlabs.ru>, <dedekind@infradead.org>
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner * Copyright (C) 2004 Nokia Corporation
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note: NS means "NAND Simulator".
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note: Input means input TO flash chip, output means output FROM chip.
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify it
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * under the terms of the GNU General Public License as published by the
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Free Software Foundation; either version 2, or (at your option) any later
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * version.
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, but
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * WITHOUT ANY WARRANTY; without even the implied warranty of
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Public License for more details.
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/vmalloc.h>
31fc1f397b2c7ef1c9bad58778e4041dfabf20c71cAndrew Morton#include <asm/div64.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/mtd.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/nand.h>
37fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic#include <linux/mtd/nand_bch.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/partitions.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
402b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter#include <linux/list.h>
41514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter#include <linux/random.h>
42a5cce42f357b3106fb0eacd85edec5ae31288956Randy Dunlap#include <linux/sched.h>
43a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter#include <linux/fs.h>
44a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter#include <linux/pagemap.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Default simulator parameters values */
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE)  || \
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    !defined(CONFIG_NANDSIM_THIRD_ID_BYTE)  || \
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE)
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_FIRST_ID_BYTE  0x98
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_THIRD_ID_BYTE  0xFF /* No byte */
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CONFIG_NANDSIM_ACCESS_DELAY
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_ACCESS_DELAY 25
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_PROGRAMM_DELAY 200
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CONFIG_NANDSIM_ERASE_DELAY
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_ERASE_DELAY 2
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_OUTPUT_CYCLE 40
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CONFIG_NANDSIM_INPUT_CYCLE
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_INPUT_CYCLE  50
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CONFIG_NANDSIM_BUS_WIDTH
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_BUS_WIDTH  8
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CONFIG_NANDSIM_DO_DELAYS
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_DO_DELAYS  0
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CONFIG_NANDSIM_LOG
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_LOG        0
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CONFIG_NANDSIM_DBG
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_NANDSIM_DBG        0
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
84e99e90aef17517d99be8e049b2f5cc563cd6862aBen Hutchings#ifndef CONFIG_NANDSIM_MAX_PARTS
85e99e90aef17517d99be8e049b2f5cc563cd6862aBen Hutchings#define CONFIG_NANDSIM_MAX_PARTS  32
86e99e90aef17517d99be8e049b2f5cc563cd6862aBen Hutchings#endif
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint first_id_byte  = CONFIG_NANDSIM_FIRST_ID_BYTE;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint third_id_byte  = CONFIG_NANDSIM_THIRD_ID_BYTE;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint fourth_id_byte = CONFIG_NANDSIM_FOURTH_ID_BYTE;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint access_delay   = CONFIG_NANDSIM_ACCESS_DELAY;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint erase_delay    = CONFIG_NANDSIM_ERASE_DELAY;
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint output_cycle   = CONFIG_NANDSIM_OUTPUT_CYCLE;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint input_cycle    = CONFIG_NANDSIM_INPUT_CYCLE;
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint bus_width      = CONFIG_NANDSIM_BUS_WIDTH;
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint do_delays      = CONFIG_NANDSIM_DO_DELAYS;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint log            = CONFIG_NANDSIM_LOG;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic uint dbg            = CONFIG_NANDSIM_DBG;
101e99e90aef17517d99be8e049b2f5cc563cd6862aBen Hutchingsstatic unsigned long parts[CONFIG_NANDSIM_MAX_PARTS];
1022b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunterstatic unsigned int parts_num;
103514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic char *badblocks = NULL;
104514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic char *weakblocks = NULL;
105514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic char *weakpages = NULL;
106514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic unsigned int bitflips = 0;
107514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic char *gravepages = NULL;
10857aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunterstatic unsigned int rptwear = 0;
109a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunterstatic unsigned int overridesize = 0;
110a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunterstatic char *cache_file = NULL;
111ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewiorstatic unsigned int bbt;
112fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelicstatic unsigned int bch;
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(first_id_byte,  uint, 0400);
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(second_id_byte, uint, 0400);
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(third_id_byte,  uint, 0400);
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(fourth_id_byte, uint, 0400);
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(access_delay,   uint, 0400);
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(programm_delay, uint, 0400);
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(erase_delay,    uint, 0400);
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(output_cycle,   uint, 0400);
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(input_cycle,    uint, 0400);
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(bus_width,      uint, 0400);
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(do_delays,      uint, 0400);
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(log,            uint, 0400);
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(dbg,            uint, 0400);
1272b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Huntermodule_param_array(parts, ulong, &parts_num, 0400);
128514087e74fb401a6621e8c836f4eaab87c269f24Adrian Huntermodule_param(badblocks,      charp, 0400);
129514087e74fb401a6621e8c836f4eaab87c269f24Adrian Huntermodule_param(weakblocks,     charp, 0400);
130514087e74fb401a6621e8c836f4eaab87c269f24Adrian Huntermodule_param(weakpages,      charp, 0400);
131514087e74fb401a6621e8c836f4eaab87c269f24Adrian Huntermodule_param(bitflips,       uint, 0400);
132514087e74fb401a6621e8c836f4eaab87c269f24Adrian Huntermodule_param(gravepages,     charp, 0400);
13357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Huntermodule_param(rptwear,        uint, 0400);
134a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Huntermodule_param(overridesize,   uint, 0400);
135a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Huntermodule_param(cache_file,     charp, 0400);
136ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewiormodule_param(bbt,	     uint, 0400);
137fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelicmodule_param(bch,	     uint, 0400);
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
139a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian HunterMODULE_PARM_DESC(first_id_byte,  "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(third_id_byte,  "The third byte returned by NAND Flash 'read ID' command");
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");
143a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian HunterMODULE_PARM_DESC(access_delay,   "Initial page access delay (microseconds)");
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds");
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(erase_delay,    "Sector erase delay (milliseconds)");
1466029a3a4625e485e01cf9a024d190c9d4e6b6681Andrey YurovskyMODULE_PARM_DESC(output_cycle,   "Word output (from flash) time (nanoseconds)");
1476029a3a4625e485e01cf9a024d190c9d4e6b6681Andrey YurovskyMODULE_PARM_DESC(input_cycle,    "Word input (to flash) time (nanoseconds)");
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(bus_width,      "Chip's bus width (8- or 16-bit)");
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(do_delays,      "Simulate NAND delays using busy-waits if not zero");
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(log,            "Perform logging if not zero");
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(dbg,            "Output debug information if not zero");
1522b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian HunterMODULE_PARM_DESC(parts,          "Partition sizes (in erase blocks) separated by commas");
153514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter/* Page and erase block positions for the following parameters are independent of any partitions */
154514087e74fb401a6621e8c836f4eaab87c269f24Adrian HunterMODULE_PARM_DESC(badblocks,      "Erase blocks that are initially marked bad, separated by commas");
155514087e74fb401a6621e8c836f4eaab87c269f24Adrian HunterMODULE_PARM_DESC(weakblocks,     "Weak erase blocks [: remaining erase cycles (defaults to 3)]"
156514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter				 " separated by commas e.g. 113:2 means eb 113"
157514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter				 " can be erased only twice before failing");
158514087e74fb401a6621e8c836f4eaab87c269f24Adrian HunterMODULE_PARM_DESC(weakpages,      "Weak pages [: maximum writes (defaults to 3)]"
159514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter				 " separated by commas e.g. 1401:2 means page 1401"
160514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter				 " can be written only twice before failing");
161514087e74fb401a6621e8c836f4eaab87c269f24Adrian HunterMODULE_PARM_DESC(bitflips,       "Maximum number of random bit flips per page (zero by default)");
162514087e74fb401a6621e8c836f4eaab87c269f24Adrian HunterMODULE_PARM_DESC(gravepages,     "Pages that lose data [: maximum reads (defaults to 3)]"
163514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter				 " separated by commas e.g. 1401:2 means page 1401"
164514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter				 " can be read only twice before failing");
16525985edcedea6396277003854657b5f3cb31a628Lucas De MarchiMODULE_PARM_DESC(rptwear,        "Number of erases between reporting wear, if not zero");
166a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian HunterMODULE_PARM_DESC(overridesize,   "Specifies the NAND Flash size overriding the ID bytes. "
167a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter				 "The size is specified in erase blocks and as the exponent of a power of two"
168a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter				 " e.g. 5 means a size of 32 erase blocks");
169a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian HunterMODULE_PARM_DESC(cache_file,     "File to use to cache nand pages instead of memory");
170ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej SiewiorMODULE_PARM_DESC(bbt,		 "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area");
171fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan DjelicMODULE_PARM_DESC(bch,		 "Enable BCH ecc and set how many bits should "
172fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic				 "be correctable in 512-byte blocks");
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The largest possible page size */
17575352662c54421b48ed58200565395b123952748Sebastian Andrzej Siewior#define NS_LARGEST_PAGE_SIZE	4096
17661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The prefix for simulator output */
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_OUTPUT_PREFIX "[nandsim]"
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Simulator's output macros (logging, debugging, warning, error) */
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_LOG(args...) \
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0)
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_DBG(args...) \
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0)
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_WARN(args...) \
1862b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0)
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_ERR(args...) \
1882b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0)
18957aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter#define NS_INFO(args...) \
19057aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0)
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Busy-wait delay macros (microseconds, milliseconds) */
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_UDELAY(us) \
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        do { if (do_delays) udelay(us); } while(0)
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_MDELAY(us) \
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        do { if (do_delays) mdelay(us); } while(0)
19761b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Is the nandsim structure initialized ? */
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0)
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Good operation completion status */
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0)))
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Operation failed completion status */
20561b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns))
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Calculate the page offset in flash RAM image by (row, column) address */
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_RAW_OFFSET(ns) \
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	(((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column)
21061b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Calculate the OOB offset in flash RAM image by (row, column) address */
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* After a command is input, the simulator goes to one of the following states */
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_CMD_READ0        0x00000001 /* read data from the beginning of page */
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_CMD_READ1        0x00000002 /* read data from the second half of page */
2174a0c50c07a6100ca58d465bac951533347e18d71Artem Bityutskiy#define STATE_CMD_READSTART    0x00000003 /* read data second command (large page devices) */
218daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan#define STATE_CMD_PAGEPROG     0x00000004 /* start page program */
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_CMD_READOOB      0x00000005 /* read OOB area */
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_CMD_ERASE1       0x00000006 /* sector erase first command */
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_CMD_STATUS       0x00000007 /* read status */
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_CMD_STATUS_M     0x00000008 /* read multi-plane status (isn't implemented) */
223daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan#define STATE_CMD_SEQIN        0x00000009 /* sequential data input */
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_CMD_READID       0x0000000A /* read ID */
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_CMD_ERASE2       0x0000000B /* sector erase second command */
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_CMD_RESET        0x0000000C /* reset */
22774216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy#define STATE_CMD_RNDOUT       0x0000000D /* random output command */
22874216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy#define STATE_CMD_RNDOUTSTART  0x0000000E /* random output start command */
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_CMD_MASK         0x0000000F /* command states mask */
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2318e87d7820a6362b6304924befb22d1ee79b754f3Joe Perches/* After an address is input, the simulator goes to one of these states */
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_ADDR_PAGE        0x00000010 /* full (row, column) address is accepted */
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_ADDR_SEC         0x00000020 /* sector address was accepted */
23474216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy#define STATE_ADDR_COLUMN      0x00000030 /* column address was accepted */
23574216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy#define STATE_ADDR_ZERO        0x00000040 /* one byte zero address was accepted */
23674216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy#define STATE_ADDR_MASK        0x00000070 /* address states mask */
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
238daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan/* During data input/output the simulator is in these states */
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_DATAIN           0x00000100 /* waiting for data input */
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_DATAIN_MASK      0x00000100 /* data input states mask */
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_DATAOUT          0x00001000 /* waiting for page data output */
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_DATAOUT_ID       0x00002000 /* waiting for ID bytes output */
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_DATAOUT_STATUS   0x00003000 /* waiting for status output */
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_DATAOUT_MASK     0x00007000 /* data output states mask */
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Previous operation is done, ready to accept new requests */
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_READY            0x00000000
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This state is used to mark that the next state isn't known yet */
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATE_UNKNOWN          0x10000000
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Simulator's actions bit masks */
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACTION_CPY       0x00100000 /* copy page/OOB to the internal buffer */
256daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan#define ACTION_PRGPAGE   0x00200000 /* program the internal buffer to flash */
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACTION_SECERASE  0x00300000 /* erase sector */
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACTION_ZEROOFF   0x00400000 /* don't add any offset to address */
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACTION_HALFOFF   0x00500000 /* add to address half of page */
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACTION_OOBOFF    0x00600000 /* add to address OOB offset */
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACTION_MASK      0x00700000 /* action mask */
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26374216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy#define NS_OPER_NUM      13 /* Number of operations supported by the simulator */
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_OPER_STATES   6  /* Maximum number of states in operation */
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OPT_ANY          0xFFFFFFFF /* any chip supports this operation */
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OPT_PAGE256      0x00000001 /* 256-byte  page chips */
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OPT_PAGE512      0x00000002 /* 512-byte  page chips */
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OPT_PAGE2048     0x00000008 /* 2048-byte page chips */
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OPT_SMARTMEDIA   0x00000010 /* SmartMedia technology chips */
271daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan#define OPT_AUTOINCR     0x00000020 /* page number auto incrementation is possible */
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */
27375352662c54421b48ed58200565395b123952748Sebastian Andrzej Siewior#define OPT_PAGE4096     0x00000080 /* 4096-byte page chips */
27475352662c54421b48ed58200565395b123952748Sebastian Andrzej Siewior#define OPT_LARGEPAGE    (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OPT_SMALLPAGE    (OPT_PAGE256  | OPT_PAGE512)  /* 256 and 512-byte page chips */
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
277daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan/* Remove action bits from state */
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_STATE(x) ((x) & ~ACTION_MASK)
27961b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
28061b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner/*
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Maximum previous states which need to be saved. Currently saving is
282daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan * only needed for page program operation with preceded read command
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (which is only valid for 512-byte pages).
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NS_MAX_PREVSTATES 1
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
287a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter/* Maximum page cache pages needed to read or write a NAND page to the cache_file */
288a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter#define NS_MAX_HELD_PAGES 16
289a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
29061b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner/*
291d086d43640a40dda7783f3c56724048685586d17Vijay Kumar * A union to represent flash memory contents and flash buffer.
292d086d43640a40dda7783f3c56724048685586d17Vijay Kumar */
293d086d43640a40dda7783f3c56724048685586d17Vijay Kumarunion ns_mem {
294d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	u_char *byte;    /* for byte access */
295d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	uint16_t *word;  /* for 16-bit word access */
296d086d43640a40dda7783f3c56724048685586d17Vijay Kumar};
297d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
298d086d43640a40dda7783f3c56724048685586d17Vijay Kumar/*
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The structure which describes all the internal simulator data.
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct nandsim {
302e99e90aef17517d99be8e049b2f5cc563cd6862aBen Hutchings	struct mtd_partition partitions[CONFIG_NANDSIM_MAX_PARTS];
3032b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	unsigned int nbparts;
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint busw;              /* flash chip bus width (8 or 16) */
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u_char ids[4];          /* chip's ID bytes */
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint32_t options;       /* chip's characteristic bits */
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint32_t state;         /* current chip state */
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint32_t nxstate;       /* next expected state */
31061b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint32_t *op;           /* current operation, NULL operations isn't known yet  */
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint16_t npstates;      /* number of previous states saved */
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint16_t stateidx;      /* current state index */
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
316d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	/* The simulated NAND flash pages array */
317d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	union ns_mem *pages;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3198a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev	/* Slab allocator for nand pages */
3208a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev	struct kmem_cache *nand_pages_slab;
3218a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Internal buffer of page + OOB size bytes */
323d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	union ns_mem buf;
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* NAND flash "geometry" */
3260bfa4df2b65a94ce761306ab53447010b928b7ddArtem Bityutskiy	struct {
3276eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter		uint64_t totsz;     /* total flash size, bytes */
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint32_t secsz;     /* flash sector (erase block) size, bytes */
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint pgsz;          /* NAND flash page size, bytes */
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint oobsz;         /* page OOB area size, bytes */
3316eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter		uint64_t totszoob;  /* total flash size including OOB, bytes */
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint pgszoob;       /* page size including OOB , bytes*/
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint secszoob;      /* sector size including OOB, bytes */
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint pgnum;         /* total number of pages */
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint pgsec;         /* number of pages per sector */
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint secshift;      /* bits number in sector size */
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint pgshift;       /* bits number in page size */
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint oobshift;      /* bits number in OOB size */
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint pgaddrbytes;   /* bytes per page address */
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint secaddrbytes;  /* bytes per sector address */
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint idbytes;       /* the number ID bytes that this chip outputs */
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} geom;
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* NAND flash internal registers */
3450bfa4df2b65a94ce761306ab53447010b928b7ddArtem Bityutskiy	struct {
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned command; /* the command register */
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		u_char   status;  /* the status register */
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint     row;     /* the page number */
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint     column;  /* the offset within page */
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint     count;   /* internal counter */
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint     num;     /* number of bytes which must be processed */
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uint     off;     /* fixed page offset */
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} regs;
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* NAND flash lines state */
3560bfa4df2b65a94ce761306ab53447010b928b7ddArtem Bityutskiy        struct {
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                int ce;  /* chip Enable */
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                int cle; /* command Latch Enable */
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                int ale; /* address Latch Enable */
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                int wp;  /* write Protect */
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        } lines;
362a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
363a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	/* Fields needed when using a cache file */
364a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	struct file *cfile; /* Open file */
365a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	unsigned char *pages_written; /* Which pages have been written */
366a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	void *file_buf;
367a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	struct page *held_pages[NS_MAX_HELD_PAGES];
368a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	int held_cnt;
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Operations array. To perform any operation the simulator must pass
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * through the correspondent states chain.
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct nandsim_operations {
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint32_t reqopts;  /* options which are required to perform the operation */
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint32_t states[NS_OPER_STATES]; /* operation's states */
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} ops[NS_OPER_NUM] = {
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read page + OOB from the beginning */
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY,
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			STATE_DATAOUT, STATE_READY}},
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read page + OOB from the second half */
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY,
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			STATE_DATAOUT, STATE_READY}},
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read OOB */
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY,
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			STATE_DATAOUT, STATE_READY}},
388daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan	/* Program page starting from the beginning */
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN,
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
391daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan	/* Program page starting from the beginning */
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE,
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
394daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan	/* Program page starting from the second half */
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE,
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
397daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan	/* Program OOB */
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE,
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Erase sector */
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}},
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read status */
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}},
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read multi-plane status */
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_SMARTMEDIA, {STATE_CMD_STATUS_M, STATE_DATAOUT_STATUS_M, STATE_READY}},
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read ID */
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}},
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Large page devices read page */
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY,
41074216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			       STATE_DATAOUT, STATE_READY}},
41174216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy	/* Large page devices random page read */
41274216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy	{OPT_LARGEPAGE, {STATE_CMD_RNDOUT, STATE_ADDR_COLUMN, STATE_CMD_RNDOUTSTART | ACTION_CPY,
41374216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			       STATE_DATAOUT, STATE_READY}},
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
416514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstruct weak_block {
417514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	struct list_head list;
418514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int erase_block_no;
419514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int max_erases;
420514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int erases_done;
421514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter};
422514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
423514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic LIST_HEAD(weak_blocks);
424514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
425514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstruct weak_page {
426514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	struct list_head list;
427514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int page_no;
428514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int max_writes;
429514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int writes_done;
430514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter};
431514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
432514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic LIST_HEAD(weak_pages);
433514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
434514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstruct grave_page {
435514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	struct list_head list;
436514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int page_no;
437514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int max_reads;
438514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int reads_done;
439514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter};
440514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
441514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic LIST_HEAD(grave_pages);
442514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
44357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunterstatic unsigned long *erase_block_wear = NULL;
44457aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunterstatic unsigned int wear_eb_count = 0;
44557aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunterstatic unsigned long total_wear = 0;
44657aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunterstatic unsigned int rptwear_cnt = 0;
44757aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* MTD structure for NAND controller */
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mtd_info *nsmtd;
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4548a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev * Allocate array of page pointers, create slab allocation for an array
4558a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev * and initialize the array by NULL pointers.
456d086d43640a40dda7783f3c56724048685586d17Vijay Kumar *
457d086d43640a40dda7783f3c56724048685586d17Vijay Kumar * RETURNS: 0 if success, -ENOMEM if memory alloc fails.
458d086d43640a40dda7783f3c56724048685586d17Vijay Kumar */
459a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic int alloc_device(struct nandsim *ns)
460d086d43640a40dda7783f3c56724048685586d17Vijay Kumar{
461a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	struct file *cfile;
462a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	int i, err;
463a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
464a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (cache_file) {
465a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
466a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		if (IS_ERR(cfile))
467a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			return PTR_ERR(cfile);
468a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		if (!cfile->f_op || (!cfile->f_op->read && !cfile->f_op->aio_read)) {
469a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			NS_ERR("alloc_device: cache file not readable\n");
470a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			err = -EINVAL;
471a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			goto err_close;
472a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		}
473a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		if (!cfile->f_op->write && !cfile->f_op->aio_write) {
474a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			NS_ERR("alloc_device: cache file not writeable\n");
475a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			err = -EINVAL;
476a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			goto err_close;
477a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		}
478309b5e4e4154721f8079bc250d2233fd4b3aa039Joe Perches		ns->pages_written = vzalloc(ns->geom.pgnum);
479a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		if (!ns->pages_written) {
480a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			NS_ERR("alloc_device: unable to allocate pages written array\n");
481a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			err = -ENOMEM;
482a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			goto err_close;
483a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		}
484a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
485a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		if (!ns->file_buf) {
486a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			NS_ERR("alloc_device: unable to allocate file buf\n");
487a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			err = -ENOMEM;
488a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			goto err_free;
489a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		}
490a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		ns->cfile = cfile;
491a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		return 0;
492a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	}
493d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
494d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem));
495d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	if (!ns->pages) {
496a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		NS_ERR("alloc_device: unable to allocate page array\n");
497d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		return -ENOMEM;
498d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	}
499d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	for (i = 0; i < ns->geom.pgnum; i++) {
500d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		ns->pages[i].byte = NULL;
501d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	}
5028a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev	ns->nand_pages_slab = kmem_cache_create("nandsim",
5038a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev						ns->geom.pgszoob, 0, 0, NULL);
5048a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev	if (!ns->nand_pages_slab) {
5058a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev		NS_ERR("cache_create: unable to create kmem_cache\n");
5068a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev		return -ENOMEM;
5078a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev	}
508d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
509d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	return 0;
510a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
511a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Huntererr_free:
512a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	vfree(ns->pages_written);
513a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Huntererr_close:
514a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	filp_close(cfile, NULL);
515a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	return err;
516d086d43640a40dda7783f3c56724048685586d17Vijay Kumar}
517d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
518d086d43640a40dda7783f3c56724048685586d17Vijay Kumar/*
519d086d43640a40dda7783f3c56724048685586d17Vijay Kumar * Free any allocated pages, and free the array of page pointers.
520d086d43640a40dda7783f3c56724048685586d17Vijay Kumar */
521a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic void free_device(struct nandsim *ns)
522d086d43640a40dda7783f3c56724048685586d17Vijay Kumar{
523d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	int i;
524d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
525a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (ns->cfile) {
526a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		kfree(ns->file_buf);
527a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		vfree(ns->pages_written);
528a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		filp_close(ns->cfile, NULL);
529a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		return;
530a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	}
531a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
532d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	if (ns->pages) {
533d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		for (i = 0; i < ns->geom.pgnum; i++) {
534d086d43640a40dda7783f3c56724048685586d17Vijay Kumar			if (ns->pages[i].byte)
5358a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev				kmem_cache_free(ns->nand_pages_slab,
5368a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev						ns->pages[i].byte);
537d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		}
5388a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev		kmem_cache_destroy(ns->nand_pages_slab);
539d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		vfree(ns->pages);
540d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	}
541d086d43640a40dda7783f3c56724048685586d17Vijay Kumar}
542d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
5432b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunterstatic char *get_partition_name(int i)
5442b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter{
5452b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	char buf[64];
5462b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	sprintf(buf, "NAND simulator partition %d", i);
5472b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	return kstrdup(buf, GFP_KERNEL);
5482b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter}
5492b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter
5500f07a0be39735651091418c09b257785d12fbc59David Woodhousestatic uint64_t divide(uint64_t n, uint32_t d)
5516eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter{
5526eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter	do_div(n, d);
5536eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter	return n;
5546eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter}
5556eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter
556d086d43640a40dda7783f3c56724048685586d17Vijay Kumar/*
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Initialize the nandsim structure.
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * RETURNS: 0 if success, -ERRNO if failure.
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
561a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic int init_nandsim(struct mtd_info *mtd)
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5637b8516b780ec639f26a4c6cdb51e342d8feecd44Kulikov Vasiliy	struct nand_chip *chip = mtd->priv;
5647b8516b780ec639f26a4c6cdb51e342d8feecd44Kulikov Vasiliy	struct nandsim   *ns   = chip->priv;
5652b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	int i, ret = 0;
5660f07a0be39735651091418c09b257785d12fbc59David Woodhouse	uint64_t remains;
5670f07a0be39735651091418c09b257785d12fbc59David Woodhouse	uint64_t next_offset;
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (NS_IS_INITIALIZED(ns)) {
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("init_nandsim: nandsim is already initialized\n");
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Force mtd to not do delays */
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chip->chip_delay = 0;
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Initialize the NAND flash parameters */
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8;
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->geom.totsz    = mtd->size;
58028318776a80bc3261f9af91ef79e6e38bb9f5becJoern Engel	ns->geom.pgsz     = mtd->writesize;
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->geom.oobsz    = mtd->oobsize;
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->geom.secsz    = mtd->erasesize;
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->geom.pgszoob  = ns->geom.pgsz + ns->geom.oobsz;
5846eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter	ns->geom.pgnum    = divide(ns->geom.totsz, ns->geom.pgsz);
5856eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter	ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz;
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->geom.secshift = ffs(ns->geom.secsz) - 1;
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->geom.pgshift  = chip->page_shift;
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->geom.oobshift = ffs(ns->geom.oobsz) - 1;
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->geom.pgsec    = ns->geom.secsz / ns->geom.pgsz;
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec;
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->options = 0;
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->geom.pgsz == 256) {
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->options |= OPT_PAGE256;
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (ns->geom.pgsz == 512) {
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->options |= (OPT_PAGE512 | OPT_AUTOINCR);
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->busw == 8)
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->options |= OPT_PAGE512_8BIT;
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (ns->geom.pgsz == 2048) {
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->options |= OPT_PAGE2048;
60275352662c54421b48ed58200565395b123952748Sebastian Andrzej Siewior	} else if (ns->geom.pgsz == 4096) {
60375352662c54421b48ed58200565395b123952748Sebastian Andrzej Siewior		ns->options |= OPT_PAGE4096;
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz);
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->options & OPT_SMALLPAGE) {
610af3deccfa67341a9df39b6f734afcc85998ad4b7Adrian Hunter		if (ns->geom.totsz <= (32 << 20)) {
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->geom.pgaddrbytes  = 3;
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->geom.secaddrbytes = 2;
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->geom.pgaddrbytes  = 4;
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->geom.secaddrbytes = 3;
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->geom.totsz <= (128 << 20)) {
6194a0c50c07a6100ca58d465bac951533347e18d71Artem Bityutskiy			ns->geom.pgaddrbytes  = 4;
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->geom.secaddrbytes = 2;
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->geom.pgaddrbytes  = 5;
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->geom.secaddrbytes = 3;
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
62661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
6272b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	/* Fill the partition_info structure */
6282b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	if (parts_num > ARRAY_SIZE(ns->partitions)) {
6292b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		NS_ERR("too many partitions.\n");
6302b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		ret = -EINVAL;
6312b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		goto error;
6322b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	}
6332b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	remains = ns->geom.totsz;
6342b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	next_offset = 0;
6352b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	for (i = 0; i < parts_num; ++i) {
6360f07a0be39735651091418c09b257785d12fbc59David Woodhouse		uint64_t part_sz = (uint64_t)parts[i] * ns->geom.secsz;
6376eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter
6386eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter		if (!part_sz || part_sz > remains) {
6392b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter			NS_ERR("bad partition size.\n");
6402b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter			ret = -EINVAL;
6412b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter			goto error;
6422b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		}
6432b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		ns->partitions[i].name   = get_partition_name(i);
6442b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		ns->partitions[i].offset = next_offset;
6456eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter		ns->partitions[i].size   = part_sz;
6462b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		next_offset += ns->partitions[i].size;
6472b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		remains -= ns->partitions[i].size;
6482b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	}
6492b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	ns->nbparts = parts_num;
6502b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	if (remains) {
6512b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) {
6522b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter			NS_ERR("too many partitions.\n");
6532b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter			ret = -EINVAL;
6542b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter			goto error;
6552b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		}
6562b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		ns->partitions[i].name   = get_partition_name(i);
6572b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		ns->partitions[i].offset = next_offset;
6582b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		ns->partitions[i].size   = remains;
6592b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		ns->nbparts += 1;
6602b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	}
6612b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Detect how many ID bytes the NAND chip outputs */
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        for (i = 0; nand_flash_ids[i].name != NULL; i++) {
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                if (second_id_byte != nand_flash_ids[i].id)
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        continue;
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR))
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->options |= OPT_AUTOINCR;
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->busw == 16)
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_WARN("16-bit flashes support wasn't tested\n");
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
673e4c094a595ba8ea402e6b2153f7bbf6ef039eea0Andrew Morton	printk("flash size: %llu MiB\n",
674e4c094a595ba8ea402e6b2153f7bbf6ef039eea0Andrew Morton			(unsigned long long)ns->geom.totsz >> 20);
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("page size: %u bytes\n",         ns->geom.pgsz);
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("OOB area size: %u bytes\n",     ns->geom.oobsz);
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("sector size: %u KiB\n",         ns->geom.secsz >> 10);
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("pages number: %u\n",            ns->geom.pgnum);
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("pages per sector: %u\n",        ns->geom.pgsec);
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("bus width: %u\n",               ns->busw);
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("bits in sector size: %u\n",     ns->geom.secshift);
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("bits in page size: %u\n",       ns->geom.pgshift);
683e4c094a595ba8ea402e6b2153f7bbf6ef039eea0Andrew Morton	printk("bits in OOB size: %u\n",	ns->geom.oobshift);
684e4c094a595ba8ea402e6b2153f7bbf6ef039eea0Andrew Morton	printk("flash size with OOB: %llu KiB\n",
685e4c094a595ba8ea402e6b2153f7bbf6ef039eea0Andrew Morton			(unsigned long long)ns->geom.totszoob >> 10);
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("page address bytes: %u\n",      ns->geom.pgaddrbytes);
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("sector address bytes: %u\n",    ns->geom.secaddrbytes);
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("options: %#x\n",                ns->options);
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6902b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	if ((ret = alloc_device(ns)) != 0)
691d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		goto error;
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Allocate / initialize the internal buffer */
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ns->buf.byte) {
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->geom.pgszoob);
6982b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		ret = -ENOMEM;
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserror:
706d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	free_device(ns);
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7082b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	return ret;
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Free the nandsim structure.
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
714a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic void free_nandsim(struct nandsim *ns)
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(ns->buf.byte);
717d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	free_device(ns);
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
722514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd)
723514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter{
724514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	char *w;
725514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	int zero_ok;
726514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int erase_block_no;
727514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	loff_t offset;
728514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
729514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	if (!badblocks)
730514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		return 0;
731514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	w = badblocks;
732514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	do {
733514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		zero_ok = (*w == '0' ? 1 : 0);
734514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		erase_block_no = simple_strtoul(w, &w, 0);
735514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (!zero_ok && !erase_block_no) {
736514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			NS_ERR("invalid badblocks.\n");
737514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return -EINVAL;
738514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
739514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		offset = erase_block_no * ns->geom.secsz;
7405942ddbc500d1c9b75e571b656be97f65b26adfeArtem Bityutskiy		if (mtd_block_markbad(mtd, offset)) {
741514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			NS_ERR("invalid badblocks.\n");
742514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return -EINVAL;
743514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
744514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (*w == ',')
745514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			w += 1;
746514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	} while (*w);
747514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	return 0;
748514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter}
749514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
750514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic int parse_weakblocks(void)
751514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter{
752514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	char *w;
753514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	int zero_ok;
754514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int erase_block_no;
755514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int max_erases;
756514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	struct weak_block *wb;
757514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
758514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	if (!weakblocks)
759514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		return 0;
760514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	w = weakblocks;
761514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	do {
762514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		zero_ok = (*w == '0' ? 1 : 0);
763514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		erase_block_no = simple_strtoul(w, &w, 0);
764514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (!zero_ok && !erase_block_no) {
765514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			NS_ERR("invalid weakblocks.\n");
766514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return -EINVAL;
767514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
768514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		max_erases = 3;
769514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (*w == ':') {
770514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			w += 1;
771514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			max_erases = simple_strtoul(w, &w, 0);
772514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
773514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (*w == ',')
774514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			w += 1;
775514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		wb = kzalloc(sizeof(*wb), GFP_KERNEL);
776514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (!wb) {
777514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			NS_ERR("unable to allocate memory.\n");
778514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return -ENOMEM;
779514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
780514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		wb->erase_block_no = erase_block_no;
781514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		wb->max_erases = max_erases;
782514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		list_add(&wb->list, &weak_blocks);
783514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	} while (*w);
784514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	return 0;
785514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter}
786514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
787514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic int erase_error(unsigned int erase_block_no)
788514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter{
789514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	struct weak_block *wb;
790514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
791514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	list_for_each_entry(wb, &weak_blocks, list)
792514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (wb->erase_block_no == erase_block_no) {
793514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			if (wb->erases_done >= wb->max_erases)
794514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter				return 1;
795514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			wb->erases_done += 1;
796514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return 0;
797514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
798514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	return 0;
799514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter}
800514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
801514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic int parse_weakpages(void)
802514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter{
803514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	char *w;
804514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	int zero_ok;
805514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int page_no;
806514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int max_writes;
807514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	struct weak_page *wp;
808514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
809514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	if (!weakpages)
810514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		return 0;
811514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	w = weakpages;
812514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	do {
813514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		zero_ok = (*w == '0' ? 1 : 0);
814514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		page_no = simple_strtoul(w, &w, 0);
815514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (!zero_ok && !page_no) {
816514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			NS_ERR("invalid weakpagess.\n");
817514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return -EINVAL;
818514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
819514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		max_writes = 3;
820514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (*w == ':') {
821514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			w += 1;
822514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			max_writes = simple_strtoul(w, &w, 0);
823514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
824514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (*w == ',')
825514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			w += 1;
826514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		wp = kzalloc(sizeof(*wp), GFP_KERNEL);
827514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (!wp) {
828514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			NS_ERR("unable to allocate memory.\n");
829514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return -ENOMEM;
830514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
831514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		wp->page_no = page_no;
832514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		wp->max_writes = max_writes;
833514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		list_add(&wp->list, &weak_pages);
834514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	} while (*w);
835514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	return 0;
836514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter}
837514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
838514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic int write_error(unsigned int page_no)
839514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter{
840514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	struct weak_page *wp;
841514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
842514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	list_for_each_entry(wp, &weak_pages, list)
843514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (wp->page_no == page_no) {
844514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			if (wp->writes_done >= wp->max_writes)
845514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter				return 1;
846514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			wp->writes_done += 1;
847514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return 0;
848514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
849514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	return 0;
850514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter}
851514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
852514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic int parse_gravepages(void)
853514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter{
854514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	char *g;
855514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	int zero_ok;
856514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int page_no;
857514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int max_reads;
858514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	struct grave_page *gp;
859514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
860514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	if (!gravepages)
861514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		return 0;
862514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	g = gravepages;
863514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	do {
864514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		zero_ok = (*g == '0' ? 1 : 0);
865514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		page_no = simple_strtoul(g, &g, 0);
866514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (!zero_ok && !page_no) {
867514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			NS_ERR("invalid gravepagess.\n");
868514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return -EINVAL;
869514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
870514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		max_reads = 3;
871514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (*g == ':') {
872514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			g += 1;
873514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			max_reads = simple_strtoul(g, &g, 0);
874514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
875514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (*g == ',')
876514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			g += 1;
877514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		gp = kzalloc(sizeof(*gp), GFP_KERNEL);
878514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (!gp) {
879514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			NS_ERR("unable to allocate memory.\n");
880514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return -ENOMEM;
881514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
882514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		gp->page_no = page_no;
883514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		gp->max_reads = max_reads;
884514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		list_add(&gp->list, &grave_pages);
885514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	} while (*g);
886514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	return 0;
887514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter}
888514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
889514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic int read_error(unsigned int page_no)
890514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter{
891514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	struct grave_page *gp;
892514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
893514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	list_for_each_entry(gp, &grave_pages, list)
894514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (gp->page_no == page_no) {
895514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			if (gp->reads_done >= gp->max_reads)
896514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter				return 1;
897514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			gp->reads_done += 1;
898514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return 0;
899514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
900514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	return 0;
901514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter}
902514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
903514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunterstatic void free_lists(void)
904514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter{
905514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	struct list_head *pos, *n;
906514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	list_for_each_safe(pos, n, &weak_blocks) {
907514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		list_del(pos);
908514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		kfree(list_entry(pos, struct weak_block, list));
909514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	}
910514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	list_for_each_safe(pos, n, &weak_pages) {
911514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		list_del(pos);
912514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		kfree(list_entry(pos, struct weak_page, list));
913514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	}
914514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	list_for_each_safe(pos, n, &grave_pages) {
915514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		list_del(pos);
916514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		kfree(list_entry(pos, struct grave_page, list));
917514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	}
91857aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	kfree(erase_block_wear);
91957aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter}
92057aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter
92157aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunterstatic int setup_wear_reporting(struct mtd_info *mtd)
92257aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter{
92357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	size_t mem;
92457aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter
92557aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	if (!rptwear)
92657aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		return 0;
9276eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter	wear_eb_count = divide(mtd->size, mtd->erasesize);
92857aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	mem = wear_eb_count * sizeof(unsigned long);
92957aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	if (mem / sizeof(unsigned long) != wear_eb_count) {
93057aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		NS_ERR("Too many erase blocks for wear reporting\n");
93157aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		return -ENOMEM;
93257aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	}
93357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	erase_block_wear = kzalloc(mem, GFP_KERNEL);
93457aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	if (!erase_block_wear) {
93557aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		NS_ERR("Too many erase blocks for wear reporting\n");
93657aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		return -ENOMEM;
93757aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	}
93857aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	return 0;
93957aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter}
94057aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter
94157aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunterstatic void update_wear(unsigned int erase_block_no)
94257aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter{
94357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	unsigned long wmin = -1, wmax = 0, avg;
94457aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	unsigned long deciles[10], decile_max[10], tot = 0;
94557aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	unsigned int i;
94657aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter
94757aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	if (!erase_block_wear)
94857aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		return;
94957aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	total_wear += 1;
95057aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	if (total_wear == 0)
95157aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		NS_ERR("Erase counter total overflow\n");
95257aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	erase_block_wear[erase_block_no] += 1;
95357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	if (erase_block_wear[erase_block_no] == 0)
95457aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no);
95557aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	rptwear_cnt += 1;
95657aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	if (rptwear_cnt < rptwear)
95757aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		return;
95857aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	rptwear_cnt = 0;
95957aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	/* Calc wear stats */
96057aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	for (i = 0; i < wear_eb_count; ++i) {
96157aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		unsigned long wear = erase_block_wear[i];
96257aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		if (wear < wmin)
96357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter			wmin = wear;
96457aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		if (wear > wmax)
96557aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter			wmax = wear;
96657aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		tot += wear;
96757aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	}
96857aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	for (i = 0; i < 9; ++i) {
96957aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		deciles[i] = 0;
97057aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		decile_max[i] = (wmax * (i + 1) + 5) / 10;
97157aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	}
97257aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	deciles[9] = 0;
97357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	decile_max[9] = wmax;
97457aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	for (i = 0; i < wear_eb_count; ++i) {
97557aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		int d;
97657aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		unsigned long wear = erase_block_wear[i];
97757aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		for (d = 0; d < 10; ++d)
97857aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter			if (wear <= decile_max[d]) {
97957aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter				deciles[d] += 1;
98057aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter				break;
98157aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter			}
98257aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	}
98357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	avg = tot / wear_eb_count;
98457aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	/* Output wear report */
98557aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	NS_INFO("*** Wear Report ***\n");
98657aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	NS_INFO("Total numbers of erases:  %lu\n", tot);
98757aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	NS_INFO("Number of erase blocks:   %u\n", wear_eb_count);
98857aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	NS_INFO("Average number of erases: %lu\n", avg);
98957aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	NS_INFO("Maximum number of erases: %lu\n", wmax);
99057aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	NS_INFO("Minimum number of erases: %lu\n", wmin);
99157aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	for (i = 0; i < 10; ++i) {
99257aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		unsigned long from = (i ? decile_max[i - 1] + 1 : 0);
99357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		if (from > decile_max[i])
99457aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter			continue;
99557aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n",
99657aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter			from,
99757aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter			decile_max[i],
99857aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter			deciles[i]);
99957aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	}
100057aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	NS_INFO("*** End of Wear Report ***\n");
1001514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter}
1002514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Returns the string representation of 'state' state.
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1006a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic char *get_state_name(uint32_t state)
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (NS_STATE(state)) {
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_READ0:
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_READ0";
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_READ1:
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_READ1";
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_PAGEPROG:
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_PAGEPROG";
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_READOOB:
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_READOOB";
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_READSTART:
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_READSTART";
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_ERASE1:
10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_ERASE1";
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_STATUS:
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_STATUS";
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_STATUS_M:
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_STATUS_M";
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_SEQIN:
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_SEQIN";
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_READID:
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_READID";
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_ERASE2:
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_ERASE2";
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_CMD_RESET:
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_CMD_RESET";
103374216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy		case STATE_CMD_RNDOUT:
103474216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			return "STATE_CMD_RNDOUT";
103574216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy		case STATE_CMD_RNDOUTSTART:
103674216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			return "STATE_CMD_RNDOUTSTART";
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_ADDR_PAGE:
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_ADDR_PAGE";
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_ADDR_SEC:
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_ADDR_SEC";
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_ADDR_ZERO:
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_ADDR_ZERO";
104374216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy		case STATE_ADDR_COLUMN:
104474216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			return "STATE_ADDR_COLUMN";
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_DATAIN:
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_DATAIN";
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_DATAOUT:
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_DATAOUT";
10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_DATAOUT_ID:
10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_DATAOUT_ID";
10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_DATAOUT_STATUS:
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_DATAOUT_STATUS";
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_DATAOUT_STATUS_M:
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_DATAOUT_STATUS_M";
10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_READY:
10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_READY";
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_UNKNOWN:
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return "STATE_UNKNOWN";
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NS_ERR("get_state_name: unknown state, BUG\n");
10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NULL;
10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Check if command is valid.
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * RETURNS: 1 if wrong command, 0 if right.
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1070a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic int check_command(int cmd)
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
107361b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case NAND_CMD_READ0:
107574216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy	case NAND_CMD_READ1:
10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case NAND_CMD_READSTART:
10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case NAND_CMD_PAGEPROG:
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case NAND_CMD_READOOB:
10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case NAND_CMD_ERASE1:
10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case NAND_CMD_STATUS:
10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case NAND_CMD_SEQIN:
10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case NAND_CMD_READID:
10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case NAND_CMD_ERASE2:
10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case NAND_CMD_RESET:
108574216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy	case NAND_CMD_RNDOUT:
108674216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy	case NAND_CMD_RNDOUTSTART:
10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
108861b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case NAND_CMD_STATUS_MULTI:
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 1;
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Returns state after command is accepted by command number.
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1098a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic uint32_t get_state_by_command(unsigned command)
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (command) {
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_READ0:
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_READ0;
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_READ1:
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_READ1;
11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_PAGEPROG:
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_PAGEPROG;
11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_READSTART:
11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_READSTART;
11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_READOOB:
11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_READOOB;
11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_ERASE1:
11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_ERASE1;
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_STATUS:
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_STATUS;
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_STATUS_MULTI:
11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_STATUS_M;
11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_SEQIN:
11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_SEQIN;
11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_READID:
11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_READID;
11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_ERASE2:
11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_ERASE2;
11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case NAND_CMD_RESET:
11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return STATE_CMD_RESET;
112574216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy		case NAND_CMD_RNDOUT:
112674216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			return STATE_CMD_RNDOUT;
112774216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy		case NAND_CMD_RNDOUTSTART:
112874216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			return STATE_CMD_RNDOUTSTART;
11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NS_ERR("get_state_by_command: unknown command, BUG\n");
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Move an address byte to the correspondent internal register.
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1138a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic inline void accept_addr_byte(struct nandsim *ns, u_char bt)
11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint byte = (uint)bt;
114161b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes))
11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.column |= (byte << 8 * ns->regs.count);
11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else {
11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.row |= (byte << 8 * (ns->regs.count -
11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						ns->geom.pgaddrbytes +
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						ns->geom.secaddrbytes));
11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
115261b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Switch to STATE_READY state.
11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1156a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic inline void switch_to_ready_state(struct nandsim *ns, u_char status)
11571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY));
11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->state       = STATE_READY;
11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->nxstate     = STATE_UNKNOWN;
11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->op          = NULL;
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->npstates    = 0;
11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->stateidx    = 0;
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->regs.num    = 0;
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->regs.count  = 0;
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->regs.off    = 0;
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->regs.row    = 0;
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->regs.column = 0;
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->regs.status = status;
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If the operation isn't known yet, try to find it in the global array
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * of supported operations.
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Operation can be unknown because of the following.
1178daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan *   1. New command was accepted and this is the first call to find the
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      correspondent states chain. In this case ns->npstates = 0;
1180daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan *   2. There are several operations which begin with the same command(s)
11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      (for example program from the second half and read from the
11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      second half operations both begin with the READ1 command). In this
11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      case the ns->pstates[] array contains previous states.
118461b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner *
11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Thus, the function tries to find operation containing the following
11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * states (if the 'flag' parameter is 0):
11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *    ns->pstates[0], ... ns->pstates[ns->npstates], ns->state
11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If (one and only one) matching operation is found, it is accepted (
11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is
11911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * zeroed).
119261b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner *
1193daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan * If there are several matches, the current state is pushed to the
11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ns->pstates.
11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The operation can be unknown only while commands are input to the chip.
11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * As soon as address command is accepted, the operation must be known.
11981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * In such situation the function is called with 'flag' != 0, and the
11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * operation is searched using the following pattern:
12001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *     ns->pstates[0], ... ns->pstates[ns->npstates], <address input>
120161b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner *
1202daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan * It is supposed that this pattern must either match one operation or
12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * none. There can't be ambiguity in that case.
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1205daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan * If no matches found, the function does the following:
12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   1. if there are saved states present, try to ignore them and search
12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      again only using the last command. If nothing was found, switch
12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      to the STATE_READY state.
12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   2. if there are no saved states, switch to the STATE_READY state.
12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
12111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * RETURNS: -2 - no matched operations found.
12121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *          -1 - several matches.
12131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *           0 - operation is found.
12141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1215a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic int find_operation(struct nandsim *ns, uint32_t flag)
12161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int opsfound = 0;
12181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j, idx = 0;
121961b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < NS_OPER_NUM; i++) {
12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int found = 1;
122361b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
12241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(ns->options & ops[i].reqopts))
12251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Ignore operations we can't perform */
12261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
122761b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
12281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (flag) {
12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK))
12301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				continue;
12311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
12321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates]))
12331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				continue;
12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
12351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
123661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner		for (j = 0; j < ns->npstates; j++)
12371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j])
12381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				&& (ns->options & ops[idx].reqopts)) {
12391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				found = 0;
12401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
12411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
12421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (found) {
12441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			idx = i;
12451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			opsfound += 1;
12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
12471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (opsfound == 1) {
12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Exact match */
12511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->op = &ops[idx].states[0];
12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (flag) {
125361b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner			/*
12541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * In this case the find_operation function was
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * called when address has just began input. But it isn't
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * yet fully input and the current state must
12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * not be one of STATE_ADDR_*, but the STATE_ADDR_*
12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * state must be the next state (ns->nxstate).
12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->stateidx = ns->npstates - 1;
12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
12621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->stateidx = ns->npstates;
12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->npstates = 0;
12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->state = ns->op[ns->stateidx];
12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->nxstate = ns->op[ns->stateidx + 1];
12671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n",
12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				idx, get_state_name(ns->state), get_state_name(ns->nxstate));
12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
12701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
127161b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (opsfound == 0) {
12731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Nothing was found. Try to ignore previous commands (if any) and search again */
12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->npstates != 0) {
12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_DBG("find_operation: no operation found, try again with state %s\n",
12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					get_state_name(ns->state));
12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->npstates = 0;
12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return find_operation(ns, 0);
12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("find_operation: no operations found\n");
12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -2;
12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
128561b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (flag) {
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* This shouldn't happen */
12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("find_operation: BUG, operation must be known if address is input\n");
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -2;
12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
129161b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NS_DBG("find_operation: there is still ambiguity\n");
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->pstates[ns->npstates++] = ns->state;
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -1;
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1299a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunterstatic void put_pages(struct nandsim *ns)
1300a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter{
1301a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	int i;
1302a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1303a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	for (i = 0; i < ns->held_cnt; i++)
1304a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		page_cache_release(ns->held_pages[i]);
1305a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter}
1306a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1307a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter/* Get page cache pages in advance to provide NOFS memory allocation */
1308a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunterstatic int get_pages(struct nandsim *ns, struct file *file, size_t count, loff_t pos)
1309a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter{
1310a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	pgoff_t index, start_index, end_index;
1311a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	struct page *page;
1312a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	struct address_space *mapping = file->f_mapping;
1313a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1314a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	start_index = pos >> PAGE_CACHE_SHIFT;
1315a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	end_index = (pos + count - 1) >> PAGE_CACHE_SHIFT;
1316a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (end_index - start_index + 1 > NS_MAX_HELD_PAGES)
1317a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		return -EINVAL;
1318a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	ns->held_cnt = 0;
1319a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	for (index = start_index; index <= end_index; index++) {
1320a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		page = find_get_page(mapping, index);
1321a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		if (page == NULL) {
1322a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			page = find_or_create_page(mapping, index, GFP_NOFS);
1323a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			if (page == NULL) {
1324a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				write_inode_now(mapping->host, 1);
1325a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				page = find_or_create_page(mapping, index, GFP_NOFS);
1326a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			}
1327a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			if (page == NULL) {
1328a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				put_pages(ns);
1329a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				return -ENOMEM;
1330a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			}
1331a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			unlock_page(page);
1332a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		}
1333a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		ns->held_pages[ns->held_cnt++] = page;
1334a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	}
1335a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	return 0;
1336a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter}
1337a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1338a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunterstatic int set_memalloc(void)
1339a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter{
1340a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (current->flags & PF_MEMALLOC)
1341a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		return 0;
1342a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	current->flags |= PF_MEMALLOC;
1343a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	return 1;
1344a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter}
1345a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1346a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunterstatic void clear_memalloc(int memalloc)
1347a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter{
1348a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (memalloc)
1349a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		current->flags &= ~PF_MEMALLOC;
1350a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter}
1351a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1352a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunterstatic ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos)
1353a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter{
1354a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	mm_segment_t old_fs;
1355a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	ssize_t tx;
1356a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	int err, memalloc;
1357a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1358a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	err = get_pages(ns, file, count, *pos);
1359a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (err)
1360a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		return err;
1361a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	old_fs = get_fs();
1362a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	set_fs(get_ds());
1363a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	memalloc = set_memalloc();
1364a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	tx = vfs_read(file, (char __user *)buf, count, pos);
1365a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	clear_memalloc(memalloc);
1366a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	set_fs(old_fs);
1367a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	put_pages(ns);
1368a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	return tx;
1369a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter}
1370a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1371a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunterstatic ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos)
1372a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter{
1373a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	mm_segment_t old_fs;
1374a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	ssize_t tx;
1375a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	int err, memalloc;
1376a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1377a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	err = get_pages(ns, file, count, *pos);
1378a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (err)
1379a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		return err;
1380a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	old_fs = get_fs();
1381a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	set_fs(get_ds());
1382a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	memalloc = set_memalloc();
1383a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	tx = vfs_write(file, (char __user *)buf, count, pos);
1384a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	clear_memalloc(memalloc);
1385a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	set_fs(old_fs);
1386a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	put_pages(ns);
1387a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	return tx;
1388a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter}
1389a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
13901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1391d086d43640a40dda7783f3c56724048685586d17Vijay Kumar * Returns a pointer to the current page.
1392d086d43640a40dda7783f3c56724048685586d17Vijay Kumar */
1393d086d43640a40dda7783f3c56724048685586d17Vijay Kumarstatic inline union ns_mem *NS_GET_PAGE(struct nandsim *ns)
1394d086d43640a40dda7783f3c56724048685586d17Vijay Kumar{
1395d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	return &(ns->pages[ns->regs.row]);
1396d086d43640a40dda7783f3c56724048685586d17Vijay Kumar}
1397d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
1398d086d43640a40dda7783f3c56724048685586d17Vijay Kumar/*
1399d086d43640a40dda7783f3c56724048685586d17Vijay Kumar * Retuns a pointer to the current byte, within the current page.
1400d086d43640a40dda7783f3c56724048685586d17Vijay Kumar */
1401d086d43640a40dda7783f3c56724048685586d17Vijay Kumarstatic inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns)
1402d086d43640a40dda7783f3c56724048685586d17Vijay Kumar{
1403d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
1404d086d43640a40dda7783f3c56724048685586d17Vijay Kumar}
1405d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
1406a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunterint do_read_error(struct nandsim *ns, int num)
1407a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter{
1408a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	unsigned int page_no = ns->regs.row;
1409a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1410a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (read_error(page_no)) {
1411a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		int i;
1412a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		memset(ns->buf.byte, 0xFF, num);
1413a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		for (i = 0; i < num; ++i)
1414a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			ns->buf.byte[i] = random32();
1415a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		NS_WARN("simulating read error in page %u\n", page_no);
1416a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		return 1;
1417a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	}
1418a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	return 0;
1419a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter}
1420a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1421a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Huntervoid do_bit_flips(struct nandsim *ns, int num)
1422a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter{
1423a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (bitflips && random32() < (1 << 22)) {
1424a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		int flips = 1;
1425a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		if (bitflips > 1)
1426a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			flips = (random32() % (int) bitflips) + 1;
1427a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		while (flips--) {
1428a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			int pos = random32() % (num * 8);
1429a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
1430a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			NS_WARN("read_page: flipping bit %d in page %d "
1431a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				"reading from %d ecc: corrected=%u failed=%u\n",
1432a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				pos, ns->regs.row, ns->regs.column + ns->regs.off,
1433a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed);
1434a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		}
1435a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	}
1436a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter}
1437a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1438d086d43640a40dda7783f3c56724048685586d17Vijay Kumar/*
1439d086d43640a40dda7783f3c56724048685586d17Vijay Kumar * Fill the NAND buffer with data read from the specified page.
1440d086d43640a40dda7783f3c56724048685586d17Vijay Kumar */
1441d086d43640a40dda7783f3c56724048685586d17Vijay Kumarstatic void read_page(struct nandsim *ns, int num)
1442d086d43640a40dda7783f3c56724048685586d17Vijay Kumar{
1443d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	union ns_mem *mypage;
1444d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
1445a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (ns->cfile) {
1446a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		if (!ns->pages_written[ns->regs.row]) {
1447a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			NS_DBG("read_page: page %d not written\n", ns->regs.row);
1448a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			memset(ns->buf.byte, 0xFF, num);
1449a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		} else {
1450a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			loff_t pos;
1451a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			ssize_t tx;
1452a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1453a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			NS_DBG("read_page: page %d written, reading from %d\n",
1454a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				ns->regs.row, ns->regs.column + ns->regs.off);
1455a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			if (do_read_error(ns, num))
1456a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				return;
1457a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			pos = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off;
1458a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			tx = read_file(ns, ns->cfile, ns->buf.byte, num, &pos);
1459a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			if (tx != num) {
1460a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
1461a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				return;
1462a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			}
1463a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			do_bit_flips(ns, num);
1464a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		}
1465a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		return;
1466a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	}
1467a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1468d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	mypage = NS_GET_PAGE(ns);
1469d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	if (mypage->byte == NULL) {
1470d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
1471d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		memset(ns->buf.byte, 0xFF, num);
1472d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	} else {
1473d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		NS_DBG("read_page: page %d allocated, reading from %d\n",
1474d086d43640a40dda7783f3c56724048685586d17Vijay Kumar			ns->regs.row, ns->regs.column + ns->regs.off);
1475a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		if (do_read_error(ns, num))
1476514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return;
1477d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
1478a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		do_bit_flips(ns, num);
1479d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	}
1480d086d43640a40dda7783f3c56724048685586d17Vijay Kumar}
1481d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
1482d086d43640a40dda7783f3c56724048685586d17Vijay Kumar/*
1483d086d43640a40dda7783f3c56724048685586d17Vijay Kumar * Erase all pages in the specified sector.
1484d086d43640a40dda7783f3c56724048685586d17Vijay Kumar */
1485d086d43640a40dda7783f3c56724048685586d17Vijay Kumarstatic void erase_sector(struct nandsim *ns)
1486d086d43640a40dda7783f3c56724048685586d17Vijay Kumar{
1487d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	union ns_mem *mypage;
1488d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	int i;
1489d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
1490a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (ns->cfile) {
1491a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		for (i = 0; i < ns->geom.pgsec; i++)
1492a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			if (ns->pages_written[ns->regs.row + i]) {
1493a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i);
1494a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				ns->pages_written[ns->regs.row + i] = 0;
1495a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			}
1496a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		return;
1497a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	}
1498a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1499d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	mypage = NS_GET_PAGE(ns);
1500d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	for (i = 0; i < ns->geom.pgsec; i++) {
1501d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		if (mypage->byte != NULL) {
1502d086d43640a40dda7783f3c56724048685586d17Vijay Kumar			NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i);
15038a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev			kmem_cache_free(ns->nand_pages_slab, mypage->byte);
1504d086d43640a40dda7783f3c56724048685586d17Vijay Kumar			mypage->byte = NULL;
1505d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		}
1506d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		mypage++;
1507d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	}
1508d086d43640a40dda7783f3c56724048685586d17Vijay Kumar}
1509d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
1510d086d43640a40dda7783f3c56724048685586d17Vijay Kumar/*
1511d086d43640a40dda7783f3c56724048685586d17Vijay Kumar * Program the specified page with the contents from the NAND buffer.
1512d086d43640a40dda7783f3c56724048685586d17Vijay Kumar */
1513d086d43640a40dda7783f3c56724048685586d17Vijay Kumarstatic int prog_page(struct nandsim *ns, int num)
1514d086d43640a40dda7783f3c56724048685586d17Vijay Kumar{
151582810b7b6cc7a74c68881a13b0eb66c7a6370fccArtem Bityutskiy	int i;
1516d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	union ns_mem *mypage;
1517d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	u_char *pg_off;
1518d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
1519a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	if (ns->cfile) {
1520a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		loff_t off, pos;
1521a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		ssize_t tx;
1522a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		int all;
1523a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1524a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		NS_DBG("prog_page: writing page %d\n", ns->regs.row);
1525a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		pg_off = ns->file_buf + ns->regs.column + ns->regs.off;
1526a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		off = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off;
1527a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		if (!ns->pages_written[ns->regs.row]) {
1528a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			all = 1;
1529a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			memset(ns->file_buf, 0xff, ns->geom.pgszoob);
1530a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		} else {
1531a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			all = 0;
1532a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			pos = off;
1533a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			tx = read_file(ns, ns->cfile, pg_off, num, &pos);
1534a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			if (tx != num) {
1535a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
1536a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				return -1;
1537a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			}
1538a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		}
1539a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		for (i = 0; i < num; i++)
1540a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			pg_off[i] &= ns->buf.byte[i];
1541a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		if (all) {
1542a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			pos = (loff_t)ns->regs.row * ns->geom.pgszoob;
1543a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, &pos);
1544a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			if (tx != ns->geom.pgszoob) {
1545a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
1546a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				return -1;
1547a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			}
1548a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			ns->pages_written[ns->regs.row] = 1;
1549a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		} else {
1550a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			pos = off;
1551a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			tx = write_file(ns, ns->cfile, pg_off, num, &pos);
1552a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			if (tx != num) {
1553a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
1554a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter				return -1;
1555a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter			}
1556a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		}
1557a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter		return 0;
1558a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter	}
1559a9fc8991883cdf029bd373a82cbc2d12a10799ddAdrian Hunter
1560d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	mypage = NS_GET_PAGE(ns);
1561d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	if (mypage->byte == NULL) {
1562d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		NS_DBG("prog_page: allocating page %d\n", ns->regs.row);
156398b830d26095007aeb04041147b93d2b74e0a0c0Artem Bityutskiy		/*
156498b830d26095007aeb04041147b93d2b74e0a0c0Artem Bityutskiy		 * We allocate memory with GFP_NOFS because a flash FS may
156598b830d26095007aeb04041147b93d2b74e0a0c0Artem Bityutskiy		 * utilize this. If it is holding an FS lock, then gets here,
15668a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev		 * then kernel memory alloc runs writeback which goes to the FS
15678a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev		 * again and deadlocks. This was seen in practice.
156898b830d26095007aeb04041147b93d2b74e0a0c0Artem Bityutskiy		 */
15698a4c2495b142fe612b291a810d9e695f269c26dbAlexey Korolev		mypage->byte = kmem_cache_alloc(ns->nand_pages_slab, GFP_NOFS);
1570d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		if (mypage->byte == NULL) {
1571d086d43640a40dda7783f3c56724048685586d17Vijay Kumar			NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
1572d086d43640a40dda7783f3c56724048685586d17Vijay Kumar			return -1;
1573d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		}
1574d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		memset(mypage->byte, 0xFF, ns->geom.pgszoob);
1575d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	}
1576d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
1577d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	pg_off = NS_PAGE_BYTE_OFF(ns);
157882810b7b6cc7a74c68881a13b0eb66c7a6370fccArtem Bityutskiy	for (i = 0; i < num; i++)
157982810b7b6cc7a74c68881a13b0eb66c7a6370fccArtem Bityutskiy		pg_off[i] &= ns->buf.byte[i];
1580d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
1581d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	return 0;
1582d086d43640a40dda7783f3c56724048685586d17Vijay Kumar}
1583d086d43640a40dda7783f3c56724048685586d17Vijay Kumar
1584d086d43640a40dda7783f3c56724048685586d17Vijay Kumar/*
15851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If state has any action bit, perform this action.
15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * RETURNS: 0 if success, -1 if error.
15881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1589a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic int do_state_action(struct nandsim *ns, uint32_t action)
15901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1591d086d43640a40dda7783f3c56724048685586d17Vijay Kumar	int num;
15921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int busdiv = ns->busw == 8 ? 1 : 2;
1593514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	unsigned int erase_block_no, page_no;
15941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	action &= ACTION_MASK;
159661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
15971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check that page address input is correct */
15981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) {
15991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row);
16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (action) {
16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ACTION_CPY:
16061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
16071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Copy page data to the internal buffer.
16081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
16091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Column shouldn't be very large */
16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) {
16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_ERR("do_state_action: column number is too large\n");
16131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
1616d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		read_page(ns, num);
16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
16191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			num, NS_RAW_OFFSET(ns) + ns->regs.off);
162061b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
16211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->regs.off == 0)
16221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_LOG("read page %d\n", ns->regs.row);
16231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else if (ns->regs.off < ns->geom.pgsz)
16241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_LOG("read page %d (second half)\n", ns->regs.row);
16251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
16261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_LOG("read OOB of page %d\n", ns->regs.row);
162761b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
16281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_UDELAY(access_delay);
16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv);
16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ACTION_SECERASE:
16341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Erase sector.
16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
163761b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
16381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->lines.wp) {
16391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_ERR("do_state_action: device is write-protected, ignore sector erase\n");
16401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
16411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
164261b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
16431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			|| (ns->regs.row & ~(ns->geom.secsz - 1))) {
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row);
16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
164861b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
16491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.row = (ns->regs.row <<
16501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.column = 0;
165261b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
1653514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift);
1654514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ns->regs.row, NS_RAW_OFFSET(ns));
1657514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		NS_LOG("erase sector %u\n", erase_block_no);
16581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1659d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		erase_sector(ns);
166061b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
16611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_MDELAY(erase_delay);
166261b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
166357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		if (erase_block_wear)
166457aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter			update_wear(erase_block_no);
166557aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter
1666514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (erase_error(erase_block_no)) {
1667514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			NS_WARN("simulating erase failure in erase block %u\n", erase_block_no);
1668514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return -1;
1669514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
1670514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
16711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
16721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ACTION_PRGPAGE:
16741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
1675daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan		 * Program page - move internal buffer data to the page.
16761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
16771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->lines.wp) {
16791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_WARN("do_state_action: device is write-protected, programm\n");
16801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
16811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
16821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
16841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (num != ns->regs.count) {
16851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n",
16861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ns->regs.count, num);
16871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
16881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
16891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1690d086d43640a40dda7783f3c56724048685586d17Vijay Kumar		if (prog_page(ns, num) == -1)
1691d086d43640a40dda7783f3c56724048685586d17Vijay Kumar			return -1;
16921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1693514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		page_no = ns->regs.row;
1694514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
16951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
16961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
16971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_LOG("programm page %d\n", ns->regs.row);
169861b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
16991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_UDELAY(programm_delay);
17001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
170161b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
1702514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		if (write_error(page_no)) {
1703514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			NS_WARN("simulating write failure in page %u\n", page_no);
1704514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter			return -1;
1705514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		}
1706514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
17071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
170861b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
17091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ACTION_ZEROOFF:
17101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("do_state_action: set internal offset to 0\n");
17111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.off = 0;
17121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
17131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ACTION_HALFOFF:
17151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(ns->options & OPT_PAGE512_8BIT)) {
17161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_ERR("do_state_action: BUG! can't skip half of page for non-512"
17171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"byte page size 8x chips\n");
17181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
17191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
17201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2);
17211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.off = ns->geom.pgsz/2;
17221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
17231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ACTION_OOBOFF:
17251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz);
17261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.off = ns->geom.pgsz;
17271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
172861b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
17291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
17301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("do_state_action: BUG! unknown action\n");
17311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
17341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
17371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Switch simulator's state.
17381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1739a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic void switch_state(struct nandsim *ns)
17401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->op) {
17421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
17431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * The current operation have already been identified.
17441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Just follow the states chain.
17451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
174661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
17471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->stateidx += 1;
17481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->state = ns->nxstate;
17491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->nxstate = ns->op[ns->stateidx + 1];
17501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("switch_state: operation is known, switch to the next state, "
17521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			"state: %s, nxstate: %s\n",
17531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			get_state_name(ns->state), get_state_name(ns->nxstate));
17541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* See, whether we need to do some action */
17561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
17571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
17581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
17591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
176061b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
17611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
17621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
17631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * We don't yet know which operation we perform.
17641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Try to identify it.
17651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
17661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
176761b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner		/*
17681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 *  The only event causing the switch_state function to
17691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 *  be called with yet unknown operation is new command.
17701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
17711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->state = get_state_by_command(ns->regs.command);
17721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("switch_state: operation is unknown, try to find it\n");
17741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (find_operation(ns, 0) != 0)
17761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
17771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
17791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
17801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
17811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
17821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* For 16x devices column means the page offset in words */
17851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) {
17861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("switch_state: double the column number for 16x device\n");
17871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.column <<= 1;
17881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (NS_STATE(ns->nxstate) == STATE_READY) {
17911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
17921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * The current state is the last. Return to STATE_READY
17931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
17941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		u_char status = NS_STATUS_OK(ns);
179661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
17971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* In case of data states, see if all bytes were input/output */
17981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK))
17991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			&& ns->regs.count != ns->regs.num) {
18001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_WARN("switch_state: not all bytes were processed, %d left\n",
18011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ns->regs.num - ns->regs.count);
18021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			status = NS_STATUS_FAILED(ns);
18031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
180461b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
18051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("switch_state: operation complete, switch to STATE_READY state\n");
18061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch_to_ready_state(ns, status);
18081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
18101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) {
181161b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner		/*
18121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * If the next state is data input/output, switch to it now
18131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
181461b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
18151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->state      = ns->nxstate;
18161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->nxstate    = ns->op[++ns->stateidx + 1];
18171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.num   = ns->regs.count = 0;
18181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("switch_state: the next state is data I/O, switch, "
18201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			"state: %s, nxstate: %s\n",
18211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			get_state_name(ns->state), get_state_name(ns->nxstate));
18221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
18241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Set the internal register to the count of bytes which
18251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * are expected to be input or output
18261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
18271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (NS_STATE(ns->state)) {
18281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case STATE_DATAIN:
18291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case STATE_DATAOUT:
18301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
18311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
183261b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
18331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case STATE_DATAOUT_ID:
18341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ns->regs.num = ns->geom.idbytes;
18351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
183661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
18371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case STATE_DATAOUT_STATUS:
18381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case STATE_DATAOUT_STATUS_M:
18391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ns->regs.count = ns->regs.num = 0;
18401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
184161b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
18421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
18431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				NS_ERR("switch_state: BUG! unknown data state\n");
18441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
18451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (ns->nxstate & STATE_ADDR_MASK) {
18471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
18481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * If the next state is address input, set the internal
18491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * register to the number of expected address bytes
18501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
18511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.count = 0;
185361b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
18541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (NS_STATE(ns->nxstate)) {
18551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case STATE_ADDR_PAGE:
18561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ns->regs.num = ns->geom.pgaddrbytes;
185761b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
18581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
18591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case STATE_ADDR_SEC:
18601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ns->regs.num = ns->geom.secaddrbytes;
18611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
186261b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
18631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case STATE_ADDR_ZERO:
18641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ns->regs.num = 1;
18651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
18661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
186774216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			case STATE_ADDR_COLUMN:
186874216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy				/* Column address is always 2 bytes */
186974216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy				ns->regs.num = ns->geom.pgaddrbytes - ns->geom.secaddrbytes;
187074216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy				break;
187174216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy
18721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
18731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				NS_ERR("switch_state: BUG! unknown address state\n");
18741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
18751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
187661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner		/*
18771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Just reset internal counters.
18781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
18791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.num = 0;
18811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.count = 0;
18821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
18831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
18841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1885a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic u_char ns_nand_read_byte(struct mtd_info *mtd)
18861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
18877b8516b780ec639f26a4c6cdb51e342d8feecd44Kulikov Vasiliy	struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
18881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u_char outb = 0x00;
18891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Sanity and correctness checks */
18911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ns->lines.ce) {
18921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb);
18931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return outb;
18941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
18951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->lines.ale || ns->lines.cle) {
18961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb);
18971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return outb;
18981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
18991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(ns->state & STATE_DATAOUT_MASK)) {
19001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_WARN("read_byte: unexpected data output cycle, state is %s "
19011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			"return %#x\n", get_state_name(ns->state), (uint)outb);
19021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return outb;
19031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
19041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Status register may be read as many times as it is wanted */
19061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) {
19071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("read_byte: return %#x status\n", ns->regs.status);
19081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ns->regs.status;
19091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
19101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check if there is any data in the internal buffer which may be read */
19121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->regs.count == ns->regs.num) {
19131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb);
19141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return outb;
19151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
19161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (NS_STATE(ns->state)) {
19181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_DATAOUT:
19191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (ns->busw == 8) {
19201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outb = ns->buf.byte[ns->regs.count];
19211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ns->regs.count += 1;
19221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else {
19231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]);
19241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ns->regs.count += 2;
19251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
19261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
19271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case STATE_DATAOUT_ID:
19281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num);
19291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb = ns->ids[ns->regs.count];
19301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->regs.count += 1;
19311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
19321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
19331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			BUG();
19341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
193561b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
19361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->regs.count == ns->regs.num) {
19371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("read_byte: all bytes were read\n");
19381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
1940daf05ec00c6e60a2c705820e7f93cbd31c804fe3srimugunthan		 * The OPT_AUTOINCR allows to read next consecutive pages without
19411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * new read operation cycle.
19421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
19431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) {
19441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->regs.count = 0;
19451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (ns->regs.row + 1 < ns->geom.pgnum)
19461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ns->regs.row += 1;
19471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row);
19481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			do_state_action(ns, ACTION_CPY);
19491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
19501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else if (NS_STATE(ns->nxstate) == STATE_READY)
19511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch_state(ns);
195261b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
19531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
195461b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
19551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return outb;
19561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
19571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1958a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
19591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
19607b8516b780ec639f26a4c6cdb51e342d8feecd44Kulikov Vasiliy	struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
196161b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
19621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Sanity and correctness checks */
19631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ns->lines.ce) {
19641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("write_byte: chip is disabled, ignore write\n");
19651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
19661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
19671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->lines.ale && ns->lines.cle) {
19681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n");
19691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
19701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
197161b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
19721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->lines.cle == 1) {
19731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
19741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * The byte written is a command.
19751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
19761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (byte == NAND_CMD_RESET) {
19781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_LOG("reset chip\n");
19791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch_to_ready_state(ns, NS_STATUS_OK(ns));
19801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
19811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
19821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
198374216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy		/* Check that the command byte is correct */
198474216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy		if (check_command(byte)) {
198574216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			NS_ERR("write_byte: unknown command %#x\n", (uint)byte);
198674216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			return;
198774216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy		}
198874216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy
19891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS
19901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			|| NS_STATE(ns->state) == STATE_DATAOUT_STATUS_M
199174216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			|| NS_STATE(ns->state) == STATE_DATAOUT) {
199274216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			int row = ns->regs.row;
199374216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy
19941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch_state(ns);
199574216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy			if (byte == NAND_CMD_RNDOUT)
199674216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy				ns->regs.row = row;
199774216be41a61a809ad17b091068307e3d89f4a2fArtem Bityutskiy		}
19981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Check if chip is expecting command */
20001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) {
20019359ea461b382de3249469d2165da45f4762b910Adrian Hunter			/* Do not warn if only 2 id bytes are read */
20029359ea461b382de3249469d2165da45f4762b910Adrian Hunter			if (!(ns->regs.command == NAND_CMD_READID &&
20039359ea461b382de3249469d2165da45f4762b910Adrian Hunter			    NS_STATE(ns->state) == STATE_DATAOUT_ID && ns->regs.count == 2)) {
20049359ea461b382de3249469d2165da45f4762b910Adrian Hunter				/*
20059359ea461b382de3249469d2165da45f4762b910Adrian Hunter				 * We are in situation when something else (not command)
20069359ea461b382de3249469d2165da45f4762b910Adrian Hunter				 * was expected but command was input. In this case ignore
20079359ea461b382de3249469d2165da45f4762b910Adrian Hunter				 * previous command(s)/state(s) and accept the last one.
20089359ea461b382de3249469d2165da45f4762b910Adrian Hunter				 */
20099359ea461b382de3249469d2165da45f4762b910Adrian Hunter				NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, "
20109359ea461b382de3249469d2165da45f4762b910Adrian Hunter					"ignore previous states\n", (uint)byte, get_state_name(ns->nxstate));
20119359ea461b382de3249469d2165da45f4762b910Adrian Hunter			}
20121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
20131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
201461b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
20151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("command byte corresponding to %s state accepted\n",
20161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			get_state_name(get_state_by_command(byte)));
20171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.command = byte;
20181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch_state(ns);
20191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (ns->lines.ale == 1) {
20211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
20221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * The byte written is an address.
20231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
20241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) {
20261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_DBG("write_byte: operation isn't known yet, identify it\n");
20281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (find_operation(ns, 1) < 0)
20301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return;
203161b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
20321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
20331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
20341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return;
20351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
203661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
20371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->regs.count = 0;
20381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch (NS_STATE(ns->nxstate)) {
20391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case STATE_ADDR_PAGE:
20401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ns->regs.num = ns->geom.pgaddrbytes;
20411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
20421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case STATE_ADDR_SEC:
20431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ns->regs.num = ns->geom.secaddrbytes;
20441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
20451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case STATE_ADDR_ZERO:
20461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ns->regs.num = 1;
20471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
20481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				default:
20491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					BUG();
20501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
20511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
20521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Check that chip is expecting address */
20541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(ns->nxstate & STATE_ADDR_MASK)) {
20551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, "
20561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate));
20571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
20581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
20591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
206061b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
20611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Check if this is expected byte */
20621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->regs.count == ns->regs.num) {
20631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_ERR("write_byte: no more address bytes expected\n");
20641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
20651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
20661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
20671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		accept_addr_byte(ns, byte);
20691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ns->regs.count += 1;
20711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n",
20731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				(uint)byte, ns->regs.count, ns->regs.num);
20741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->regs.count == ns->regs.num) {
20761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column);
20771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch_state(ns);
20781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
207961b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
20801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
20811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
20821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * The byte written is an input data.
20831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
208461b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
20851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Check that chip is expecting data input */
20861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(ns->state & STATE_DATAIN_MASK)) {
20871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, "
20881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"switch to %s\n", (uint)byte,
20891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				get_state_name(ns->state), get_state_name(STATE_READY));
20901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
20911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
20921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
20931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Check if this is expected byte */
20951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->regs.count == ns->regs.num) {
20961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n",
20971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ns->regs.num);
20981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
20991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
21001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ns->busw == 8) {
21021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->buf.byte[ns->regs.count] = byte;
21031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->regs.count += 1;
21041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
21051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte);
21061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->regs.count += 2;
21071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
21081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
21091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
21111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
21121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21137abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixnerstatic void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask)
21147abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner{
21157abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner	struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
21167abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner
21177abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner	ns->lines.cle = bitmask & NAND_CLE ? 1 : 0;
21187abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner	ns->lines.ale = bitmask & NAND_ALE ? 1 : 0;
21197abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner	ns->lines.ce = bitmask & NAND_NCE ? 1 : 0;
21207abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner
21217abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner	if (cmd != NAND_CMD_NONE)
21227abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner		ns_nand_write_byte(mtd, cmd);
21237abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner}
21247abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner
2125a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic int ns_device_ready(struct mtd_info *mtd)
21261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
21271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NS_DBG("device_ready\n");
21281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 1;
21291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
21301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2131a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic uint16_t ns_nand_read_word(struct mtd_info *mtd)
21321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
21331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
21341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NS_DBG("read_word\n");
213661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
21371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8);
21381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
21391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2140a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
21411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
21427b8516b780ec639f26a4c6cdb51e342d8feecd44Kulikov Vasiliy	struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
21431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check that chip is expecting data input */
21451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(ns->state & STATE_DATAIN_MASK)) {
21461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("write_buf: data input isn't expected, state is %s, "
21471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			"switch to STATE_READY\n", get_state_name(ns->state));
21481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
21491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
21501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
21511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check if these are expected bytes */
21531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->regs.count + len > ns->regs.num) {
21541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("write_buf: too many input bytes\n");
21551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
21561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
21571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
21581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(ns->buf.byte + ns->regs.count, buf, len);
21601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->regs.count += len;
216161b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
21621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->regs.count == ns->regs.num) {
21631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("write_buf: %d bytes were written\n", ns->regs.count);
21641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
21651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
21661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2167a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
21681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
21697b8516b780ec639f26a4c6cdb51e342d8feecd44Kulikov Vasiliy	struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
21701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Sanity and correctness checks */
21721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ns->lines.ce) {
21731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("read_buf: chip is disabled\n");
21741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
21751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
21761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->lines.ale || ns->lines.cle) {
21771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("read_buf: ALE or CLE pin is high\n");
21781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
21791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
21801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(ns->state & STATE_DATAOUT_MASK)) {
21811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_WARN("read_buf: unexpected data output cycle, current state is %s\n",
21821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			get_state_name(ns->state));
21831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
21841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
21851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (NS_STATE(ns->state) != STATE_DATAOUT) {
21871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int i;
21881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < len; i++)
21901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buf[i] = ((struct nand_chip *)mtd->priv)->read_byte(mtd);
21911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
21931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
21941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check if these are expected bytes */
21961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->regs.count + len > ns->regs.num) {
21971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("read_buf: too many bytes to read\n");
21981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
21991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
22001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(buf, ns->buf.byte + ns->regs.count, len);
22031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns->regs.count += len;
220461b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
22051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ns->regs.count == ns->regs.num) {
22061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) {
22071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ns->regs.count = 0;
22081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (ns->regs.row + 1 < ns->geom.pgnum)
22091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ns->regs.row += 1;
22101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row);
22111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			do_state_action(ns, ACTION_CPY);
22121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
22131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else if (NS_STATE(ns->nxstate) == STATE_READY)
22141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch_state(ns);
22151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
221661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
22171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
22181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
22191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2220a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9fVijay Kumarstatic int ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
22211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
22221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len);
22231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!memcmp(buf, &ns_verify_buf[0], len)) {
22251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("verify_buf: the buffer is OK\n");
22261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
22271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
22281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_DBG("verify_buf: the buffer is wrong\n");
22291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
22301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
22321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
22341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Module initialization function
22351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
22362b9175c174b83b8d97db9398efe948fa9092938fAdrian Bunkstatic int __init ns_init_module(void)
22371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
22381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nand_chip *chip;
22391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nandsim *nand;
22402b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	int retval = -ENOMEM, i;
22411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bus_width != 8 && bus_width != 16) {
22431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
22441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
22451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
224661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
22471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Allocate and initialize mtd_info, nand_chip and nandsim structures */
224895b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	nsmtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)
22491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				+ sizeof(struct nandsim), GFP_KERNEL);
22501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!nsmtd) {
22511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("unable to allocate core structures.\n");
22521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
22531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chip        = (struct nand_chip *)(nsmtd + 1);
22551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        nsmtd->priv = (void *)chip;
22561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nand        = (struct nandsim *)(chip + 1);
225761b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner	chip->priv  = (void *)nand;
22581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
22601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Register simulator's callbacks.
22611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
22627abd3ef9875eb2afcdcd4f450680298a2983a55eThomas Gleixner	chip->cmd_ctrl	 = ns_hwcontrol;
22631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chip->read_byte  = ns_nand_read_byte;
22641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chip->dev_ready  = ns_device_ready;
22651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chip->write_buf  = ns_nand_write_buf;
22661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chip->read_buf   = ns_nand_read_buf;
22671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chip->verify_buf = ns_nand_verify_buf;
22681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chip->read_word  = ns_nand_read_word;
22696dfc6d250d0b7ebaa6423c44dcd09fcfe68deabdThomas Gleixner	chip->ecc.mode   = NAND_ECC_SOFT;
2270a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter	/* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
2271a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter	/* and 'badblocks' parameters to work */
2272515022870f0f648b9c506a285b1c7e92901dd37fArtem B. Bityuckiy	chip->options   |= NAND_SKIP_BBTSCAN;
22731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2274ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior	switch (bbt) {
2275ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior	case 2:
2276a40f73419f02e40555f692785ea1c1813d5b4c12Brian Norris		 chip->bbt_options |= NAND_BBT_NO_OOB;
2277ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior	case 1:
2278bb9ebd4e714385a2592a482845865ef2d58b2868Brian Norris		 chip->bbt_options |= NAND_BBT_USE_FLASH;
2279ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior	case 0:
2280ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior		break;
2281ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior	default:
2282ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior		NS_ERR("bbt has to be 0..2\n");
2283ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior		retval = -EINVAL;
2284ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior		goto error;
2285ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior	}
228661b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner	/*
22871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Perform minimum nandsim structure initialization to handle
228861b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner	 * the initial ID read command correctly
22891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
22901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (third_id_byte != 0xFF || fourth_id_byte != 0xFF)
22911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nand->geom.idbytes = 4;
22921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
22931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nand->geom.idbytes = 2;
22941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nand->regs.status = NS_STATUS_OK(nand);
22951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nand->nxstate = STATE_UNKNOWN;
22961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nand->options |= OPT_PAGE256; /* temporary value */
22971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nand->ids[0] = first_id_byte;
22981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nand->ids[1] = second_id_byte;
22991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nand->ids[2] = third_id_byte;
23001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nand->ids[3] = fourth_id_byte;
23011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bus_width == 16) {
23021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nand->busw = 16;
23031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		chip->options |= NAND_BUSWIDTH_16;
23041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
23051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2306552d9205186428a1e2a49ed577bcbba9f777af37David Woodhouse	nsmtd->owner = THIS_MODULE;
2307552d9205186428a1e2a49ed577bcbba9f777af37David Woodhouse
2308514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	if ((retval = parse_weakblocks()) != 0)
2309514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		goto error;
2310514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
2311514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	if ((retval = parse_weakpages()) != 0)
2312514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		goto error;
2313514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
2314514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	if ((retval = parse_gravepages()) != 0)
2315514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		goto error;
2316514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
2317fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic	retval = nand_scan_ident(nsmtd, 1, NULL);
2318fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic	if (retval) {
2319fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		NS_ERR("cannot scan NAND Simulator device\n");
2320fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		if (retval > 0)
2321fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic			retval = -ENXIO;
2322fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		goto error;
2323fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic	}
2324fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic
2325fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic	if (bch) {
2326fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		unsigned int eccsteps, eccbytes;
2327fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		if (!mtd_nand_has_bch()) {
2328fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic			NS_ERR("BCH ECC support is disabled\n");
2329fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic			retval = -EINVAL;
2330fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic			goto error;
2331fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		}
2332fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		/* use 512-byte ecc blocks */
2333fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		eccsteps = nsmtd->writesize/512;
2334fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		eccbytes = (bch*13+7)/8;
2335fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		/* do not bother supporting small page devices */
2336fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		if ((nsmtd->oobsize < 64) || !eccsteps) {
2337fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic			NS_ERR("bch not available on small page devices\n");
2338fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic			retval = -EINVAL;
2339fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic			goto error;
2340fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		}
2341fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		if ((eccbytes*eccsteps+2) > nsmtd->oobsize) {
2342fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic			NS_ERR("invalid bch value %u\n", bch);
2343fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic			retval = -EINVAL;
2344fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic			goto error;
2345fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		}
2346fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		chip->ecc.mode = NAND_ECC_SOFT_BCH;
2347fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		chip->ecc.size = 512;
2348fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		chip->ecc.bytes = eccbytes;
2349fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic		NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size);
2350fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic	}
2351fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic
2352fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic	retval = nand_scan_tail(nsmtd);
2353fc2ff592b5b41f3f32e790dd124eeb3bc80706c8Ivan Djelic	if (retval) {
23541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NS_ERR("can't register NAND Simulator\n");
23551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (retval > 0)
23561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			retval = -ENXIO;
23571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
23581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
23591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2360a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter	if (overridesize) {
23610f07a0be39735651091418c09b257785d12fbc59David Woodhouse		uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize;
2362a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter		if (new_size >> overridesize != nsmtd->erasesize) {
2363a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter			NS_ERR("overridesize is too big\n");
2364a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter			goto err_exit;
2365a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter		}
2366a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter		/* N.B. This relies on nand_scan not doing anything with the size before we change it */
2367a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter		nsmtd->size = new_size;
2368a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter		chip->chipsize = new_size;
23696eda7a55f786b75e7d3d636a9431e6c850b20d72Adrian Hunter		chip->chip_shift = ffs(nsmtd->erasesize) + overridesize - 1;
237007293b20083cb66df35bf2041f0c554eaac43e8cAdrian Hunter		chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
2371a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter	}
2372a5ac8aeb29000fcab8d91848273a6616fcd039eeAdrian Hunter
237357aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter	if ((retval = setup_wear_reporting(nsmtd)) != 0)
237457aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter		goto err_exit;
237557aa6b545f6f772dd317ccd29bdada999b16a13dAdrian Hunter
23762b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	if ((retval = init_nandsim(nsmtd)) != 0)
23772b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		goto err_exit;
237861b03bd7c3b55498c6180d43bf71b7bf49114b64Thomas Gleixner
2379ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior	if ((retval = nand_default_bbt(nsmtd)) != 0)
2380514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter		goto err_exit;
2381514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter
2382ce85b79fe809eaf34b84a9ebf4ac37ee37b3455bSebastian Andrzej Siewior	if ((retval = parse_badblocks(nand, nsmtd)) != 0)
23832b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		goto err_exit;
2384515022870f0f648b9c506a285b1c7e92901dd37fArtem B. Bityuckiy
23852b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	/* Register NAND partitions */
2386ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles	retval = mtd_device_register(nsmtd, &nand->partitions[0],
2387ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles				     nand->nbparts);
2388ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles	if (retval != 0)
23892b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		goto err_exit;
23901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        return 0;
23921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23932b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Huntererr_exit:
23942b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	free_nandsim(nand);
23952b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	nand_release(nsmtd);
23962b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i)
23972b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		kfree(nand->partitions[i].name);
23981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserror:
23991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(nsmtd);
2400514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	free_lists();
24011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
24031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
24041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(ns_init_module);
24061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
24081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Module clean-up function
24091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
24101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit ns_cleanup_module(void)
24111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
24127b8516b780ec639f26a4c6cdb51e342d8feecd44Kulikov Vasiliy	struct nandsim *ns = ((struct nand_chip *)nsmtd->priv)->priv;
24132b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	int i;
24141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_nandsim(ns);    /* Free nandsim private resources */
24162b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	nand_release(nsmtd); /* Unregister driver */
24172b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter	for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
24182b77a0ed54eeea61937e7f71b0487b815edfbcdfAdrian Hunter		kfree(ns->partitions[i].name);
24191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(nsmtd);        /* Free other structures */
2420514087e74fb401a6621e8c836f4eaab87c269f24Adrian Hunter	free_lists();
24211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
24221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(ns_cleanup_module);
24241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE ("GPL");
24261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR ("Artem B. Bityuckiy");
24271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION ("The NAND flash simulator");
2428