Index: head/sys/dev/nand/nand_generic.c =================================================================== --- head/sys/dev/nand/nand_generic.c (revision 303875) +++ head/sys/dev/nand/nand_generic.c (revision 303876) @@ -1,1374 +1,1364 @@ /*- * 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. */ /* Generic NAND driver */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nfc_if.h" #include "nand_if.h" #include "nandbus_if.h" static int onfi_nand_probe(device_t dev); static int large_nand_probe(device_t dev); static int small_nand_probe(device_t dev); static int generic_nand_attach(device_t dev); static int generic_nand_detach(device_t dev); static int generic_erase_block(device_t, uint32_t); static int generic_erase_block_intlv(device_t, uint32_t); static int generic_read_page (device_t, uint32_t, void *, uint32_t, uint32_t); static int generic_read_oob(device_t, uint32_t, void *, uint32_t, uint32_t); static int generic_program_page(device_t, uint32_t, void *, uint32_t, uint32_t); static int generic_program_page_intlv(device_t, uint32_t, void *, uint32_t, uint32_t); static int generic_program_oob(device_t, uint32_t, void *, uint32_t, uint32_t); static int generic_is_blk_bad(device_t, uint32_t, uint8_t *); static int generic_get_ecc(device_t, void *, void *, int *); static int generic_correct_ecc(device_t, void *, void *, void *); static int small_read_page(device_t, uint32_t, void *, uint32_t, uint32_t); static int small_read_oob(device_t, uint32_t, void *, uint32_t, uint32_t); static int small_program_page(device_t, uint32_t, void *, uint32_t, uint32_t); static int small_program_oob(device_t, uint32_t, void *, uint32_t, uint32_t); static int onfi_is_blk_bad(device_t, uint32_t, uint8_t *); static int onfi_read_parameter(struct nand_chip *, struct onfi_chip_params *); static int nand_send_address(device_t, int32_t, int32_t, int8_t); static device_method_t onand_methods[] = { /* Device interface */ DEVMETHOD(device_probe, onfi_nand_probe), DEVMETHOD(device_attach, generic_nand_attach), DEVMETHOD(device_detach, generic_nand_detach), DEVMETHOD(nand_read_page, generic_read_page), DEVMETHOD(nand_program_page, generic_program_page), DEVMETHOD(nand_program_page_intlv, generic_program_page_intlv), DEVMETHOD(nand_read_oob, generic_read_oob), DEVMETHOD(nand_program_oob, generic_program_oob), DEVMETHOD(nand_erase_block, generic_erase_block), DEVMETHOD(nand_erase_block_intlv, generic_erase_block_intlv), DEVMETHOD(nand_is_blk_bad, onfi_is_blk_bad), DEVMETHOD(nand_get_ecc, generic_get_ecc), DEVMETHOD(nand_correct_ecc, generic_correct_ecc), { 0, 0 } }; static device_method_t lnand_methods[] = { /* Device interface */ DEVMETHOD(device_probe, large_nand_probe), DEVMETHOD(device_attach, generic_nand_attach), DEVMETHOD(device_detach, generic_nand_detach), DEVMETHOD(nand_read_page, generic_read_page), DEVMETHOD(nand_program_page, generic_program_page), DEVMETHOD(nand_read_oob, generic_read_oob), DEVMETHOD(nand_program_oob, generic_program_oob), DEVMETHOD(nand_erase_block, generic_erase_block), DEVMETHOD(nand_is_blk_bad, generic_is_blk_bad), DEVMETHOD(nand_get_ecc, generic_get_ecc), DEVMETHOD(nand_correct_ecc, generic_correct_ecc), { 0, 0 } }; static device_method_t snand_methods[] = { /* Device interface */ DEVMETHOD(device_probe, small_nand_probe), DEVMETHOD(device_attach, generic_nand_attach), DEVMETHOD(device_detach, generic_nand_detach), DEVMETHOD(nand_read_page, small_read_page), DEVMETHOD(nand_program_page, small_program_page), DEVMETHOD(nand_read_oob, small_read_oob), DEVMETHOD(nand_program_oob, small_program_oob), DEVMETHOD(nand_erase_block, generic_erase_block), DEVMETHOD(nand_is_blk_bad, generic_is_blk_bad), DEVMETHOD(nand_get_ecc, generic_get_ecc), DEVMETHOD(nand_correct_ecc, generic_correct_ecc), { 0, 0 } }; devclass_t onand_devclass; devclass_t lnand_devclass; devclass_t snand_devclass; driver_t onand_driver = { "onand", onand_methods, sizeof(struct nand_chip) }; driver_t lnand_driver = { "lnand", lnand_methods, sizeof(struct nand_chip) }; driver_t snand_driver = { "snand", snand_methods, sizeof(struct nand_chip) }; DRIVER_MODULE(onand, nandbus, onand_driver, onand_devclass, 0, 0); DRIVER_MODULE(lnand, nandbus, lnand_driver, lnand_devclass, 0, 0); DRIVER_MODULE(snand, nandbus, snand_driver, snand_devclass, 0, 0); static int onfi_nand_probe(device_t dev) { struct nandbus_ivar *ivar; ivar = device_get_ivars(dev); if (ivar && ivar->is_onfi) { device_set_desc(dev, "ONFI compliant NAND"); return (BUS_PROBE_DEFAULT); } return (ENODEV); } static int large_nand_probe(device_t dev) { struct nandbus_ivar *ivar; ivar = device_get_ivars(dev); if (ivar && !ivar->is_onfi && ivar->params->page_size >= 512) { device_set_desc(dev, ivar->params->name); return (BUS_PROBE_DEFAULT); } return (ENODEV); } static int small_nand_probe(device_t dev) { struct nandbus_ivar *ivar; ivar = device_get_ivars(dev); if (ivar && !ivar->is_onfi && ivar->params->page_size == 512) { device_set_desc(dev, ivar->params->name); return (BUS_PROBE_DEFAULT); } return (ENODEV); } static int generic_nand_attach(device_t dev) { struct nand_chip *chip; struct nandbus_ivar *ivar; struct onfi_chip_params *onfi_chip_params; device_t nandbus, nfc; int err; chip = device_get_softc(dev); chip->dev = dev; ivar = device_get_ivars(dev); chip->id.man_id = ivar->man_id; chip->id.dev_id = ivar->dev_id; chip->num = ivar->cs; /* TODO remove when HW ECC supported */ nandbus = device_get_parent(dev); nfc = device_get_parent(nandbus); chip->nand = device_get_softc(nfc); if (ivar->is_onfi) { onfi_chip_params = malloc(sizeof(struct onfi_chip_params), M_NAND, M_WAITOK | M_ZERO); - if (onfi_chip_params == NULL) - return (ENOMEM); if (onfi_read_parameter(chip, onfi_chip_params)) { nand_debug(NDBG_GEN,"Could not read parameter page!\n"); free(onfi_chip_params, M_NAND); return (ENXIO); } nand_onfi_set_params(chip, onfi_chip_params); /* Set proper column and row cycles */ ivar->cols = (onfi_chip_params->address_cycles >> 4) & 0xf; ivar->rows = onfi_chip_params->address_cycles & 0xf; free(onfi_chip_params, M_NAND); } else { nand_set_params(chip, ivar->params); } err = nand_init_stat(chip); if (err) { generic_nand_detach(dev); return (err); } err = nand_init_bbt(chip); if (err) { generic_nand_detach(dev); return (err); } err = nand_make_dev(chip); if (err) { generic_nand_detach(dev); return (err); } err = create_geom_disk(chip); if (err) { generic_nand_detach(dev); return (err); } return (0); } static int generic_nand_detach(device_t dev) { struct nand_chip *chip; chip = device_get_softc(dev); nand_destroy_bbt(chip); destroy_geom_disk(chip); nand_destroy_dev(chip); nand_destroy_stat(chip); return (0); } static int can_write(device_t nandbus) { uint8_t status; if (NANDBUS_WAIT_READY(nandbus, &status)) return (0); if (!(status & NAND_STATUS_WP)) { nand_debug(NDBG_GEN,"Chip is write-protected"); return (0); } return (1); } static int check_fail(device_t nandbus) { uint8_t status; NANDBUS_WAIT_READY(nandbus, &status); if (status & NAND_STATUS_FAIL) { nand_debug(NDBG_GEN,"Status failed %x", status); return (ENXIO); } return (0); } static uint16_t onfi_crc(const void *buf, size_t buflen) { int i, j; uint16_t crc; const uint8_t *bufptr; bufptr = buf; crc = 0x4f4e; for (j = 0; j < buflen; j++) { crc ^= *bufptr++ << 8; for (i = 0; i < 8; i++) if (crc & 0x8000) crc = (crc << 1) ^ 0x8005; else crc <<= 1; } return crc; } static int onfi_read_parameter(struct nand_chip *chip, struct onfi_chip_params *chip_params) { device_t nandbus; struct onfi_params params; int found, sigcount, trycopy; nand_debug(NDBG_GEN,"read parameter"); nandbus = device_get_parent(chip->dev); NANDBUS_SELECT_CS(nandbus, chip->num); if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_READ_PARAMETER)) return (ENXIO); if (nand_send_address(chip->dev, -1, -1, PAGE_PARAMETER_DEF)) return (ENXIO); if (NANDBUS_START_COMMAND(nandbus)) return (ENXIO); /* * XXX Bogus DELAY, we really need a nandbus_wait_ready() here, but it's * not accessible from here (static to nandbus). */ DELAY(1000); /* * The ONFI spec mandates a minimum of three copies of the parameter * data, so loop up to 3 times trying to find good data. Each copy is * validated by a signature of "ONFI" and a crc. There is a very strange * rule that the signature is valid if any 2 of the 4 bytes are correct. */ for (found= 0, trycopy = 0; !found && trycopy < 3; trycopy++) { NANDBUS_READ_BUFFER(nandbus, ¶ms, sizeof(struct onfi_params)); sigcount = params.signature[0] == 'O'; sigcount += params.signature[1] == 'N'; sigcount += params.signature[2] == 'F'; sigcount += params.signature[3] == 'I'; if (sigcount < 2) continue; if (onfi_crc(¶ms, 254) != params.crc) continue; found = 1; } if (!found) return (ENXIO); chip_params->luns = params.luns; chip_params->blocks_per_lun = le32dec(¶ms.blocks_per_lun); chip_params->pages_per_block = le32dec(¶ms.pages_per_block); chip_params->bytes_per_page = le32dec(¶ms.bytes_per_page); chip_params->spare_bytes_per_page = le16dec(¶ms.spare_bytes_per_page); chip_params->t_bers = le16dec(¶ms.t_bers); chip_params->t_prog = le16dec(¶ms.t_prog); chip_params->t_r = le16dec(¶ms.t_r); chip_params->t_ccs = le16dec(¶ms.t_ccs); chip_params->features = le16dec(¶ms.features); chip_params->address_cycles = params.address_cycles; return (0); } static int send_read_page(device_t nand, uint8_t start_command, uint8_t end_command, uint32_t row, uint32_t column) { device_t nandbus = device_get_parent(nand); if (NANDBUS_SEND_COMMAND(nandbus, start_command)) return (ENXIO); if (nand_send_address(nand, row, column, -1)) return (ENXIO); if (NANDBUS_SEND_COMMAND(nandbus, end_command)) return (ENXIO); if (NANDBUS_START_COMMAND(nandbus)) return (ENXIO); return (0); } static int generic_read_page(device_t nand, uint32_t page, void *buf, uint32_t len, uint32_t offset) { struct nand_chip *chip; struct page_stat *pg_stat; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN,"%p raw read page %x[%x] at %x", nand, page, len, offset); chip = device_get_softc(nand); nandbus = device_get_parent(nand); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_END, row, offset)) return (ENXIO); DELAY(chip->t_r); NANDBUS_READ_BUFFER(nandbus, buf, len); if (check_fail(nandbus)) return (ENXIO); pg_stat = &(chip->pg_stat[page]); pg_stat->page_raw_read++; return (0); } static int generic_read_oob(device_t nand, uint32_t page, void* buf, uint32_t len, uint32_t offset) { struct nand_chip *chip; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN,"%p raw read oob %x[%x] at %x", nand, page, len, offset); chip = device_get_softc(nand); nandbus = device_get_parent(nand); if (nand_check_page_boundary(chip, page)) { nand_debug(NDBG_GEN,"page boundary check failed: %08x\n", page); return (ENXIO); } page_to_row(&chip->chip_geom, page, &row); offset += chip->chip_geom.page_size; if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_END, row, offset)) return (ENXIO); DELAY(chip->t_r); NANDBUS_READ_BUFFER(nandbus, buf, len); if (check_fail(nandbus)) return (ENXIO); return (0); } static int send_start_program_page(device_t nand, uint32_t row, uint32_t column) { device_t nandbus = device_get_parent(nand); if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_PROG)) return (ENXIO); if (nand_send_address(nand, row, column, -1)) return (ENXIO); return (0); } static int send_end_program_page(device_t nandbus, uint8_t end_command) { if (NANDBUS_SEND_COMMAND(nandbus, end_command)) return (ENXIO); if (NANDBUS_START_COMMAND(nandbus)) return (ENXIO); return (0); } static int generic_program_page(device_t nand, uint32_t page, void *buf, uint32_t len, uint32_t offset) { struct nand_chip *chip; struct page_stat *pg_stat; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN,"%p raw prog page %x[%x] at %x", nand, page, len, offset); chip = device_get_softc(nand); nandbus = device_get_parent(nand); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (!can_write(nandbus)) return (ENXIO); if (send_start_program_page(nand, row, offset)) return (ENXIO); NANDBUS_WRITE_BUFFER(nandbus, buf, len); if (send_end_program_page(nandbus, NAND_CMD_PROG_END)) return (ENXIO); DELAY(chip->t_prog); if (check_fail(nandbus)) return (ENXIO); pg_stat = &(chip->pg_stat[page]); pg_stat->page_raw_written++; return (0); } static int generic_program_page_intlv(device_t nand, uint32_t page, void *buf, uint32_t len, uint32_t offset) { struct nand_chip *chip; struct page_stat *pg_stat; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN,"%p raw prog page %x[%x] at %x", nand, page, len, offset); chip = device_get_softc(nand); nandbus = device_get_parent(nand); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (!can_write(nandbus)) return (ENXIO); if (send_start_program_page(nand, row, offset)) return (ENXIO); NANDBUS_WRITE_BUFFER(nandbus, buf, len); if (send_end_program_page(nandbus, NAND_CMD_PROG_INTLV)) return (ENXIO); DELAY(chip->t_prog); if (check_fail(nandbus)) return (ENXIO); pg_stat = &(chip->pg_stat[page]); pg_stat->page_raw_written++; return (0); } static int generic_program_oob(device_t nand, uint32_t page, void* buf, uint32_t len, uint32_t offset) { struct nand_chip *chip; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN,"%p raw prog oob %x[%x] at %x", nand, page, len, offset); chip = device_get_softc(nand); nandbus = device_get_parent(nand); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); offset += chip->chip_geom.page_size; if (!can_write(nandbus)) return (ENXIO); if (send_start_program_page(nand, row, offset)) return (ENXIO); NANDBUS_WRITE_BUFFER(nandbus, buf, len); if (send_end_program_page(nandbus, NAND_CMD_PROG_END)) return (ENXIO); DELAY(chip->t_prog); if (check_fail(nandbus)) return (ENXIO); return (0); } static int send_erase_block(device_t nand, uint32_t row, uint8_t second_command) { device_t nandbus = device_get_parent(nand); if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_ERASE)) return (ENXIO); if (nand_send_address(nand, row, -1, -1)) return (ENXIO); if (NANDBUS_SEND_COMMAND(nandbus, second_command)) return (ENXIO); if (NANDBUS_START_COMMAND(nandbus)) return (ENXIO); return (0); } static int generic_erase_block(device_t nand, uint32_t block) { struct block_stat *blk_stat; struct nand_chip *chip; device_t nandbus; int row; nand_debug(NDBG_GEN,"%p erase block %x", nand, block); nandbus = device_get_parent(nand); chip = device_get_softc(nand); if (block >= (chip->chip_geom.blks_per_lun * chip->chip_geom.luns)) return (ENXIO); row = (block << chip->chip_geom.blk_shift) & chip->chip_geom.blk_mask; nand_debug(NDBG_GEN,"%p erase block row %x", nand, row); if (!can_write(nandbus)) return (ENXIO); send_erase_block(nand, row, NAND_CMD_ERASE_END); DELAY(chip->t_bers); if (check_fail(nandbus)) return (ENXIO); blk_stat = &(chip->blk_stat[block]); blk_stat->block_erased++; return (0); } static int generic_erase_block_intlv(device_t nand, uint32_t block) { struct block_stat *blk_stat; struct nand_chip *chip; device_t nandbus; int row; nand_debug(NDBG_GEN,"%p erase block %x", nand, block); nandbus = device_get_parent(nand); chip = device_get_softc(nand); if (block >= (chip->chip_geom.blks_per_lun * chip->chip_geom.luns)) return (ENXIO); row = (block << chip->chip_geom.blk_shift) & chip->chip_geom.blk_mask; if (!can_write(nandbus)) return (ENXIO); send_erase_block(nand, row, NAND_CMD_ERASE_INTLV); DELAY(chip->t_bers); if (check_fail(nandbus)) return (ENXIO); blk_stat = &(chip->blk_stat[block]); blk_stat->block_erased++; return (0); } static int onfi_is_blk_bad(device_t device, uint32_t block_number, uint8_t *bad) { struct nand_chip *chip; int page_number, i, j, err; uint8_t *oob; chip = device_get_softc(device); oob = malloc(chip->chip_geom.oob_size, M_NAND, M_WAITOK); - if (!oob) { - device_printf(device, "%s: cannot allocate oob\n", __func__); - return (ENOMEM); - } page_number = block_number * chip->chip_geom.pgs_per_blk; *bad = 0; /* Check OOB of first and last page */ for (i = 0; i < 2; i++, page_number+= chip->chip_geom.pgs_per_blk - 1) { err = generic_read_oob(device, page_number, oob, chip->chip_geom.oob_size, 0); if (err) { device_printf(device, "%s: cannot allocate oob\n", __func__); free(oob, M_NAND); return (ENOMEM); } for (j = 0; j < chip->chip_geom.oob_size; j++) { if (!oob[j]) { *bad = 1; free(oob, M_NAND); return (0); } } } free(oob, M_NAND); return (0); } static int send_small_read_page(device_t nand, uint8_t start_command, uint32_t row, uint32_t column) { device_t nandbus = device_get_parent(nand); if (NANDBUS_SEND_COMMAND(nandbus, start_command)) return (ENXIO); if (nand_send_address(nand, row, column, -1)) return (ENXIO); if (NANDBUS_START_COMMAND(nandbus)) return (ENXIO); return (0); } static int small_read_page(device_t nand, uint32_t page, void *buf, uint32_t len, uint32_t offset) { struct nand_chip *chip; struct page_stat *pg_stat; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN,"%p small read page %x[%x] at %x", nand, page, len, offset); chip = device_get_softc(nand); nandbus = device_get_parent(nand); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (offset < 256) { if (send_small_read_page(nand, NAND_CMD_SMALLA, row, offset)) return (ENXIO); } else { offset -= 256; if (send_small_read_page(nandbus, NAND_CMD_SMALLB, row, offset)) return (ENXIO); } DELAY(chip->t_r); NANDBUS_READ_BUFFER(nandbus, buf, len); if (check_fail(nandbus)) return (ENXIO); pg_stat = &(chip->pg_stat[page]); pg_stat->page_raw_read++; return (0); } static int small_read_oob(device_t nand, uint32_t page, void *buf, uint32_t len, uint32_t offset) { struct nand_chip *chip; struct page_stat *pg_stat; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN,"%p small read oob %x[%x] at %x", nand, page, len, offset); chip = device_get_softc(nand); nandbus = device_get_parent(nand); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (send_small_read_page(nand, NAND_CMD_SMALLOOB, row, 0)) return (ENXIO); DELAY(chip->t_r); NANDBUS_READ_BUFFER(nandbus, buf, len); if (check_fail(nandbus)) return (ENXIO); pg_stat = &(chip->pg_stat[page]); pg_stat->page_raw_read++; return (0); } static int small_program_page(device_t nand, uint32_t page, void* buf, uint32_t len, uint32_t offset) { struct nand_chip *chip; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN,"%p small prog page %x[%x] at %x", nand, page, len, offset); chip = device_get_softc(nand); nandbus = device_get_parent(nand); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (!can_write(nandbus)) return (ENXIO); if (offset < 256) { if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLA)) return (ENXIO); } else { if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLB)) return (ENXIO); } if (send_start_program_page(nand, row, offset)) return (ENXIO); NANDBUS_WRITE_BUFFER(nandbus, buf, len); if (send_end_program_page(nandbus, NAND_CMD_PROG_END)) return (ENXIO); DELAY(chip->t_prog); if (check_fail(nandbus)) return (ENXIO); return (0); } static int small_program_oob(device_t nand, uint32_t page, void* buf, uint32_t len, uint32_t offset) { struct nand_chip *chip; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN,"%p small prog oob %x[%x] at %x", nand, page, len, offset); chip = device_get_softc(nand); nandbus = device_get_parent(nand); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (!can_write(nandbus)) return (ENXIO); if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLOOB)) return (ENXIO); if (send_start_program_page(nand, row, offset)) return (ENXIO); NANDBUS_WRITE_BUFFER(nandbus, buf, len); if (send_end_program_page(nandbus, NAND_CMD_PROG_END)) return (ENXIO); DELAY(chip->t_prog); if (check_fail(nandbus)) return (ENXIO); return (0); } int nand_send_address(device_t nand, int32_t row, int32_t col, int8_t id) { struct nandbus_ivar *ivar; device_t nandbus; uint8_t addr; int err = 0; int i; nandbus = device_get_parent(nand); ivar = device_get_ivars(nand); if (id != -1) { nand_debug(NDBG_GEN,"send_address: send id %02x", id); err = NANDBUS_SEND_ADDRESS(nandbus, id); } if (!err && col != -1) { for (i = 0; i < ivar->cols; i++, col >>= 8) { addr = (uint8_t)(col & 0xff); nand_debug(NDBG_GEN,"send_address: send address column " "%02x", addr); err = NANDBUS_SEND_ADDRESS(nandbus, addr); if (err) break; } } if (!err && row != -1) { for (i = 0; i < ivar->rows; i++, row >>= 8) { addr = (uint8_t)(row & 0xff); nand_debug(NDBG_GEN,"send_address: send address row " "%02x", addr); err = NANDBUS_SEND_ADDRESS(nandbus, addr); if (err) break; } } return (err); } static int generic_is_blk_bad(device_t dev, uint32_t block, uint8_t *bad) { struct nand_chip *chip; int page_number, err, i; uint8_t *oob; chip = device_get_softc(dev); oob = malloc(chip->chip_geom.oob_size, M_NAND, M_WAITOK); - if (!oob) { - device_printf(dev, "%s: cannot allocate OOB\n", __func__); - return (ENOMEM); - } page_number = block * chip->chip_geom.pgs_per_blk; *bad = 0; /* Check OOB of first and second page */ for (i = 0; i < 2; i++) { err = NAND_READ_OOB(dev, page_number + i, oob, chip->chip_geom.oob_size, 0); if (err) { device_printf(dev, "%s: cannot allocate OOB\n", __func__); free(oob, M_NAND); return (ENOMEM); } if (!oob[0]) { *bad = 1; free(oob, M_NAND); return (0); } } free(oob, M_NAND); return (0); } static int generic_get_ecc(device_t dev, void *buf, void *ecc, int *needwrite) { struct nand_chip *chip = device_get_softc(dev); struct chip_geom *cg = &chip->chip_geom; return (NANDBUS_GET_ECC(device_get_parent(dev), buf, cg->page_size, ecc, needwrite)); } static int generic_correct_ecc(device_t dev, void *buf, void *readecc, void *calcecc) { struct nand_chip *chip = device_get_softc(dev); struct chip_geom *cg = &chip->chip_geom; return (NANDBUS_CORRECT_ECC(device_get_parent(dev), buf, cg->page_size, readecc, calcecc)); } #if 0 int nand_chng_read_col(device_t nand, uint32_t col, void *buf, size_t len) { struct nand_chip *chip; device_t nandbus; chip = device_get_softc(nand); nandbus = device_get_parent(nand); if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL)) return (ENXIO); if (NANDBUS_SEND_ADDRESS(nandbus, -1, col, -1)) return (ENXIO); if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL_END)) return (ENXIO); if (NANDBUS_START_COMMAND(nandbus)) return (ENXIO); if (buf != NULL && len > 0) NANDBUS_READ_BUFFER(nandbus, buf, len); return (0); } int nand_chng_write_col(device_t dev, uint32_t col, void *buf, size_t len) { struct nand_chip *chip; device_t nandbus; chip = device_get_softc(dev); nandbus = device_get_parent(dev); if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_WRITE_COL)) return (ENXIO); if (NANDBUS_SEND_ADDRESS(nandbus, -1, col, -1)) return (ENXIO); if (buf != NULL && len > 0) NANDBUS_WRITE_BUFFER(nandbus, buf, len); if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL_END)) return (ENXIO); if (NANDBUS_START_COMMAND(nandbus)) return (ENXIO); return (0); } int nand_copyback_read(device_t dev, uint32_t page, uint32_t col, void *buf, size_t len) { struct nand_chip *chip; struct page_stat *pg_stat; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN," raw read page %x[%x] at %x", page, col, len); chip = device_get_softc(dev); nandbus = device_get_parent(dev); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_CPBK, row, 0)) return (ENXIO); DELAY(chip->t_r); if (check_fail(nandbus)) return (ENXIO); if (buf != NULL && len > 0) NANDBUS_READ_BUFFER(nandbus, buf, len); pg_stat = &(chip->pg_stat[page]); pg_stat->page_raw_read++; return (0); } int nand_copyback_prog(device_t dev, uint32_t page, uint32_t col, void *buf, size_t len) { struct nand_chip *chip; struct page_stat *pg_stat; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN,"copyback prog page %x[%x]", page, len); chip = device_get_softc(dev); nandbus = device_get_parent(dev); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (!can_write(nandbus)) return (ENXIO); if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_WRITE_COL)) return (ENXIO); if (NANDBUS_SEND_ADDRESS(nandbus, row, col, -1)) return (ENXIO); if (buf != NULL && len > 0) NANDBUS_WRITE_BUFFER(nandbus, buf, len); if (send_end_program_page(nandbus, NAND_CMD_PROG_END)) return (ENXIO); DELAY(chip->t_prog); if (check_fail(nandbus)) return (ENXIO); pg_stat = &(chip->pg_stat[page]); pg_stat->page_raw_written++; return (0); } int nand_copyback_prog_intlv(device_t dev, uint32_t page) { struct nand_chip *chip; struct page_stat *pg_stat; device_t nandbus; uint32_t row; nand_debug(NDBG_GEN,"cache prog page %x", page); chip = device_get_softc(dev); nandbus = device_get_parent(dev); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (!can_write(nandbus)) return (ENXIO); if (send_start_program_page(nand, row, 0)) return (ENXIO); if (send_end_program_page(nandbus, NAND_CMD_PROG_INTLV)) return (ENXIO); DELAY(chip->t_prog); if (check_fail(nandbus)) return (ENXIO); pg_stat = &(chip->pg_stat[page]); pg_stat->page_raw_written++; return (0); } int nand_prog_cache(device_t dev, uint32_t page, uint32_t col, void *buf, size_t len, uint8_t end) { struct nand_chip *chip; struct page_stat *pg_stat; device_t nandbus; uint32_t row; uint8_t command; nand_debug(NDBG_GEN,"cache prog page %x[%x]", page, len); chip = device_get_softc(dev); nandbus = device_get_parent(dev); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (!can_write(nandbus)) return (ENXIO); if (send_start_program_page(dev, row, 0)) return (ENXIO); NANDBUS_WRITE_BUFFER(nandbus, buf, len); if (end) command = NAND_CMD_PROG_END; else command = NAND_CMD_PROG_CACHE; if (send_end_program_page(nandbus, command)) return (ENXIO); DELAY(chip->t_prog); if (check_fail(nandbus)) return (ENXIO); pg_stat = &(chip->pg_stat[page]); pg_stat->page_raw_written++; return (0); } int nand_read_cache(device_t dev, uint32_t page, uint32_t col, void *buf, size_t len, uint8_t end) { struct nand_chip *chip; struct page_stat *pg_stat; device_t nandbus; uint32_t row; uint8_t command; nand_debug(NDBG_GEN,"cache read page %x[%x] ", page, len); chip = device_get_softc(dev); nandbus = device_get_parent(dev); if (nand_check_page_boundary(chip, page)) return (ENXIO); page_to_row(&chip->chip_geom, page, &row); if (page != -1) { if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_READ)) return (ENXIO); if (NANDBUS_SEND_ADDRESS(nandbus, row, col, -1)) return (ENXIO); } if (end) command = NAND_CMD_READ_CACHE_END; else command = NAND_CMD_READ_CACHE; if (NANDBUS_SEND_COMMAND(nandbus, command)) return (ENXIO); if (NANDBUS_START_COMMAND(nandbus)) return (ENXIO); DELAY(chip->t_r); if (check_fail(nandbus)) return (ENXIO); if (buf != NULL && len > 0) NANDBUS_READ_BUFFER(nandbus, buf, len); pg_stat = &(chip->pg_stat[page]); pg_stat->page_raw_read++; return (0); } int nand_get_feature(device_t dev, uint8_t feat, void *buf) { struct nand_chip *chip; device_t nandbus; nand_debug(NDBG_GEN,"nand get feature"); chip = device_get_softc(dev); nandbus = device_get_parent(dev); if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_GET_FEATURE)) return (ENXIO); if (NANDBUS_SEND_ADDRESS(nandbus, -1, -1, feat)) return (ENXIO); if (NANDBUS_START_COMMAND(nandbus)) return (ENXIO); DELAY(chip->t_r); NANDBUS_READ_BUFFER(nandbus, buf, 4); return (0); } int nand_set_feature(device_t dev, uint8_t feat, void *buf) { struct nand_chip *chip; device_t nandbus; nand_debug(NDBG_GEN,"nand set feature"); chip = device_get_softc(dev); nandbus = device_get_parent(dev); if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SET_FEATURE)) return (ENXIO); if (NANDBUS_SEND_ADDRESS(nandbus, -1, -1, feat)) return (ENXIO); NANDBUS_WRITE_BUFFER(nandbus, buf, 4); if (NANDBUS_START_COMMAND(nandbus)) return (ENXIO); return (0); } #endif Index: head/sys/dev/nand/nandsim_chip.c =================================================================== --- head/sys/dev/nand/nandsim_chip.c (revision 303875) +++ head/sys/dev/nand/nandsim_chip.c (revision 303876) @@ -1,901 +1,896 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 *)¶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"); }