1235537Sgber/*- 2235537Sgber * Copyright (C) 2009-2012 Semihalf 3235537Sgber * All rights reserved. 4235537Sgber * 5235537Sgber * Redistribution and use in source and binary forms, with or without 6235537Sgber * modification, are permitted provided that the following conditions 7235537Sgber * are met: 8235537Sgber * 1. Redistributions of source code must retain the above copyright 9235537Sgber * notice, this list of conditions and the following disclaimer. 10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright 11235537Sgber * notice, this list of conditions and the following disclaimer in the 12235537Sgber * documentation and/or other materials provided with the distribution. 13235537Sgber * 14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17235537Sgber * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24235537Sgber * SUCH DAMAGE. 25235537Sgber */ 26235537Sgber 27235537Sgber/* Simulated NAND controller driver */ 28235537Sgber 29235537Sgber#include <sys/cdefs.h> 30235537Sgber__FBSDID("$FreeBSD$"); 31235537Sgber 32235537Sgber#include <sys/param.h> 33235537Sgber#include <sys/systm.h> 34235537Sgber#include <sys/proc.h> 35235537Sgber#include <sys/bus.h> 36235537Sgber#include <sys/conf.h> 37235537Sgber#include <sys/kernel.h> 38235537Sgber#include <sys/module.h> 39235537Sgber#include <sys/malloc.h> 40235537Sgber 41235537Sgber#include <dev/nand/nand.h> 42235537Sgber#include <dev/nand/nandsim.h> 43235537Sgber#include <dev/nand/nandsim_chip.h> 44235537Sgber#include <dev/nand/nandsim_log.h> 45235537Sgber#include <dev/nand/nandsim_swap.h> 46235537Sgber 47235537Sgberstruct sim_param sim; 48235537Sgberstruct sim_ctrl_conf ctrls[MAX_SIM_DEV]; 49235537Sgber 50235537Sgberstatic struct cdev *nandsim_dev; 51235537Sgberstatic d_ioctl_t nandsim_ioctl; 52235537Sgber 53235537Sgberstatic void nandsim_init_sim_param(struct sim_param *); 54235537Sgberstatic int nandsim_create_ctrl(struct sim_ctrl *); 55235537Sgberstatic int nandsim_destroy_ctrl(int); 56235537Sgberstatic int nandsim_ctrl_status(struct sim_ctrl *); 57235537Sgberstatic int nandsim_create_chip(struct sim_chip *); 58235537Sgberstatic int nandsim_destroy_chip(struct sim_ctrl_chip *); 59235537Sgberstatic int nandsim_chip_status(struct sim_chip *); 60235537Sgberstatic int nandsim_start_ctrl(int); 61235537Sgberstatic int nandsim_stop_ctrl(int); 62235537Sgberstatic int nandsim_inject_error(struct sim_error *); 63235537Sgberstatic int nandsim_get_block_state(struct sim_block_state *); 64235537Sgberstatic int nandsim_set_block_state(struct sim_block_state *); 65235537Sgberstatic int nandsim_modify(struct sim_mod *); 66235537Sgberstatic int nandsim_dump(struct sim_dump *); 67235537Sgberstatic int nandsim_restore(struct sim_dump *); 68235537Sgberstatic int nandsim_freeze(struct sim_ctrl_chip *); 69235537Sgberstatic void nandsim_print_log(struct sim_log *); 70235537Sgberstatic struct nandsim_chip *get_nandsim_chip(uint8_t, uint8_t); 71235537Sgber 72235537Sgberstatic struct cdevsw nandsim_cdevsw = { 73235537Sgber .d_version = D_VERSION, 74235537Sgber .d_ioctl = nandsim_ioctl, 75235537Sgber .d_name = "nandsim", 76235537Sgber}; 77235537Sgber 78235537Sgberint 79235537Sgbernandsim_ioctl(struct cdev *dev, u_long cmd, caddr_t data, 80235537Sgber int flags, struct thread *td) 81235537Sgber{ 82235537Sgber int ret = 0; 83235537Sgber 84235537Sgber switch (cmd) { 85235537Sgber case NANDSIM_SIM_PARAM: 86235537Sgber nandsim_init_sim_param((struct sim_param *)data); 87235537Sgber break; 88235537Sgber case NANDSIM_CREATE_CTRL: 89235537Sgber ret = nandsim_create_ctrl((struct sim_ctrl *)data); 90235537Sgber break; 91235537Sgber case NANDSIM_DESTROY_CTRL: 92235537Sgber ret = nandsim_destroy_ctrl(*(int *)data); 93235537Sgber break; 94235537Sgber case NANDSIM_STATUS_CTRL: 95235537Sgber ret = nandsim_ctrl_status((struct sim_ctrl *)data); 96235537Sgber break; 97235537Sgber case NANDSIM_CREATE_CHIP: 98235537Sgber ret = nandsim_create_chip((struct sim_chip *)data); 99235537Sgber break; 100235537Sgber case NANDSIM_DESTROY_CHIP: 101235537Sgber ret = nandsim_destroy_chip((struct sim_ctrl_chip *)data); 102235537Sgber break; 103235537Sgber case NANDSIM_STATUS_CHIP: 104235537Sgber ret = nandsim_chip_status((struct sim_chip *)data); 105235537Sgber break; 106235537Sgber case NANDSIM_MODIFY: 107235537Sgber ret = nandsim_modify((struct sim_mod *)data); 108235537Sgber break; 109235537Sgber case NANDSIM_START_CTRL: 110235537Sgber ret = nandsim_start_ctrl(*(int *)data); 111235537Sgber break; 112235537Sgber case NANDSIM_STOP_CTRL: 113235537Sgber ret = nandsim_stop_ctrl(*(int *)data); 114235537Sgber break; 115235537Sgber case NANDSIM_INJECT_ERROR: 116235537Sgber ret = nandsim_inject_error((struct sim_error *)data); 117235537Sgber break; 118235537Sgber case NANDSIM_SET_BLOCK_STATE: 119235537Sgber ret = nandsim_set_block_state((struct sim_block_state *)data); 120235537Sgber break; 121235537Sgber case NANDSIM_GET_BLOCK_STATE: 122235537Sgber ret = nandsim_get_block_state((struct sim_block_state *)data); 123235537Sgber break; 124235537Sgber case NANDSIM_PRINT_LOG: 125235537Sgber nandsim_print_log((struct sim_log *)data); 126235537Sgber break; 127235537Sgber case NANDSIM_DUMP: 128235537Sgber ret = nandsim_dump((struct sim_dump *)data); 129235537Sgber break; 130235537Sgber case NANDSIM_RESTORE: 131235537Sgber ret = nandsim_restore((struct sim_dump *)data); 132235537Sgber break; 133235537Sgber case NANDSIM_FREEZE: 134235537Sgber ret = nandsim_freeze((struct sim_ctrl_chip *)data); 135235537Sgber break; 136235537Sgber default: 137235537Sgber ret = EINVAL; 138235537Sgber break; 139235537Sgber } 140235537Sgber 141235537Sgber return (ret); 142235537Sgber} 143235537Sgber 144235537Sgberstatic void 145235537Sgbernandsim_init_sim_param(struct sim_param *param) 146235537Sgber{ 147235537Sgber 148235537Sgber if (!param) 149235537Sgber return; 150235537Sgber 151235537Sgber nand_debug(NDBG_SIM,"log level:%d output %d", param->log_level, 152235537Sgber param->log_output); 153235537Sgber nandsim_log_level = param->log_level; 154235537Sgber nandsim_log_output = param->log_output; 155235537Sgber} 156235537Sgber 157235537Sgberstatic int 158235537Sgbernandsim_create_ctrl(struct sim_ctrl *ctrl) 159235537Sgber{ 160235537Sgber struct sim_ctrl_conf *sim_ctrl; 161235537Sgber 162235537Sgber nand_debug(NDBG_SIM,"create controller num:%d cs:%d",ctrl->num, 163235537Sgber ctrl->num_cs); 164235537Sgber 165235537Sgber if (ctrl->num >= MAX_SIM_DEV) { 166235537Sgber return (EINVAL); 167235537Sgber } 168235537Sgber 169235537Sgber sim_ctrl = &ctrls[ctrl->num]; 170235537Sgber if(sim_ctrl->created) 171235537Sgber return (EEXIST); 172235537Sgber 173235537Sgber sim_ctrl->num = ctrl->num; 174235537Sgber sim_ctrl->num_cs = ctrl->num_cs; 175235537Sgber sim_ctrl->ecc = ctrl->ecc; 176235537Sgber memcpy(sim_ctrl->ecc_layout, ctrl->ecc_layout, 177235537Sgber MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0])); 178235537Sgber strlcpy(sim_ctrl->filename, ctrl->filename, 179235537Sgber FILENAME_SIZE); 180235537Sgber sim_ctrl->created = 1; 181235537Sgber 182235537Sgber return (0); 183235537Sgber} 184235537Sgber 185235537Sgberstatic int 186235537Sgbernandsim_destroy_ctrl(int ctrl_num) 187235537Sgber{ 188235537Sgber 189235537Sgber nand_debug(NDBG_SIM,"destroy controller num:%d", ctrl_num); 190235537Sgber 191235537Sgber if (ctrl_num >= MAX_SIM_DEV) { 192235537Sgber return (EINVAL); 193235537Sgber } 194235537Sgber 195235537Sgber if (!ctrls[ctrl_num].created) { 196235537Sgber return (ENODEV); 197235537Sgber } 198235537Sgber 199235537Sgber if (ctrls[ctrl_num].running) { 200235537Sgber return (EBUSY); 201235537Sgber } 202235537Sgber 203235537Sgber memset(&ctrls[ctrl_num], 0, sizeof(ctrls[ctrl_num])); 204235537Sgber 205235537Sgber return (0); 206235537Sgber} 207235537Sgber 208235537Sgberstatic int 209235537Sgbernandsim_ctrl_status(struct sim_ctrl *ctrl) 210235537Sgber{ 211235537Sgber 212235537Sgber nand_debug(NDBG_SIM,"status controller num:%d cs:%d",ctrl->num, 213235537Sgber ctrl->num_cs); 214235537Sgber 215235537Sgber if (ctrl->num >= MAX_SIM_DEV) { 216235537Sgber return (EINVAL); 217235537Sgber } 218235537Sgber 219235537Sgber ctrl->num_cs = ctrls[ctrl->num].num_cs; 220235537Sgber ctrl->ecc = ctrls[ctrl->num].ecc; 221235537Sgber memcpy(ctrl->ecc_layout, ctrls[ctrl->num].ecc_layout, 222235537Sgber MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0])); 223235537Sgber strlcpy(ctrl->filename, ctrls[ctrl->num].filename, 224235537Sgber FILENAME_SIZE); 225235537Sgber ctrl->running = ctrls[ctrl->num].running; 226235537Sgber ctrl->created = ctrls[ctrl->num].created; 227235537Sgber 228235537Sgber return (0); 229235537Sgber} 230235537Sgber 231235537Sgberstatic int 232235537Sgbernandsim_create_chip(struct sim_chip *chip) 233235537Sgber{ 234235537Sgber struct sim_chip *sim_chip; 235235537Sgber 236235537Sgber nand_debug(NDBG_SIM,"create chip num:%d at ctrl:%d", chip->num, 237235537Sgber chip->ctrl_num); 238235537Sgber 239235537Sgber if (chip->ctrl_num >= MAX_SIM_DEV || 240235537Sgber chip->num >= MAX_CTRL_CS) { 241235537Sgber return (EINVAL); 242235537Sgber } 243235537Sgber 244235537Sgber if (ctrls[chip->ctrl_num].chips[chip->num]) { 245235537Sgber return (EEXIST); 246235537Sgber } 247235537Sgber 248235537Sgber sim_chip = malloc(sizeof(*sim_chip), M_NANDSIM, 249235537Sgber M_WAITOK); 250235537Sgber if (sim_chip == NULL) { 251235537Sgber return (ENOMEM); 252235537Sgber } 253235537Sgber 254235537Sgber memcpy(sim_chip, chip, sizeof(*sim_chip)); 255235537Sgber ctrls[chip->ctrl_num].chips[chip->num] = sim_chip; 256235537Sgber sim_chip->created = 1; 257235537Sgber 258235537Sgber return (0); 259235537Sgber} 260235537Sgber 261235537Sgberstatic int 262235537Sgbernandsim_destroy_chip(struct sim_ctrl_chip *chip) 263235537Sgber{ 264235537Sgber struct sim_ctrl_conf *ctrl_conf; 265235537Sgber 266235537Sgber nand_debug(NDBG_SIM,"destroy chip num:%d at ctrl:%d", chip->chip_num, 267235537Sgber chip->ctrl_num); 268235537Sgber 269235537Sgber if (chip->ctrl_num >= MAX_SIM_DEV || 270235537Sgber chip->chip_num >= MAX_CTRL_CS) 271235537Sgber return (EINVAL); 272235537Sgber 273235537Sgber ctrl_conf = &ctrls[chip->ctrl_num]; 274235537Sgber 275235537Sgber if (!ctrl_conf->created || !ctrl_conf->chips[chip->chip_num]) 276235537Sgber return (ENODEV); 277235537Sgber 278235537Sgber if (ctrl_conf->running) 279235537Sgber return (EBUSY); 280235537Sgber 281235537Sgber free(ctrl_conf->chips[chip->chip_num], M_NANDSIM); 282235537Sgber ctrl_conf->chips[chip->chip_num] = NULL; 283235537Sgber 284235537Sgber return (0); 285235537Sgber} 286235537Sgber 287235537Sgberstatic int 288235537Sgbernandsim_chip_status(struct sim_chip *chip) 289235537Sgber{ 290235537Sgber struct sim_ctrl_conf *ctrl_conf; 291235537Sgber 292235537Sgber nand_debug(NDBG_SIM,"status for chip num:%d at ctrl:%d", chip->num, 293235537Sgber chip->ctrl_num); 294235537Sgber 295235537Sgber if (chip->ctrl_num >= MAX_SIM_DEV && 296235537Sgber chip->num >= MAX_CTRL_CS) 297235537Sgber return (EINVAL); 298235537Sgber 299235537Sgber ctrl_conf = &ctrls[chip->ctrl_num]; 300235537Sgber if (!ctrl_conf->chips[chip->num]) 301235537Sgber chip->created = 0; 302235537Sgber else 303235537Sgber memcpy(chip, ctrl_conf->chips[chip->num], sizeof(*chip)); 304235537Sgber 305235537Sgber return (0); 306235537Sgber} 307235537Sgber 308235537Sgberstatic int 309235537Sgbernandsim_start_ctrl(int num) 310235537Sgber{ 311235537Sgber device_t nexus, ndev; 312235537Sgber devclass_t nexus_devclass; 313235537Sgber int ret = 0; 314235537Sgber 315235537Sgber nand_debug(NDBG_SIM,"start ctlr num:%d", num); 316235537Sgber 317235537Sgber if (num >= MAX_SIM_DEV) 318235537Sgber return (EINVAL); 319235537Sgber 320235537Sgber if (!ctrls[num].created) 321235537Sgber return (ENODEV); 322235537Sgber 323235537Sgber if (ctrls[num].running) 324235537Sgber return (EBUSY); 325235537Sgber 326235537Sgber /* We will add our device as a child of the nexus0 device */ 327235537Sgber if (!(nexus_devclass = devclass_find("nexus")) || 328235537Sgber !(nexus = devclass_get_device(nexus_devclass, 0))) 329235537Sgber return (EFAULT); 330235537Sgber 331235537Sgber /* 332235537Sgber * Create a newbus device representing this frontend instance 333235537Sgber * 334235537Sgber * XXX powerpc nexus doesn't implement bus_add_child, so child 335235537Sgber * must be added by device_add_child(). 336235537Sgber */ 337235537Sgber#if defined(__powerpc__) 338235537Sgber ndev = device_add_child(nexus, "nandsim", num); 339235537Sgber#else 340235537Sgber ndev = BUS_ADD_CHILD(nexus, 0, "nandsim", num); 341235537Sgber#endif 342235537Sgber if (!ndev) 343235537Sgber return (EFAULT); 344235537Sgber 345235537Sgber mtx_lock(&Giant); 346235537Sgber ret = device_probe_and_attach(ndev); 347235537Sgber mtx_unlock(&Giant); 348235537Sgber 349235537Sgber if (ret == 0) { 350235537Sgber ctrls[num].sim_ctrl_dev = ndev; 351235537Sgber ctrls[num].running = 1; 352235537Sgber } 353235537Sgber 354235537Sgber return (ret); 355235537Sgber} 356235537Sgber 357235537Sgberstatic int 358235537Sgbernandsim_stop_ctrl(int num) 359235537Sgber{ 360235537Sgber device_t nexus; 361235537Sgber devclass_t nexus_devclass; 362235537Sgber int ret = 0; 363235537Sgber 364235537Sgber nand_debug(NDBG_SIM,"stop controller num:%d", num); 365235537Sgber 366235537Sgber if (num >= MAX_SIM_DEV) { 367235537Sgber return (EINVAL); 368235537Sgber } 369235537Sgber 370235537Sgber if (!ctrls[num].created || !ctrls[num].running) { 371235537Sgber return (ENODEV); 372235537Sgber } 373235537Sgber 374235537Sgber /* We will add our device as a child of the nexus0 device */ 375235537Sgber if (!(nexus_devclass = devclass_find("nexus")) || 376235537Sgber !(nexus = devclass_get_device(nexus_devclass, 0))) { 377235537Sgber return (ENODEV); 378235537Sgber } 379235537Sgber 380235537Sgber mtx_lock(&Giant); 381235537Sgber if (ctrls[num].sim_ctrl_dev) { 382235537Sgber ret = device_delete_child(nexus, ctrls[num].sim_ctrl_dev); 383235537Sgber ctrls[num].sim_ctrl_dev = NULL; 384235537Sgber } 385235537Sgber mtx_unlock(&Giant); 386235537Sgber 387235537Sgber ctrls[num].running = 0; 388235537Sgber 389235537Sgber return (ret); 390235537Sgber} 391235537Sgber 392235537Sgberstatic struct nandsim_chip * 393235537Sgberget_nandsim_chip(uint8_t ctrl_num, uint8_t chip_num) 394235537Sgber{ 395235537Sgber struct nandsim_softc *sc; 396235537Sgber 397235537Sgber if (!ctrls[ctrl_num].sim_ctrl_dev) 398235537Sgber return (NULL); 399235537Sgber 400235537Sgber sc = device_get_softc(ctrls[ctrl_num].sim_ctrl_dev); 401235537Sgber return (sc->chips[chip_num]); 402235537Sgber} 403235537Sgber 404235537Sgberstatic void 405235537Sgbernandsim_print_log(struct sim_log *sim_log) 406235537Sgber{ 407235537Sgber struct nandsim_softc *sc; 408235537Sgber int len1, len2; 409235537Sgber 410235537Sgber if (!ctrls[sim_log->ctrl_num].sim_ctrl_dev) 411235537Sgber return; 412235537Sgber 413235537Sgber sc = device_get_softc(ctrls[sim_log->ctrl_num].sim_ctrl_dev); 414235537Sgber if (sc->log_buff) { 415235537Sgber len1 = strlen(&sc->log_buff[sc->log_idx + 1]); 416235537Sgber if (len1 >= sim_log->len) 417235537Sgber len1 = sim_log->len; 418235537Sgber copyout(&sc->log_buff[sc->log_idx + 1], sim_log->log, len1); 419235537Sgber len2 = strlen(sc->log_buff); 420235537Sgber if (len2 >= (sim_log->len - len1)) 421235537Sgber len2 = (sim_log->len - len1); 422235537Sgber copyout(sc->log_buff, &sim_log->log[len1], len2); 423235537Sgber sim_log->len = len1 + len2; 424235537Sgber } 425235537Sgber} 426235537Sgber 427235537Sgberstatic int 428235537Sgbernandsim_inject_error(struct sim_error *error) 429235537Sgber{ 430235537Sgber struct nandsim_chip *chip; 431235537Sgber struct block_space *bs; 432235537Sgber struct onfi_params *param; 433235537Sgber int page, page_size, block, offset; 434235537Sgber 435235537Sgber nand_debug(NDBG_SIM,"inject error for chip %d at ctrl %d\n", 436235537Sgber error->chip_num, error->ctrl_num); 437235537Sgber 438235537Sgber if (error->ctrl_num >= MAX_SIM_DEV || 439235537Sgber error->chip_num >= MAX_CTRL_CS) 440235537Sgber return (EINVAL); 441235537Sgber 442235537Sgber if (!ctrls[error->ctrl_num].created || !ctrls[error->ctrl_num].running) 443235537Sgber return (ENODEV); 444235537Sgber 445235537Sgber chip = get_nandsim_chip(error->ctrl_num, error->chip_num); 446235537Sgber param = &chip->params; 447235537Sgber page_size = param->bytes_per_page + param->spare_bytes_per_page; 448235537Sgber block = error->page_num / param->pages_per_block; 449235537Sgber page = error->page_num % param->pages_per_block; 450235537Sgber 451235537Sgber bs = get_bs(chip->swap, block, 1); 452235537Sgber if (!bs) 453235537Sgber return (EINVAL); 454235537Sgber 455235537Sgber offset = (page * page_size) + error->column; 456235537Sgber memset(&bs->blk_ptr[offset], error->pattern, error->len); 457235537Sgber 458235537Sgber return (0); 459235537Sgber} 460235537Sgber 461235537Sgberstatic int 462235537Sgbernandsim_set_block_state(struct sim_block_state *bs) 463235537Sgber{ 464235537Sgber struct onfi_params *params; 465235537Sgber struct nandsim_chip *chip; 466235537Sgber int blocks; 467235537Sgber 468235537Sgber nand_debug(NDBG_SIM,"set block state for %d:%d block %d\n", 469235537Sgber bs->chip_num, bs->ctrl_num, bs->block_num); 470235537Sgber 471235537Sgber if (bs->ctrl_num >= MAX_SIM_DEV || 472235537Sgber bs->chip_num >= MAX_CTRL_CS) 473235537Sgber return (EINVAL); 474235537Sgber 475235537Sgber chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num); 476235537Sgber params = &chip->params; 477235537Sgber blocks = params->luns * params->blocks_per_lun; 478235537Sgber 479235537Sgber if (bs->block_num > blocks) 480235537Sgber return (EINVAL); 481235537Sgber 482235537Sgber chip->blk_state[bs->block_num].is_bad = bs->state; 483235537Sgber 484235537Sgber if (bs->wearout >= 0) 485235537Sgber chip->blk_state[bs->block_num].wear_lev = bs->wearout; 486235537Sgber 487235537Sgber return (0); 488235537Sgber} 489235537Sgber 490235537Sgberstatic int 491235537Sgbernandsim_get_block_state(struct sim_block_state *bs) 492235537Sgber{ 493235537Sgber struct onfi_params *params; 494235537Sgber struct nandsim_chip *chip; 495235537Sgber int blocks; 496235537Sgber 497235537Sgber if (bs->ctrl_num >= MAX_SIM_DEV || 498235537Sgber bs->chip_num >= MAX_CTRL_CS) 499235537Sgber return (EINVAL); 500235537Sgber 501235537Sgber nand_debug(NDBG_SIM,"get block state for %d:%d block %d\n", 502235537Sgber bs->chip_num, bs->ctrl_num, bs->block_num); 503235537Sgber 504235537Sgber chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num); 505235537Sgber params = &chip->params; 506235537Sgber blocks = params->luns * params->blocks_per_lun; 507235537Sgber 508235537Sgber if (bs->block_num > blocks) 509235537Sgber return (EINVAL); 510235537Sgber 511235537Sgber bs->state = chip->blk_state[bs->block_num].is_bad; 512235537Sgber bs->wearout = chip->blk_state[bs->block_num].wear_lev; 513235537Sgber 514235537Sgber return (0); 515235537Sgber} 516235537Sgber 517235537Sgberstatic int 518235537Sgbernandsim_dump(struct sim_dump *dump) 519235537Sgber{ 520235537Sgber struct nandsim_chip *chip; 521235537Sgber struct block_space *bs; 522235537Sgber int blk_size; 523235537Sgber 524235537Sgber nand_debug(NDBG_SIM,"dump chip %d %d\n", dump->ctrl_num, dump->chip_num); 525235537Sgber 526235537Sgber if (dump->ctrl_num >= MAX_SIM_DEV || 527235537Sgber dump->chip_num >= MAX_CTRL_CS) 528235537Sgber return (EINVAL); 529235537Sgber 530235537Sgber chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num); 531235537Sgber blk_size = chip->cg.block_size + 532235537Sgber (chip->cg.oob_size * chip->cg.pgs_per_blk); 533235537Sgber 534235537Sgber bs = get_bs(chip->swap, dump->block_num, 0); 535235537Sgber if (!bs) 536235537Sgber return (EINVAL); 537235537Sgber 538235537Sgber if (dump->len > blk_size) 539235537Sgber dump->len = blk_size; 540235537Sgber 541235537Sgber copyout(bs->blk_ptr, dump->data, dump->len); 542235537Sgber 543235537Sgber return (0); 544235537Sgber} 545235537Sgber 546235537Sgberstatic int 547235537Sgbernandsim_restore(struct sim_dump *dump) 548235537Sgber{ 549235537Sgber struct nandsim_chip *chip; 550235537Sgber struct block_space *bs; 551235537Sgber int blk_size; 552235537Sgber 553235537Sgber nand_debug(NDBG_SIM,"restore chip %d %d\n", dump->ctrl_num, 554235537Sgber dump->chip_num); 555235537Sgber 556235537Sgber if (dump->ctrl_num >= MAX_SIM_DEV || 557235537Sgber dump->chip_num >= MAX_CTRL_CS) 558235537Sgber return (EINVAL); 559235537Sgber 560235537Sgber chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num); 561235537Sgber blk_size = chip->cg.block_size + 562235537Sgber (chip->cg.oob_size * chip->cg.pgs_per_blk); 563235537Sgber 564235537Sgber bs = get_bs(chip->swap, dump->block_num, 1); 565235537Sgber if (!bs) 566235537Sgber return (EINVAL); 567235537Sgber 568235537Sgber if (dump->len > blk_size) 569235537Sgber dump->len = blk_size; 570235537Sgber 571235537Sgber 572235537Sgber copyin(dump->data, bs->blk_ptr, dump->len); 573235537Sgber 574235537Sgber return (0); 575235537Sgber} 576235537Sgber 577235537Sgberstatic int 578235537Sgbernandsim_freeze(struct sim_ctrl_chip *ctrl_chip) 579235537Sgber{ 580235537Sgber struct nandsim_chip *chip; 581235537Sgber 582235537Sgber if (ctrl_chip->ctrl_num >= MAX_SIM_DEV || 583235537Sgber ctrl_chip->chip_num >= MAX_CTRL_CS) 584235537Sgber return (EINVAL); 585235537Sgber 586235537Sgber chip = get_nandsim_chip(ctrl_chip->ctrl_num, ctrl_chip->chip_num); 587235537Sgber nandsim_chip_freeze(chip); 588235537Sgber 589235537Sgber return (0); 590235537Sgber} 591235537Sgber 592235537Sgberstatic int 593235537Sgbernandsim_modify(struct sim_mod *mod) 594235537Sgber{ 595235537Sgber struct sim_chip *sim_conf = NULL; 596235537Sgber struct nandsim_chip *sim_chip = NULL; 597235537Sgber 598235537Sgber nand_debug(NDBG_SIM,"modify ctlr %d chip %d", mod->ctrl_num, 599235537Sgber mod->chip_num); 600235537Sgber 601235537Sgber if (mod->field != SIM_MOD_LOG_LEVEL) { 602235537Sgber if (mod->ctrl_num >= MAX_SIM_DEV || 603235537Sgber mod->chip_num >= MAX_CTRL_CS) 604235537Sgber return (EINVAL); 605235537Sgber 606235537Sgber sim_conf = ctrls[mod->ctrl_num].chips[mod->chip_num]; 607235537Sgber sim_chip = get_nandsim_chip(mod->ctrl_num, mod->chip_num); 608235537Sgber } 609235537Sgber 610235537Sgber switch (mod->field) { 611235537Sgber case SIM_MOD_LOG_LEVEL: 612235537Sgber nandsim_log_level = mod->new_value; 613235537Sgber break; 614235537Sgber case SIM_MOD_ERASE_TIME: 615235537Sgber sim_conf->erase_time = sim_chip->erase_delay = mod->new_value; 616235537Sgber break; 617235537Sgber case SIM_MOD_PROG_TIME: 618235537Sgber sim_conf->prog_time = sim_chip->prog_delay = mod->new_value; 619235537Sgber break; 620235537Sgber case SIM_MOD_READ_TIME: 621235537Sgber sim_conf->read_time = sim_chip->read_delay = mod->new_value; 622235537Sgber break; 623235537Sgber case SIM_MOD_ERROR_RATIO: 624235537Sgber sim_conf->error_ratio = mod->new_value; 625235537Sgber sim_chip->error_ratio = mod->new_value; 626235537Sgber break; 627235537Sgber default: 628235537Sgber break; 629235537Sgber } 630235537Sgber 631235537Sgber return (0); 632235537Sgber} 633235537Sgberstatic int 634235537Sgbernandsim_modevent(module_t mod __unused, int type, void *data __unused) 635235537Sgber{ 636235537Sgber struct sim_ctrl_chip chip_ctrl; 637235537Sgber int i, j; 638235537Sgber 639235537Sgber switch (type) { 640235537Sgber case MOD_LOAD: 641235537Sgber nandsim_dev = make_dev(&nandsim_cdevsw, 0, 642235537Sgber UID_ROOT, GID_WHEEL, 0666, "nandsim.ioctl"); 643235537Sgber break; 644235537Sgber case MOD_UNLOAD: 645235537Sgber for (i = 0; i < MAX_SIM_DEV; i++) { 646235537Sgber nandsim_stop_ctrl(i); 647235537Sgber chip_ctrl.ctrl_num = i; 648235537Sgber for (j = 0; j < MAX_CTRL_CS; j++) { 649235537Sgber chip_ctrl.chip_num = j; 650235537Sgber nandsim_destroy_chip(&chip_ctrl); 651235537Sgber } 652235537Sgber nandsim_destroy_ctrl(i); 653235537Sgber } 654235537Sgber destroy_dev(nandsim_dev); 655235537Sgber break; 656235537Sgber case MOD_SHUTDOWN: 657235537Sgber break; 658235537Sgber default: 659235537Sgber return (EOPNOTSUPP); 660235537Sgber } 661235537Sgber return (0); 662235537Sgber} 663235537Sgber 664235537SgberDEV_MODULE(nandsim, nandsim_modevent, NULL); 665235537SgberMODULE_VERSION(nandsim, 1); 666237605StakawataMODULE_DEPEND(nandsim, nand, 1, 1, 1); 667237605StakawataMODULE_DEPEND(nandsim, alq, 1, 1, 1); 668