fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / mtd / nand / nandsim.c
index a0af92c..c3bca95 100644 (file)
@@ -25,7 +25,6 @@
  * $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)  || \
@@ -165,7 +160,7 @@ MODULE_PARM_DESC(dbg,            "Output debug information if not zero");
 /* 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 */
@@ -231,6 +226,14 @@ MODULE_PARM_DESC(dbg,            "Output debug information if not zero");
  */
 #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.
  */
@@ -248,17 +251,11 @@ struct nandsim {
        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,7 +440,7 @@ 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;
@@ -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,8 +571,7 @@ 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) {
 
@@ -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,8 +633,7 @@ 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;
 
@@ -646,8 +651,7 @@ accept_addr_byte(struct nandsim *ns, u_char bt)
 /*
  * 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));
 
@@ -706,8 +710,7 @@ 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;
@@ -791,15 +794,94 @@ 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;
@@ -823,7 +905,7 @@ 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);
@@ -864,7 +946,7 @@ do_state_action(struct nandsim *ns, uint32_t action)
                                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);
 
@@ -887,8 +969,8 @@ 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);
@@ -929,8 +1011,7 @@ 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) {
                /*
@@ -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;
@@ -1207,8 +1225,7 @@ ns_nand_read_byte(struct mtd_info *mtd)
        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;
 
@@ -1359,15 +1376,25 @@ 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;
 
@@ -1376,19 +1403,7 @@ ns_nand_read_word(struct mtd_info *mtd)
        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);
-}
-
-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;
 
@@ -1415,8 +1430,7 @@ ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
        }
 }
 
-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;
 
@@ -1469,8 +1483,7 @@ ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
        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);
 
@@ -1498,14 +1511,12 @@ static int __init ns_init_module(void)
        }
 
        /* 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);
@@ -1514,16 +1525,14 @@ static int __init ns_init_module(void)
        /*
         * 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->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;
 
        /*
@@ -1546,6 +1555,8 @@ static 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)