diff options
Diffstat (limited to 'sys/dev/nand/nandsim_chip.c')
-rw-r--r-- | sys/dev/nand/nandsim_chip.c | 901 |
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 *)¶ms->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"); +} |