summaryrefslogtreecommitdiffstats
path: root/sys/dev/nand/nandsim_chip.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/nand/nandsim_chip.c')
-rw-r--r--sys/dev/nand/nandsim_chip.c901
1 files changed, 901 insertions, 0 deletions
diff --git a/sys/dev/nand/nandsim_chip.c b/sys/dev/nand/nandsim_chip.c
new file mode 100644
index 0000000..f04ad80
--- /dev/null
+++ b/sys/dev/nand/nandsim_chip.c
@@ -0,0 +1,901 @@
+/*-
+ * Copyright (C) 2009-2012 Semihalf
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/kthread.h>
+#include <sys/unistd.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandsim_chip.h>
+#include <dev/nand/nandsim_log.h>
+#include <dev/nand/nandsim_swap.h>
+
+MALLOC_DEFINE(M_NANDSIM, "NANDsim", "NANDsim dynamic data");
+
+#define NANDSIM_CHIP_LOCK(chip) mtx_lock(&(chip)->ns_lock)
+#define NANDSIM_CHIP_UNLOCK(chip) mtx_unlock(&(chip)->ns_lock)
+
+static nandsim_evh_t erase_evh;
+static nandsim_evh_t idle_evh;
+static nandsim_evh_t poweron_evh;
+static nandsim_evh_t reset_evh;
+static nandsim_evh_t read_evh;
+static nandsim_evh_t readid_evh;
+static nandsim_evh_t readparam_evh;
+static nandsim_evh_t write_evh;
+
+static void nandsim_loop(void *);
+static void nandsim_undefined(struct nandsim_chip *, uint8_t);
+static void nandsim_bad_address(struct nandsim_chip *, uint8_t *);
+static void nandsim_ignore_address(struct nandsim_chip *, uint8_t);
+static void nandsim_sm_error(struct nandsim_chip *);
+static void nandsim_start_handler(struct nandsim_chip *, nandsim_evh_t);
+
+static void nandsim_callout_eh(void *);
+static int nandsim_delay(struct nandsim_chip *, int);
+
+static int nandsim_bbm_init(struct nandsim_chip *, uint32_t, uint32_t *);
+static int nandsim_blk_state_init(struct nandsim_chip *, uint32_t, uint32_t);
+static void nandsim_blk_state_destroy(struct nandsim_chip *);
+static int nandchip_is_block_valid(struct nandsim_chip *, int);
+
+static void nandchip_set_status(struct nandsim_chip *, uint8_t);
+static void nandchip_clear_status(struct nandsim_chip *, uint8_t);
+
+struct proc *nandsim_proc;
+
+struct nandsim_chip *
+nandsim_chip_init(struct nandsim_softc* sc, uint8_t chip_num,
+ struct sim_chip *sim_chip)
+{
+ struct nandsim_chip *chip;
+ struct onfi_params *chip_param;
+ char swapfile[20];
+ uint32_t size;
+ int error;
+
+ chip = malloc(sizeof(*chip), M_NANDSIM, M_WAITOK | M_ZERO);
+ if (!chip)
+ return (NULL);
+
+ mtx_init(&chip->ns_lock, "nandsim lock", NULL, MTX_DEF);
+ callout_init(&chip->ns_callout, CALLOUT_MPSAFE);
+ STAILQ_INIT(&chip->nandsim_events);
+
+ chip->chip_num = chip_num;
+ chip->ctrl_num = sim_chip->ctrl_num;
+ chip->sc = sc;
+
+ if (!sim_chip->is_wp)
+ nandchip_set_status(chip, NAND_STATUS_WP);
+
+ chip_param = &chip->params;
+
+ chip->id.dev_id = sim_chip->device_id;
+ chip->id.man_id = sim_chip->manufact_id;
+
+ chip->error_ratio = sim_chip->error_ratio;
+ chip->wear_level = sim_chip->wear_level;
+ chip->prog_delay = sim_chip->prog_time;
+ chip->erase_delay = sim_chip->erase_time;
+ chip->read_delay = sim_chip->read_time;
+
+ chip_param->t_prog = sim_chip->prog_time;
+ chip_param->t_bers = sim_chip->erase_time;
+ chip_param->t_r = sim_chip->read_time;
+ bcopy("onfi", &chip_param->signature, 4);
+
+ chip_param->manufacturer_id = sim_chip->manufact_id;
+ strncpy(chip_param->manufacturer_name, sim_chip->manufacturer, 12);
+ chip_param->manufacturer_name[11] = 0;
+ strncpy(chip_param->device_model, sim_chip->device_model, 20);
+ chip_param->device_model[19] = 0;
+
+ chip_param->bytes_per_page = sim_chip->page_size;
+ chip_param->spare_bytes_per_page = sim_chip->oob_size;
+ chip_param->pages_per_block = sim_chip->pgs_per_blk;
+ chip_param->blocks_per_lun = sim_chip->blks_per_lun;
+ chip_param->luns = sim_chip->luns;
+
+ init_chip_geom(&chip->cg, chip_param->luns, chip_param->blocks_per_lun,
+ chip_param->pages_per_block, chip_param->bytes_per_page,
+ chip_param->spare_bytes_per_page);
+
+ chip_param->address_cycles = sim_chip->row_addr_cycles |
+ (sim_chip->col_addr_cycles << 4);
+ chip_param->features = sim_chip->features;
+ if (sim_chip->width == 16)
+ chip_param->features |= ONFI_FEAT_16BIT;
+
+ size = chip_param->blocks_per_lun * chip_param->luns;
+
+ error = nandsim_blk_state_init(chip, size, sim_chip->wear_level);
+ if (error) {
+ mtx_destroy(&chip->ns_lock);
+ free(chip, M_NANDSIM);
+ return (NULL);
+ }
+
+ error = nandsim_bbm_init(chip, size, sim_chip->bad_block_map);
+ if (error) {
+ mtx_destroy(&chip->ns_lock);
+ nandsim_blk_state_destroy(chip);
+ free(chip, M_NANDSIM);
+ return (NULL);
+ }
+
+ nandsim_start_handler(chip, poweron_evh);
+
+ nand_debug(NDBG_SIM,"Create thread for chip%d [%8p]", chip->chip_num,
+ chip);
+ /* Create chip thread */
+ error = kproc_kthread_add(nandsim_loop, chip, &nandsim_proc,
+ &chip->nandsim_td, RFSTOPPED | RFHIGHPID,
+ 0, "nandsim", "chip");
+ if (error) {
+ mtx_destroy(&chip->ns_lock);
+ nandsim_blk_state_destroy(chip);
+ free(chip, M_NANDSIM);
+ return (NULL);
+ }
+
+ thread_lock(chip->nandsim_td);
+ sched_class(chip->nandsim_td, PRI_REALTIME);
+ sched_add(chip->nandsim_td, SRQ_BORING);
+ thread_unlock(chip->nandsim_td);
+
+ size = (chip_param->bytes_per_page +
+ chip_param->spare_bytes_per_page) *
+ chip_param->pages_per_block;
+
+ sprintf(swapfile, "chip%d%d.swp", chip->ctrl_num, chip->chip_num);
+ chip->swap = nandsim_swap_init(swapfile, chip_param->blocks_per_lun *
+ chip_param->luns, size);
+ if (!chip->swap)
+ nandsim_chip_destroy(chip);
+
+ /* Wait for new thread to enter main loop */
+ tsleep(chip->nandsim_td, PWAIT, "ns_chip", 1 * hz);
+
+ return (chip);
+}
+
+static int
+nandsim_blk_state_init(struct nandsim_chip *chip, uint32_t size,
+ uint32_t wear_lev)
+{
+ int i;
+
+ if (!chip || size == 0)
+ return (-1);
+
+ chip->blk_state = malloc(size * sizeof(struct nandsim_block_state),
+ M_NANDSIM, M_WAITOK | M_ZERO);
+ if (!chip->blk_state) {
+ return (-1);
+ }
+
+ for (i = 0; i < size; i++) {
+ if (wear_lev)
+ chip->blk_state[i].wear_lev = wear_lev;
+ else
+ chip->blk_state[i].wear_lev = -1;
+ }
+
+ return (0);
+}
+
+static void
+nandsim_blk_state_destroy(struct nandsim_chip *chip)
+{
+
+ if (chip && chip->blk_state)
+ free(chip->blk_state, M_NANDSIM);
+}
+
+static int
+nandsim_bbm_init(struct nandsim_chip *chip, uint32_t size,
+ uint32_t *sim_bbm)
+{
+ uint32_t index;
+ int i;
+
+ if ((chip == NULL) || (size == 0))
+ return (-1);
+
+ if (chip->blk_state == NULL)
+ return (-1);
+
+ if (sim_bbm == NULL)
+ return (0);
+
+ for (i = 0; i < MAX_BAD_BLOCKS; i++) {
+ index = sim_bbm[i];
+
+ if (index == 0xffffffff)
+ break;
+ else if (index > size)
+ return (-1);
+ else
+ chip->blk_state[index].is_bad = 1;
+ }
+
+ return (0);
+}
+
+void
+nandsim_chip_destroy(struct nandsim_chip *chip)
+{
+ struct nandsim_ev *ev;
+
+ ev = create_event(chip, NANDSIM_EV_EXIT, 0);
+ if (ev)
+ send_event(ev);
+}
+
+void
+nandsim_chip_freeze(struct nandsim_chip *chip)
+{
+
+ chip->flags |= NANDSIM_CHIP_FROZEN;
+}
+
+static void
+nandsim_loop(void *arg)
+{
+ struct nandsim_chip *chip = (struct nandsim_chip *)arg;
+ struct nandsim_ev *ev;
+
+ nand_debug(NDBG_SIM,"Start main loop for chip%d [%8p]", chip->chip_num,
+ chip);
+ for(;;) {
+ NANDSIM_CHIP_LOCK(chip);
+ if (!(chip->flags & NANDSIM_CHIP_ACTIVE)) {
+ chip->flags |= NANDSIM_CHIP_ACTIVE;
+ wakeup(chip->nandsim_td);
+ }
+
+ if (STAILQ_EMPTY(&chip->nandsim_events)) {
+ nand_debug(NDBG_SIM,"Chip%d [%8p] going sleep",
+ chip->chip_num, chip);
+ msleep(chip, &chip->ns_lock, PRIBIO, "nandev", 0);
+ }
+
+ ev = STAILQ_FIRST(&chip->nandsim_events);
+ STAILQ_REMOVE_HEAD(&chip->nandsim_events, links);
+ NANDSIM_CHIP_UNLOCK(chip);
+ if (ev->type == NANDSIM_EV_EXIT) {
+ NANDSIM_CHIP_LOCK(chip);
+ destroy_event(ev);
+ wakeup(ev);
+ while (!STAILQ_EMPTY(&chip->nandsim_events)) {
+ ev = STAILQ_FIRST(&chip->nandsim_events);
+ STAILQ_REMOVE_HEAD(&chip->nandsim_events,
+ links);
+ destroy_event(ev);
+ wakeup(ev);
+ };
+ NANDSIM_CHIP_UNLOCK(chip);
+ nandsim_log(chip, NANDSIM_LOG_SM, "destroyed\n");
+ mtx_destroy(&chip->ns_lock);
+ nandsim_blk_state_destroy(chip);
+ nandsim_swap_destroy(chip->swap);
+ free(chip, M_NANDSIM);
+ nandsim_proc = NULL;
+
+ kthread_exit();
+ }
+
+ if (!(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ nand_debug(NDBG_SIM,"Chip [%x] get event [%x]",
+ chip->chip_num, ev->type);
+ chip->ev_handler(chip, ev->type, ev->data);
+ }
+
+ wakeup(ev);
+ destroy_event(ev);
+ }
+
+}
+
+struct nandsim_ev *
+create_event(struct nandsim_chip *chip, uint8_t type, uint8_t data_size)
+{
+ struct nandsim_ev *ev;
+
+ ev = malloc(sizeof(*ev), M_NANDSIM, M_NOWAIT | M_ZERO);
+ if (!ev) {
+ nand_debug(NDBG_SIM,"Cannot create event");
+ return (NULL);
+ }
+
+ if (data_size > 0)
+ ev->data = malloc(sizeof(*ev), M_NANDSIM, M_NOWAIT | M_ZERO);
+ ev->type = type;
+ ev->chip = chip;
+
+ return (ev);
+}
+
+void
+destroy_event(struct nandsim_ev *ev)
+{
+
+ if (ev->data)
+ free(ev->data, M_NANDSIM);
+ free(ev, M_NANDSIM);
+}
+
+int
+send_event(struct nandsim_ev *ev)
+{
+ struct nandsim_chip *chip = ev->chip;
+
+ if (!(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ nand_debug(NDBG_SIM,"Chip%d [%p] send event %x",
+ chip->chip_num, chip, ev->type);
+
+ NANDSIM_CHIP_LOCK(chip);
+ STAILQ_INSERT_TAIL(&chip->nandsim_events, ev, links);
+ NANDSIM_CHIP_UNLOCK(chip);
+
+ wakeup(chip);
+ if ((ev->type != NANDSIM_EV_TIMEOUT) && chip->nandsim_td &&
+ (curthread != chip->nandsim_td))
+ tsleep(ev, PWAIT, "ns_ev", 5 * hz);
+ }
+
+ return (0);
+}
+
+static void
+nandsim_callout_eh(void *arg)
+{
+ struct nandsim_ev *ev = (struct nandsim_ev *)arg;
+
+ send_event(ev);
+}
+
+static int
+nandsim_delay(struct nandsim_chip *chip, int timeout)
+{
+ struct nandsim_ev *ev;
+ struct timeval delay;
+ int tm;
+
+ nand_debug(NDBG_SIM,"Chip[%d] Set delay: %d", chip->chip_num, timeout);
+
+ ev = create_event(chip, NANDSIM_EV_TIMEOUT, 0);
+ if (!ev)
+ return (-1);
+
+ chip->sm_state = NANDSIM_STATE_TIMEOUT;
+ tm = (timeout/10000) * (hz / 100);
+ if (callout_reset(&chip->ns_callout, tm, nandsim_callout_eh, ev))
+ return (-1);
+
+ delay.tv_sec = chip->read_delay / 1000000;
+ delay.tv_usec = chip->read_delay % 1000000;
+ timevaladd(&chip->delay_tv, &delay);
+
+ return (0);
+}
+
+static void
+nandsim_start_handler(struct nandsim_chip *chip, nandsim_evh_t evh)
+{
+ struct nandsim_ev *ev;
+
+ chip->ev_handler = evh;
+
+ nand_debug(NDBG_SIM,"Start handler %p for chip%d [%p]", evh,
+ chip->chip_num, chip);
+ ev = create_event(chip, NANDSIM_EV_START, 0);
+ if (!ev)
+ nandsim_sm_error(chip);
+
+ send_event(ev);
+}
+
+static void
+nandchip_set_data(struct nandsim_chip *chip, uint8_t *data, uint32_t len,
+ uint32_t idx)
+{
+
+ nand_debug(NDBG_SIM,"Chip [%x] data %p [%x] at %x", chip->chip_num,
+ data, len, idx);
+ chip->data.data_ptr = data;
+ chip->data.size = len;
+ chip->data.index = idx;
+}
+
+static int
+nandchip_chip_space(struct nandsim_chip *chip, int32_t row, int32_t column,
+ size_t size, uint8_t writing)
+{
+ struct block_space *blk_space;
+ uint32_t lun, block, page, offset, block_size;
+ int err;
+
+ block_size = chip->cg.block_size +
+ (chip->cg.oob_size * chip->cg.pgs_per_blk);
+
+ err = nand_row_to_blkpg(&chip->cg, row, &lun, &block, &page);
+ if (err) {
+ nand_debug(NDBG_SIM,"cannot get address\n");
+ return (-1);
+ }
+
+ if (!nandchip_is_block_valid(chip, block)) {
+ nandchip_set_data(chip, NULL, 0, 0);
+ return (-1);
+ }
+
+ blk_space = get_bs(chip->swap, block, writing);
+ if (!blk_space) {
+ nandchip_set_data(chip, NULL, 0, 0);
+ return (-1);
+ }
+
+ if (size > block_size)
+ size = block_size;
+
+ if (size == block_size) {
+ offset = 0;
+ column = 0;
+ } else
+ offset = page * (chip->cg.page_size + chip->cg.oob_size);
+
+ nandchip_set_data(chip, &blk_space->blk_ptr[offset], size, column);
+
+ return (0);
+}
+
+static int
+nandchip_get_addr_byte(struct nandsim_chip *chip, void *data, uint32_t *value)
+{
+ int ncycles = 0;
+ uint8_t byte;
+ uint8_t *buffer;
+
+ buffer = (uint8_t *)value;
+ byte = *((uint8_t *)data);
+
+ KASSERT((chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW ||
+ chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL),
+ ("unexpected state"));
+
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ ncycles = chip->params.address_cycles & 0xf;
+ buffer[chip->sm_addr_cycle++] = byte;
+ } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
+ ncycles = (chip->params.address_cycles >> 4) & 0xf;
+ buffer[chip->sm_addr_cycle++] = byte;
+ }
+
+ nand_debug(NDBG_SIM, "Chip [%x] read addr byte: %02x (%d of %d)\n",
+ chip->chip_num, byte, chip->sm_addr_cycle, ncycles);
+
+ if (chip->sm_addr_cycle == ncycles) {
+ chip->sm_addr_cycle = 0;
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+nandchip_is_block_valid(struct nandsim_chip *chip, int block_num)
+{
+
+ if (!chip || !chip->blk_state)
+ return (0);
+
+ if (chip->blk_state[block_num].wear_lev == 0 ||
+ chip->blk_state[block_num].is_bad)
+ return (0);
+
+ return (1);
+}
+
+static void
+nandchip_set_status(struct nandsim_chip *chip, uint8_t flags)
+{
+
+ chip->chip_status |= flags;
+}
+
+static void
+nandchip_clear_status(struct nandsim_chip *chip, uint8_t flags)
+{
+
+ chip->chip_status &= ~flags;
+}
+
+uint8_t
+nandchip_get_status(struct nandsim_chip *chip)
+{
+ return (chip->chip_status);
+}
+
+void
+nandsim_chip_timeout(struct nandsim_chip *chip)
+{
+ struct timeval tv;
+
+ getmicrotime(&tv);
+
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT &&
+ timevalcmp(&tv, &chip->delay_tv, >=)) {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ }
+}
+void
+poweron_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ uint8_t cmd;
+
+ if (type == NANDSIM_EV_START)
+ chip->sm_state = NANDSIM_STATE_IDLE;
+ else if (type == NANDSIM_EV_CMD) {
+ cmd = *(uint8_t *)data;
+ switch(cmd) {
+ case NAND_CMD_RESET:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in RESET state\n");
+ nandsim_start_handler(chip, reset_evh);
+ break;
+ default:
+ nandsim_undefined(chip, type);
+ break;
+ }
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+idle_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ uint8_t cmd;
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in IDLE state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else if (type == NANDSIM_EV_CMD) {
+ nandchip_clear_status(chip, NAND_STATUS_FAIL);
+ getmicrotime(&chip->delay_tv);
+ cmd = *(uint8_t *)data;
+ switch(cmd) {
+ case NAND_CMD_READ_ID:
+ nandsim_start_handler(chip, readid_evh);
+ break;
+ case NAND_CMD_READ_PARAMETER:
+ nandsim_start_handler(chip, readparam_evh);
+ break;
+ case NAND_CMD_READ:
+ nandsim_start_handler(chip, read_evh);
+ break;
+ case NAND_CMD_PROG:
+ nandsim_start_handler(chip, write_evh);
+ break;
+ case NAND_CMD_ERASE:
+ nandsim_start_handler(chip, erase_evh);
+ break;
+ default:
+ nandsim_undefined(chip, type);
+ break;
+ }
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+readid_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ struct onfi_params *params;
+ uint8_t addr;
+
+ params = &chip->params;
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in READID state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_BYTE;
+ } else if (type == NANDSIM_EV_ADDR) {
+
+ addr = *((uint8_t *)data);
+
+ if (addr == 0x0)
+ nandchip_set_data(chip, (uint8_t *)&chip->id, 2, 0);
+ else if (addr == ONFI_SIG_ADDR)
+ nandchip_set_data(chip, (uint8_t *)&params->signature,
+ 4, 0);
+ else
+ nandsim_bad_address(chip, &addr);
+
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+readparam_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ struct onfi_params *params;
+ uint8_t addr;
+
+ params = &chip->params;
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in READPARAM state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_BYTE;
+ } else if (type == NANDSIM_EV_ADDR) {
+ addr = *((uint8_t *)data);
+
+ if (addr == 0) {
+ nandchip_set_data(chip, (uint8_t *)params,
+ sizeof(*params), 0);
+ } else
+ nandsim_bad_address(chip, &addr);
+
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+read_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ static uint32_t column = 0, row = 0;
+ uint32_t size;
+ uint8_t cmd;
+
+ size = chip->cg.page_size + chip->cg.oob_size;
+
+ switch (type) {
+ case NANDSIM_EV_START:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in READ state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_COL;
+ break;
+ case NANDSIM_EV_ADDR:
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
+ if (nandchip_get_addr_byte(chip, data, &column))
+ break;
+
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
+ } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ if (nandchip_get_addr_byte(chip, data, &row))
+ break;
+
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else
+ nandsim_ignore_address(chip, *((uint8_t *)data));
+ break;
+ case NANDSIM_EV_CMD:
+ cmd = *(uint8_t *)data;
+ if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
+ cmd == NAND_CMD_READ_END) {
+ if (chip->read_delay != 0 &&
+ nandsim_delay(chip, chip->read_delay) == 0)
+ nandchip_clear_status(chip, NAND_STATUS_RDY);
+ else {
+ nandchip_chip_space(chip, row, column, size, 0);
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ }
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ case NANDSIM_EV_TIMEOUT:
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
+ nandchip_chip_space(chip, row, column, size, 0);
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ }
+}
+void
+write_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ static uint32_t column, row;
+ uint32_t size;
+ uint8_t cmd;
+ int err;
+
+ size = chip->cg.page_size + chip->cg.oob_size;
+
+ switch(type) {
+ case NANDSIM_EV_START:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in WRITE state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_COL;
+ break;
+ case NANDSIM_EV_ADDR:
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
+ if (nandchip_get_addr_byte(chip, data, &column))
+ break;
+
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
+ } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ if (nandchip_get_addr_byte(chip, data, &row))
+ break;
+
+ err = nandchip_chip_space(chip, row, column, size, 1);
+ if (err == -1)
+ nandchip_set_status(chip, NAND_STATUS_FAIL);
+
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else
+ nandsim_ignore_address(chip, *((uint8_t *)data));
+ break;
+ case NANDSIM_EV_CMD:
+ cmd = *(uint8_t *)data;
+ if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
+ cmd == NAND_CMD_PROG_END) {
+ if (chip->prog_delay != 0 &&
+ nandsim_delay(chip, chip->prog_delay) == 0)
+ nandchip_clear_status(chip, NAND_STATUS_RDY);
+ else {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ }
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ case NANDSIM_EV_TIMEOUT:
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
+ nandsim_start_handler(chip, idle_evh);
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ }
+}
+
+void
+erase_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ static uint32_t row, block_size;
+ uint32_t lun, block, page;
+ int err;
+ uint8_t cmd;
+
+ block_size = chip->cg.block_size +
+ (chip->cg.oob_size * chip->cg.pgs_per_blk);
+
+ switch (type) {
+ case NANDSIM_EV_START:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in ERASE state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
+ break;
+ case NANDSIM_EV_CMD:
+ cmd = *(uint8_t *)data;
+ if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
+ cmd == NAND_CMD_ERASE_END) {
+ if (chip->data.data_ptr != NULL &&
+ chip->data.size == block_size)
+ memset(chip->data.data_ptr, 0xff, block_size);
+ else
+ nand_debug(NDBG_SIM,"Bad block erase data\n");
+
+ err = nand_row_to_blkpg(&chip->cg, row, &lun,
+ &block, &page);
+ if (!err) {
+ if (chip->blk_state[block].wear_lev > 0)
+ chip->blk_state[block].wear_lev--;
+ }
+
+ if (chip->erase_delay != 0 &&
+ nandsim_delay(chip, chip->erase_delay) == 0)
+ nandchip_clear_status(chip, NAND_STATUS_RDY);
+ else {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ }
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ case NANDSIM_EV_ADDR:
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ if (nandchip_get_addr_byte(chip, data, &row))
+ break;
+
+ err = nandchip_chip_space(chip, row, 0, block_size, 1);
+ if (err == -1) {
+ nandchip_set_status(chip, NAND_STATUS_FAIL);
+ }
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else
+ nandsim_ignore_address(chip, *((uint8_t *)data));
+ break;
+ case NANDSIM_EV_TIMEOUT:
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ }
+}
+
+void
+reset_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in RESET state\n");
+ chip->sm_state = NANDSIM_STATE_TIMEOUT;
+ nandchip_set_data(chip, NULL, 0, 0);
+ DELAY(500);
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+}
+
+static void
+nandsim_undefined(struct nandsim_chip *chip, uint8_t type)
+{
+
+ nandsim_log(chip, NANDSIM_LOG_ERR,
+ "ERR: Chip received ev %x in state %x\n",
+ type, chip->sm_state);
+ nandsim_start_handler(chip, idle_evh);
+}
+
+static void
+nandsim_bad_address(struct nandsim_chip *chip, uint8_t *addr)
+{
+
+ nandsim_log(chip, NANDSIM_LOG_ERR,
+ "ERR: Chip received out of range address"
+ "%02x%02x - %02x%02x%02x\n", addr[0], addr[1], addr[2],
+ addr[3], addr[4]);
+}
+
+static void
+nandsim_ignore_address(struct nandsim_chip *chip, uint8_t byte)
+{
+ nandsim_log(chip, NANDSIM_LOG_SM, "ignored address byte: %d\n", byte);
+}
+
+static void
+nandsim_sm_error(struct nandsim_chip *chip)
+{
+
+ nandsim_log(chip, NANDSIM_LOG_ERR, "ERR: State machine error."
+ "Restart required.\n");
+}
OpenPOWER on IntegriCloud