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