718cf2ccb9
Mainly focus on files that use BSD 2-Clause license, however the tool I was using misidentified many licenses so this was mostly a manual - error prone - task. The Software Package Data Exchange (SPDX) group provides a specification to make it easier for automated tools to detect and summarize well known opensource licenses. We are gradually adopting the specification, noting that the tags are considered only advisory and do not, in any way, superceed or replace the license texts.
899 lines
22 KiB
C
899 lines
22 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* 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);
|
|
|
|
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);
|
|
|
|
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");
|
|
}
|