fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / mtd / nand / nandsim.c
index 13feefd..c3bca95 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Author: Artem B. Bityuckiy <dedekind@oktetlabs.ru>, <dedekind@infradead.org>
  *
- * Copyright (C) 2004 Nokia Corporation 
+ * Copyright (C) 2004 Nokia Corporation
  *
  * Note: NS means "NAND Simulator".
  * Note: Input means input TO flash chip, output means output FROM chip.
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
  *
- * $Id: nandsim.c,v 1.7 2004/12/06 11:53:06 dedekind Exp $
+ * $Id: nandsim.c,v 1.8 2005/03/19 15:33:56 dedekind Exp $
  */
 
-#include <linux/config.h>
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/delay.h>
-#ifdef CONFIG_NS_ABS_POS
-#include <asm/io.h>
-#endif
-
 
 /* Default simulator parameters values */
 #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE)  || \
@@ -126,7 +121,7 @@ MODULE_PARM_DESC(dbg,            "Output debug information if not zero");
 
 /* The largest possible page size */
 #define NS_LARGEST_PAGE_SIZE   2048
-       
+
 /* The prefix for simulator output */
 #define NS_OUTPUT_PREFIX "[nandsim]"
 
@@ -145,7 +140,7 @@ MODULE_PARM_DESC(dbg,            "Output debug information if not zero");
         do { if (do_delays) udelay(us); } while(0)
 #define NS_MDELAY(us) \
         do { if (do_delays) mdelay(us); } while(0)
-       
+
 /* Is the nandsim structure initialized ? */
 #define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0)
 
@@ -153,19 +148,19 @@ MODULE_PARM_DESC(dbg,            "Output debug information if not zero");
 #define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0)))
 
 /* Operation failed completion status */
-#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns)) 
+#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns))
 
 /* Calculate the page offset in flash RAM image by (row, column) address */
 #define NS_RAW_OFFSET(ns) \
        (((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column)
-       
+
 /* Calculate the OOB offset in flash RAM image by (row, column) address */
 #define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)
 
 /* After a command is input, the simulator goes to one of the following states */
 #define STATE_CMD_READ0        0x00000001 /* read data from the beginning of page */
 #define STATE_CMD_READ1        0x00000002 /* read data from the second half of page */
-#define STATE_CMD_READSTART      0x00000003 /* read data second command (large page devices) */
+#define STATE_CMD_READSTART    0x00000003 /* read data second command (large page devices) */
 #define STATE_CMD_PAGEPROG     0x00000004 /* start page programm */
 #define STATE_CMD_READOOB      0x00000005 /* read OOB area */
 #define STATE_CMD_ERASE1       0x00000006 /* sector erase first command */
@@ -223,15 +218,23 @@ MODULE_PARM_DESC(dbg,            "Output debug information if not zero");
 
 /* Remove action bits ftom state */
 #define NS_STATE(x) ((x) & ~ACTION_MASK)
-       
-/* 
+
+/*
  * Maximum previous states which need to be saved. Currently saving is
  * only needed for page programm operation with preceeded read command
  * (which is only valid for 512-byte pages).
  */
 #define NS_MAX_PREVSTATES 1
 
-/* 
+/*
+ * A union to represent flash memory contents and flash buffer.
+ */
+union ns_mem {
+       u_char *byte;    /* for byte access */
+       uint16_t *word;  /* for 16-bit word access */
+};
+
+/*
  * The structure which describes all the internal simulator data.
  */
 struct nandsim {
@@ -242,23 +245,17 @@ struct nandsim {
        uint32_t options;       /* chip's characteristic bits */
        uint32_t state;         /* current chip state */
        uint32_t nxstate;       /* next expected state */
-       
+
        uint32_t *op;           /* current operation, NULL operations isn't known yet  */
        uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */
        uint16_t npstates;      /* number of previous states saved */
        uint16_t stateidx;      /* current state index */
 
-       /* The simulated NAND flash image */
-       union flash_media {
-               u_char *byte;
-               uint16_t    *word;
-       } mem;
+       /* The simulated NAND flash pages array */
+       union ns_mem *pages;
 
        /* Internal buffer of page + OOB size bytes */
-       union internal_buffer {
-               u_char *byte;    /* for byte access */
-               uint16_t *word;  /* for 16-bit word access */
-       } buf;
+       union ns_mem buf;
 
        /* NAND flash "geometry" */
        struct nandsin_geometry {
@@ -346,13 +343,50 @@ static struct mtd_info *nsmtd;
 
 static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
 
+/*
+ * Allocate array of page pointers and initialize the array to NULL
+ * pointers.
+ *
+ * RETURNS: 0 if success, -ENOMEM if memory alloc fails.
+ */
+static int alloc_device(struct nandsim *ns)
+{
+       int i;
+
+       ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem));
+       if (!ns->pages) {
+               NS_ERR("alloc_map: unable to allocate page array\n");
+               return -ENOMEM;
+       }
+       for (i = 0; i < ns->geom.pgnum; i++) {
+               ns->pages[i].byte = NULL;
+       }
+
+       return 0;
+}
+
+/*
+ * Free any allocated pages, and free the array of page pointers.
+ */
+static void free_device(struct nandsim *ns)
+{
+       int i;
+
+       if (ns->pages) {
+               for (i = 0; i < ns->geom.pgnum; i++) {
+                       if (ns->pages[i].byte)
+                               kfree(ns->pages[i].byte);
+               }
+               vfree(ns->pages);
+       }
+}
+
 /*
  * Initialize the nandsim structure.
  *
  * RETURNS: 0 if success, -ERRNO if failure.
  */
-static int
-init_nandsim(struct mtd_info *mtd)
+static int init_nandsim(struct mtd_info *mtd)
 {
        struct nand_chip *chip = (struct nand_chip *)mtd->priv;
        struct nandsim   *ns   = (struct nandsim *)(chip->priv);
@@ -369,7 +403,7 @@ init_nandsim(struct mtd_info *mtd)
        /* Initialize the NAND flash parameters */
        ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8;
        ns->geom.totsz    = mtd->size;
-       ns->geom.pgsz     = mtd->oobblock;
+       ns->geom.pgsz     = mtd->writesize;
        ns->geom.oobsz    = mtd->oobsize;
        ns->geom.secsz    = mtd->erasesize;
        ns->geom.pgszoob  = ns->geom.pgsz + ns->geom.oobsz;
@@ -406,14 +440,14 @@ init_nandsim(struct mtd_info *mtd)
                }
        } else {
                if (ns->geom.totsz <= (128 << 20)) {
-                       ns->geom.pgaddrbytes  = 5;
+                       ns->geom.pgaddrbytes  = 4;
                        ns->geom.secaddrbytes = 2;
                } else {
                        ns->geom.pgaddrbytes  = 5;
                        ns->geom.secaddrbytes = 3;
                }
        }
-       
+
        /* Detect how many ID bytes the NAND chip outputs */
         for (i = 0; nand_flash_ids[i].name != NULL; i++) {
                 if (second_id_byte != nand_flash_ids[i].id)
@@ -440,23 +474,8 @@ init_nandsim(struct mtd_info *mtd)
        printk("sector address bytes: %u\n",    ns->geom.secaddrbytes);
        printk("options: %#x\n",                ns->options);
 
-       /* Map / allocate and initialize the flash image */
-#ifdef CONFIG_NS_ABS_POS
-       ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob);
-       if (!ns->mem.byte) {
-               NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n", 
-                       (void *)CONFIG_NS_ABS_POS);
-               return -ENOMEM;
-       }
-#else
-       ns->mem.byte = vmalloc(ns->geom.totszoob);
-       if (!ns->mem.byte) {
-               NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n",
-                       ns->geom.totszoob);
-               return -ENOMEM;
-       }
-       memset(ns->mem.byte, 0xFF, ns->geom.totszoob);
-#endif
+       if (alloc_device(ns) != 0)
+               goto error;
 
        /* Allocate / initialize the internal buffer */
        ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
@@ -475,11 +494,7 @@ init_nandsim(struct mtd_info *mtd)
        return 0;
 
 error:
-#ifdef CONFIG_NS_ABS_POS
-       iounmap(ns->mem.byte);
-#else
-       vfree(ns->mem.byte);
-#endif
+       free_device(ns);
 
        return -ENOMEM;
 }
@@ -487,16 +502,10 @@ error:
 /*
  * Free the nandsim structure.
  */
-static void
-free_nandsim(struct nandsim *ns)
+static void free_nandsim(struct nandsim *ns)
 {
        kfree(ns->buf.byte);
-
-#ifdef CONFIG_NS_ABS_POS
-       iounmap(ns->mem.byte);
-#else
-       vfree(ns->mem.byte);
-#endif
+       free_device(ns);
 
        return;
 }
@@ -504,8 +513,7 @@ free_nandsim(struct nandsim *ns)
 /*
  * Returns the string representation of 'state' state.
  */
-static char *
-get_state_name(uint32_t state)
+static char *get_state_name(uint32_t state)
 {
        switch (NS_STATE(state)) {
                case STATE_CMD_READ0:
@@ -563,11 +571,10 @@ get_state_name(uint32_t state)
  *
  * RETURNS: 1 if wrong command, 0 if right.
  */
-static int
-check_command(int cmd)
+static int check_command(int cmd)
 {
        switch (cmd) {
-               
+
        case NAND_CMD_READ0:
        case NAND_CMD_READSTART:
        case NAND_CMD_PAGEPROG:
@@ -580,7 +587,7 @@ check_command(int cmd)
        case NAND_CMD_RESET:
        case NAND_CMD_READ1:
                return 0;
-               
+
        case NAND_CMD_STATUS_MULTI:
        default:
                return 1;
@@ -590,8 +597,7 @@ check_command(int cmd)
 /*
  * Returns state after command is accepted by command number.
  */
-static uint32_t
-get_state_by_command(unsigned command)
+static uint32_t get_state_by_command(unsigned command)
 {
        switch (command) {
                case NAND_CMD_READ0:
@@ -627,11 +633,10 @@ get_state_by_command(unsigned command)
 /*
  * Move an address byte to the correspondent internal register.
  */
-static inline void
-accept_addr_byte(struct nandsim *ns, u_char bt)
+static inline void accept_addr_byte(struct nandsim *ns, u_char bt)
 {
        uint byte = (uint)bt;
-       
+
        if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes))
                ns->regs.column |= (byte << 8 * ns->regs.count);
        else {
@@ -642,12 +647,11 @@ accept_addr_byte(struct nandsim *ns, u_char bt)
 
        return;
 }
-               
+
 /*
  * Switch to STATE_READY state.
  */
-static inline void 
-switch_to_ready_state(struct nandsim *ns, u_char status)
+static inline void switch_to_ready_state(struct nandsim *ns, u_char status)
 {
        NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY));
 
@@ -675,7 +679,7 @@ switch_to_ready_state(struct nandsim *ns, u_char status)
  *      (for example program from the second half and read from the
  *      second half operations both begin with the READ1 command). In this
  *      case the ns->pstates[] array contains previous states.
- * 
+ *
  * Thus, the function tries to find operation containing the following
  * states (if the 'flag' parameter is 0):
  *    ns->pstates[0], ... ns->pstates[ns->npstates], ns->state
@@ -683,7 +687,7 @@ switch_to_ready_state(struct nandsim *ns, u_char status)
  * If (one and only one) matching operation is found, it is accepted (
  * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is
  * zeroed).
- * 
+ *
  * If there are several maches, the current state is pushed to the
  * ns->pstates.
  *
@@ -692,7 +696,7 @@ switch_to_ready_state(struct nandsim *ns, u_char status)
  * In such situation the function is called with 'flag' != 0, and the
  * operation is searched using the following pattern:
  *     ns->pstates[0], ... ns->pstates[ns->npstates], <address input>
- * 
+ *
  * It is supposed that this pattern must either match one operation on
  * none. There can't be ambiguity in that case.
  *
@@ -706,20 +710,19 @@ switch_to_ready_state(struct nandsim *ns, u_char status)
  *          -1 - several matches.
  *           0 - operation is found.
  */
-static int
-find_operation(struct nandsim *ns, uint32_t flag)
+static int find_operation(struct nandsim *ns, uint32_t flag)
 {
        int opsfound = 0;
        int i, j, idx = 0;
-       
+
        for (i = 0; i < NS_OPER_NUM; i++) {
 
                int found = 1;
-       
+
                if (!(ns->options & ops[i].reqopts))
                        /* Ignore operations we can't perform */
                        continue;
-                       
+
                if (flag) {
                        if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK))
                                continue;
@@ -728,7 +731,7 @@ find_operation(struct nandsim *ns, uint32_t flag)
                                continue;
                }
 
-               for (j = 0; j < ns->npstates; j++) 
+               for (j = 0; j < ns->npstates; j++)
                        if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j])
                                && (ns->options & ops[idx].reqopts)) {
                                found = 0;
@@ -745,7 +748,7 @@ find_operation(struct nandsim *ns, uint32_t flag)
                /* Exact match */
                ns->op = &ops[idx].states[0];
                if (flag) {
-                       /* 
+                       /*
                         * In this case the find_operation function was
                         * called when address has just began input. But it isn't
                         * yet fully input and the current state must
@@ -763,7 +766,7 @@ find_operation(struct nandsim *ns, uint32_t flag)
                                idx, get_state_name(ns->state), get_state_name(ns->nxstate));
                return 0;
        }
-       
+
        if (opsfound == 0) {
                /* Nothing was found. Try to ignore previous commands (if any) and search again */
                if (ns->npstates != 0) {
@@ -777,13 +780,13 @@ find_operation(struct nandsim *ns, uint32_t flag)
                switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
                return -2;
        }
-       
+
        if (flag) {
                /* This shouldn't happen */
                NS_DBG("find_operation: BUG, operation must be known if address is input\n");
                return -2;
        }
-       
+
        NS_DBG("find_operation: there is still ambiguity\n");
 
        ns->pstates[ns->npstates++] = ns->state;
@@ -791,19 +794,98 @@ find_operation(struct nandsim *ns, uint32_t flag)
        return -1;
 }
 
+/*
+ * Returns a pointer to the current page.
+ */
+static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns)
+{
+       return &(ns->pages[ns->regs.row]);
+}
+
+/*
+ * Retuns a pointer to the current byte, within the current page.
+ */
+static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns)
+{
+       return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
+}
+
+/*
+ * Fill the NAND buffer with data read from the specified page.
+ */
+static void read_page(struct nandsim *ns, int num)
+{
+       union ns_mem *mypage;
+
+       mypage = NS_GET_PAGE(ns);
+       if (mypage->byte == NULL) {
+               NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
+               memset(ns->buf.byte, 0xFF, num);
+       } else {
+               NS_DBG("read_page: page %d allocated, reading from %d\n",
+                       ns->regs.row, ns->regs.column + ns->regs.off);
+               memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
+       }
+}
+
+/*
+ * Erase all pages in the specified sector.
+ */
+static void erase_sector(struct nandsim *ns)
+{
+       union ns_mem *mypage;
+       int i;
+
+       mypage = NS_GET_PAGE(ns);
+       for (i = 0; i < ns->geom.pgsec; i++) {
+               if (mypage->byte != NULL) {
+                       NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i);
+                       kfree(mypage->byte);
+                       mypage->byte = NULL;
+               }
+               mypage++;
+       }
+}
+
+/*
+ * Program the specified page with the contents from the NAND buffer.
+ */
+static int prog_page(struct nandsim *ns, int num)
+{
+       int i;
+       union ns_mem *mypage;
+       u_char *pg_off;
+
+       mypage = NS_GET_PAGE(ns);
+       if (mypage->byte == NULL) {
+               NS_DBG("prog_page: allocating page %d\n", ns->regs.row);
+               mypage->byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
+               if (mypage->byte == NULL) {
+                       NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
+                       return -1;
+               }
+               memset(mypage->byte, 0xFF, ns->geom.pgszoob);
+       }
+
+       pg_off = NS_PAGE_BYTE_OFF(ns);
+       for (i = 0; i < num; i++)
+               pg_off[i] &= ns->buf.byte[i];
+
+       return 0;
+}
+
 /*
  * If state has any action bit, perform this action.
  *
  * RETURNS: 0 if success, -1 if error.
  */
-static int
-do_state_action(struct nandsim *ns, uint32_t action)
+static int do_state_action(struct nandsim *ns, uint32_t action)
 {
-       int i, num;
+       int num;
        int busdiv = ns->busw == 8 ? 1 : 2;
 
        action &= ACTION_MASK;
-       
+
        /* Check that page address input is correct */
        if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) {
                NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row);
@@ -823,18 +905,18 @@ do_state_action(struct nandsim *ns, uint32_t action)
                        break;
                }
                num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
-               memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num);
+               read_page(ns, num);
 
                NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
                        num, NS_RAW_OFFSET(ns) + ns->regs.off);
-               
+
                if (ns->regs.off == 0)
                        NS_LOG("read page %d\n", ns->regs.row);
                else if (ns->regs.off < ns->geom.pgsz)
                        NS_LOG("read page %d (second half)\n", ns->regs.row);
                else
                        NS_LOG("read OOB of page %d\n", ns->regs.row);
-               
+
                NS_UDELAY(access_delay);
                NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv);
 
@@ -844,30 +926,30 @@ do_state_action(struct nandsim *ns, uint32_t action)
                /*
                 * Erase sector.
                 */
-               
+
                if (ns->lines.wp) {
                        NS_ERR("do_state_action: device is write-protected, ignore sector erase\n");
                        return -1;
                }
-               
+
                if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec
                        || (ns->regs.row & ~(ns->geom.secsz - 1))) {
                        NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row);
                        return -1;
                }
-               
+
                ns->regs.row = (ns->regs.row <<
                                8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
                ns->regs.column = 0;
-               
+
                NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
                                ns->regs.row, NS_RAW_OFFSET(ns));
                NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
 
-               memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob);
-               
+               erase_sector(ns);
+
                NS_MDELAY(erase_delay);
-               
+
                break;
 
        case ACTION_PRGPAGE:
@@ -887,18 +969,18 @@ do_state_action(struct nandsim *ns, uint32_t action)
                        return -1;
                }
 
-               for (i = 0; i < num; i++)
-                       ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i];
+               if (prog_page(ns, num) == -1)
+                       return -1;
 
                NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
                        num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
                NS_LOG("programm page %d\n", ns->regs.row);
-               
+
                NS_UDELAY(programm_delay);
                NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
-               
+
                break;
-       
+
        case ACTION_ZEROOFF:
                NS_DBG("do_state_action: set internal offset to 0\n");
                ns->regs.off = 0;
@@ -918,7 +1000,7 @@ do_state_action(struct nandsim *ns, uint32_t action)
                NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz);
                ns->regs.off = ns->geom.pgsz;
                break;
-               
+
        default:
                NS_DBG("do_state_action: BUG! unknown action\n");
        }
@@ -929,15 +1011,14 @@ do_state_action(struct nandsim *ns, uint32_t action)
 /*
  * Switch simulator's state.
  */
-static void
-switch_state(struct nandsim *ns)
+static void switch_state(struct nandsim *ns)
 {
        if (ns->op) {
                /*
                 * The current operation have already been identified.
                 * Just follow the states chain.
                 */
-               
+
                ns->stateidx += 1;
                ns->state = ns->nxstate;
                ns->nxstate = ns->op[ns->stateidx + 1];
@@ -951,14 +1032,14 @@ switch_state(struct nandsim *ns)
                        switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
                        return;
                }
-               
+
        } else {
                /*
                 * We don't yet know which operation we perform.
                 * Try to identify it.
                 */
 
-               /*  
+               /*
                 *  The only event causing the switch_state function to
                 *  be called with yet unknown operation is new command.
                 */
@@ -987,7 +1068,7 @@ switch_state(struct nandsim *ns)
                 */
 
                u_char status = NS_STATUS_OK(ns);
-               
+
                /* In case of data states, see if all bytes were input/output */
                if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK))
                        && ns->regs.count != ns->regs.num) {
@@ -995,17 +1076,17 @@ switch_state(struct nandsim *ns)
                                        ns->regs.num - ns->regs.count);
                        status = NS_STATUS_FAILED(ns);
                }
-                               
+
                NS_DBG("switch_state: operation complete, switch to STATE_READY state\n");
 
                switch_to_ready_state(ns, status);
 
                return;
        } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) {
-               /* 
+               /*
                 * If the next state is data input/output, switch to it now
                 */
-               
+
                ns->state      = ns->nxstate;
                ns->nxstate    = ns->op[++ns->stateidx + 1];
                ns->regs.num   = ns->regs.count = 0;
@@ -1023,16 +1104,16 @@ switch_state(struct nandsim *ns)
                        case STATE_DATAOUT:
                                ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
                                break;
-                               
+
                        case STATE_DATAOUT_ID:
                                ns->regs.num = ns->geom.idbytes;
                                break;
-                               
+
                        case STATE_DATAOUT_STATUS:
                        case STATE_DATAOUT_STATUS_M:
                                ns->regs.count = ns->regs.num = 0;
                                break;
-                               
+
                        default:
                                NS_ERR("switch_state: BUG! unknown data state\n");
                }
@@ -1044,16 +1125,16 @@ switch_state(struct nandsim *ns)
                 */
 
                ns->regs.count = 0;
-               
+
                switch (NS_STATE(ns->nxstate)) {
                        case STATE_ADDR_PAGE:
                                ns->regs.num = ns->geom.pgaddrbytes;
-               
+
                                break;
                        case STATE_ADDR_SEC:
                                ns->regs.num = ns->geom.secaddrbytes;
                                break;
-       
+
                        case STATE_ADDR_ZERO:
                                ns->regs.num = 1;
                                break;
@@ -1062,7 +1143,7 @@ switch_state(struct nandsim *ns)
                                NS_ERR("switch_state: BUG! unknown address state\n");
                }
        } else {
-               /* 
+               /*
                 * Just reset internal counters.
                 */
 
@@ -1071,70 +1152,7 @@ switch_state(struct nandsim *ns)
        }
 }
 
-static void
-ns_hwcontrol(struct mtd_info *mtd, int cmd)
-{
-       struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
-
-       switch (cmd) {
-
-       /* set CLE line high */
-       case NAND_CTL_SETCLE:
-               NS_DBG("ns_hwcontrol: start command latch cycles\n");
-               ns->lines.cle  = 1;
-               break;
-
-       /* set CLE line low */
-       case NAND_CTL_CLRCLE:
-               NS_DBG("ns_hwcontrol: stop command latch cycles\n");
-               ns->lines.cle  = 0;
-               break;
-
-       /* set ALE line high */
-       case NAND_CTL_SETALE:
-               NS_DBG("ns_hwcontrol: start address latch cycles\n");
-               ns->lines.ale   = 1;
-               break;
-
-       /* set ALE line low */
-       case NAND_CTL_CLRALE:
-               NS_DBG("ns_hwcontrol: stop address latch cycles\n");
-               ns->lines.ale  = 0;
-               break;
-
-       /* set WP line high */
-       case NAND_CTL_SETWP:
-               NS_DBG("ns_hwcontrol: enable write protection\n");
-               ns->lines.wp = 1;
-               break;
-
-       /* set WP line low */
-       case NAND_CTL_CLRWP:
-               NS_DBG("ns_hwcontrol: disable write protection\n");
-               ns->lines.wp = 0;
-               break;
-
-       /* set CE line low */
-       case NAND_CTL_SETNCE:
-               NS_DBG("ns_hwcontrol: enable chip\n");
-               ns->lines.ce = 1;
-               break;
-
-       /* set CE line high */
-       case NAND_CTL_CLRNCE:
-               NS_DBG("ns_hwcontrol: disable chip\n");
-               ns->lines.ce = 0;
-               break;
-
-       default:
-               NS_ERR("hwcontrol: unknown command\n");
-        }
-
-       return;
-}
-
-static u_char
-ns_nand_read_byte(struct mtd_info *mtd)
+static u_char ns_nand_read_byte(struct mtd_info *mtd)
 {
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
        u_char outb = 0x00;
@@ -1184,7 +1202,7 @@ ns_nand_read_byte(struct mtd_info *mtd)
                default:
                        BUG();
        }
-       
+
        if (ns->regs.count == ns->regs.num) {
                NS_DBG("read_byte: all bytes were read\n");
 
@@ -1201,17 +1219,16 @@ ns_nand_read_byte(struct mtd_info *mtd)
                }
                else if (NS_STATE(ns->nxstate) == STATE_READY)
                        switch_state(ns);
-               
+
        }
-       
+
        return outb;
 }
 
-static void
-ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
+static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
 {
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
-       
+
        /* Sanity and correctness checks */
        if (!ns->lines.ce) {
                NS_ERR("write_byte: chip is disabled, ignore write\n");
@@ -1221,7 +1238,7 @@ ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
                NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n");
                return;
        }
-                       
+
        if (ns->lines.cle == 1) {
                /*
                 * The byte written is a command.
@@ -1233,7 +1250,7 @@ ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
                        return;
                }
 
-               /* 
+               /*
                 * Chip might still be in STATE_DATAOUT
                 * (if OPT_AUTOINCR feature is supported), STATE_DATAOUT_STATUS or
                 * STATE_DATAOUT_STATUS_M state. If so, switch state.
@@ -1254,13 +1271,13 @@ ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
                                "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate));
                        switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
                }
-               
+
                /* Check that the command byte is correct */
                if (check_command(byte)) {
                        NS_ERR("write_byte: unknown command %#x\n", (uint)byte);
                        return;
                }
-               
+
                NS_DBG("command byte corresponding to %s state accepted\n",
                        get_state_name(get_state_by_command(byte)));
                ns->regs.command = byte;
@@ -1277,12 +1294,12 @@ ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
 
                        if (find_operation(ns, 1) < 0)
                                return;
-                       
+
                        if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
                                switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
                                return;
                        }
-                               
+
                        ns->regs.count = 0;
                        switch (NS_STATE(ns->nxstate)) {
                                case STATE_ADDR_PAGE:
@@ -1306,7 +1323,7 @@ ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
                        switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
                        return;
                }
-               
+
                /* Check if this is expected byte */
                if (ns->regs.count == ns->regs.num) {
                        NS_ERR("write_byte: no more address bytes expected\n");
@@ -1325,12 +1342,12 @@ ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
                        NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column);
                        switch_state(ns);
                }
-               
+
        } else {
                /*
                 * The byte written is an input data.
                 */
-               
+
                /* Check that chip is expecting data input */
                if (!(ns->state & STATE_DATAIN_MASK)) {
                        NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, "
@@ -1359,36 +1376,34 @@ ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
        return;
 }
 
-static int
-ns_device_ready(struct mtd_info *mtd)
+static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask)
+{
+       struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
+
+       ns->lines.cle = bitmask & NAND_CLE ? 1 : 0;
+       ns->lines.ale = bitmask & NAND_ALE ? 1 : 0;
+       ns->lines.ce = bitmask & NAND_NCE ? 1 : 0;
+
+       if (cmd != NAND_CMD_NONE)
+               ns_nand_write_byte(mtd, cmd);
+}
+
+static int ns_device_ready(struct mtd_info *mtd)
 {
        NS_DBG("device_ready\n");
        return 1;
 }
 
-static uint16_t
-ns_nand_read_word(struct mtd_info *mtd)
+static uint16_t ns_nand_read_word(struct mtd_info *mtd)
 {
        struct nand_chip *chip = (struct nand_chip *)mtd->priv;
 
        NS_DBG("read_word\n");
-       
-       return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8);
-}
 
-static void
-ns_nand_write_word(struct mtd_info *mtd, uint16_t word)
-{
-       struct nand_chip *chip = (struct nand_chip *)mtd->priv;
-       
-       NS_DBG("write_word\n");
-       
-       chip->write_byte(mtd, word & 0xFF);
-       chip->write_byte(mtd, word >> 8);
+       return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8);
 }
 
-static void 
-ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
 {
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
 
@@ -1409,14 +1424,13 @@ ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
 
        memcpy(ns->buf.byte + ns->regs.count, buf, len);
        ns->regs.count += len;
-       
+
        if (ns->regs.count == ns->regs.num) {
                NS_DBG("write_buf: %d bytes were written\n", ns->regs.count);
        }
 }
 
-static void 
-ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 {
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
 
@@ -1453,7 +1467,7 @@ ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 
        memcpy(buf, ns->buf.byte + ns->regs.count, len);
        ns->regs.count += len;
-       
+
        if (ns->regs.count == ns->regs.num) {
                if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) {
                        ns->regs.count = 0;
@@ -1465,12 +1479,11 @@ ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
                else if (NS_STATE(ns->nxstate) == STATE_READY)
                        switch_state(ns);
        }
-       
+
        return;
 }
 
-static int 
-ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+static int ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
 {
        ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len);
 
@@ -1483,37 +1496,10 @@ ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
        }
 }
 
-/*
- * Having only NAND chip IDs we call nand_scan which detects NAND flash
- * parameters and then calls scan_bbt in order to scan/find/build the
- * NAND flash bad block table. But since at that moment the NAND flash
- * image isn't allocated in the simulator, errors arise. To avoid this
- * we redefine the scan_bbt callback and initialize the nandsim structure
- * before the flash media scanning.
- */
-int ns_scan_bbt(struct mtd_info *mtd)
-{ 
-       struct nand_chip *chip = (struct nand_chip *)mtd->priv;
-       struct nandsim   *ns   = (struct nandsim *)(chip->priv);
-       int retval;
-
-       if (!NS_IS_INITIALIZED(ns))
-               if ((retval = init_nandsim(mtd)) != 0) {
-                       NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
-                       return retval;
-               }
-       if ((retval = nand_default_bbt(mtd)) != 0) {
-               free_nandsim(ns);
-               return retval;
-       }
-
-       return 0;
-}
-
 /*
  * Module initialization function
  */
-int __init ns_init_module(void)
+static int __init ns_init_module(void)
 {
        struct nand_chip *chip;
        struct nandsim *nand;
@@ -1523,39 +1509,35 @@ int __init ns_init_module(void)
                NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
                return -EINVAL;
        }
-       
+
        /* Allocate and initialize mtd_info, nand_chip and nandsim structures */
-       nsmtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)
+       nsmtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)
                                + sizeof(struct nandsim), GFP_KERNEL);
        if (!nsmtd) {
                NS_ERR("unable to allocate core structures.\n");
                return -ENOMEM;
        }
-       memset(nsmtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip) +
-                       sizeof(struct nandsim));
        chip        = (struct nand_chip *)(nsmtd + 1);
         nsmtd->priv = (void *)chip;
        nand        = (struct nandsim *)(chip + 1);
-       chip->priv  = (void *)nand;     
+       chip->priv  = (void *)nand;
 
        /*
         * Register simulator's callbacks.
         */
-       chip->hwcontrol  = ns_hwcontrol;
+       chip->cmd_ctrl   = ns_hwcontrol;
        chip->read_byte  = ns_nand_read_byte;
        chip->dev_ready  = ns_device_ready;
-       chip->scan_bbt   = ns_scan_bbt;
-       chip->write_byte = ns_nand_write_byte;
        chip->write_buf  = ns_nand_write_buf;
        chip->read_buf   = ns_nand_read_buf;
        chip->verify_buf = ns_nand_verify_buf;
-       chip->write_word = ns_nand_write_word;
        chip->read_word  = ns_nand_read_word;
-       chip->eccmode    = NAND_ECC_SOFT;
+       chip->ecc.mode   = NAND_ECC_SOFT;
+       chip->options   |= NAND_SKIP_BBTSCAN;
 
-       /* 
+       /*
         * Perform minimum nandsim structure initialization to handle
-        * the initial ID read command correctly 
+        * the initial ID read command correctly
         */
        if (third_id_byte != 0xFF || fourth_id_byte != 0xFF)
                nand->geom.idbytes = 4;
@@ -1573,6 +1555,8 @@ int __init ns_init_module(void)
                chip->options |= NAND_BUSWIDTH_16;
        }
 
+       nsmtd->owner = THIS_MODULE;
+
        if ((retval = nand_scan(nsmtd, 1)) != 0) {
                NS_ERR("can't register NAND Simulator\n");
                if (retval > 0)
@@ -1580,6 +1564,16 @@ int __init ns_init_module(void)
                goto error;
        }
 
+       if ((retval = init_nandsim(nsmtd)) != 0) {
+               NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
+               goto error;
+       }
+
+       if ((retval = nand_default_bbt(nsmtd)) != 0) {
+               free_nandsim(nand);
+               goto error;
+       }
+
        /* Register NAND as one big partition */
        add_mtd_partitions(nsmtd, &nand->part, 1);