freebsd-dev/sys/dev/nand/nandsim_chip.c
Jung-uk Kim fd90e2ed54 CALLOUT_MPSAFE has lost its meaning since r141428, i.e., for more than ten
years for head.  However, it is continuously misused as the mpsafe argument
for callout_init(9).  Deprecate the flag and clean up callout_init() calls
to make them more consistent.

Differential Revision:	https://reviews.freebsd.org/D2613
Reviewed by:	jhb
MFC after:	2 weeks
2015-05-22 17:05:21 +00:00

902 lines
22 KiB
C

/*-
* 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, 1);
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");
}