aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/nand
diff options
context:
space:
mode:
authorGrzegorz Bernacki <gber@FreeBSD.org>2012-05-17 10:11:18 +0000
committerGrzegorz Bernacki <gber@FreeBSD.org>2012-05-17 10:11:18 +0000
commit7f725bcd5c9ca6cbae5eab5f38c65b16da86b37d (patch)
treea325137a898341311de8641f7212e28b7d87950e /sys/dev/nand
parentcac3dcd5f981d8843fb0acc27b4d28af1339af66 (diff)
downloadsrc-7f725bcd5c9ca6cbae5eab5f38c65b16da86b37d.tar.gz
src-7f725bcd5c9ca6cbae5eab5f38c65b16da86b37d.zip
Import work done under project/nand (@235533) into head.
The NAND Flash environment consists of several distinct components: - NAND framework (drivers harness for NAND controllers and NAND chips) - NAND simulator (NANDsim) - NAND file system (NAND FS) - Companion tools and utilities - Documentation (manual pages) This work is still experimental. Please use with caution. Obtained from: Semihalf Supported by: FreeBSD Foundation, Juniper Networks
Notes
Notes: svn path=/head/; revision=235537
Diffstat (limited to 'sys/dev/nand')
-rw-r--r--sys/dev/nand/nand.c832
-rw-r--r--sys/dev/nand/nand.h385
-rw-r--r--sys/dev/nand/nand_bbt.c273
-rw-r--r--sys/dev/nand/nand_cdev.c413
-rw-r--r--sys/dev/nand/nand_dev.h90
-rw-r--r--sys/dev/nand/nand_ecc_pos.h56
-rw-r--r--sys/dev/nand/nand_generic.c1320
-rw-r--r--sys/dev/nand/nand_geom.c414
-rw-r--r--sys/dev/nand/nand_id.c60
-rw-r--r--sys/dev/nand/nand_if.m168
-rw-r--r--sys/dev/nand/nandbus.c530
-rw-r--r--sys/dev/nand/nandbus.h49
-rw-r--r--sys/dev/nand/nandbus_if.m100
-rw-r--r--sys/dev/nand/nandsim.c665
-rw-r--r--sys/dev/nand/nandsim.h175
-rw-r--r--sys/dev/nand/nandsim_chip.c901
-rw-r--r--sys/dev/nand/nandsim_chip.h159
-rw-r--r--sys/dev/nand/nandsim_ctrl.c396
-rw-r--r--sys/dev/nand/nandsim_log.c186
-rw-r--r--sys/dev/nand/nandsim_log.h52
-rw-r--r--sys/dev/nand/nandsim_swap.c389
-rw-r--r--sys/dev/nand/nandsim_swap.h64
-rw-r--r--sys/dev/nand/nfc_if.m165
-rw-r--r--sys/dev/nand/nfc_mv.c236
24 files changed, 8078 insertions, 0 deletions
diff --git a/sys/dev/nand/nand.c b/sys/dev/nand/nand.c
new file mode 100644
index 000000000000..ad5bc40410df
--- /dev/null
+++ b/sys/dev/nand/nand.c
@@ -0,0 +1,832 @@
+/*-
+ * 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/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/callout.h>
+#include <sys/sysctl.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include <dev/nand/nand_ecc_pos.h>
+#include "nfc_if.h"
+#include "nand_if.h"
+#include "nandbus_if.h"
+#include <machine/stdarg.h>
+
+#define NAND_RESET_DELAY 1000 /* tRST */
+#define NAND_ERASE_DELAY 3000 /* tBERS */
+#define NAND_PROG_DELAY 700 /* tPROG */
+#define NAND_READ_DELAY 50 /* tR */
+
+#define BIT0(x) ((x) & 0x1)
+#define BIT1(x) (BIT0(x >> 1))
+#define BIT2(x) (BIT0(x >> 2))
+#define BIT3(x) (BIT0(x >> 3))
+#define BIT4(x) (BIT0(x >> 4))
+#define BIT5(x) (BIT0(x >> 5))
+#define BIT6(x) (BIT0(x >> 6))
+#define BIT7(x) (BIT0(x >> 7))
+
+#define SOFTECC_SIZE 256
+#define SOFTECC_BYTES 3
+
+int nand_debug_flag = 0;
+SYSCTL_INT(_debug, OID_AUTO, nand_debug, CTLFLAG_RW, &nand_debug_flag, 0,
+ "NAND subsystem debug flag");
+
+static void
+nand_tunable_init(void *arg)
+{
+
+ TUNABLE_INT_FETCH("debug.nand", &nand_debug_flag);
+}
+
+SYSINIT(nand_tunables, SI_SUB_VFS, SI_ORDER_ANY, nand_tunable_init, NULL);
+
+MALLOC_DEFINE(M_NAND, "NAND", "NAND dynamic data");
+
+static void calculate_ecc(const uint8_t *, uint8_t *);
+static int correct_ecc(uint8_t *, uint8_t *, uint8_t *);
+
+void
+nand_debug(int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!(nand_debug_flag & level))
+ return;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+}
+
+void
+nand_init(struct nand_softc *nand, device_t dev, int ecc_mode,
+ int ecc_bytes, int ecc_size, uint16_t *eccposition, char *cdev_name)
+{
+
+ nand->ecc.eccmode = ecc_mode;
+ nand->chip_cdev_name = cdev_name;
+
+ if (ecc_mode == NAND_ECC_SOFT) {
+ nand->ecc.eccbytes = SOFTECC_BYTES;
+ nand->ecc.eccsize = SOFTECC_SIZE;
+ } else if (ecc_mode != NAND_ECC_NONE) {
+ nand->ecc.eccbytes = ecc_bytes;
+ nand->ecc.eccsize = ecc_size;
+ if (eccposition)
+ nand->ecc.eccpositions = eccposition;
+ }
+}
+
+void
+nand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params)
+{
+ struct chip_geom *cg;
+
+ cg = &chip->chip_geom;
+
+ init_chip_geom(cg, params->luns, params->blocks_per_lun,
+ params->pages_per_block, params->bytes_per_page,
+ params->spare_bytes_per_page);
+ chip->t_bers = params->t_bers;
+ chip->t_prog = params->t_prog;
+ chip->t_r = params->t_r;
+ chip->t_ccs = params->t_ccs;
+
+ if (params->features & ONFI_FEAT_16BIT)
+ chip->flags |= NAND_16_BIT;
+}
+
+void
+nand_set_params(struct nand_chip *chip, struct nand_params *params)
+{
+ struct chip_geom *cg;
+ uint32_t blocks_per_chip;
+
+ cg = &chip->chip_geom;
+ blocks_per_chip = (params->chip_size << 20) /
+ (params->page_size * params->pages_per_block);
+
+ init_chip_geom(cg, 1, blocks_per_chip,
+ params->pages_per_block, params->page_size,
+ params->oob_size);
+
+ chip->t_bers = NAND_ERASE_DELAY;
+ chip->t_prog = NAND_PROG_DELAY;
+ chip->t_r = NAND_READ_DELAY;
+ chip->t_ccs = 0;
+
+ if (params->flags & NAND_16_BIT)
+ chip->flags |= NAND_16_BIT;
+}
+
+int
+nand_init_stat(struct nand_chip *chip)
+{
+ struct block_stat *blk_stat;
+ struct page_stat *pg_stat;
+ struct chip_geom *cg;
+ uint32_t blks, pgs;
+
+ cg = &chip->chip_geom;
+ blks = cg->blks_per_lun * cg->luns;
+ blk_stat = malloc(sizeof(struct block_stat) * blks, M_NAND,
+ M_WAITOK | M_ZERO);
+ if (!blk_stat)
+ return (ENOMEM);
+
+ pgs = blks * cg->pgs_per_blk;
+ pg_stat = malloc(sizeof(struct page_stat) * pgs, M_NAND,
+ M_WAITOK | M_ZERO);
+ if (!pg_stat) {
+ free(blk_stat, M_NAND);
+ return (ENOMEM);
+ }
+
+ chip->blk_stat = blk_stat;
+ chip->pg_stat = pg_stat;
+
+ return (0);
+}
+
+void
+nand_destroy_stat(struct nand_chip *chip)
+{
+
+ free(chip->pg_stat, M_NAND);
+ free(chip->blk_stat, M_NAND);
+}
+
+int
+init_chip_geom(struct chip_geom *cg, uint32_t luns, uint32_t blks_per_lun,
+ uint32_t pgs_per_blk, uint32_t pg_size, uint32_t oob_size)
+{
+ int shift;
+
+ if (!cg)
+ return (-1);
+
+ cg->luns = luns;
+ cg->blks_per_lun = blks_per_lun;
+ cg->blks_per_chip = blks_per_lun * luns;
+ cg->pgs_per_blk = pgs_per_blk;
+
+ cg->page_size = pg_size;
+ cg->oob_size = oob_size;
+ cg->block_size = cg->page_size * cg->pgs_per_blk;
+ cg->chip_size = cg->block_size * cg->blks_per_chip;
+
+ shift = fls(cg->pgs_per_blk - 1);
+ cg->pg_mask = (1 << shift) - 1;
+ cg->blk_shift = shift;
+
+ if (cg->blks_per_lun > 0) {
+ shift = fls(cg->blks_per_lun - 1);
+ cg->blk_mask = ((1 << shift) - 1) << cg->blk_shift;
+ } else {
+ shift = 0;
+ cg->blk_mask = 0;
+ }
+
+ cg->lun_shift = shift + cg->blk_shift;
+ shift = fls(cg->luns - 1);
+ cg->lun_mask = ((1 << shift) - 1) << cg->lun_shift;
+
+ nand_debug(NDBG_NAND, "Masks: lun 0x%x blk 0x%x page 0x%x\n"
+ "Shifts: lun %d blk %d",
+ cg->lun_mask, cg->blk_mask, cg->pg_mask,
+ cg->lun_shift, cg->blk_shift);
+
+ return (0);
+}
+
+int
+nand_row_to_blkpg(struct chip_geom *cg, uint32_t row, uint32_t *lun,
+ uint32_t *blk, uint32_t *pg)
+{
+
+ if (!cg || !lun || !blk || !pg)
+ return (-1);
+
+ if (row & ~(cg->lun_mask | cg->blk_mask | cg->pg_mask)) {
+ nand_debug(NDBG_NAND,"Address out of bounds\n");
+ return (-1);
+ }
+
+ *lun = (row & cg->lun_mask) >> cg->lun_shift;
+ *blk = (row & cg->blk_mask) >> cg->blk_shift;
+ *pg = (row & cg->pg_mask);
+
+ nand_debug(NDBG_NAND,"address %x-%x-%x\n", *lun, *blk, *pg);
+
+ return (0);
+}
+
+int page_to_row(struct chip_geom *cg, uint32_t page, uint32_t *row)
+{
+ uint32_t lun, block, pg_in_blk;
+
+ if (!cg || !row)
+ return (-1);
+
+ block = page / cg->pgs_per_blk;
+ pg_in_blk = page % cg->pgs_per_blk;
+
+ lun = block / cg->blks_per_lun;
+ block = block % cg->blks_per_lun;
+
+ *row = (lun << cg->lun_shift) & cg->lun_mask;
+ *row |= ((block << cg->blk_shift) & cg->blk_mask);
+ *row |= (pg_in_blk & cg->pg_mask);
+
+ return (0);
+}
+
+int
+nand_check_page_boundary(struct nand_chip *chip, uint32_t page)
+{
+ struct chip_geom* cg;
+
+ cg = &chip->chip_geom;
+ if (page >= (cg->pgs_per_blk * cg->blks_per_lun * cg->luns)) {
+ nand_debug(NDBG_GEN,"%s: page number too big %#x\n",
+ __func__, page);
+ return (1);
+ }
+
+ return (0);
+}
+
+void
+nand_get_chip_param(struct nand_chip *chip, struct chip_param_io *param)
+{
+ struct chip_geom *cg;
+
+ cg = &chip->chip_geom;
+ param->page_size = cg->page_size;
+ param->oob_size = cg->oob_size;
+
+ param->blocks = cg->blks_per_lun * cg->luns;
+ param->pages_per_block = cg->pgs_per_blk;
+}
+
+static uint16_t *
+default_software_ecc_positions(struct nand_chip *chip)
+{
+ struct nand_ecc_data *eccd;
+
+ eccd = &chip->nand->ecc;
+
+ if (eccd->eccpositions)
+ return (eccd->eccpositions);
+
+ switch (chip->chip_geom.oob_size) {
+ case 16:
+ return ((uint16_t *)&default_software_ecc_positions_16);
+ case 64:
+ return ((uint16_t *)&default_software_ecc_positions_64);
+ case 128:
+ return ((uint16_t *)&default_software_ecc_positions_128);
+ default:
+ return (NULL); /* No ecc bytes positions defs available */
+ }
+
+ return (NULL);
+}
+
+static void
+calculate_ecc(const uint8_t *buf, uint8_t *ecc)
+{
+ uint8_t p8, byte;
+ int i;
+
+ memset(ecc, 0, 3);
+
+ for (i = 0; i < 256; i++) {
+ byte = buf[i];
+ ecc[0] ^= (BIT0(byte) ^ BIT2(byte) ^ BIT4(byte) ^
+ BIT6(byte)) << 2;
+ ecc[0] ^= (BIT1(byte) ^ BIT3(byte) ^ BIT5(byte) ^
+ BIT7(byte)) << 3;
+ ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT4(byte) ^
+ BIT5(byte)) << 4;
+ ecc[0] ^= (BIT2(byte) ^ BIT3(byte) ^ BIT6(byte) ^
+ BIT7(byte)) << 5;
+ ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^
+ BIT3(byte)) << 6;
+ ecc[0] ^= (BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^
+ BIT7(byte)) << 7;
+
+ p8 = BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^
+ BIT3(byte) ^ BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^
+ BIT7(byte);
+
+ if (p8) {
+ ecc[2] ^= (0x1 << BIT0(i));
+ ecc[2] ^= (0x4 << BIT1(i));
+ ecc[2] ^= (0x10 << BIT2(i));
+ ecc[2] ^= (0x40 << BIT3(i));
+
+ ecc[1] ^= (0x1 << BIT4(i));
+ ecc[1] ^= (0x4 << BIT5(i));
+ ecc[1] ^= (0x10 << BIT6(i));
+ ecc[1] ^= (0x40 << BIT7(i));
+ }
+ }
+ ecc[0] = ~ecc[0];
+ ecc[1] = ~ecc[1];
+ ecc[2] = ~ecc[2];
+ ecc[0] |= 3;
+}
+
+static int
+correct_ecc(uint8_t *buf, uint8_t *calc_ecc, uint8_t *read_ecc)
+{
+ uint8_t ecc0, ecc1, ecc2, onesnum, bit, byte;
+ uint16_t addr = 0;
+
+ ecc0 = calc_ecc[0] ^ read_ecc[0];
+ ecc1 = calc_ecc[1] ^ read_ecc[1];
+ ecc2 = calc_ecc[2] ^ read_ecc[2];
+
+ if (!ecc0 && !ecc1 && !ecc2)
+ return (ECC_OK);
+
+ addr = BIT3(ecc0) | (BIT5(ecc0) << 1) | (BIT7(ecc0) << 2);
+ addr |= (BIT1(ecc2) << 3) | (BIT3(ecc2) << 4) |
+ (BIT5(ecc2) << 5) | (BIT7(ecc2) << 6);
+ addr |= (BIT1(ecc1) << 7) | (BIT3(ecc1) << 8) |
+ (BIT5(ecc1) << 9) | (BIT7(ecc1) << 10);
+
+ onesnum = 0;
+ while (ecc0 || ecc1 || ecc2) {
+ if (ecc0 & 1)
+ onesnum++;
+ if (ecc1 & 1)
+ onesnum++;
+ if (ecc2 & 1)
+ onesnum++;
+
+ ecc0 >>= 1;
+ ecc1 >>= 1;
+ ecc2 >>= 1;
+ }
+
+ if (onesnum == 11) {
+ /* Correctable error */
+ bit = addr & 7;
+ byte = addr >> 3;
+ buf[byte] ^= (1 << bit);
+ return (ECC_CORRECTABLE);
+ } else if (onesnum == 1) {
+ /* ECC error */
+ return (ECC_ERROR_ECC);
+ } else {
+ /* Uncorrectable error */
+ return (ECC_UNCORRECTABLE);
+ }
+
+ return (0);
+}
+
+int
+nand_softecc_get(device_t dev, uint8_t *buf, int pagesize, uint8_t *ecc)
+{
+ int steps = pagesize / SOFTECC_SIZE;
+ int i = 0, j = 0;
+
+ for (; i < (steps * SOFTECC_BYTES);
+ i += SOFTECC_BYTES, j += SOFTECC_SIZE) {
+ calculate_ecc(&buf[j], &ecc[i]);
+ }
+
+ return (0);
+}
+
+int
+nand_softecc_correct(device_t dev, uint8_t *buf, int pagesize,
+ uint8_t *readecc, uint8_t *calcecc)
+{
+ int steps = pagesize / SOFTECC_SIZE;
+ int i = 0, j = 0, ret = 0;
+
+ for (i = 0; i < (steps * SOFTECC_BYTES);
+ i += SOFTECC_BYTES, j += SOFTECC_SIZE) {
+ ret += correct_ecc(&buf[j], &calcecc[i], &readecc[i]);
+ if (ret < 0)
+ return (ret);
+ }
+
+ return (ret);
+}
+
+static int
+offset_to_page(struct chip_geom *cg, uint32_t offset)
+{
+
+ return (offset / cg->page_size);
+}
+
+int
+nand_read_pages(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len)
+{
+ struct chip_geom *cg;
+ struct nand_ecc_data *eccd;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ void *oob = NULL;
+ uint8_t *ptr;
+ uint16_t *eccpos = NULL;
+ uint32_t page, num, steps = 0;
+ int i, retval = 0, needwrite;
+
+ nand_debug(NDBG_NAND,"%p read page %x[%x]", chip, offset, len);
+ cg = &chip->chip_geom;
+ eccd = &chip->nand->ecc;
+ page = offset_to_page(cg, offset);
+ num = len / cg->page_size;
+
+ if (eccd->eccmode != NAND_ECC_NONE) {
+ steps = cg->page_size / eccd->eccsize;
+ eccpos = default_software_ecc_positions(chip);
+ oob = malloc(cg->oob_size, M_NAND, M_WAITOK);
+ }
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ ptr = (uint8_t *)buf;
+ while (num--) {
+ pg_stat = &(chip->pg_stat[page]);
+
+ if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
+ retval = ENXIO;
+ break;
+ }
+
+ if (eccd->eccmode != NAND_ECC_NONE) {
+ if (NAND_GET_ECC(chip->dev, ptr, eccd->ecccalculated,
+ &needwrite)) {
+ retval = ENXIO;
+ break;
+ }
+ nand_debug(NDBG_ECC,"%s: ECC calculated:",
+ __func__);
+ if (nand_debug_flag & NDBG_ECC)
+ for (i = 0; i < (eccd->eccbytes * steps); i++)
+ printf("%x ", eccd->ecccalculated[i]);
+
+ nand_debug(NDBG_ECC,"\n");
+
+ if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size,
+ 0)) {
+ retval = ENXIO;
+ break;
+ }
+ for (i = 0; i < (eccd->eccbytes * steps); i++)
+ eccd->eccread[i] = ((uint8_t *)oob)[eccpos[i]];
+
+ nand_debug(NDBG_ECC,"%s: ECC read:", __func__);
+ if (nand_debug_flag & NDBG_ECC)
+ for (i = 0; i < (eccd->eccbytes * steps); i++)
+ printf("%x ", eccd->eccread[i]);
+ nand_debug(NDBG_ECC,"\n");
+
+ retval = NAND_CORRECT_ECC(chip->dev, ptr, eccd->eccread,
+ eccd->ecccalculated);
+
+ nand_debug(NDBG_ECC, "NAND_CORRECT_ECC() returned %d",
+ retval);
+
+ if (retval == 0)
+ pg_stat->ecc_stat.ecc_succeded++;
+ else if (retval > 0) {
+ pg_stat->ecc_stat.ecc_corrected += retval;
+ retval = ECC_CORRECTABLE;
+ } else {
+ pg_stat->ecc_stat.ecc_failed++;
+ break;
+ }
+ }
+
+ pg_stat->page_read++;
+ page++;
+ ptr += cg->page_size;
+ }
+
+ NANDBUS_UNLOCK(nandbus);
+
+ if (oob)
+ free(oob, M_NAND);
+
+ return (retval);
+}
+
+int
+nand_read_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len)
+{
+ struct chip_geom *cg;
+ device_t nandbus;
+ uint8_t *ptr;
+ uint32_t page, num, end, begin = 0, begin_off;
+ int retval = 0;
+
+ cg = &chip->chip_geom;
+ page = offset_to_page(cg, offset);
+ begin_off = offset - page * cg->page_size;
+ if (begin_off) {
+ begin = cg->page_size - begin_off;
+ len -= begin;
+ }
+ num = len / cg->page_size;
+ end = len % cg->page_size;
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ ptr = (uint8_t *)buf;
+ if (begin_off) {
+ if (NAND_READ_PAGE(chip->dev, page, ptr, begin, begin_off)) {
+ NANDBUS_UNLOCK(nandbus);
+ return (ENXIO);
+ }
+
+ page++;
+ ptr += begin;
+ }
+
+ while (num--) {
+ if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
+ NANDBUS_UNLOCK(nandbus);
+ return (ENXIO);
+ }
+
+ page++;
+ ptr += cg->page_size;
+ }
+
+ if (end)
+ if (NAND_READ_PAGE(chip->dev, page, ptr, end, 0)) {
+ NANDBUS_UNLOCK(nandbus);
+ return (ENXIO);
+ }
+
+ NANDBUS_UNLOCK(nandbus);
+
+ return (retval);
+}
+
+
+int
+nand_prog_pages(struct nand_chip *chip, uint32_t offset, uint8_t *buf,
+ uint32_t len)
+{
+ struct chip_geom *cg;
+ struct page_stat *pg_stat;
+ struct nand_ecc_data *eccd;
+ device_t nandbus;
+ uint32_t page, num;
+ uint8_t *oob = NULL;
+ uint16_t *eccpos = NULL;
+ int steps = 0, i, needwrite, err = 0;
+
+ nand_debug(NDBG_NAND,"%p prog page %x[%x]", chip, offset, len);
+
+ eccd = &chip->nand->ecc;
+ cg = &chip->chip_geom;
+ page = offset_to_page(cg, offset);
+ num = len / cg->page_size;
+
+ if (eccd->eccmode != NAND_ECC_NONE) {
+ steps = cg->page_size / eccd->eccsize;
+ oob = malloc(cg->oob_size, M_NAND, M_WAITOK);
+ eccpos = default_software_ecc_positions(chip);
+ }
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ while (num--) {
+ if (NAND_PROGRAM_PAGE(chip->dev, page, buf, cg->page_size, 0)) {
+ err = ENXIO;
+ break;
+ }
+
+ if (eccd->eccmode != NAND_ECC_NONE) {
+ if (NAND_GET_ECC(chip->dev, buf, &eccd->ecccalculated,
+ &needwrite)) {
+ err = ENXIO;
+ break;
+ }
+ nand_debug(NDBG_ECC,"ECC calculated:");
+ if (nand_debug_flag & NDBG_ECC)
+ for (i = 0; i < (eccd->eccbytes * steps); i++)
+ printf("%x ", eccd->ecccalculated[i]);
+
+ nand_debug(NDBG_ECC,"\n");
+
+ if (needwrite) {
+ if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size,
+ 0)) {
+ err = ENXIO;
+ break;
+ }
+
+ for (i = 0; i < (eccd->eccbytes * steps); i++)
+ oob[eccpos[i]] = eccd->ecccalculated[i];
+
+ if (NAND_PROGRAM_OOB(chip->dev, page, oob,
+ cg->oob_size, 0)) {
+ err = ENXIO;
+ break;
+ }
+ }
+ }
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_written++;
+
+ page++;
+ buf += cg->page_size;
+ }
+
+ NANDBUS_UNLOCK(nandbus);
+
+ if (oob)
+ free(oob, M_NAND);
+
+ return (err);
+}
+
+int
+nand_prog_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len)
+{
+ struct chip_geom *cg;
+ device_t nandbus;
+ uint8_t *ptr;
+ uint32_t page, num, end, begin = 0, begin_off;
+ int retval = 0;
+
+ cg = &chip->chip_geom;
+ page = offset_to_page(cg, offset);
+ begin_off = offset - page * cg->page_size;
+ if (begin_off) {
+ begin = cg->page_size - begin_off;
+ len -= begin;
+ }
+ num = len / cg->page_size;
+ end = len % cg->page_size;
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ ptr = (uint8_t *)buf;
+ if (begin_off) {
+ if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, begin, begin_off)) {
+ NANDBUS_UNLOCK(nandbus);
+ return (ENXIO);
+ }
+
+ page++;
+ ptr += begin;
+ }
+
+ while (num--) {
+ if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
+ NANDBUS_UNLOCK(nandbus);
+ return (ENXIO);
+ }
+
+ page++;
+ ptr += cg->page_size;
+ }
+
+ if (end)
+ retval = NAND_PROGRAM_PAGE(chip->dev, page, ptr, end, 0);
+
+ NANDBUS_UNLOCK(nandbus);
+
+ return (retval);
+}
+
+int
+nand_read_oob(struct nand_chip *chip, uint32_t page, void *buf,
+ uint32_t len)
+{
+ device_t nandbus;
+ int retval = 0;
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ retval = NAND_READ_OOB(chip->dev, page, buf, len, 0);
+
+ NANDBUS_UNLOCK(nandbus);
+
+ return (retval);
+}
+
+
+int
+nand_prog_oob(struct nand_chip *chip, uint32_t page, void *buf,
+ uint32_t len)
+{
+ device_t nandbus;
+ int retval = 0;
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ retval = NAND_PROGRAM_OOB(chip->dev, page, buf, len, 0);
+
+ NANDBUS_UNLOCK(nandbus);
+
+ return (retval);
+}
+
+int
+nand_erase_blocks(struct nand_chip *chip, off_t offset, size_t len)
+{
+ device_t nandbus;
+ struct chip_geom *cg;
+ uint32_t block, num_blocks;
+ int err = 0;
+
+ cg = &chip->chip_geom;
+ if ((offset % cg->block_size) || (len % cg->block_size))
+ return (EINVAL);
+
+ block = offset / cg->block_size;
+ num_blocks = len / cg->block_size;
+ nand_debug(NDBG_NAND,"%p erase blocks %d[%d]", chip, block, num_blocks);
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ while (num_blocks--) {
+ if (!nand_check_bad_block(chip, block)) {
+ if (NAND_ERASE_BLOCK(chip->dev, block)) {
+ nand_debug(NDBG_NAND,"%p erase blocks %d error",
+ chip, block);
+ nand_mark_bad_block(chip, block);
+ err = ENXIO;
+ }
+ } else
+ err = ENXIO;
+
+ block++;
+ };
+
+ NANDBUS_UNLOCK(nandbus);
+
+ if (err)
+ nand_update_bbt(chip);
+
+ return (err);
+}
diff --git a/sys/dev/nand/nand.h b/sys/dev/nand/nand.h
new file mode 100644
index 000000000000..05101acddad9
--- /dev/null
+++ b/sys/dev/nand/nand.h
@@ -0,0 +1,385 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_NAND_H_
+#define _DEV_NAND_H_
+
+#include <sys/bus.h>
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+#include <sys/taskqueue.h>
+#include <sys/queue.h>
+#include <sys/bio.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/malloc.h>
+
+#include <dev/nand/nand_dev.h>
+
+MALLOC_DECLARE(M_NAND);
+
+/* Read commands */
+#define NAND_CMD_READ 0x00
+#define NAND_CMD_CHNG_READ_COL 0x05
+#define NAND_CMD_READ_END 0x30
+#define NAND_CMD_READ_CACHE 0x31
+#define NAND_CMD_READ_CPBK 0x35
+#define NAND_CMD_READ_CACHE_END 0x3F
+#define NAND_CMD_CHNG_READ_COL_END 0xE0
+
+/* Erase commands */
+#define NAND_CMD_ERASE 0x60
+#define NAND_CMD_ERASE_END 0xD0
+#define NAND_CMD_ERASE_INTLV 0xD1
+
+/* Program commands */
+#define NAND_CMD_PROG 0x80
+#define NAND_CMD_CHNG_WRITE_COL 0x85
+#define NAND_CMD_PROG_END 0x10
+#define NAND_CMD_PROG_INTLV 0x11
+#define NAND_CMD_PROG_CACHE 0x15
+
+/* Misc commands */
+#define NAND_CMD_STATUS 0x70
+#define NAND_CMD_STATUS_ENH 0x78
+#define NAND_CMD_READ_ID 0x90
+#define NAND_CMD_READ_PARAMETER 0xec
+#define NAND_CMD_READ_UNIQUE_ID 0xed
+#define NAND_CMD_GET_FEATURE 0xee
+#define NAND_CMD_SET_FEATURE 0xef
+
+/* Reset commands */
+#define NAND_CMD_SYNCH_RESET 0xfc
+#define NAND_CMD_RESET 0xff
+
+/* Small page flash commands */
+#define NAND_CMD_SMALLA 0x00
+#define NAND_CMD_SMALLB 0x01
+#define NAND_CMD_SMALLOOB 0x50
+
+#define NAND_STATUS_FAIL 0x1
+#define NAND_STATUS_FAILC 0x2
+#define NAND_STATUS_ARDY 0x20
+#define NAND_STATUS_RDY 0x40
+#define NAND_STATUS_WP 0x80
+
+#define NAND_LP_OOB_COLUMN_START 0x800
+#define NAND_LP_OOBSZ 0x40
+#define NAND_SP_OOB_COLUMN_START 0x200
+#define NAND_SP_OOBSZ 0x10
+
+#define PAGE_PARAM_LENGTH 0x100
+#define PAGE_PARAMETER_DEF 0x0
+#define PAGE_PARAMETER_RED_1 0x100
+#define PAGE_PARAMETER_RED_2 0x200
+
+#define ONFI_SIG_ADDR 0x20
+
+#define NAND_MAX_CHIPS 0x4
+#define NAND_MAX_OOBSZ 512
+#define NAND_MAX_PAGESZ 16384
+
+#define NAND_SMALL_PAGE_SIZE 0x200
+
+#define NAND_16_BIT 0x00000001
+
+#define NAND_ECC_NONE 0x0
+#define NAND_ECC_SOFT 0x1
+#define NAND_ECC_FULLHW 0x2
+#define NAND_ECC_PARTHW 0x4
+#define NAND_ECC_MODE_MASK 0x7
+
+#define ECC_OK 0
+#define ECC_CORRECTABLE 1
+#define ECC_ERROR_ECC (-1)
+#define ECC_UNCORRECTABLE (-2)
+
+#define NAND_MAN_SAMSUNG 0xec
+#define NAND_MAN_HYNIX 0xad
+#define NAND_MAN_STMICRO 0x20
+
+struct nand_id {
+ uint8_t man_id;
+ uint8_t dev_id;
+};
+
+struct nand_params {
+ struct nand_id id;
+ char *name;
+ uint32_t chip_size;
+ uint32_t page_size;
+ uint32_t oob_size;
+ uint32_t pages_per_block;
+ uint32_t flags;
+};
+
+/* nand debug levels */
+#define NDBG_NAND 0x01
+#define NDBG_CDEV 0x02
+#define NDBG_GEN 0x04
+#define NDBG_GEOM 0x08
+#define NDBG_BUS 0x10
+#define NDBG_SIM 0x20
+#define NDBG_CTRL 0x40
+#define NDBG_DRV 0x80
+#define NDBG_ECC 0x100
+
+/* nand_debug_function */
+void nand_debug(int level, const char *fmt, ...);
+extern int nand_debug_flag;
+
+/* ONFI features bit*/
+#define ONFI_FEAT_16BIT 0x01
+#define ONFI_FEAT_MULT_LUN 0x02
+#define ONFI_FEAT_INTLV_OPS 0x04
+#define ONFI_FEAT_CPBK_RESTRICT 0x08
+#define ONFI_FEAT_SRC_SYNCH 0x10
+
+/* ONFI optional commands bits */
+#define ONFI_OPTCOM_PROG_CACHE 0x01
+#define ONFI_OPTCOM_READ_CACHE 0x02
+#define ONFI_OPTCOM_GETSET_FEAT 0x04
+#define ONFI_OPTCOM_STATUS_ENH 0x08
+#define ONFI_OPTCOM_COPYBACK 0x10
+#define ONFI_OPTCOM_UNIQUE_ID 0x20
+
+
+/* Layout of parameter page is defined in ONFI */
+struct onfi_params {
+ char signature[4];
+ uint16_t rev;
+ uint16_t features;
+ uint16_t optional_commands;
+ uint8_t res1[22];
+ char manufacturer_name[12];
+ char device_model[20];
+ uint8_t manufacturer_id;
+ uint16_t date;
+ uint8_t res2[13];
+ uint32_t bytes_per_page;
+ uint16_t spare_bytes_per_page;
+ uint32_t bytes_per_partial_page;
+ uint16_t spare_bytes_per_partial_page;
+ uint32_t pages_per_block;
+ uint32_t blocks_per_lun;
+ uint8_t luns;
+ uint8_t address_cycles;
+ uint8_t bits_per_cell;
+ uint16_t max_bad_block_per_lun;
+ uint16_t block_endurance;
+ uint8_t guaranteed_valid_blocks;
+ uint16_t valid_block_endurance;
+ uint8_t programs_per_page;
+ uint8_t partial_prog_attr;
+ uint8_t bits_of_ecc;
+ uint8_t interleaved_addr_bits;
+ uint8_t interleaved_oper_attr;
+ uint8_t res3[13];
+ uint8_t pin_capacitance;
+ uint16_t asynch_timing_mode_support;
+ uint16_t asynch_prog_cache_timing_mode_support;
+ uint16_t t_prog; /* us, max page program time */
+ uint16_t t_bers; /* us, max block erase time */
+ uint16_t t_r; /* us, max page read time */
+ uint16_t t_ccs; /* ns, min change column setup time */
+ uint16_t source_synch_timing_mode_support;
+ uint8_t source_synch_feat;
+ uint16_t clk_input_capacitance;
+ uint16_t io_capacitance;
+ uint16_t input_capacitance;
+ uint8_t input_capacitance_max;
+ uint8_t driver_strength_support;
+ uint8_t res4[12];
+ uint16_t vendor_rev;
+ uint8_t vendor_spec[8];
+ uint16_t crc;
+};
+
+struct nand_ecc_data {
+ int eccsize; /* Number of data bytes per ECC step */
+ int eccmode;
+ int eccbytes; /* Number of ECC bytes per step */
+
+ uint16_t *eccpositions; /* Positions of ecc bytes */
+ uint8_t ecccalculated[NAND_MAX_OOBSZ];
+ uint8_t eccread[NAND_MAX_OOBSZ];
+};
+
+struct ecc_stat {
+ uint32_t ecc_succeded;
+ uint32_t ecc_corrected;
+ uint32_t ecc_failed;
+};
+
+struct page_stat {
+ struct ecc_stat ecc_stat;
+ uint32_t page_read;
+ uint32_t page_raw_read;
+ uint32_t page_written;
+ uint32_t page_raw_written;
+};
+
+struct block_stat {
+ uint32_t block_erased;
+};
+
+struct chip_geom {
+ uint32_t chip_size;
+ uint32_t block_size;
+ uint32_t page_size;
+ uint32_t oob_size;
+
+ uint32_t luns;
+ uint32_t blks_per_lun;
+ uint32_t blks_per_chip;
+ uint32_t pgs_per_blk;
+
+ uint32_t pg_mask;
+ uint32_t blk_mask;
+ uint32_t lun_mask;
+ uint8_t blk_shift;
+ uint8_t lun_shift;
+};
+
+struct nand_chip {
+ device_t dev;
+ struct nand_id id;
+ struct chip_geom chip_geom;
+
+ uint16_t t_prog; /* us, max page program time */
+ uint16_t t_bers; /* us, max block erase time */
+ uint16_t t_r; /* us, max page read time */
+ uint16_t t_ccs; /* ns, min change column setup time */
+ uint8_t num;
+ uint8_t flags;
+
+ struct page_stat *pg_stat;
+ struct block_stat *blk_stat;
+ struct nand_softc *nand;
+ struct nand_bbt *bbt;
+ struct nand_ops *ops;
+ struct cdev *cdev;
+
+ struct disk *ndisk;
+ struct disk *rdisk;
+ struct bio_queue_head bioq; /* bio queue */
+ struct mtx qlock; /* bioq lock */
+ struct taskqueue *tq; /* private task queue for i/o request */
+ struct task iotask; /* i/o processing */
+
+};
+
+struct nand_softc {
+ uint8_t flags;
+
+ char *chip_cdev_name;
+ struct nand_ecc_data ecc;
+};
+
+/* NAND ops */
+int nand_erase_blocks(struct nand_chip *chip, off_t offset, size_t len);
+int nand_prog_pages(struct nand_chip *chip, uint32_t offset, uint8_t *buf,
+ uint32_t len);
+int nand_read_pages(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len);
+int nand_read_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len);
+int nand_prog_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len);
+int nand_read_oob(struct nand_chip *chip, uint32_t page, void *buf,
+ uint32_t len);
+int nand_prog_oob(struct nand_chip *chip, uint32_t page, void *buf,
+ uint32_t len);
+
+int nand_select_cs(device_t dev, uint8_t cs);
+
+int nand_read_parameter(struct nand_softc *nand, struct onfi_params *param);
+int nand_synch_reset(struct nand_softc *nand);
+int nand_chng_read_col(device_t dev, uint32_t col, void *buf, size_t len);
+int nand_chng_write_col(device_t dev, uint32_t col, void *buf, size_t len);
+int nand_get_feature(device_t dev, uint8_t feat, void* buf);
+int nand_set_feature(device_t dev, uint8_t feat, void* buf);
+
+
+int nand_erase_block_intlv(device_t dev, uint32_t block);
+int nand_copyback_read(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len);
+int nand_copyback_prog(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len);
+int nand_copyback_prog_intlv(device_t dev, uint32_t page);
+int nand_prog_cache(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len, uint8_t end);
+int nand_prog_intlv(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len);
+int nand_read_cache(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len, uint8_t end);
+
+int nand_write_ecc(struct nand_softc *nand, uint32_t page, uint8_t *data);
+int nand_read_ecc(struct nand_softc *nand, uint32_t page, uint8_t *data);
+
+int nand_softecc_get(device_t dev, uint8_t *buf, int pagesize, uint8_t *ecc);
+int nand_softecc_correct(device_t dev, uint8_t *buf, int pagesize,
+ uint8_t *readecc, uint8_t *calcecc);
+
+/* Chip initialization */
+void nand_init(struct nand_softc *nand, device_t dev, int ecc_mode,
+ int ecc_bytes, int ecc_size, uint16_t* eccposition, char* cdev_name);
+void nand_detach(struct nand_softc *nand);
+struct nand_params *nand_get_params(struct nand_id *id);
+
+void nand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params);
+void nand_set_params(struct nand_chip *chip, struct nand_params *params);
+int nand_init_stat(struct nand_chip *chip);
+void nand_destroy_stat(struct nand_chip *chip);
+
+/* BBT */
+int nand_init_bbt(struct nand_chip *chip);
+void nand_destroy_bbt(struct nand_chip *chip);
+int nand_update_bbt(struct nand_chip *chip);
+int nand_mark_bad_block(struct nand_chip* chip, uint32_t block_num);
+int nand_check_bad_block(struct nand_chip* chip, uint32_t block_num);
+
+/* cdev creation/removal */
+int nand_make_dev(struct nand_chip* chip);
+void nand_destroy_dev(struct nand_chip *chip);
+
+int create_geom_disk(struct nand_chip* chip);
+int create_geom_raw_disk(struct nand_chip *chip);
+void destroy_geom_disk(struct nand_chip *chip);
+void destroy_geom_raw_disk(struct nand_chip *chip);
+
+int init_chip_geom(struct chip_geom* cg, uint32_t luns, uint32_t blks_per_lun,
+ uint32_t pgs_per_blk, uint32_t pg_size, uint32_t oob_size);
+int nand_row_to_blkpg(struct chip_geom *cg, uint32_t row, uint32_t *lun,
+ uint32_t *blk, uint32_t *pg);
+int page_to_row(struct chip_geom *cg, uint32_t page, uint32_t *row);
+int nand_check_page_boundary(struct nand_chip *chip, uint32_t page);
+void nand_get_chip_param(struct nand_chip *chip, struct chip_param_io *param);
+
+#endif /* _DEV_NAND_H_ */
diff --git a/sys/dev/nand/nand_bbt.c b/sys/dev/nand/nand_bbt.c
new file mode 100644
index 000000000000..d3f163af3b17
--- /dev/null
+++ b/sys/dev/nand/nand_bbt.c
@@ -0,0 +1,273 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+
+#include <dev/nand/nand.h>
+
+#include "nand_if.h"
+
+#define BBT_PRIMARY_PATTERN 0x01020304
+#define BBT_SECONDARY_PATTERN 0x05060708
+
+enum bbt_place {
+ BBT_NONE,
+ BBT_PRIMARY,
+ BBT_SECONDARY
+};
+
+struct nand_bbt {
+ struct nand_chip *chip;
+ uint32_t primary_map;
+ uint32_t secondary_map;
+ enum bbt_place active;
+ struct bbt_header *hdr;
+ uint32_t tab_len;
+ uint32_t *table;
+};
+
+struct bbt_header {
+ uint32_t pattern;
+ int32_t seq_nr;
+};
+
+static int nand_bbt_save(struct nand_bbt *);
+static int nand_bbt_load_hdr(struct nand_bbt *, struct bbt_header *, int8_t);
+static int nand_bbt_load_table(struct nand_bbt *);
+static int nand_bbt_prescan(struct nand_bbt *);
+
+int
+nand_init_bbt(struct nand_chip *chip)
+{
+ struct chip_geom *cg;
+ struct nand_bbt *bbt;
+ int err;
+
+ cg = &chip->chip_geom;
+
+ bbt = malloc(sizeof(struct nand_bbt), M_NAND, M_ZERO | M_WAITOK);
+ if (!bbt) {
+ device_printf(chip->dev,
+ "Cannot allocate memory for bad block struct");
+ return (ENOMEM);
+ }
+
+ bbt->chip = chip;
+ bbt->active = BBT_NONE;
+ bbt->primary_map = cg->chip_size - cg->block_size;
+ bbt->secondary_map = cg->chip_size - 2 * cg->block_size;
+ bbt->tab_len = cg->blks_per_chip * sizeof(uint32_t);
+ bbt->hdr = malloc(sizeof(struct bbt_header) + bbt->tab_len, M_NAND,
+ M_WAITOK);
+ if (!bbt->hdr) {
+ device_printf(chip->dev, "Cannot allocate %d bytes for BB "
+ "Table", bbt->tab_len);
+ free(bbt, M_NAND);
+ return (ENOMEM);
+ }
+ bbt->hdr->seq_nr = 0;
+ bbt->table = (uint32_t *)((uint8_t *)bbt->hdr +
+ sizeof(struct bbt_header));
+
+ err = nand_bbt_load_table(bbt);
+ if (err) {
+ free(bbt->table, M_NAND);
+ free(bbt, M_NAND);
+ return (err);
+ }
+
+ chip->bbt = bbt;
+ if (bbt->active == BBT_NONE) {
+ bbt->active = BBT_PRIMARY;
+ memset(bbt->table, 0xff, bbt->tab_len);
+ nand_bbt_prescan(bbt);
+ nand_bbt_save(bbt);
+ } else
+ device_printf(chip->dev, "Found BBT table for chip\n");
+
+ return (0);
+}
+
+void
+nand_destroy_bbt(struct nand_chip *chip)
+{
+
+ if (chip->bbt) {
+ nand_bbt_save(chip->bbt);
+
+ free(chip->bbt->hdr, M_NAND);
+ free(chip->bbt, M_NAND);
+ chip->bbt = NULL;
+ }
+}
+
+int
+nand_update_bbt(struct nand_chip *chip)
+{
+
+ nand_bbt_save(chip->bbt);
+
+ return (0);
+}
+
+static int
+nand_bbt_save(struct nand_bbt *bbt)
+{
+ enum bbt_place next;
+ uint32_t addr;
+ int32_t err;
+
+ if (bbt->active == BBT_PRIMARY) {
+ addr = bbt->secondary_map;
+ bbt->hdr->pattern = BBT_SECONDARY_PATTERN;
+ next = BBT_SECONDARY;
+ } else {
+ addr = bbt->primary_map;
+ bbt->hdr->pattern = BBT_PRIMARY_PATTERN;
+ next = BBT_PRIMARY;
+ }
+
+ err = nand_erase_blocks(bbt->chip, addr,
+ bbt->chip->chip_geom.block_size);
+ if (err)
+ return (err);
+
+ bbt->hdr->seq_nr++;
+
+ err = nand_prog_pages_raw(bbt->chip, addr, bbt->hdr,
+ bbt->tab_len + sizeof(struct bbt_header));
+ if (err)
+ return (err);
+
+ bbt->active = next;
+ return (0);
+}
+
+static int
+nand_bbt_load_hdr(struct nand_bbt *bbt, struct bbt_header *hdr, int8_t primary)
+{
+ uint32_t addr;
+
+ if (primary)
+ addr = bbt->primary_map;
+ else
+ addr = bbt->secondary_map;
+
+ return (nand_read_pages_raw(bbt->chip, addr, hdr,
+ sizeof(struct bbt_header)));
+}
+
+static int
+nand_bbt_load_table(struct nand_bbt *bbt)
+{
+ struct bbt_header hdr1, hdr2;
+ uint32_t address = 0;
+ int err = 0;
+
+ bzero(&hdr1, sizeof(hdr1));
+ bzero(&hdr2, sizeof(hdr2));
+
+ nand_bbt_load_hdr(bbt, &hdr1, 1);
+ if (hdr1.pattern == BBT_PRIMARY_PATTERN) {
+ bbt->active = BBT_PRIMARY;
+ address = bbt->primary_map;
+ } else
+ bzero(&hdr1, sizeof(hdr1));
+
+
+ nand_bbt_load_hdr(bbt, &hdr2, 0);
+ if ((hdr2.pattern == BBT_SECONDARY_PATTERN) &&
+ (hdr2.seq_nr > hdr1.seq_nr)) {
+ bbt->active = BBT_SECONDARY;
+ address = bbt->secondary_map;
+ } else
+ bzero(&hdr2, sizeof(hdr2));
+
+ if (bbt->active != BBT_NONE)
+ err = nand_read_pages_raw(bbt->chip, address, bbt->hdr,
+ bbt->tab_len + sizeof(struct bbt_header));
+
+ return (err);
+}
+
+static int
+nand_bbt_prescan(struct nand_bbt *bbt)
+{
+ int32_t i;
+ uint8_t bad;
+ bool printed_hash = 0;
+
+ device_printf(bbt->chip->dev, "No BBT found. Prescan chip...\n");
+ for (i = 0; i < bbt->chip->chip_geom.blks_per_chip; i++) {
+ if (NAND_IS_BLK_BAD(bbt->chip->dev, i, &bad))
+ return (ENXIO);
+
+ if (bad) {
+ device_printf(bbt->chip->dev, "Bad block(%d)\n", i);
+ bbt->table[i] = 0x0FFFFFFF;
+ }
+ if (!(i % 100)) {
+ printf("#");
+ printed_hash = 1;
+ }
+ }
+
+ if (printed_hash)
+ printf("\n");
+
+ return (0);
+}
+
+int
+nand_check_bad_block(struct nand_chip *chip, uint32_t block_number)
+{
+
+ if (!chip || !chip->bbt)
+ return (0);
+
+ if ((chip->bbt->table[block_number] & 0xF0000000) == 0)
+ return (1);
+
+ return (0);
+}
+
+int
+nand_mark_bad_block(struct nand_chip *chip, uint32_t block_number)
+{
+
+ chip->bbt->table[block_number] = 0x0FFFFFFF;
+
+ return (0);
+}
diff --git a/sys/dev/nand/nand_cdev.c b/sys/dev/nand/nand_cdev.c
new file mode 100644
index 000000000000..ac27ff361537
--- /dev/null
+++ b/sys/dev/nand/nand_cdev.c
@@ -0,0 +1,413 @@
+/*-
+ * 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/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/uio.h>
+#include <sys/bio.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include <dev/nand/nand_dev.h>
+#include "nand_if.h"
+#include "nandbus_if.h"
+
+static int nand_page_stat(struct nand_chip *, struct page_stat_io *);
+static int nand_block_stat(struct nand_chip *, struct block_stat_io *);
+
+static d_ioctl_t nand_ioctl;
+static d_open_t nand_open;
+static d_strategy_t nand_strategy;
+
+static struct cdevsw nand_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name = "nand",
+ .d_open = nand_open,
+ .d_read = physread,
+ .d_write = physwrite,
+ .d_ioctl = nand_ioctl,
+ .d_strategy = nand_strategy,
+};
+
+static int
+offset_to_page(struct chip_geom *cg, uint32_t offset)
+{
+
+ return (offset / cg->page_size);
+}
+
+static int
+offset_to_page_off(struct chip_geom *cg, uint32_t offset)
+{
+
+ return (offset % cg->page_size);
+}
+
+int
+nand_make_dev(struct nand_chip *chip)
+{
+ struct nandbus_ivar *ivar;
+ device_t parent, nandbus;
+ int parent_unit, unit;
+ char *name;
+
+ ivar = device_get_ivars(chip->dev);
+ nandbus = device_get_parent(chip->dev);
+
+ if (ivar->chip_cdev_name) {
+ name = ivar->chip_cdev_name;
+
+ /*
+ * If we got distinct name for chip device we can enumarete it
+ * based on contoller number.
+ */
+ parent = device_get_parent(nandbus);
+ } else {
+ name = "nand";
+ parent = nandbus;
+ }
+
+ parent_unit = device_get_unit(parent);
+ unit = parent_unit * 4 + chip->num;
+ chip->cdev = make_dev(&nand_cdevsw, unit, UID_ROOT, GID_WHEEL,
+ 0666, "%s%d.%d", name, parent_unit, chip->num);
+
+ if (chip->cdev == NULL)
+ return (ENXIO);
+
+ if (bootverbose)
+ device_printf(chip->dev, "Created cdev %s%d.%d for chip "
+ "[0x%0x, 0x%0x]\n", name, parent_unit, chip->num,
+ ivar->man_id, ivar->dev_id);
+
+ chip->cdev->si_drv1 = chip;
+
+ return (0);
+}
+
+void
+nand_destroy_dev(struct nand_chip *chip)
+{
+
+ if (chip->cdev)
+ destroy_dev(chip->cdev);
+}
+
+static int
+nand_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+
+ return (0);
+}
+
+static int
+nand_read(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
+{
+ struct chip_geom *cg;
+ device_t nandbus;
+ int start_page, count, off, err = 0;
+ uint8_t *ptr, *tmp;
+
+ nand_debug(NDBG_CDEV, "Read from chip%d [%p] at %d\n", chip->num,
+ chip, offset);
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ cg = &chip->chip_geom;
+ start_page = offset_to_page(cg, offset);
+ off = offset_to_page_off(cg, offset);
+ count = (len > cg->page_size - off) ? cg->page_size - off : len;
+
+ ptr = (uint8_t *)buf;
+ while (len > 0) {
+ if (len < cg->page_size) {
+ tmp = malloc(cg->page_size, M_NAND, M_WAITOK);
+ if (!tmp) {
+ err = ENOMEM;
+ break;
+ }
+ err = NAND_READ_PAGE(chip->dev, start_page,
+ tmp, cg->page_size, 0);
+ if (err) {
+ free(tmp, M_NAND);
+ break;
+ }
+ bcopy(tmp + off, ptr, count);
+ free(tmp, M_NAND);
+ } else {
+ err = NAND_READ_PAGE(chip->dev, start_page,
+ ptr, cg->page_size, 0);
+ if (err)
+ break;
+ }
+
+ len -= count;
+ start_page++;
+ ptr += count;
+ count = (len > cg->page_size) ? cg->page_size : len;
+ off = 0;
+ }
+
+ NANDBUS_UNLOCK(nandbus);
+ return (err);
+}
+
+static int
+nand_write(struct nand_chip *chip, uint32_t offset, void* buf, uint32_t len)
+{
+ struct chip_geom *cg;
+ device_t nandbus;
+ int off, start_page, err = 0;
+ uint8_t *ptr;
+
+ nand_debug(NDBG_CDEV, "Write to chip %d [%p] at %d\n", chip->num,
+ chip, offset);
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ cg = &chip->chip_geom;
+ start_page = offset_to_page(cg, offset);
+ off = offset_to_page_off(cg, offset);
+
+ if (off != 0 || (len % cg->page_size) != 0) {
+ printf("Not aligned write start [0x%08x] size [0x%08x]\n",
+ off, len);
+ NANDBUS_UNLOCK(nandbus);
+ return (EINVAL);
+ }
+
+ ptr = (uint8_t *)buf;
+ while (len > 0) {
+ err = NAND_PROGRAM_PAGE(chip->dev, start_page, ptr,
+ cg->page_size, 0);
+ if (err)
+ break;
+
+ len -= cg->page_size;
+ start_page++;
+ ptr += cg->page_size;
+ }
+
+ NANDBUS_UNLOCK(nandbus);
+ return (err);
+}
+
+static void
+nand_strategy(struct bio *bp)
+{
+ struct nand_chip *chip;
+ struct cdev *dev;
+ int err = 0;
+
+ dev = bp->bio_dev;
+ chip = dev->si_drv1;
+
+ nand_debug(NDBG_CDEV, "Strategy %s on chip %d [%p]\n",
+ (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" : "WRITE",
+ chip->num, chip);
+
+ if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
+ err = nand_read(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ } else {
+ err = nand_write(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ }
+
+ if (err == 0)
+ bp->bio_resid = 0;
+ else {
+ bp->bio_error = EIO;
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_resid = bp->bio_bcount;
+ }
+
+ biodone(bp);
+}
+
+static int
+nand_oob_access(struct nand_chip *chip, uint32_t page, uint32_t offset,
+ uint32_t len, uint8_t *data, uint8_t write)
+{
+ struct chip_geom *cg;
+ uint8_t *buf = NULL;
+ int ret = 0;
+
+ cg = &chip->chip_geom;
+
+ buf = malloc(cg->oob_size, M_NAND, M_WAITOK);
+ if (!buf)
+ return (ENOMEM);
+
+ memset(buf, 0xff, cg->oob_size);
+
+ if (!write) {
+ ret = nand_read_oob(chip, page, buf, cg->oob_size);
+ copyout(buf, data, len);
+ } else {
+ copyin(data, buf, len);
+ ret = nand_prog_oob(chip, page, buf, cg->oob_size);
+ }
+
+ free(buf, M_NAND);
+
+ return (ret);
+}
+
+static int
+nand_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
+ struct thread *td)
+{
+ struct nand_chip *chip;
+ struct nand_oob_rw *oob_rw = NULL;
+ struct nand_raw_rw *raw_rw = NULL;
+ device_t nandbus;
+ uint8_t *buf = NULL;
+ int ret = 0;
+ uint8_t status;
+
+ chip = (struct nand_chip *)dev->si_drv1;
+ nandbus = device_get_parent(chip->dev);
+
+ if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) {
+ raw_rw = (struct nand_raw_rw *)data;
+ buf = malloc(raw_rw->len, M_NAND, M_WAITOK);
+ }
+ switch(cmd) {
+ case NAND_IO_ERASE:
+ ret = nand_erase_blocks(chip, ((off_t *)data)[0],
+ ((off_t *)data)[1]);
+ break;
+
+ case NAND_IO_OOB_READ:
+ oob_rw = (struct nand_oob_rw *)data;
+ ret = nand_oob_access(chip, oob_rw->page, 0,
+ oob_rw->len, oob_rw->data, 0);
+ break;
+
+ case NAND_IO_OOB_PROG:
+ oob_rw = (struct nand_oob_rw *)data;
+ ret = nand_oob_access(chip, oob_rw->page, 0,
+ oob_rw->len, oob_rw->data, 1);
+ break;
+
+ case NAND_IO_GET_STATUS:
+ NANDBUS_LOCK(nandbus);
+ ret = NANDBUS_GET_STATUS(nandbus, &status);
+ if (ret == 0)
+ *(uint8_t *)data = status;
+ NANDBUS_UNLOCK(nandbus);
+ break;
+
+ case NAND_IO_RAW_PROG:
+ ret = copyin(raw_rw->data, buf, raw_rw->len);
+ if (ret)
+ break;
+ ret = nand_prog_pages_raw(chip, raw_rw->off, buf,
+ raw_rw->len);
+ break;
+
+ case NAND_IO_RAW_READ:
+ ret = nand_read_pages_raw(chip, raw_rw->off, buf,
+ raw_rw->len);
+ if (ret)
+ break;
+ ret = copyout(buf, raw_rw->data, raw_rw->len);
+ break;
+
+ case NAND_IO_PAGE_STAT:
+ ret = nand_page_stat(chip, (struct page_stat_io *)data);
+ break;
+
+ case NAND_IO_BLOCK_STAT:
+ ret = nand_block_stat(chip, (struct block_stat_io *)data);
+ break;
+
+ case NAND_IO_GET_CHIP_PARAM:
+ nand_get_chip_param(chip, (struct chip_param_io *)data);
+ break;
+
+ default:
+ printf("Unknown nand_ioctl request \n");
+ ret = EIO;
+ }
+
+ if (buf)
+ free(buf, M_NAND);
+
+ return (ret);
+}
+
+static int
+nand_page_stat(struct nand_chip *chip, struct page_stat_io *page_stat)
+{
+ struct chip_geom *cg;
+ struct page_stat *stat;
+ int num_pages;
+
+ cg = &chip->chip_geom;
+ num_pages = cg->pgs_per_blk * cg->blks_per_lun * cg->luns;
+ if (page_stat->page_num >= num_pages)
+ return (EINVAL);
+
+ stat = &chip->pg_stat[page_stat->page_num];
+ page_stat->page_read = stat->page_read;
+ page_stat->page_written = stat->page_written;
+ page_stat->page_raw_read = stat->page_raw_read;
+ page_stat->page_raw_written = stat->page_raw_written;
+ page_stat->ecc_succeded = stat->ecc_stat.ecc_succeded;
+ page_stat->ecc_corrected = stat->ecc_stat.ecc_corrected;
+ page_stat->ecc_failed = stat->ecc_stat.ecc_failed;
+
+ return (0);
+}
+
+static int
+nand_block_stat(struct nand_chip *chip, struct block_stat_io *block_stat)
+{
+ struct chip_geom *cg;
+ uint32_t block_num = block_stat->block_num;
+
+ cg = &chip->chip_geom;
+ if (block_num >= cg->blks_per_lun * cg->luns)
+ return (EINVAL);
+
+ block_stat->block_erased = chip->blk_stat[block_num].block_erased;
+
+ return (0);
+}
diff --git a/sys/dev/nand/nand_dev.h b/sys/dev/nand/nand_dev.h
new file mode 100644
index 000000000000..bc7d6c49c063
--- /dev/null
+++ b/sys/dev/nand/nand_dev.h
@@ -0,0 +1,90 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_NAND_CDEV_H_
+#define _DEV_NAND_CDEV_H_
+
+#include <sys/ioccom.h>
+#include <sys/param.h>
+
+struct nand_raw_rw {
+ off_t off;
+ off_t len;
+ uint8_t *data;
+};
+
+struct nand_oob_rw {
+ uint32_t page;
+ off_t len;
+ uint8_t *data;
+};
+
+#define NAND_IOCTL_GROUP 'N'
+#define NAND_IO_ERASE _IOWR(NAND_IOCTL_GROUP, 0x0, off_t[2])
+
+#define NAND_IO_OOB_READ _IOWR(NAND_IOCTL_GROUP, 0x1, struct nand_oob_rw)
+
+#define NAND_IO_OOB_PROG _IOWR(NAND_IOCTL_GROUP, 0x2, struct nand_oob_rw)
+
+#define NAND_IO_RAW_READ _IOWR(NAND_IOCTL_GROUP, 0x3, struct nand_raw_rw)
+
+#define NAND_IO_RAW_PROG _IOWR(NAND_IOCTL_GROUP, 0x4, struct nand_raw_rw)
+
+#define NAND_IO_GET_STATUS _IOWR(NAND_IOCTL_GROUP, 0x5, uint8_t)
+
+struct page_stat_io {
+ uint32_t page_num;
+ uint32_t page_read;
+ uint32_t page_written;
+ uint32_t page_raw_read;
+ uint32_t page_raw_written;
+ uint32_t ecc_succeded;
+ uint32_t ecc_corrected;
+ uint32_t ecc_failed;
+};
+#define NAND_IO_PAGE_STAT _IOWR(NAND_IOCTL_GROUP, 0x6, \
+ struct page_stat_io)
+
+struct block_stat_io {
+ uint32_t block_num;
+ uint32_t block_erased;
+};
+#define NAND_IO_BLOCK_STAT _IOWR(NAND_IOCTL_GROUP, 0x7, \
+ struct block_stat_io)
+
+struct chip_param_io {
+ uint32_t page_size;
+ uint32_t oob_size;
+
+ uint32_t blocks;
+ uint32_t pages_per_block;
+};
+#define NAND_IO_GET_CHIP_PARAM _IOWR(NAND_IOCTL_GROUP, 0x8, \
+ struct chip_param_io)
+
+#endif /* _DEV_NAND_CDEV_H_ */
diff --git a/sys/dev/nand/nand_ecc_pos.h b/sys/dev/nand/nand_ecc_pos.h
new file mode 100644
index 000000000000..f40415cc4f30
--- /dev/null
+++ b/sys/dev/nand/nand_ecc_pos.h
@@ -0,0 +1,56 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_NAND_ECC_POS_H_
+#define _DEV_NAND_ECC_POS_H_
+
+static uint16_t default_software_ecc_positions_16[] = {2, 0, 1, 7, 4, 6};
+
+static uint16_t default_software_ecc_positions_64[] = {
+
+ 42, 40, 41, 45, 43, 44, 48, 46,
+ 47, 51, 49, 50, 54, 52, 53, 57,
+ 55, 56, 60, 58, 59, 63, 61, 62
+};
+
+static uint16_t default_software_ecc_positions_128[] = {
+ 8, 9, 10, 11, 12, 13,
+ 18, 19, 20, 21, 22, 23,
+ 28, 29, 30, 31, 32, 33,
+ 38, 39, 40, 41, 42, 43,
+ 48, 49, 50, 51, 52, 53,
+ 58, 59, 60, 61, 62, 63,
+ 68, 69, 70, 71, 72, 73,
+ 78, 79, 80, 81, 82, 83,
+ 88, 89, 90, 91, 92, 93,
+ 98, 99, 100, 101, 102, 103,
+ 108, 109, 110, 111, 112, 113,
+ 118, 119, 120, 121, 122, 123,
+};
+#endif /* _DEV_NAND_ECC_POS_H_ */
+
diff --git a/sys/dev/nand/nand_generic.c b/sys/dev/nand/nand_generic.c
new file mode 100644
index 000000000000..85e81beb6b69
--- /dev/null
+++ b/sys/dev/nand/nand_generic.c
@@ -0,0 +1,1320 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/time.h>
+#include <sys/malloc.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#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_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_params *onfi_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_params = malloc(sizeof(struct onfi_params),
+ M_NAND, M_WAITOK | M_ZERO);
+ if (onfi_params == NULL)
+ return (ENXIO);
+
+ if (onfi_read_parameter(chip, onfi_params)) {
+ nand_debug(NDBG_GEN,"Could not read parameter page!\n");
+ free(onfi_params, M_NAND);
+ return (ENXIO);
+ }
+
+ nand_onfi_set_params(chip, onfi_params);
+ /* Set proper column and row cycles */
+ ivar->cols = (onfi_params->address_cycles >> 4) & 0xf;
+ ivar->rows = onfi_params->address_cycles & 0xf;
+ free(onfi_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 int
+onfi_read_parameter(struct nand_chip *chip, struct onfi_params *params)
+{
+ device_t nandbus;
+
+ 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);
+
+ NANDBUS_READ_BUFFER(nandbus, params, sizeof(struct onfi_params));
+
+ /* TODO */
+ /* Check for signature */
+ /* Check CRC */
+ /* Use redundant page if necessary */
+
+ 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
diff --git a/sys/dev/nand/nand_geom.c b/sys/dev/nand/nand_geom.c
new file mode 100644
index 000000000000..a8bdba28d290
--- /dev/null
+++ b/sys/dev/nand/nand_geom.c
@@ -0,0 +1,414 @@
+/*-
+ * 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/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/uio.h>
+#include <sys/bio.h>
+#include <geom/geom.h>
+#include <geom/geom_disk.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include <dev/nand/nand_dev.h>
+#include "nand_if.h"
+#include "nandbus_if.h"
+
+#define BIO_NAND_STD ((void *)1)
+#define BIO_NAND_RAW ((void *)2)
+
+static disk_ioctl_t nand_ioctl;
+static disk_getattr_t nand_getattr;
+static disk_strategy_t nand_strategy;
+static disk_strategy_t nand_strategy_raw;
+
+static int
+nand_read(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
+{
+
+ nand_debug(NDBG_GEOM, "Read from chip %d [%p] at %d", chip->num, chip,
+ offset);
+
+ return (nand_read_pages(chip, offset, buf, len));
+}
+
+static int
+nand_write(struct nand_chip *chip, uint32_t offset, void* buf, uint32_t len)
+{
+
+ nand_debug(NDBG_GEOM, "Write to chip %d [%p] at %d", chip->num, chip,
+ offset);
+
+ return (nand_prog_pages(chip, offset, buf, len));
+}
+
+static int
+nand_read_raw(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
+{
+ nand_debug(NDBG_GEOM, "Raw read from chip %d [%p] at %d", chip->num,
+ chip, offset);
+
+ return (nand_read_pages_raw(chip, offset, buf, len));
+}
+
+static int
+nand_write_raw(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
+{
+
+ nand_debug(NDBG_GEOM, "Raw write to chip %d [%p] at %d", chip->num,
+ chip, offset);
+
+ return (nand_prog_pages_raw(chip, offset, buf, len));
+}
+
+static void
+nand_strategy(struct bio *bp)
+{
+ struct nand_chip *chip;
+
+ chip = (struct nand_chip *)bp->bio_disk->d_drv1;
+
+ bp->bio_driver1 = BIO_NAND_STD;
+
+ nand_debug(NDBG_GEOM, "Strategy %s on chip %d [%p]",
+ (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" :
+ ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE ? "WRITE" :
+ ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE ? "DELETE" : "UNKNOWN")),
+ chip->num, chip);
+
+ mtx_lock(&chip->qlock);
+ bioq_insert_tail(&chip->bioq, bp);
+ mtx_unlock(&chip->qlock);
+ taskqueue_enqueue(chip->tq, &chip->iotask);
+}
+
+static void
+nand_strategy_raw(struct bio *bp)
+{
+ struct nand_chip *chip;
+
+ chip = (struct nand_chip *)bp->bio_disk->d_drv1;
+
+ /* Inform taskqueue that it's a raw access */
+ bp->bio_driver1 = BIO_NAND_RAW;
+
+ nand_debug(NDBG_GEOM, "Strategy %s on chip %d [%p]",
+ (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" :
+ ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE ? "WRITE" :
+ ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE ? "DELETE" : "UNKNOWN")),
+ chip->num, chip);
+
+ mtx_lock(&chip->qlock);
+ bioq_insert_tail(&chip->bioq, bp);
+ mtx_unlock(&chip->qlock);
+ taskqueue_enqueue(chip->tq, &chip->iotask);
+}
+
+static int
+nand_oob_access(struct nand_chip *chip, uint32_t page, uint32_t offset,
+ uint32_t len, uint8_t *data, uint8_t write)
+{
+ struct chip_geom *cg;
+ int ret = 0;
+
+ cg = &chip->chip_geom;
+
+ if (!write)
+ ret = nand_read_oob(chip, page, data, cg->oob_size);
+ else
+ ret = nand_prog_oob(chip, page, data, cg->oob_size);
+
+ return (ret);
+}
+
+static int
+nand_getattr(struct bio *bp)
+{
+ struct nand_chip *chip;
+ struct chip_geom *cg;
+ device_t dev;
+
+ if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL)
+ return (ENXIO);
+
+ chip = (struct nand_chip *)bp->bio_disk->d_drv1;
+ cg = &(chip->chip_geom);
+
+ dev = device_get_parent(chip->dev);
+ dev = device_get_parent(dev);
+
+ do {
+ if (g_handleattr_int(bp, "NAND::oobsize", cg->oob_size))
+ break;
+ else if (g_handleattr_int(bp, "NAND::pagesize", cg->page_size))
+ break;
+ else if (g_handleattr_int(bp, "NAND::blocksize",
+ cg->block_size))
+ break;
+ else if (g_handleattr(bp, "NAND::device", &(dev),
+ sizeof(device_t)))
+ break;
+
+ return (ERESTART);
+ } while (0);
+
+ return (EJUSTRETURN);
+}
+
+static int
+nand_ioctl(struct disk *ndisk, u_long cmd, void *data, int fflag,
+ struct thread *td)
+{
+ struct nand_chip *chip;
+ struct nand_oob_rw *oob_rw = NULL;
+ struct nand_raw_rw *raw_rw = NULL;
+ device_t nandbus;
+ uint8_t *buf = NULL;
+ int ret = 0;
+ uint8_t status;
+
+ chip = (struct nand_chip *)ndisk->d_drv1;
+ nandbus = device_get_parent(chip->dev);
+
+ if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) {
+ raw_rw = (struct nand_raw_rw *)data;
+ buf = malloc(raw_rw->len, M_NAND, M_WAITOK);
+ }
+ switch (cmd) {
+ case NAND_IO_ERASE:
+ ret = nand_erase_blocks(chip, ((off_t *)data)[0],
+ ((off_t *)data)[1]);
+ break;
+
+ case NAND_IO_OOB_READ:
+ oob_rw = (struct nand_oob_rw *)data;
+ ret = nand_oob_access(chip, oob_rw->page, 0,
+ oob_rw->len, oob_rw->data, 0);
+ break;
+
+ case NAND_IO_OOB_PROG:
+ oob_rw = (struct nand_oob_rw *)data;
+ ret = nand_oob_access(chip, oob_rw->page, 0,
+ oob_rw->len, oob_rw->data, 1);
+ break;
+
+ case NAND_IO_GET_STATUS:
+ NANDBUS_LOCK(nandbus);
+ ret = NANDBUS_GET_STATUS(nandbus, &status);
+ if (ret == 0)
+ *(uint8_t *)data = status;
+ NANDBUS_UNLOCK(nandbus);
+ break;
+
+ case NAND_IO_RAW_PROG:
+ copyin(raw_rw->data, buf, raw_rw->len);
+ ret = nand_prog_pages_raw(chip, raw_rw->off, buf,
+ raw_rw->len);
+ break;
+
+ case NAND_IO_RAW_READ:
+ ret = nand_read_pages_raw(chip, raw_rw->off, buf,
+ raw_rw->len);
+ copyout(buf, raw_rw->data, raw_rw->len);
+ break;
+
+ case NAND_IO_GET_CHIP_PARAM:
+ nand_get_chip_param(chip, (struct chip_param_io *)data);
+ break;
+
+ default:
+ printf("Unknown nand_ioctl request \n");
+ ret = EIO;
+ }
+
+ if (buf)
+ free(buf, M_NAND);
+
+ return (ret);
+}
+
+static void
+nand_io_proc(void *arg, int pending)
+{
+ struct nand_chip *chip = arg;
+ struct bio *bp;
+ int err = 0;
+
+ for (;;) {
+ mtx_lock(&chip->qlock);
+ bp = bioq_takefirst(&chip->bioq);
+ mtx_unlock(&chip->qlock);
+ if (bp == NULL)
+ break;
+
+ if (bp->bio_driver1 == BIO_NAND_STD) {
+ if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
+ err = nand_read(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ } else if ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE) {
+ err = nand_write(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ }
+ } else if (bp->bio_driver1 == BIO_NAND_RAW) {
+ if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
+ err = nand_read_raw(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ } else if ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE) {
+ err = nand_write_raw(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ }
+ } else
+ panic("Unknown access type in bio->bio_driver1\n");
+
+ if ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE) {
+ nand_debug(NDBG_GEOM, "Delete on chip%d offset %lld "
+ "length %ld\n", chip->num, bp->bio_offset,
+ bp->bio_bcount);
+ err = nand_erase_blocks(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_bcount);
+ }
+
+ if (err == 0 || err == ECC_CORRECTABLE)
+ bp->bio_resid = 0;
+ else {
+ nand_debug(NDBG_GEOM,"nand_[read|write|erase_blocks] "
+ "error: %d\n", err);
+
+ bp->bio_error = EIO;
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_resid = bp->bio_bcount;
+ }
+ biodone(bp);
+ }
+}
+
+int
+create_geom_disk(struct nand_chip *chip)
+{
+ struct disk *ndisk, *rdisk;
+
+ /* Create the disk device */
+ ndisk = disk_alloc();
+ ndisk->d_strategy = nand_strategy;
+ ndisk->d_ioctl = nand_ioctl;
+ ndisk->d_getattr = nand_getattr;
+ ndisk->d_name = "gnand";
+ ndisk->d_drv1 = chip;
+ ndisk->d_maxsize = chip->chip_geom.block_size;
+ ndisk->d_sectorsize = chip->chip_geom.page_size;
+ ndisk->d_mediasize = chip->chip_geom.chip_size;
+ ndisk->d_unit = chip->num +
+ 10 * device_get_unit(device_get_parent(chip->dev));
+
+ /*
+ * When using BBT, make two last blocks of device unavailable
+ * to user (because those are used to store BBT table).
+ */
+ if (chip->bbt != NULL)
+ ndisk->d_mediasize -= (2 * chip->chip_geom.block_size);
+
+ ndisk->d_flags = DISKFLAG_CANDELETE;
+
+ snprintf(ndisk->d_ident, sizeof(ndisk->d_ident),
+ "nand: Man:0x%02x Dev:0x%02x", chip->id.man_id, chip->id.dev_id);
+
+ disk_create(ndisk, DISK_VERSION);
+
+ /* Create the RAW disk device */
+ rdisk = disk_alloc();
+ rdisk->d_strategy = nand_strategy_raw;
+ rdisk->d_ioctl = nand_ioctl;
+ rdisk->d_getattr = nand_getattr;
+ rdisk->d_name = "gnand.raw";
+ rdisk->d_drv1 = chip;
+ rdisk->d_maxsize = chip->chip_geom.block_size;
+ rdisk->d_sectorsize = chip->chip_geom.page_size;
+ rdisk->d_mediasize = chip->chip_geom.chip_size;
+ rdisk->d_unit = chip->num +
+ 10 * device_get_unit(device_get_parent(chip->dev));
+
+ rdisk->d_flags = DISKFLAG_CANDELETE;
+
+ snprintf(rdisk->d_ident, sizeof(rdisk->d_ident),
+ "nand_raw: Man:0x%02x Dev:0x%02x", chip->id.man_id,
+ chip->id.dev_id);
+
+ disk_create(rdisk, DISK_VERSION);
+
+ chip->ndisk = ndisk;
+ chip->rdisk = rdisk;
+
+ mtx_init(&chip->qlock, "NAND I/O lock", NULL, MTX_DEF);
+ bioq_init(&chip->bioq);
+
+ TASK_INIT(&chip->iotask, 0, nand_io_proc, chip);
+ chip->tq = taskqueue_create("nand_taskq", M_WAITOK,
+ taskqueue_thread_enqueue, &chip->tq);
+ taskqueue_start_threads(&chip->tq, 1, PI_DISK, "nand taskq");
+
+ if (bootverbose)
+ device_printf(chip->dev, "Created gnand%d for chip [0x%0x, "
+ "0x%0x]\n", ndisk->d_unit, chip->id.man_id,
+ chip->id.dev_id);
+
+ return (0);
+}
+
+void
+destroy_geom_disk(struct nand_chip *chip)
+{
+ struct bio *bp;
+
+ taskqueue_free(chip->tq);
+ disk_destroy(chip->ndisk);
+ disk_destroy(chip->rdisk);
+
+ mtx_lock(&chip->qlock);
+ for (;;) {
+ bp = bioq_takefirst(&chip->bioq);
+ if (bp == NULL)
+ break;
+ bp->bio_error = EIO;
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_resid = bp->bio_bcount;
+
+ biodone(bp);
+ }
+ mtx_unlock(&chip->qlock);
+
+ mtx_destroy(&chip->qlock);
+}
diff --git a/sys/dev/nand/nand_id.c b/sys/dev/nand/nand_id.c
new file mode 100644
index 000000000000..75c583425567
--- /dev/null
+++ b/sys/dev/nand/nand_id.c
@@ -0,0 +1,60 @@
+/*-
+ * 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/systm.h>
+
+#include <dev/nand/nand.h>
+
+struct nand_params nand_ids[] = {
+ { { NAND_MAN_SAMSUNG, 0x75 }, "Samsung K9F5608U0B",
+ 0x20, 0x200, 0x10, 0x20, 0 },
+ { { NAND_MAN_SAMSUNG, 0xd3 }, "Samsung NAND 1GiB 3,3V 8-bit",
+ 0x400, 0x800, 0x40, 0x40, 0 },
+ { { NAND_MAN_HYNIX, 0x76 }, "Hynix NAND 64MiB 3,3V 8-bit",
+ 0x40, 0x200, 0x10, 0x20, 0 },
+ { { NAND_MAN_HYNIX, 0xdc }, "Hynix NAND 512MiB 3,3V 8-bit",
+ 0x200, 0x800, 0x40, 0x40, 0 },
+ { { NAND_MAN_HYNIX, 0x79 }, "NAND 128MB 3,3V 8-bit",
+ 0x80, 0x200, 0x10, 0x20, 0 },
+ { { NAND_MAN_STMICRO, 0xf1 }, "STMicro 128MB 3,3V 8-bit",
+ 0x80, 2048, 64, 0x40, 0 },
+};
+
+struct nand_params *nand_get_params(struct nand_id *id)
+{
+ int i;
+
+ for (i = 0; i < sizeof(nand_ids) / sizeof(nand_ids[0]); i++)
+ if (nand_ids[i].id.man_id == id->man_id &&
+ nand_ids[i].id.dev_id == id->dev_id)
+ return (&nand_ids[i]);
+
+ return (NULL);
+}
diff --git a/sys/dev/nand/nand_if.m b/sys/dev/nand/nand_if.m
new file mode 100644
index 000000000000..49c8879b6890
--- /dev/null
+++ b/sys/dev/nand/nand_if.m
@@ -0,0 +1,168 @@
+#-
+# 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.
+#
+# $FreeBSD$
+
+# NAND chip interface description
+#
+
+#include <sys/bus.h>
+#include <dev/nand/nand.h>
+
+INTERFACE nand;
+
+CODE {
+ static int nand_method_not_supported(device_t dev)
+ {
+ return (ENOENT);
+ }
+};
+
+# Read NAND page
+#
+# Return values:
+# 0: Success
+#
+METHOD int read_page {
+ device_t dev;
+ uint32_t page;
+ void* buf;
+ uint32_t len;
+ uint32_t offset;
+};
+
+# Program NAND page
+#
+# Return values:
+# 0: Success
+#
+METHOD int program_page {
+ device_t dev;
+ uint32_t page;
+ void* buf;
+ uint32_t len;
+ uint32_t offset;
+};
+
+# Program NAND page interleaved
+#
+# Return values:
+# 0: Success
+#
+METHOD int program_page_intlv {
+ device_t dev;
+ uint32_t page;
+ void* buf;
+ uint32_t len;
+ uint32_t offset;
+} DEFAULT nand_method_not_supported;
+
+# Read NAND oob
+#
+# Return values:
+# 0: Success
+#
+METHOD int read_oob {
+ device_t dev;
+ uint32_t page;
+ void* buf;
+ uint32_t len;
+ uint32_t offset;
+};
+
+# Program NAND oob
+#
+# Return values:
+# 0: Success
+#
+METHOD int program_oob {
+ device_t dev;
+ uint32_t page;
+ void* buf;
+ uint32_t len;
+ uint32_t offset;
+};
+
+# Erase NAND block
+#
+# Return values:
+# 0: Success
+#
+METHOD int erase_block {
+ device_t dev;
+ uint32_t block;
+};
+
+# Erase NAND block interleaved
+#
+# Return values:
+# 0: Success
+#
+METHOD int erase_block_intlv {
+ device_t dev;
+ uint32_t block;
+} DEFAULT nand_method_not_supported;
+
+# NAND get status
+#
+# Return values:
+# 0: Success
+#
+METHOD int get_status {
+ device_t dev;
+ uint8_t *status;
+};
+
+# NAND check if block is bad
+#
+# Return values:
+# 0: Success
+#
+METHOD int is_blk_bad {
+ device_t dev;
+ uint32_t block_number;
+ uint8_t *bad;
+};
+
+# NAND get ECC
+#
+#
+METHOD int get_ecc {
+ device_t dev;
+ void *buf;
+ void *ecc;
+ int *needwrite;
+};
+
+# NAND correct ECC
+#
+#
+METHOD int correct_ecc {
+ device_t dev;
+ void *buf;
+ void *readecc;
+ void *calcecc;
+};
+
diff --git a/sys/dev/nand/nandbus.c b/sys/dev/nand/nandbus.c
new file mode 100644
index 000000000000..322d708b1658
--- /dev/null
+++ b/sys/dev/nand/nandbus.c
@@ -0,0 +1,530 @@
+/*-
+ * 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/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include "nand_if.h"
+#include "nandbus_if.h"
+#include "nfc_if.h"
+
+#define NAND_NCS 4
+
+static int nandbus_probe(device_t dev);
+static int nandbus_attach(device_t dev);
+static int nandbus_detach(device_t dev);
+
+static int nandbus_child_location_str(device_t, device_t, char *, size_t);
+static int nandbus_child_pnpinfo_str(device_t, device_t, char *, size_t);
+
+static int nandbus_get_status(device_t, uint8_t *);
+static void nandbus_read_buffer(device_t, void *, uint32_t);
+static int nandbus_select_cs(device_t, uint8_t);
+static int nandbus_send_command(device_t, uint8_t);
+static int nandbus_send_address(device_t, uint8_t);
+static int nandbus_start_command(device_t);
+static int nandbus_wait_ready(device_t, uint8_t *);
+static void nandbus_write_buffer(device_t, void *, uint32_t);
+static int nandbus_get_ecc(device_t, void *, uint32_t, void *, int *);
+static int nandbus_correct_ecc(device_t, void *, int, void *, void *);
+static void nandbus_lock(device_t);
+static void nandbus_unlock(device_t);
+
+static int nand_readid(device_t, uint8_t *, uint8_t *);
+static int nand_probe_onfi(device_t, uint8_t *);
+static int nand_reset(device_t);
+
+struct nandbus_softc {
+ device_t dev;
+ struct cv nandbus_cv;
+ struct mtx nandbus_mtx;
+ uint8_t busy;
+};
+
+static device_method_t nandbus_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, nandbus_probe),
+ DEVMETHOD(device_attach, nandbus_attach),
+ DEVMETHOD(device_detach, nandbus_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+ DEVMETHOD(bus_child_pnpinfo_str, nandbus_child_pnpinfo_str),
+ DEVMETHOD(bus_child_location_str, nandbus_child_location_str),
+
+ /* nandbus interface */
+ DEVMETHOD(nandbus_get_status, nandbus_get_status),
+ DEVMETHOD(nandbus_read_buffer, nandbus_read_buffer),
+ DEVMETHOD(nandbus_select_cs, nandbus_select_cs),
+ DEVMETHOD(nandbus_send_command, nandbus_send_command),
+ DEVMETHOD(nandbus_send_address, nandbus_send_address),
+ DEVMETHOD(nandbus_start_command,nandbus_start_command),
+ DEVMETHOD(nandbus_wait_ready, nandbus_wait_ready),
+ DEVMETHOD(nandbus_write_buffer, nandbus_write_buffer),
+ DEVMETHOD(nandbus_get_ecc, nandbus_get_ecc),
+ DEVMETHOD(nandbus_correct_ecc, nandbus_correct_ecc),
+ DEVMETHOD(nandbus_lock, nandbus_lock),
+ DEVMETHOD(nandbus_unlock, nandbus_unlock),
+ { 0, 0 }
+};
+
+devclass_t nandbus_devclass;
+
+driver_t nandbus_driver = {
+ "nandbus",
+ nandbus_methods,
+ sizeof(struct nandbus_softc)
+};
+
+DRIVER_MODULE(nandbus, nand, nandbus_driver, nandbus_devclass, 0, 0);
+
+int
+nandbus_create(device_t nfc)
+{
+ device_t child;
+
+ child = device_add_child(nfc, "nandbus", -1);
+ if (!child)
+ return (ENODEV);
+
+ bus_generic_attach(nfc);
+
+ return(0);
+}
+
+void
+nandbus_destroy(device_t nfc)
+{
+ device_t *children;
+ int nchildren, i;
+
+ mtx_lock(&Giant);
+ /* Detach & delete all children */
+ if (!device_get_children(nfc, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++)
+ device_delete_child(nfc, children[i]);
+
+ free(children, M_TEMP);
+ }
+ mtx_unlock(&Giant);
+}
+
+static int
+nandbus_probe(device_t dev)
+{
+
+ device_set_desc(dev, "NAND bus");
+
+ return (0);
+}
+
+static int
+nandbus_attach(device_t dev)
+{
+ device_t child, nfc;
+ struct nand_id chip_id;
+ struct nandbus_softc *sc;
+ struct nandbus_ivar *ivar;
+ struct nand_softc *nfc_sc;
+ struct nand_params *chip_params;
+ uint8_t cs, onfi;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ nfc = device_get_parent(dev);
+ nfc_sc = device_get_softc(nfc);
+
+ mtx_init(&sc->nandbus_mtx, "nandbus lock", MTX_DEF, 0);
+ cv_init(&sc->nandbus_cv, "nandbus cv");
+
+ /* Check each possible CS for existing nand devices */
+ for (cs = 0; cs < NAND_NCS; cs++) {
+ nand_debug(NDBG_BUS,"probe chip select %x", cs);
+
+ /* Select & reset chip */
+ if (nandbus_select_cs(dev, cs))
+ break;
+
+ if (nand_reset(dev))
+ continue;
+
+ /* Read manufacturer and device id */
+ if (nand_readid(dev, &chip_id.man_id, &chip_id.dev_id))
+ continue;
+
+ if (chip_id.man_id == 0xff)
+ continue;
+
+ /* Check if chip is ONFI compliant */
+ if (nand_probe_onfi(dev, &onfi) != 0) {
+ continue;
+ }
+
+ ivar = malloc(sizeof(struct nandbus_ivar),
+ M_NAND, M_WAITOK);
+
+ if (onfi == 1) {
+ ivar->cs = cs;
+ ivar->cols = 0;
+ ivar->rows = 0;
+ ivar->params = NULL;
+ ivar->man_id = chip_id.man_id;
+ ivar->dev_id = chip_id.dev_id;
+ ivar->is_onfi = onfi;
+ ivar->chip_cdev_name = nfc_sc->chip_cdev_name;
+
+ child = device_add_child(dev, NULL, -1);
+ device_set_ivars(child, ivar);
+ continue;
+ }
+
+ chip_params = nand_get_params(&chip_id);
+ if (chip_params == NULL) {
+ nand_debug(NDBG_BUS,"Chip description not found! "
+ "(manuf: 0x%0x, chipid: 0x%0x)\n",
+ chip_id.man_id, chip_id.dev_id);
+ free(ivar, M_NAND);
+ continue;
+ }
+
+ ivar->cs = cs;
+ ivar->cols = 1;
+ ivar->rows = 2;
+ ivar->params = chip_params;
+ ivar->man_id = chip_id.man_id;
+ ivar->dev_id = chip_id.dev_id;
+ ivar->is_onfi = onfi;
+ ivar->chip_cdev_name = nfc_sc->chip_cdev_name;
+
+ /*
+ * Check what type of device we have.
+ * devices bigger than 32MiB have on more row (3)
+ */
+ if (chip_params->chip_size > 32)
+ ivar->rows++;
+ /* Large page devices have one more col (2) */
+ if (chip_params->chip_size >= 128 &&
+ chip_params->page_size > 512)
+ ivar->cols++;
+
+ child = device_add_child(dev, NULL, -1);
+ device_set_ivars(child, ivar);
+ }
+
+ bus_generic_attach(dev);
+ return (0);
+}
+
+static int
+nandbus_detach(device_t dev)
+{
+ struct nandbus_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_generic_detach(dev);
+
+ mtx_destroy(&sc->nandbus_mtx);
+ cv_destroy(&sc->nandbus_cv);
+
+ return (0);
+}
+
+static int
+nandbus_child_location_str(device_t bus, device_t child, char *buf,
+ size_t buflen)
+{
+ struct nandbus_ivar *ivar = device_get_ivars(child);
+
+ snprintf(buf, buflen, "at cs#%d", ivar->cs);
+ return (0);
+}
+
+static int
+nandbus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
+ size_t buflen)
+{
+ // XXX man id, model id ????
+ *buf = '\0';
+ return (0);
+}
+
+static int
+nand_readid(device_t bus, uint8_t *man_id, uint8_t *dev_id)
+{
+ device_t nfc;
+
+ if (!bus || !man_id || !dev_id)
+ return (EINVAL);
+
+ nand_debug(NDBG_BUS,"read id");
+
+ nfc = device_get_parent(bus);
+
+ if (NFC_SEND_COMMAND(nfc, NAND_CMD_READ_ID)) {
+ nand_debug(NDBG_BUS,"Error : could not send READ ID command");
+ return (ENXIO);
+ }
+
+ if (NFC_SEND_ADDRESS(nfc, 0)) {
+ nand_debug(NDBG_BUS,"Error : could not sent address to chip");
+ return (ENXIO);
+ }
+
+ if (NFC_START_COMMAND(nfc) != 0) {
+ nand_debug(NDBG_BUS,"Error : could not start command");
+ return (ENXIO);
+ }
+
+ DELAY(25);
+
+ *man_id = NFC_READ_BYTE(nfc);
+ *dev_id = NFC_READ_BYTE(nfc);
+
+ nand_debug(NDBG_BUS,"manufacturer id: %x chip id: %x", *man_id,
+ *dev_id);
+
+ return (0);
+}
+
+static int
+nand_probe_onfi(device_t bus, uint8_t *onfi_compliant)
+{
+ device_t nfc;
+ char onfi_id[] = {'o', 'n', 'f', 'i', '\0'};
+ int i;
+
+ nand_debug(NDBG_BUS,"probing ONFI");
+
+ nfc = device_get_parent(bus);
+
+ if (NFC_SEND_COMMAND(nfc, NAND_CMD_READ_ID)) {
+ nand_debug(NDBG_BUS,"Error : could not sent READ ID command");
+ return (ENXIO);
+ }
+
+ if (NFC_SEND_ADDRESS(nfc, ONFI_SIG_ADDR)) {
+ nand_debug(NDBG_BUS,"Error : could not sent address to chip");
+ return (ENXIO);
+ }
+
+ if (NFC_START_COMMAND(nfc) != 0) {
+ nand_debug(NDBG_BUS,"Error : could not start command");
+ return (ENXIO);
+ }
+ for (i = 0; onfi_id[i] != '\0'; i++)
+ if (NFC_READ_BYTE(nfc) != onfi_id[i]) {
+ nand_debug(NDBG_BUS,"ONFI non-compliant");
+ *onfi_compliant = 0;
+ return (0);
+ }
+
+ nand_debug(NDBG_BUS,"ONFI compliant");
+ *onfi_compliant = 1;
+
+ return (0);
+}
+
+static int
+nand_reset(device_t bus)
+{
+ device_t nfc;
+ nand_debug(NDBG_BUS,"resetting...");
+
+ nfc = device_get_parent(bus);
+
+ if (NFC_SEND_COMMAND(nfc, NAND_CMD_RESET) != 0) {
+ nand_debug(NDBG_BUS,"Error : could not sent RESET command");
+ return (ENXIO);
+ }
+
+ if (NFC_START_COMMAND(nfc) != 0) {
+ nand_debug(NDBG_BUS,"Error : could not start RESET command");
+ return (ENXIO);
+ }
+
+ DELAY(1000);
+
+ return (0);
+}
+
+void
+nandbus_lock(device_t dev)
+{
+ struct nandbus_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->nandbus_mtx);
+ if (sc->busy)
+ cv_wait(&sc->nandbus_cv, &sc->nandbus_mtx);
+ sc->busy = 1;
+ mtx_unlock(&sc->nandbus_mtx);
+}
+
+void
+nandbus_unlock(device_t dev)
+{
+ struct nandbus_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->nandbus_mtx);
+ sc->busy = 0;
+ cv_signal(&sc->nandbus_cv);
+ mtx_unlock(&sc->nandbus_mtx);
+}
+
+int
+nandbus_select_cs(device_t dev, uint8_t cs)
+{
+
+ return (NFC_SELECT_CS(device_get_parent(dev), cs));
+}
+
+int
+nandbus_send_command(device_t dev, uint8_t command)
+{
+ int err;
+
+ if ((err = NFC_SEND_COMMAND(device_get_parent(dev), command)))
+ nand_debug(NDBG_BUS,"Err: Could not send command %x, err %x",
+ command, err);
+
+ return (err);
+}
+
+int
+nandbus_send_address(device_t dev, uint8_t address)
+{
+ int err;
+
+ if ((err = NFC_SEND_ADDRESS(device_get_parent(dev), address)))
+ nand_debug(NDBG_BUS,"Err: Could not send address %x, err %x",
+ address, err);
+
+ return (err);
+}
+
+int
+nandbus_start_command(device_t dev)
+{
+ int err;
+
+ if ((err = NFC_START_COMMAND(device_get_parent(dev))))
+ nand_debug(NDBG_BUS,"Err: Could not start command, err %x",
+ err);
+
+ return (err);
+}
+
+void
+nandbus_read_buffer(device_t dev, void *buf, uint32_t len)
+{
+
+ NFC_READ_BUF(device_get_parent(dev), buf, len);
+}
+
+void
+nandbus_write_buffer(device_t dev, void *buf, uint32_t len)
+{
+
+ NFC_WRITE_BUF(device_get_parent(dev), buf, len);
+}
+
+int
+nandbus_get_status(device_t dev, uint8_t *status)
+{
+ int err;
+
+ if ((err = NANDBUS_SEND_COMMAND(dev, NAND_CMD_STATUS)))
+ return (err);
+ if ((err = NANDBUS_START_COMMAND(dev)))
+ return (err);
+
+ *status = NFC_READ_BYTE(device_get_parent(dev));
+
+ return (0);
+}
+
+int
+nandbus_wait_ready(device_t dev, uint8_t *status)
+{
+ struct timeval tv, tv2;
+
+ tv2.tv_sec = 0;
+ tv2.tv_usec = 50 * 5000; /* 10ms */
+
+ getmicrotime(&tv);
+ timevaladd(&tv, &tv2);
+
+ do {
+ if (NANDBUS_GET_STATUS(dev, status))
+ return (ENXIO);
+
+ if (*status & NAND_STATUS_RDY)
+ return (0);
+
+ getmicrotime(&tv2);
+ } while (timevalcmp(&tv2, &tv, <=));
+
+ return (EBUSY);
+}
+
+int
+nandbus_get_ecc(device_t dev, void *buf, uint32_t pagesize, void *ecc,
+ int *needwrite)
+{
+
+ return (NFC_GET_ECC(device_get_parent(dev), buf, pagesize, ecc, needwrite));
+}
+
+int
+nandbus_correct_ecc(device_t dev, void *buf, int pagesize, void *readecc,
+ void *calcecc)
+{
+
+ return (NFC_CORRECT_ECC(device_get_parent(dev), buf, pagesize,
+ readecc, calcecc));
+}
+
diff --git a/sys/dev/nand/nandbus.h b/sys/dev/nand/nandbus.h
new file mode 100644
index 000000000000..417fbf5e14b5
--- /dev/null
+++ b/sys/dev/nand/nandbus.h
@@ -0,0 +1,49 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NANDBUS_H_
+#define _NANDBUS_H_
+
+struct nandbus_ivar {
+ uint8_t cs;
+ uint8_t cols;
+ uint8_t rows;
+ uint8_t man_id;
+ uint8_t dev_id;
+ uint8_t is_onfi;
+ char *chip_cdev_name;
+ struct nand_params *params;
+};
+
+extern devclass_t nandbus_devclass;
+extern driver_t nandbus_driver;
+
+int nandbus_create(device_t nfc);
+void nandbus_destroy(device_t nfc);
+
+#endif /* _NANDBUS_H_ */
diff --git a/sys/dev/nand/nandbus_if.m b/sys/dev/nand/nandbus_if.m
new file mode 100644
index 000000000000..e914e18de661
--- /dev/null
+++ b/sys/dev/nand/nandbus_if.m
@@ -0,0 +1,100 @@
+#-
+# 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.
+#
+# $FreeBSD$
+
+# NAND bus interface description
+#
+
+#include <sys/bus.h>
+#include <dev/nand/nand.h>
+
+INTERFACE nandbus;
+
+METHOD int get_status {
+ device_t dev;
+ uint8_t * status;
+};
+
+METHOD void read_buffer {
+ device_t dev;
+ void * buf;
+ uint32_t len;
+};
+
+METHOD int select_cs {
+ device_t dev;
+ uint8_t cs;
+};
+
+METHOD int send_command {
+ device_t dev;
+ uint8_t command;
+};
+
+METHOD int send_address {
+ device_t dev;
+ uint8_t address;
+};
+
+METHOD int start_command {
+ device_t dev;
+};
+
+METHOD int wait_ready {
+ device_t dev;
+ uint8_t * status;
+}
+
+METHOD void write_buffer {
+ device_t dev;
+ void * buf;
+ uint32_t len;
+};
+
+METHOD int get_ecc {
+ device_t dev;
+ void * buf;
+ uint32_t pagesize;
+ void * ecc;
+ int * needwrite;
+};
+
+METHOD int correct_ecc {
+ device_t dev;
+ void * buf;
+ int pagesize;
+ void * readecc;
+ void * calcecc;
+};
+
+METHOD void lock {
+ device_t dev;
+};
+
+METHOD void unlock {
+ device_t dev;
+};
+
diff --git a/sys/dev/nand/nandsim.c b/sys/dev/nand/nandsim.c
new file mode 100644
index 000000000000..5390c01ca2a9
--- /dev/null
+++ b/sys/dev/nand/nandsim.c
@@ -0,0 +1,665 @@
+/*-
+ * 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.
+ */
+
+/* Simulated NAND controller driver */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandsim.h>
+#include <dev/nand/nandsim_chip.h>
+#include <dev/nand/nandsim_log.h>
+#include <dev/nand/nandsim_swap.h>
+
+struct sim_param sim;
+struct sim_ctrl_conf ctrls[MAX_SIM_DEV];
+
+static struct cdev *nandsim_dev;
+static d_ioctl_t nandsim_ioctl;
+
+static void nandsim_init_sim_param(struct sim_param *);
+static int nandsim_create_ctrl(struct sim_ctrl *);
+static int nandsim_destroy_ctrl(int);
+static int nandsim_ctrl_status(struct sim_ctrl *);
+static int nandsim_create_chip(struct sim_chip *);
+static int nandsim_destroy_chip(struct sim_ctrl_chip *);
+static int nandsim_chip_status(struct sim_chip *);
+static int nandsim_start_ctrl(int);
+static int nandsim_stop_ctrl(int);
+static int nandsim_inject_error(struct sim_error *);
+static int nandsim_get_block_state(struct sim_block_state *);
+static int nandsim_set_block_state(struct sim_block_state *);
+static int nandsim_modify(struct sim_mod *);
+static int nandsim_dump(struct sim_dump *);
+static int nandsim_restore(struct sim_dump *);
+static int nandsim_freeze(struct sim_ctrl_chip *);
+static void nandsim_print_log(struct sim_log *);
+static struct nandsim_chip *get_nandsim_chip(uint8_t, uint8_t);
+
+static struct cdevsw nandsim_cdevsw = {
+ .d_version = D_VERSION,
+ .d_ioctl = nandsim_ioctl,
+ .d_name = "nandsim",
+};
+
+int
+nandsim_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
+ int flags, struct thread *td)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case NANDSIM_SIM_PARAM:
+ nandsim_init_sim_param((struct sim_param *)data);
+ break;
+ case NANDSIM_CREATE_CTRL:
+ ret = nandsim_create_ctrl((struct sim_ctrl *)data);
+ break;
+ case NANDSIM_DESTROY_CTRL:
+ ret = nandsim_destroy_ctrl(*(int *)data);
+ break;
+ case NANDSIM_STATUS_CTRL:
+ ret = nandsim_ctrl_status((struct sim_ctrl *)data);
+ break;
+ case NANDSIM_CREATE_CHIP:
+ ret = nandsim_create_chip((struct sim_chip *)data);
+ break;
+ case NANDSIM_DESTROY_CHIP:
+ ret = nandsim_destroy_chip((struct sim_ctrl_chip *)data);
+ break;
+ case NANDSIM_STATUS_CHIP:
+ ret = nandsim_chip_status((struct sim_chip *)data);
+ break;
+ case NANDSIM_MODIFY:
+ ret = nandsim_modify((struct sim_mod *)data);
+ break;
+ case NANDSIM_START_CTRL:
+ ret = nandsim_start_ctrl(*(int *)data);
+ break;
+ case NANDSIM_STOP_CTRL:
+ ret = nandsim_stop_ctrl(*(int *)data);
+ break;
+ case NANDSIM_INJECT_ERROR:
+ ret = nandsim_inject_error((struct sim_error *)data);
+ break;
+ case NANDSIM_SET_BLOCK_STATE:
+ ret = nandsim_set_block_state((struct sim_block_state *)data);
+ break;
+ case NANDSIM_GET_BLOCK_STATE:
+ ret = nandsim_get_block_state((struct sim_block_state *)data);
+ break;
+ case NANDSIM_PRINT_LOG:
+ nandsim_print_log((struct sim_log *)data);
+ break;
+ case NANDSIM_DUMP:
+ ret = nandsim_dump((struct sim_dump *)data);
+ break;
+ case NANDSIM_RESTORE:
+ ret = nandsim_restore((struct sim_dump *)data);
+ break;
+ case NANDSIM_FREEZE:
+ ret = nandsim_freeze((struct sim_ctrl_chip *)data);
+ break;
+ default:
+ ret = EINVAL;
+ break;
+ }
+
+ return (ret);
+}
+
+static void
+nandsim_init_sim_param(struct sim_param *param)
+{
+
+ if (!param)
+ return;
+
+ nand_debug(NDBG_SIM,"log level:%d output %d", param->log_level,
+ param->log_output);
+ nandsim_log_level = param->log_level;
+ nandsim_log_output = param->log_output;
+}
+
+static int
+nandsim_create_ctrl(struct sim_ctrl *ctrl)
+{
+ struct sim_ctrl_conf *sim_ctrl;
+
+ nand_debug(NDBG_SIM,"create controller num:%d cs:%d",ctrl->num,
+ ctrl->num_cs);
+
+ if (ctrl->num >= MAX_SIM_DEV) {
+ return (EINVAL);
+ }
+
+ sim_ctrl = &ctrls[ctrl->num];
+ if(sim_ctrl->created)
+ return (EEXIST);
+
+ sim_ctrl->num = ctrl->num;
+ sim_ctrl->num_cs = ctrl->num_cs;
+ sim_ctrl->ecc = ctrl->ecc;
+ memcpy(sim_ctrl->ecc_layout, ctrl->ecc_layout,
+ MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
+ strlcpy(sim_ctrl->filename, ctrl->filename,
+ FILENAME_SIZE);
+ sim_ctrl->created = 1;
+
+ return (0);
+}
+
+static int
+nandsim_destroy_ctrl(int ctrl_num)
+{
+
+ nand_debug(NDBG_SIM,"destroy controller num:%d", ctrl_num);
+
+ if (ctrl_num >= MAX_SIM_DEV) {
+ return (EINVAL);
+ }
+
+ if (!ctrls[ctrl_num].created) {
+ return (ENODEV);
+ }
+
+ if (ctrls[ctrl_num].running) {
+ return (EBUSY);
+ }
+
+ memset(&ctrls[ctrl_num], 0, sizeof(ctrls[ctrl_num]));
+
+ return (0);
+}
+
+static int
+nandsim_ctrl_status(struct sim_ctrl *ctrl)
+{
+
+ nand_debug(NDBG_SIM,"status controller num:%d cs:%d",ctrl->num,
+ ctrl->num_cs);
+
+ if (ctrl->num >= MAX_SIM_DEV) {
+ return (EINVAL);
+ }
+
+ ctrl->num_cs = ctrls[ctrl->num].num_cs;
+ ctrl->ecc = ctrls[ctrl->num].ecc;
+ memcpy(ctrl->ecc_layout, ctrls[ctrl->num].ecc_layout,
+ MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
+ strlcpy(ctrl->filename, ctrls[ctrl->num].filename,
+ FILENAME_SIZE);
+ ctrl->running = ctrls[ctrl->num].running;
+ ctrl->created = ctrls[ctrl->num].created;
+
+ return (0);
+}
+
+static int
+nandsim_create_chip(struct sim_chip *chip)
+{
+ struct sim_chip *sim_chip;
+
+ nand_debug(NDBG_SIM,"create chip num:%d at ctrl:%d", chip->num,
+ chip->ctrl_num);
+
+ if (chip->ctrl_num >= MAX_SIM_DEV ||
+ chip->num >= MAX_CTRL_CS) {
+ return (EINVAL);
+ }
+
+ if (ctrls[chip->ctrl_num].chips[chip->num]) {
+ return (EEXIST);
+ }
+
+ sim_chip = malloc(sizeof(*sim_chip), M_NANDSIM,
+ M_WAITOK);
+ if (sim_chip == NULL) {
+ return (ENOMEM);
+ }
+
+ memcpy(sim_chip, chip, sizeof(*sim_chip));
+ ctrls[chip->ctrl_num].chips[chip->num] = sim_chip;
+ sim_chip->created = 1;
+
+ return (0);
+}
+
+static int
+nandsim_destroy_chip(struct sim_ctrl_chip *chip)
+{
+ struct sim_ctrl_conf *ctrl_conf;
+
+ nand_debug(NDBG_SIM,"destroy chip num:%d at ctrl:%d", chip->chip_num,
+ chip->ctrl_num);
+
+ if (chip->ctrl_num >= MAX_SIM_DEV ||
+ chip->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ ctrl_conf = &ctrls[chip->ctrl_num];
+
+ if (!ctrl_conf->created || !ctrl_conf->chips[chip->chip_num])
+ return (ENODEV);
+
+ if (ctrl_conf->running)
+ return (EBUSY);
+
+ free(ctrl_conf->chips[chip->chip_num], M_NANDSIM);
+ ctrl_conf->chips[chip->chip_num] = NULL;
+
+ return (0);
+}
+
+static int
+nandsim_chip_status(struct sim_chip *chip)
+{
+ struct sim_ctrl_conf *ctrl_conf;
+
+ nand_debug(NDBG_SIM,"status for chip num:%d at ctrl:%d", chip->num,
+ chip->ctrl_num);
+
+ if (chip->ctrl_num >= MAX_SIM_DEV &&
+ chip->num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ ctrl_conf = &ctrls[chip->ctrl_num];
+ if (!ctrl_conf->chips[chip->num])
+ chip->created = 0;
+ else
+ memcpy(chip, ctrl_conf->chips[chip->num], sizeof(*chip));
+
+ return (0);
+}
+
+static int
+nandsim_start_ctrl(int num)
+{
+ device_t nexus, ndev;
+ devclass_t nexus_devclass;
+ int ret = 0;
+
+ nand_debug(NDBG_SIM,"start ctlr num:%d", num);
+
+ if (num >= MAX_SIM_DEV)
+ return (EINVAL);
+
+ if (!ctrls[num].created)
+ return (ENODEV);
+
+ if (ctrls[num].running)
+ return (EBUSY);
+
+ /* We will add our device as a child of the nexus0 device */
+ if (!(nexus_devclass = devclass_find("nexus")) ||
+ !(nexus = devclass_get_device(nexus_devclass, 0)))
+ return (EFAULT);
+
+ /*
+ * Create a newbus device representing this frontend instance
+ *
+ * XXX powerpc nexus doesn't implement bus_add_child, so child
+ * must be added by device_add_child().
+ */
+#if defined(__powerpc__)
+ ndev = device_add_child(nexus, "nandsim", num);
+#else
+ ndev = BUS_ADD_CHILD(nexus, 0, "nandsim", num);
+#endif
+ if (!ndev)
+ return (EFAULT);
+
+ mtx_lock(&Giant);
+ ret = device_probe_and_attach(ndev);
+ mtx_unlock(&Giant);
+
+ if (ret == 0) {
+ ctrls[num].sim_ctrl_dev = ndev;
+ ctrls[num].running = 1;
+ }
+
+ return (ret);
+}
+
+static int
+nandsim_stop_ctrl(int num)
+{
+ device_t nexus;
+ devclass_t nexus_devclass;
+ int ret = 0;
+
+ nand_debug(NDBG_SIM,"stop controller num:%d", num);
+
+ if (num >= MAX_SIM_DEV) {
+ return (EINVAL);
+ }
+
+ if (!ctrls[num].created || !ctrls[num].running) {
+ return (ENODEV);
+ }
+
+ /* We will add our device as a child of the nexus0 device */
+ if (!(nexus_devclass = devclass_find("nexus")) ||
+ !(nexus = devclass_get_device(nexus_devclass, 0))) {
+ return (ENODEV);
+ }
+
+ mtx_lock(&Giant);
+ if (ctrls[num].sim_ctrl_dev) {
+ ret = device_delete_child(nexus, ctrls[num].sim_ctrl_dev);
+ ctrls[num].sim_ctrl_dev = NULL;
+ }
+ mtx_unlock(&Giant);
+
+ ctrls[num].running = 0;
+
+ return (ret);
+}
+
+static struct nandsim_chip *
+get_nandsim_chip(uint8_t ctrl_num, uint8_t chip_num)
+{
+ struct nandsim_softc *sc;
+
+ if (!ctrls[ctrl_num].sim_ctrl_dev)
+ return (NULL);
+
+ sc = device_get_softc(ctrls[ctrl_num].sim_ctrl_dev);
+ return (sc->chips[chip_num]);
+}
+
+static void
+nandsim_print_log(struct sim_log *sim_log)
+{
+ struct nandsim_softc *sc;
+ int len1, len2;
+
+ if (!ctrls[sim_log->ctrl_num].sim_ctrl_dev)
+ return;
+
+ sc = device_get_softc(ctrls[sim_log->ctrl_num].sim_ctrl_dev);
+ if (sc->log_buff) {
+ len1 = strlen(&sc->log_buff[sc->log_idx + 1]);
+ if (len1 >= sim_log->len)
+ len1 = sim_log->len;
+ copyout(&sc->log_buff[sc->log_idx + 1], sim_log->log, len1);
+ len2 = strlen(sc->log_buff);
+ if (len2 >= (sim_log->len - len1))
+ len2 = (sim_log->len - len1);
+ copyout(sc->log_buff, &sim_log->log[len1], len2);
+ sim_log->len = len1 + len2;
+ }
+}
+
+static int
+nandsim_inject_error(struct sim_error *error)
+{
+ struct nandsim_chip *chip;
+ struct block_space *bs;
+ struct onfi_params *param;
+ int page, page_size, block, offset;
+
+ nand_debug(NDBG_SIM,"inject error for chip %d at ctrl %d\n",
+ error->chip_num, error->ctrl_num);
+
+ if (error->ctrl_num >= MAX_SIM_DEV ||
+ error->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ if (!ctrls[error->ctrl_num].created || !ctrls[error->ctrl_num].running)
+ return (ENODEV);
+
+ chip = get_nandsim_chip(error->ctrl_num, error->chip_num);
+ param = &chip->params;
+ page_size = param->bytes_per_page + param->spare_bytes_per_page;
+ block = error->page_num / param->pages_per_block;
+ page = error->page_num % param->pages_per_block;
+
+ bs = get_bs(chip->swap, block, 1);
+ if (!bs)
+ return (EINVAL);
+
+ offset = (page * page_size) + error->column;
+ memset(&bs->blk_ptr[offset], error->pattern, error->len);
+
+ return (0);
+}
+
+static int
+nandsim_set_block_state(struct sim_block_state *bs)
+{
+ struct onfi_params *params;
+ struct nandsim_chip *chip;
+ int blocks;
+
+ nand_debug(NDBG_SIM,"set block state for %d:%d block %d\n",
+ bs->chip_num, bs->ctrl_num, bs->block_num);
+
+ if (bs->ctrl_num >= MAX_SIM_DEV ||
+ bs->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
+ params = &chip->params;
+ blocks = params->luns * params->blocks_per_lun;
+
+ if (bs->block_num > blocks)
+ return (EINVAL);
+
+ chip->blk_state[bs->block_num].is_bad = bs->state;
+
+ if (bs->wearout >= 0)
+ chip->blk_state[bs->block_num].wear_lev = bs->wearout;
+
+ return (0);
+}
+
+static int
+nandsim_get_block_state(struct sim_block_state *bs)
+{
+ struct onfi_params *params;
+ struct nandsim_chip *chip;
+ int blocks;
+
+ if (bs->ctrl_num >= MAX_SIM_DEV ||
+ bs->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ nand_debug(NDBG_SIM,"get block state for %d:%d block %d\n",
+ bs->chip_num, bs->ctrl_num, bs->block_num);
+
+ chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
+ params = &chip->params;
+ blocks = params->luns * params->blocks_per_lun;
+
+ if (bs->block_num > blocks)
+ return (EINVAL);
+
+ bs->state = chip->blk_state[bs->block_num].is_bad;
+ bs->wearout = chip->blk_state[bs->block_num].wear_lev;
+
+ return (0);
+}
+
+static int
+nandsim_dump(struct sim_dump *dump)
+{
+ struct nandsim_chip *chip;
+ struct block_space *bs;
+ int blk_size;
+
+ nand_debug(NDBG_SIM,"dump chip %d %d\n", dump->ctrl_num, dump->chip_num);
+
+ if (dump->ctrl_num >= MAX_SIM_DEV ||
+ dump->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
+ blk_size = chip->cg.block_size +
+ (chip->cg.oob_size * chip->cg.pgs_per_blk);
+
+ bs = get_bs(chip->swap, dump->block_num, 0);
+ if (!bs)
+ return (EINVAL);
+
+ if (dump->len > blk_size)
+ dump->len = blk_size;
+
+ copyout(bs->blk_ptr, dump->data, dump->len);
+
+ return (0);
+}
+
+static int
+nandsim_restore(struct sim_dump *dump)
+{
+ struct nandsim_chip *chip;
+ struct block_space *bs;
+ int blk_size;
+
+ nand_debug(NDBG_SIM,"restore chip %d %d\n", dump->ctrl_num,
+ dump->chip_num);
+
+ if (dump->ctrl_num >= MAX_SIM_DEV ||
+ dump->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
+ blk_size = chip->cg.block_size +
+ (chip->cg.oob_size * chip->cg.pgs_per_blk);
+
+ bs = get_bs(chip->swap, dump->block_num, 1);
+ if (!bs)
+ return (EINVAL);
+
+ if (dump->len > blk_size)
+ dump->len = blk_size;
+
+
+ copyin(dump->data, bs->blk_ptr, dump->len);
+
+ return (0);
+}
+
+static int
+nandsim_freeze(struct sim_ctrl_chip *ctrl_chip)
+{
+ struct nandsim_chip *chip;
+
+ if (ctrl_chip->ctrl_num >= MAX_SIM_DEV ||
+ ctrl_chip->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ chip = get_nandsim_chip(ctrl_chip->ctrl_num, ctrl_chip->chip_num);
+ nandsim_chip_freeze(chip);
+
+ return (0);
+}
+
+static int
+nandsim_modify(struct sim_mod *mod)
+{
+ struct sim_chip *sim_conf = NULL;
+ struct nandsim_chip *sim_chip = NULL;
+
+ nand_debug(NDBG_SIM,"modify ctlr %d chip %d", mod->ctrl_num,
+ mod->chip_num);
+
+ if (mod->field != SIM_MOD_LOG_LEVEL) {
+ if (mod->ctrl_num >= MAX_SIM_DEV ||
+ mod->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ sim_conf = ctrls[mod->ctrl_num].chips[mod->chip_num];
+ sim_chip = get_nandsim_chip(mod->ctrl_num, mod->chip_num);
+ }
+
+ switch (mod->field) {
+ case SIM_MOD_LOG_LEVEL:
+ nandsim_log_level = mod->new_value;
+ break;
+ case SIM_MOD_ERASE_TIME:
+ sim_conf->erase_time = sim_chip->erase_delay = mod->new_value;
+ break;
+ case SIM_MOD_PROG_TIME:
+ sim_conf->prog_time = sim_chip->prog_delay = mod->new_value;
+ break;
+ case SIM_MOD_READ_TIME:
+ sim_conf->read_time = sim_chip->read_delay = mod->new_value;
+ break;
+ case SIM_MOD_ERROR_RATIO:
+ sim_conf->error_ratio = mod->new_value;
+ sim_chip->error_ratio = mod->new_value;
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+static int
+nandsim_modevent(module_t mod __unused, int type, void *data __unused)
+{
+ struct sim_ctrl_chip chip_ctrl;
+ int i, j;
+
+ switch (type) {
+ case MOD_LOAD:
+ nandsim_dev = make_dev(&nandsim_cdevsw, 0,
+ UID_ROOT, GID_WHEEL, 0666, "nandsim.ioctl");
+ break;
+ case MOD_UNLOAD:
+ for (i = 0; i < MAX_SIM_DEV; i++) {
+ nandsim_stop_ctrl(i);
+ chip_ctrl.ctrl_num = i;
+ for (j = 0; j < MAX_CTRL_CS; j++) {
+ chip_ctrl.chip_num = j;
+ nandsim_destroy_chip(&chip_ctrl);
+ }
+ nandsim_destroy_ctrl(i);
+ }
+ destroy_dev(nandsim_dev);
+ break;
+ case MOD_SHUTDOWN:
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+ return (0);
+}
+
+DEV_MODULE(nandsim, nandsim_modevent, NULL);
+MODULE_VERSION(nandsim, 1);
diff --git a/sys/dev/nand/nandsim.h b/sys/dev/nand/nandsim.h
new file mode 100644
index 000000000000..fbbb6d4bc7a4
--- /dev/null
+++ b/sys/dev/nand/nandsim.h
@@ -0,0 +1,175 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NANDSIM_H_
+#define _NANDSIM_H_
+
+#include <sys/ioccom.h>
+#include <sys/types.h>
+
+#define MAX_SIM_DEV 4
+#define MAX_CTRL_CS 4
+#define MAX_ECC_BYTES 512
+#define MAX_BAD_BLOCKS 512
+#define DEV_MODEL_STR_SIZE 21
+#define MAN_STR_SIZE 13
+#define FILENAME_SIZE 20
+
+#define MAX_CHIPS (MAX_SIM_DEV*MAX_CTRL_CS)
+
+#define NANDSIM_OUTPUT_NONE 0x0
+#define NANDSIM_OUTPUT_CONSOLE 0x1
+#define NANDSIM_OUTPUT_RAM 0x2
+#define NANDSIM_OUTPUT_FILE 0x3
+
+struct sim_ctrl_chip {
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+};
+
+#define NANDSIM_BASE 'A'
+
+struct sim_param {
+ uint8_t log_level;
+ uint8_t log_output;
+};
+
+#define NANDSIM_SIM_PARAM _IOW(NANDSIM_BASE, 1, struct sim_param)
+
+struct sim_ctrl {
+ uint8_t running;
+ uint8_t created;
+ uint8_t num;
+ uint8_t num_cs;
+ uint8_t ecc;
+ char filename[FILENAME_SIZE];
+ uint16_t ecc_layout[MAX_ECC_BYTES];
+};
+#define NANDSIM_CREATE_CTRL _IOW(NANDSIM_BASE, 2, struct sim_ctrl)
+#define NANDSIM_DESTROY_CTRL _IOW(NANDSIM_BASE, 3, int)
+
+struct sim_chip {
+ uint8_t num;
+ uint8_t ctrl_num;
+ uint8_t created;
+ uint8_t device_id;
+ uint8_t manufact_id;
+ char device_model[DEV_MODEL_STR_SIZE];
+ char manufacturer[MAN_STR_SIZE];
+ uint8_t col_addr_cycles;
+ uint8_t row_addr_cycles;
+ uint8_t features;
+ uint8_t width;
+ uint32_t page_size;
+ uint32_t oob_size;
+ uint32_t pgs_per_blk;
+ uint32_t blks_per_lun;
+ uint32_t luns;
+
+ uint32_t prog_time;
+ uint32_t erase_time;
+ uint32_t read_time;
+ uint32_t ccs_time;
+
+ uint32_t error_ratio;
+ uint32_t wear_level;
+ uint32_t bad_block_map[MAX_BAD_BLOCKS];
+ uint8_t is_wp;
+};
+
+#define NANDSIM_CREATE_CHIP _IOW(NANDSIM_BASE, 3, struct sim_chip)
+
+struct sim_chip_destroy {
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+};
+#define NANDSIM_DESTROY_CHIP _IOW(NANDSIM_BASE, 4, struct sim_chip_destroy)
+
+#define NANDSIM_START_CTRL _IOW(NANDSIM_BASE, 5, int)
+#define NANDSIM_STOP_CTRL _IOW(NANDSIM_BASE, 6, int)
+#define NANDSIM_RESTART_CTRL _IOW(NANDSIM_BASE, 7, int)
+
+#define NANDSIM_STATUS_CTRL _IOWR(NANDSIM_BASE, 8, struct sim_ctrl)
+#define NANDSIM_STATUS_CHIP _IOWR(NANDSIM_BASE, 9, struct sim_chip)
+
+struct sim_mod {
+ uint8_t chip_num;
+ uint8_t ctrl_num;
+ uint32_t field;
+ uint32_t new_value;
+};
+#define SIM_MOD_LOG_LEVEL 0
+#define SIM_MOD_ERASE_TIME 1
+#define SIM_MOD_PROG_TIME 2
+#define SIM_MOD_READ_TIME 3
+#define SIM_MOD_CCS_TIME 4
+#define SIM_MOD_ERROR_RATIO 5
+
+#define NANDSIM_MODIFY _IOW(NANDSIM_BASE, 10, struct sim_mod)
+#define NANDSIM_FREEZE _IOW(NANDSIM_BASE, 11, struct sim_ctrl_chip)
+
+struct sim_error {
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+ uint32_t page_num;
+ uint32_t column;
+ uint32_t len;
+ uint32_t pattern;
+};
+#define NANDSIM_INJECT_ERROR _IOW(NANDSIM_BASE, 20, struct sim_error)
+
+#define NANDSIM_GOOD_BLOCK 0
+#define NANDSIM_BAD_BLOCK 1
+struct sim_block_state {
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+ uint32_t block_num;
+ int wearout;
+ uint8_t state;
+};
+#define NANDSIM_SET_BLOCK_STATE _IOW(NANDSIM_BASE, 21, struct sim_block_state)
+#define NANDSIM_GET_BLOCK_STATE _IOWR(NANDSIM_BASE, 22, struct sim_block_state)
+
+struct sim_log {
+ uint8_t ctrl_num;
+ char* log;
+ size_t len;
+};
+#define NANDSIM_PRINT_LOG _IOWR(NANDSIM_BASE, 23, struct sim_log)
+
+struct sim_dump {
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+ uint32_t block_num;
+ uint32_t len;
+ void* data;
+};
+#define NANDSIM_DUMP _IOWR(NANDSIM_BASE, 24, struct sim_dump)
+#define NANDSIM_RESTORE _IOWR(NANDSIM_BASE, 25, struct sim_dump)
+
+#endif /* _NANDSIM_H_ */
diff --git a/sys/dev/nand/nandsim_chip.c b/sys/dev/nand/nandsim_chip.c
new file mode 100644
index 000000000000..f04ad805b167
--- /dev/null
+++ b/sys/dev/nand/nandsim_chip.c
@@ -0,0 +1,901 @@
+/*-
+ * Copyright (C) 2009-2012 Semihalf
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/kthread.h>
+#include <sys/unistd.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandsim_chip.h>
+#include <dev/nand/nandsim_log.h>
+#include <dev/nand/nandsim_swap.h>
+
+MALLOC_DEFINE(M_NANDSIM, "NANDsim", "NANDsim dynamic data");
+
+#define NANDSIM_CHIP_LOCK(chip) mtx_lock(&(chip)->ns_lock)
+#define NANDSIM_CHIP_UNLOCK(chip) mtx_unlock(&(chip)->ns_lock)
+
+static nandsim_evh_t erase_evh;
+static nandsim_evh_t idle_evh;
+static nandsim_evh_t poweron_evh;
+static nandsim_evh_t reset_evh;
+static nandsim_evh_t read_evh;
+static nandsim_evh_t readid_evh;
+static nandsim_evh_t readparam_evh;
+static nandsim_evh_t write_evh;
+
+static void nandsim_loop(void *);
+static void nandsim_undefined(struct nandsim_chip *, uint8_t);
+static void nandsim_bad_address(struct nandsim_chip *, uint8_t *);
+static void nandsim_ignore_address(struct nandsim_chip *, uint8_t);
+static void nandsim_sm_error(struct nandsim_chip *);
+static void nandsim_start_handler(struct nandsim_chip *, nandsim_evh_t);
+
+static void nandsim_callout_eh(void *);
+static int nandsim_delay(struct nandsim_chip *, int);
+
+static int nandsim_bbm_init(struct nandsim_chip *, uint32_t, uint32_t *);
+static int nandsim_blk_state_init(struct nandsim_chip *, uint32_t, uint32_t);
+static void nandsim_blk_state_destroy(struct nandsim_chip *);
+static int nandchip_is_block_valid(struct nandsim_chip *, int);
+
+static void nandchip_set_status(struct nandsim_chip *, uint8_t);
+static void nandchip_clear_status(struct nandsim_chip *, uint8_t);
+
+struct proc *nandsim_proc;
+
+struct nandsim_chip *
+nandsim_chip_init(struct nandsim_softc* sc, uint8_t chip_num,
+ struct sim_chip *sim_chip)
+{
+ struct nandsim_chip *chip;
+ struct onfi_params *chip_param;
+ char swapfile[20];
+ uint32_t size;
+ int error;
+
+ chip = malloc(sizeof(*chip), M_NANDSIM, M_WAITOK | M_ZERO);
+ if (!chip)
+ return (NULL);
+
+ mtx_init(&chip->ns_lock, "nandsim lock", NULL, MTX_DEF);
+ callout_init(&chip->ns_callout, CALLOUT_MPSAFE);
+ STAILQ_INIT(&chip->nandsim_events);
+
+ chip->chip_num = chip_num;
+ chip->ctrl_num = sim_chip->ctrl_num;
+ chip->sc = sc;
+
+ if (!sim_chip->is_wp)
+ nandchip_set_status(chip, NAND_STATUS_WP);
+
+ chip_param = &chip->params;
+
+ chip->id.dev_id = sim_chip->device_id;
+ chip->id.man_id = sim_chip->manufact_id;
+
+ chip->error_ratio = sim_chip->error_ratio;
+ chip->wear_level = sim_chip->wear_level;
+ chip->prog_delay = sim_chip->prog_time;
+ chip->erase_delay = sim_chip->erase_time;
+ chip->read_delay = sim_chip->read_time;
+
+ chip_param->t_prog = sim_chip->prog_time;
+ chip_param->t_bers = sim_chip->erase_time;
+ chip_param->t_r = sim_chip->read_time;
+ bcopy("onfi", &chip_param->signature, 4);
+
+ chip_param->manufacturer_id = sim_chip->manufact_id;
+ strncpy(chip_param->manufacturer_name, sim_chip->manufacturer, 12);
+ chip_param->manufacturer_name[11] = 0;
+ strncpy(chip_param->device_model, sim_chip->device_model, 20);
+ chip_param->device_model[19] = 0;
+
+ chip_param->bytes_per_page = sim_chip->page_size;
+ chip_param->spare_bytes_per_page = sim_chip->oob_size;
+ chip_param->pages_per_block = sim_chip->pgs_per_blk;
+ chip_param->blocks_per_lun = sim_chip->blks_per_lun;
+ chip_param->luns = sim_chip->luns;
+
+ init_chip_geom(&chip->cg, chip_param->luns, chip_param->blocks_per_lun,
+ chip_param->pages_per_block, chip_param->bytes_per_page,
+ chip_param->spare_bytes_per_page);
+
+ chip_param->address_cycles = sim_chip->row_addr_cycles |
+ (sim_chip->col_addr_cycles << 4);
+ chip_param->features = sim_chip->features;
+ if (sim_chip->width == 16)
+ chip_param->features |= ONFI_FEAT_16BIT;
+
+ size = chip_param->blocks_per_lun * chip_param->luns;
+
+ error = nandsim_blk_state_init(chip, size, sim_chip->wear_level);
+ if (error) {
+ mtx_destroy(&chip->ns_lock);
+ free(chip, M_NANDSIM);
+ return (NULL);
+ }
+
+ error = nandsim_bbm_init(chip, size, sim_chip->bad_block_map);
+ if (error) {
+ mtx_destroy(&chip->ns_lock);
+ nandsim_blk_state_destroy(chip);
+ free(chip, M_NANDSIM);
+ return (NULL);
+ }
+
+ nandsim_start_handler(chip, poweron_evh);
+
+ nand_debug(NDBG_SIM,"Create thread for chip%d [%8p]", chip->chip_num,
+ chip);
+ /* Create chip thread */
+ error = kproc_kthread_add(nandsim_loop, chip, &nandsim_proc,
+ &chip->nandsim_td, RFSTOPPED | RFHIGHPID,
+ 0, "nandsim", "chip");
+ if (error) {
+ mtx_destroy(&chip->ns_lock);
+ nandsim_blk_state_destroy(chip);
+ free(chip, M_NANDSIM);
+ return (NULL);
+ }
+
+ thread_lock(chip->nandsim_td);
+ sched_class(chip->nandsim_td, PRI_REALTIME);
+ sched_add(chip->nandsim_td, SRQ_BORING);
+ thread_unlock(chip->nandsim_td);
+
+ size = (chip_param->bytes_per_page +
+ chip_param->spare_bytes_per_page) *
+ chip_param->pages_per_block;
+
+ sprintf(swapfile, "chip%d%d.swp", chip->ctrl_num, chip->chip_num);
+ chip->swap = nandsim_swap_init(swapfile, chip_param->blocks_per_lun *
+ chip_param->luns, size);
+ if (!chip->swap)
+ nandsim_chip_destroy(chip);
+
+ /* Wait for new thread to enter main loop */
+ tsleep(chip->nandsim_td, PWAIT, "ns_chip", 1 * hz);
+
+ return (chip);
+}
+
+static int
+nandsim_blk_state_init(struct nandsim_chip *chip, uint32_t size,
+ uint32_t wear_lev)
+{
+ int i;
+
+ if (!chip || size == 0)
+ return (-1);
+
+ chip->blk_state = malloc(size * sizeof(struct nandsim_block_state),
+ M_NANDSIM, M_WAITOK | M_ZERO);
+ if (!chip->blk_state) {
+ return (-1);
+ }
+
+ for (i = 0; i < size; i++) {
+ if (wear_lev)
+ chip->blk_state[i].wear_lev = wear_lev;
+ else
+ chip->blk_state[i].wear_lev = -1;
+ }
+
+ return (0);
+}
+
+static void
+nandsim_blk_state_destroy(struct nandsim_chip *chip)
+{
+
+ if (chip && chip->blk_state)
+ free(chip->blk_state, M_NANDSIM);
+}
+
+static int
+nandsim_bbm_init(struct nandsim_chip *chip, uint32_t size,
+ uint32_t *sim_bbm)
+{
+ uint32_t index;
+ int i;
+
+ if ((chip == NULL) || (size == 0))
+ return (-1);
+
+ if (chip->blk_state == NULL)
+ return (-1);
+
+ if (sim_bbm == NULL)
+ return (0);
+
+ for (i = 0; i < MAX_BAD_BLOCKS; i++) {
+ index = sim_bbm[i];
+
+ if (index == 0xffffffff)
+ break;
+ else if (index > size)
+ return (-1);
+ else
+ chip->blk_state[index].is_bad = 1;
+ }
+
+ return (0);
+}
+
+void
+nandsim_chip_destroy(struct nandsim_chip *chip)
+{
+ struct nandsim_ev *ev;
+
+ ev = create_event(chip, NANDSIM_EV_EXIT, 0);
+ if (ev)
+ send_event(ev);
+}
+
+void
+nandsim_chip_freeze(struct nandsim_chip *chip)
+{
+
+ chip->flags |= NANDSIM_CHIP_FROZEN;
+}
+
+static void
+nandsim_loop(void *arg)
+{
+ struct nandsim_chip *chip = (struct nandsim_chip *)arg;
+ struct nandsim_ev *ev;
+
+ nand_debug(NDBG_SIM,"Start main loop for chip%d [%8p]", chip->chip_num,
+ chip);
+ for(;;) {
+ NANDSIM_CHIP_LOCK(chip);
+ if (!(chip->flags & NANDSIM_CHIP_ACTIVE)) {
+ chip->flags |= NANDSIM_CHIP_ACTIVE;
+ wakeup(chip->nandsim_td);
+ }
+
+ if (STAILQ_EMPTY(&chip->nandsim_events)) {
+ nand_debug(NDBG_SIM,"Chip%d [%8p] going sleep",
+ chip->chip_num, chip);
+ msleep(chip, &chip->ns_lock, PRIBIO, "nandev", 0);
+ }
+
+ ev = STAILQ_FIRST(&chip->nandsim_events);
+ STAILQ_REMOVE_HEAD(&chip->nandsim_events, links);
+ NANDSIM_CHIP_UNLOCK(chip);
+ if (ev->type == NANDSIM_EV_EXIT) {
+ NANDSIM_CHIP_LOCK(chip);
+ destroy_event(ev);
+ wakeup(ev);
+ while (!STAILQ_EMPTY(&chip->nandsim_events)) {
+ ev = STAILQ_FIRST(&chip->nandsim_events);
+ STAILQ_REMOVE_HEAD(&chip->nandsim_events,
+ links);
+ destroy_event(ev);
+ wakeup(ev);
+ };
+ NANDSIM_CHIP_UNLOCK(chip);
+ nandsim_log(chip, NANDSIM_LOG_SM, "destroyed\n");
+ mtx_destroy(&chip->ns_lock);
+ nandsim_blk_state_destroy(chip);
+ nandsim_swap_destroy(chip->swap);
+ free(chip, M_NANDSIM);
+ nandsim_proc = NULL;
+
+ kthread_exit();
+ }
+
+ if (!(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ nand_debug(NDBG_SIM,"Chip [%x] get event [%x]",
+ chip->chip_num, ev->type);
+ chip->ev_handler(chip, ev->type, ev->data);
+ }
+
+ wakeup(ev);
+ destroy_event(ev);
+ }
+
+}
+
+struct nandsim_ev *
+create_event(struct nandsim_chip *chip, uint8_t type, uint8_t data_size)
+{
+ struct nandsim_ev *ev;
+
+ ev = malloc(sizeof(*ev), M_NANDSIM, M_NOWAIT | M_ZERO);
+ if (!ev) {
+ nand_debug(NDBG_SIM,"Cannot create event");
+ return (NULL);
+ }
+
+ if (data_size > 0)
+ ev->data = malloc(sizeof(*ev), M_NANDSIM, M_NOWAIT | M_ZERO);
+ ev->type = type;
+ ev->chip = chip;
+
+ return (ev);
+}
+
+void
+destroy_event(struct nandsim_ev *ev)
+{
+
+ if (ev->data)
+ free(ev->data, M_NANDSIM);
+ free(ev, M_NANDSIM);
+}
+
+int
+send_event(struct nandsim_ev *ev)
+{
+ struct nandsim_chip *chip = ev->chip;
+
+ if (!(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ nand_debug(NDBG_SIM,"Chip%d [%p] send event %x",
+ chip->chip_num, chip, ev->type);
+
+ NANDSIM_CHIP_LOCK(chip);
+ STAILQ_INSERT_TAIL(&chip->nandsim_events, ev, links);
+ NANDSIM_CHIP_UNLOCK(chip);
+
+ wakeup(chip);
+ if ((ev->type != NANDSIM_EV_TIMEOUT) && chip->nandsim_td &&
+ (curthread != chip->nandsim_td))
+ tsleep(ev, PWAIT, "ns_ev", 5 * hz);
+ }
+
+ return (0);
+}
+
+static void
+nandsim_callout_eh(void *arg)
+{
+ struct nandsim_ev *ev = (struct nandsim_ev *)arg;
+
+ send_event(ev);
+}
+
+static int
+nandsim_delay(struct nandsim_chip *chip, int timeout)
+{
+ struct nandsim_ev *ev;
+ struct timeval delay;
+ int tm;
+
+ nand_debug(NDBG_SIM,"Chip[%d] Set delay: %d", chip->chip_num, timeout);
+
+ ev = create_event(chip, NANDSIM_EV_TIMEOUT, 0);
+ if (!ev)
+ return (-1);
+
+ chip->sm_state = NANDSIM_STATE_TIMEOUT;
+ tm = (timeout/10000) * (hz / 100);
+ if (callout_reset(&chip->ns_callout, tm, nandsim_callout_eh, ev))
+ return (-1);
+
+ delay.tv_sec = chip->read_delay / 1000000;
+ delay.tv_usec = chip->read_delay % 1000000;
+ timevaladd(&chip->delay_tv, &delay);
+
+ return (0);
+}
+
+static void
+nandsim_start_handler(struct nandsim_chip *chip, nandsim_evh_t evh)
+{
+ struct nandsim_ev *ev;
+
+ chip->ev_handler = evh;
+
+ nand_debug(NDBG_SIM,"Start handler %p for chip%d [%p]", evh,
+ chip->chip_num, chip);
+ ev = create_event(chip, NANDSIM_EV_START, 0);
+ if (!ev)
+ nandsim_sm_error(chip);
+
+ send_event(ev);
+}
+
+static void
+nandchip_set_data(struct nandsim_chip *chip, uint8_t *data, uint32_t len,
+ uint32_t idx)
+{
+
+ nand_debug(NDBG_SIM,"Chip [%x] data %p [%x] at %x", chip->chip_num,
+ data, len, idx);
+ chip->data.data_ptr = data;
+ chip->data.size = len;
+ chip->data.index = idx;
+}
+
+static int
+nandchip_chip_space(struct nandsim_chip *chip, int32_t row, int32_t column,
+ size_t size, uint8_t writing)
+{
+ struct block_space *blk_space;
+ uint32_t lun, block, page, offset, block_size;
+ int err;
+
+ block_size = chip->cg.block_size +
+ (chip->cg.oob_size * chip->cg.pgs_per_blk);
+
+ err = nand_row_to_blkpg(&chip->cg, row, &lun, &block, &page);
+ if (err) {
+ nand_debug(NDBG_SIM,"cannot get address\n");
+ return (-1);
+ }
+
+ if (!nandchip_is_block_valid(chip, block)) {
+ nandchip_set_data(chip, NULL, 0, 0);
+ return (-1);
+ }
+
+ blk_space = get_bs(chip->swap, block, writing);
+ if (!blk_space) {
+ nandchip_set_data(chip, NULL, 0, 0);
+ return (-1);
+ }
+
+ if (size > block_size)
+ size = block_size;
+
+ if (size == block_size) {
+ offset = 0;
+ column = 0;
+ } else
+ offset = page * (chip->cg.page_size + chip->cg.oob_size);
+
+ nandchip_set_data(chip, &blk_space->blk_ptr[offset], size, column);
+
+ return (0);
+}
+
+static int
+nandchip_get_addr_byte(struct nandsim_chip *chip, void *data, uint32_t *value)
+{
+ int ncycles = 0;
+ uint8_t byte;
+ uint8_t *buffer;
+
+ buffer = (uint8_t *)value;
+ byte = *((uint8_t *)data);
+
+ KASSERT((chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW ||
+ chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL),
+ ("unexpected state"));
+
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ ncycles = chip->params.address_cycles & 0xf;
+ buffer[chip->sm_addr_cycle++] = byte;
+ } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
+ ncycles = (chip->params.address_cycles >> 4) & 0xf;
+ buffer[chip->sm_addr_cycle++] = byte;
+ }
+
+ nand_debug(NDBG_SIM, "Chip [%x] read addr byte: %02x (%d of %d)\n",
+ chip->chip_num, byte, chip->sm_addr_cycle, ncycles);
+
+ if (chip->sm_addr_cycle == ncycles) {
+ chip->sm_addr_cycle = 0;
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+nandchip_is_block_valid(struct nandsim_chip *chip, int block_num)
+{
+
+ if (!chip || !chip->blk_state)
+ return (0);
+
+ if (chip->blk_state[block_num].wear_lev == 0 ||
+ chip->blk_state[block_num].is_bad)
+ return (0);
+
+ return (1);
+}
+
+static void
+nandchip_set_status(struct nandsim_chip *chip, uint8_t flags)
+{
+
+ chip->chip_status |= flags;
+}
+
+static void
+nandchip_clear_status(struct nandsim_chip *chip, uint8_t flags)
+{
+
+ chip->chip_status &= ~flags;
+}
+
+uint8_t
+nandchip_get_status(struct nandsim_chip *chip)
+{
+ return (chip->chip_status);
+}
+
+void
+nandsim_chip_timeout(struct nandsim_chip *chip)
+{
+ struct timeval tv;
+
+ getmicrotime(&tv);
+
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT &&
+ timevalcmp(&tv, &chip->delay_tv, >=)) {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ }
+}
+void
+poweron_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ uint8_t cmd;
+
+ if (type == NANDSIM_EV_START)
+ chip->sm_state = NANDSIM_STATE_IDLE;
+ else if (type == NANDSIM_EV_CMD) {
+ cmd = *(uint8_t *)data;
+ switch(cmd) {
+ case NAND_CMD_RESET:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in RESET state\n");
+ nandsim_start_handler(chip, reset_evh);
+ break;
+ default:
+ nandsim_undefined(chip, type);
+ break;
+ }
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+idle_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ uint8_t cmd;
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in IDLE state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else if (type == NANDSIM_EV_CMD) {
+ nandchip_clear_status(chip, NAND_STATUS_FAIL);
+ getmicrotime(&chip->delay_tv);
+ cmd = *(uint8_t *)data;
+ switch(cmd) {
+ case NAND_CMD_READ_ID:
+ nandsim_start_handler(chip, readid_evh);
+ break;
+ case NAND_CMD_READ_PARAMETER:
+ nandsim_start_handler(chip, readparam_evh);
+ break;
+ case NAND_CMD_READ:
+ nandsim_start_handler(chip, read_evh);
+ break;
+ case NAND_CMD_PROG:
+ nandsim_start_handler(chip, write_evh);
+ break;
+ case NAND_CMD_ERASE:
+ nandsim_start_handler(chip, erase_evh);
+ break;
+ default:
+ nandsim_undefined(chip, type);
+ break;
+ }
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+readid_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ struct onfi_params *params;
+ uint8_t addr;
+
+ params = &chip->params;
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in READID state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_BYTE;
+ } else if (type == NANDSIM_EV_ADDR) {
+
+ addr = *((uint8_t *)data);
+
+ if (addr == 0x0)
+ nandchip_set_data(chip, (uint8_t *)&chip->id, 2, 0);
+ else if (addr == ONFI_SIG_ADDR)
+ nandchip_set_data(chip, (uint8_t *)&params->signature,
+ 4, 0);
+ else
+ nandsim_bad_address(chip, &addr);
+
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+readparam_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ struct onfi_params *params;
+ uint8_t addr;
+
+ params = &chip->params;
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in READPARAM state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_BYTE;
+ } else if (type == NANDSIM_EV_ADDR) {
+ addr = *((uint8_t *)data);
+
+ if (addr == 0) {
+ nandchip_set_data(chip, (uint8_t *)params,
+ sizeof(*params), 0);
+ } else
+ nandsim_bad_address(chip, &addr);
+
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+read_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ static uint32_t column = 0, row = 0;
+ uint32_t size;
+ uint8_t cmd;
+
+ size = chip->cg.page_size + chip->cg.oob_size;
+
+ switch (type) {
+ case NANDSIM_EV_START:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in READ state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_COL;
+ break;
+ case NANDSIM_EV_ADDR:
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
+ if (nandchip_get_addr_byte(chip, data, &column))
+ break;
+
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
+ } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ if (nandchip_get_addr_byte(chip, data, &row))
+ break;
+
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else
+ nandsim_ignore_address(chip, *((uint8_t *)data));
+ break;
+ case NANDSIM_EV_CMD:
+ cmd = *(uint8_t *)data;
+ if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
+ cmd == NAND_CMD_READ_END) {
+ if (chip->read_delay != 0 &&
+ nandsim_delay(chip, chip->read_delay) == 0)
+ nandchip_clear_status(chip, NAND_STATUS_RDY);
+ else {
+ nandchip_chip_space(chip, row, column, size, 0);
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ }
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ case NANDSIM_EV_TIMEOUT:
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
+ nandchip_chip_space(chip, row, column, size, 0);
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ }
+}
+void
+write_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ static uint32_t column, row;
+ uint32_t size;
+ uint8_t cmd;
+ int err;
+
+ size = chip->cg.page_size + chip->cg.oob_size;
+
+ switch(type) {
+ case NANDSIM_EV_START:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in WRITE state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_COL;
+ break;
+ case NANDSIM_EV_ADDR:
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
+ if (nandchip_get_addr_byte(chip, data, &column))
+ break;
+
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
+ } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ if (nandchip_get_addr_byte(chip, data, &row))
+ break;
+
+ err = nandchip_chip_space(chip, row, column, size, 1);
+ if (err == -1)
+ nandchip_set_status(chip, NAND_STATUS_FAIL);
+
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else
+ nandsim_ignore_address(chip, *((uint8_t *)data));
+ break;
+ case NANDSIM_EV_CMD:
+ cmd = *(uint8_t *)data;
+ if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
+ cmd == NAND_CMD_PROG_END) {
+ if (chip->prog_delay != 0 &&
+ nandsim_delay(chip, chip->prog_delay) == 0)
+ nandchip_clear_status(chip, NAND_STATUS_RDY);
+ else {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ }
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ case NANDSIM_EV_TIMEOUT:
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
+ nandsim_start_handler(chip, idle_evh);
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ }
+}
+
+void
+erase_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ static uint32_t row, block_size;
+ uint32_t lun, block, page;
+ int err;
+ uint8_t cmd;
+
+ block_size = chip->cg.block_size +
+ (chip->cg.oob_size * chip->cg.pgs_per_blk);
+
+ switch (type) {
+ case NANDSIM_EV_START:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in ERASE state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
+ break;
+ case NANDSIM_EV_CMD:
+ cmd = *(uint8_t *)data;
+ if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
+ cmd == NAND_CMD_ERASE_END) {
+ if (chip->data.data_ptr != NULL &&
+ chip->data.size == block_size)
+ memset(chip->data.data_ptr, 0xff, block_size);
+ else
+ nand_debug(NDBG_SIM,"Bad block erase data\n");
+
+ err = nand_row_to_blkpg(&chip->cg, row, &lun,
+ &block, &page);
+ if (!err) {
+ if (chip->blk_state[block].wear_lev > 0)
+ chip->blk_state[block].wear_lev--;
+ }
+
+ if (chip->erase_delay != 0 &&
+ nandsim_delay(chip, chip->erase_delay) == 0)
+ nandchip_clear_status(chip, NAND_STATUS_RDY);
+ else {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ }
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ case NANDSIM_EV_ADDR:
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ if (nandchip_get_addr_byte(chip, data, &row))
+ break;
+
+ err = nandchip_chip_space(chip, row, 0, block_size, 1);
+ if (err == -1) {
+ nandchip_set_status(chip, NAND_STATUS_FAIL);
+ }
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else
+ nandsim_ignore_address(chip, *((uint8_t *)data));
+ break;
+ case NANDSIM_EV_TIMEOUT:
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ }
+}
+
+void
+reset_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in RESET state\n");
+ chip->sm_state = NANDSIM_STATE_TIMEOUT;
+ nandchip_set_data(chip, NULL, 0, 0);
+ DELAY(500);
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+}
+
+static void
+nandsim_undefined(struct nandsim_chip *chip, uint8_t type)
+{
+
+ nandsim_log(chip, NANDSIM_LOG_ERR,
+ "ERR: Chip received ev %x in state %x\n",
+ type, chip->sm_state);
+ nandsim_start_handler(chip, idle_evh);
+}
+
+static void
+nandsim_bad_address(struct nandsim_chip *chip, uint8_t *addr)
+{
+
+ nandsim_log(chip, NANDSIM_LOG_ERR,
+ "ERR: Chip received out of range address"
+ "%02x%02x - %02x%02x%02x\n", addr[0], addr[1], addr[2],
+ addr[3], addr[4]);
+}
+
+static void
+nandsim_ignore_address(struct nandsim_chip *chip, uint8_t byte)
+{
+ nandsim_log(chip, NANDSIM_LOG_SM, "ignored address byte: %d\n", byte);
+}
+
+static void
+nandsim_sm_error(struct nandsim_chip *chip)
+{
+
+ nandsim_log(chip, NANDSIM_LOG_ERR, "ERR: State machine error."
+ "Restart required.\n");
+}
diff --git a/sys/dev/nand/nandsim_chip.h b/sys/dev/nand/nandsim_chip.h
new file mode 100644
index 000000000000..a6fb2346ded7
--- /dev/null
+++ b/sys/dev/nand/nandsim_chip.h
@@ -0,0 +1,159 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NANDSIM_CHIP_H
+#define _NANDSIM_CHIP_H
+
+#include <sys/malloc.h>
+#include <sys/callout.h>
+#include <dev/nand/nand.h>
+#include <dev/nand/nandsim.h>
+#include <dev/nand/nandsim_swap.h>
+
+MALLOC_DECLARE(M_NANDSIM);
+
+#define MAX_CS_NUM 4
+struct nandsim_chip;
+
+typedef void nandsim_evh_t(struct nandsim_chip *chip, uint32_t ev, void *data);
+
+enum addr_type {
+ ADDR_NONE,
+ ADDR_ID,
+ ADDR_ROW,
+ ADDR_ROWCOL
+};
+
+struct nandsim_softc {
+ struct nand_softc nand_dev;
+ device_t dev;
+
+ struct nandsim_chip *chips[MAX_CS_NUM];
+ struct nandsim_chip *active_chip;
+
+ uint8_t address_cycle;
+ enum addr_type address_type;
+ int log_idx;
+ char *log_buff;
+ struct alq *alq;
+};
+
+struct nandsim_ev {
+ STAILQ_ENTRY(nandsim_ev) links;
+ struct nandsim_chip *chip;
+ uint8_t type;
+ void *data;
+};
+
+struct nandsim_data {
+ uint8_t *data_ptr;
+ uint32_t index;
+ uint32_t size;
+};
+
+struct nandsim_block_state {
+ int32_t wear_lev;
+ uint8_t is_bad;
+};
+
+#define NANDSIM_CHIP_ACTIVE 0x1
+#define NANDSIM_CHIP_FROZEN 0x2
+#define NANDSIM_CHIP_GET_STATUS 0x4
+
+struct nandsim_chip {
+ struct nandsim_softc *sc;
+ struct thread *nandsim_td;
+
+ STAILQ_HEAD(, nandsim_ev) nandsim_events;
+ nandsim_evh_t *ev_handler;
+ struct mtx ns_lock;
+ struct callout ns_callout;
+
+ struct chip_geom cg;
+ struct nand_id id;
+ struct onfi_params params;
+ struct nandsim_data data;
+ struct nandsim_block_state *blk_state;
+
+ struct chip_swap *swap;
+
+ uint32_t error_ratio;
+ uint32_t wear_level;
+ uint32_t sm_state;
+ uint32_t sm_addr_cycle;
+
+ uint32_t erase_delay;
+ uint32_t prog_delay;
+ uint32_t read_delay;
+ struct timeval delay_tv;
+
+ uint8_t flags;
+ uint8_t chip_status;
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+};
+
+struct sim_ctrl_conf {
+ uint8_t num;
+ uint8_t num_cs;
+ uint8_t ecc;
+ uint8_t running;
+ uint8_t created;
+ device_t sim_ctrl_dev;
+ struct sim_chip *chips[MAX_CTRL_CS];
+ uint16_t ecc_layout[MAX_ECC_BYTES];
+ char filename[FILENAME_SIZE];
+};
+
+#define NANDSIM_STATE_IDLE 0x0
+#define NANDSIM_STATE_WAIT_ADDR_BYTE 0x1
+#define NANDSIM_STATE_WAIT_CMD 0x2
+#define NANDSIM_STATE_TIMEOUT 0x3
+#define NANDSIM_STATE_WAIT_ADDR_ROW 0x4
+#define NANDSIM_STATE_WAIT_ADDR_COL 0x5
+
+#define NANDSIM_EV_START 0x1
+#define NANDSIM_EV_CMD 0x2
+#define NANDSIM_EV_ADDR 0x3
+#define NANDSIM_EV_TIMEOUT 0x4
+#define NANDSIM_EV_EXIT 0xff
+
+struct nandsim_chip *nandsim_chip_init(struct nandsim_softc *,
+ uint8_t, struct sim_chip *);
+void nandsim_chip_destroy(struct nandsim_chip *);
+void nandsim_chip_freeze(struct nandsim_chip *);
+void nandsim_chip_timeout(struct nandsim_chip *);
+int nandsim_chip_check_bad_block(struct nandsim_chip *, int);
+
+uint8_t nandchip_get_status(struct nandsim_chip *);
+
+void destroy_event(struct nandsim_ev *);
+int send_event(struct nandsim_ev *);
+struct nandsim_ev *create_event(struct nandsim_chip *, uint8_t, uint8_t);
+
+#endif /* _NANDSIM_CHIP_H */
diff --git a/sys/dev/nand/nandsim_ctrl.c b/sys/dev/nand/nandsim_ctrl.c
new file mode 100644
index 000000000000..5f7b01979203
--- /dev/null
+++ b/sys/dev/nand/nandsim_ctrl.c
@@ -0,0 +1,396 @@
+/*-
+ * 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.
+ */
+
+/* Simulated NAND controller driver */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/time.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include <dev/nand/nandsim.h>
+#include <dev/nand/nandsim_log.h>
+#include <dev/nand/nandsim_chip.h>
+#include "nfc_if.h"
+
+#define ADDRESS_SIZE 5
+
+extern struct sim_ctrl_conf ctrls[MAX_SIM_DEV];
+
+static void byte_corrupt(struct nandsim_chip *, uint8_t *);
+
+static int nandsim_attach(device_t);
+static int nandsim_detach(device_t);
+static int nandsim_probe(device_t);
+
+static uint8_t nandsim_read_byte(device_t);
+static uint16_t nandsim_read_word(device_t);
+static int nandsim_select_cs(device_t, uint8_t);
+static void nandsim_write_byte(device_t, uint8_t);
+static void nandsim_write_word(device_t, uint16_t);
+static void nandsim_read_buf(device_t, void *, uint32_t);
+static void nandsim_write_buf(device_t, void *, uint32_t);
+static int nandsim_send_command(device_t, uint8_t);
+static int nandsim_send_address(device_t, uint8_t);
+
+static device_method_t nandsim_methods[] = {
+ DEVMETHOD(device_probe, nandsim_probe),
+ DEVMETHOD(device_attach, nandsim_attach),
+ DEVMETHOD(device_detach, nandsim_detach),
+
+ DEVMETHOD(nfc_select_cs, nandsim_select_cs),
+ DEVMETHOD(nfc_send_command, nandsim_send_command),
+ DEVMETHOD(nfc_send_address, nandsim_send_address),
+ DEVMETHOD(nfc_read_byte, nandsim_read_byte),
+ DEVMETHOD(nfc_read_word, nandsim_read_word),
+ DEVMETHOD(nfc_write_byte, nandsim_write_byte),
+ DEVMETHOD(nfc_read_buf, nandsim_read_buf),
+ DEVMETHOD(nfc_write_buf, nandsim_write_buf),
+
+ { 0, 0 },
+};
+
+static driver_t nandsim_driver = {
+ "nandsim",
+ nandsim_methods,
+ sizeof(struct nandsim_softc),
+};
+
+static devclass_t nandsim_devclass;
+DRIVER_MODULE(nandsim, nexus, nandsim_driver, nandsim_devclass, 0, 0);
+DRIVER_MODULE(nandbus, nandsim, nandbus_driver, nandbus_devclass, 0, 0);
+
+static int
+nandsim_probe(device_t dev)
+{
+
+ device_set_desc(dev, "NAND controller simulator");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+nandsim_attach(device_t dev)
+{
+ struct nandsim_softc *sc;
+ struct sim_ctrl_conf *params;
+ struct sim_chip *chip;
+ uint16_t *eccpos;
+ int i, err;
+
+ sc = device_get_softc(dev);
+ params = &ctrls[device_get_unit(dev)];
+
+ if (strlen(params->filename) == 0)
+ snprintf(params->filename, FILENAME_SIZE, "ctrl%d.log",
+ params->num);
+
+ nandsim_log_init(sc, params->filename);
+ for (i = 0; i < params->num_cs; i++) {
+ chip = params->chips[i];
+ if (chip && chip->device_id != 0) {
+ sc->chips[i] = nandsim_chip_init(sc, i, chip);
+ if (chip->features & ONFI_FEAT_16BIT)
+ sc->nand_dev.flags |= NAND_16_BIT;
+ }
+ }
+
+ if (params->ecc_layout[0] != 0xffff)
+ eccpos = params->ecc_layout;
+ else
+ eccpos = NULL;
+
+ nand_init(&sc->nand_dev, dev, params->ecc, 0, 0, eccpos, "nandsim");
+
+ err = nandbus_create(dev);
+
+ return (err);
+}
+
+static int
+nandsim_detach(device_t dev)
+{
+ struct nandsim_softc *sc;
+ struct sim_ctrl_conf *params;
+ int i;
+
+ sc = device_get_softc(dev);
+ params = &ctrls[device_get_unit(dev)];
+
+ for (i = 0; i < params->num_cs; i++)
+ if (sc->chips[i] != NULL)
+ nandsim_chip_destroy(sc->chips[i]);
+
+ nandsim_log_close(sc);
+
+ return (0);
+}
+
+static int
+nandsim_select_cs(device_t dev, uint8_t cs)
+{
+ struct nandsim_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (cs >= MAX_CS_NUM)
+ return (EINVAL);
+
+ sc->active_chip = sc->chips[cs];
+
+ if (sc->active_chip)
+ nandsim_log(sc->active_chip, NANDSIM_LOG_EV,
+ "Select cs %d\n", cs);
+
+ return (0);
+}
+
+static int
+nandsim_send_command(device_t dev, uint8_t command)
+{
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+ struct nandsim_ev *ev;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip == NULL)
+ return (0);
+
+ nandsim_log(chip, NANDSIM_LOG_EV, "Send command %x\n", command);
+
+ switch (command) {
+ case NAND_CMD_READ_ID:
+ case NAND_CMD_READ_PARAMETER:
+ sc->address_type = ADDR_ID;
+ break;
+ case NAND_CMD_ERASE:
+ sc->address_type = ADDR_ROW;
+ break;
+ case NAND_CMD_READ:
+ case NAND_CMD_PROG:
+ sc->address_type = ADDR_ROWCOL;
+ break;
+ default:
+ sc->address_type = ADDR_NONE;
+ break;
+ }
+
+ if (command == NAND_CMD_STATUS)
+ chip->flags |= NANDSIM_CHIP_GET_STATUS;
+ else {
+ ev = create_event(chip, NANDSIM_EV_CMD, 1);
+ *(uint8_t *)ev->data = command;
+ send_event(ev);
+ }
+
+ return (0);
+}
+
+static int
+nandsim_send_address(device_t dev, uint8_t addr)
+{
+ struct nandsim_ev *ev;
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip == NULL)
+ return (0);
+
+ KASSERT((sc->address_type != ADDR_NONE), ("unexpected address"));
+ nandsim_log(chip, NANDSIM_LOG_EV, "Send addr %x\n", addr);
+
+ ev = create_event(chip, NANDSIM_EV_ADDR, 1);
+
+ *((uint8_t *)(ev->data)) = addr;
+
+ send_event(ev);
+ return (0);
+}
+
+static uint8_t
+nandsim_read_byte(device_t dev)
+{
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+ uint8_t ret = 0xff;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ if (chip->flags & NANDSIM_CHIP_GET_STATUS) {
+ nandsim_chip_timeout(chip);
+ ret = nandchip_get_status(chip);
+ chip->flags &= ~NANDSIM_CHIP_GET_STATUS;
+ } else if (chip->data.index < chip->data.size) {
+ ret = chip->data.data_ptr[chip->data.index++];
+ byte_corrupt(chip, &ret);
+ }
+ nandsim_log(chip, NANDSIM_LOG_DATA, "read %02x\n", ret);
+ }
+
+ return (ret);
+}
+
+static uint16_t
+nandsim_read_word(device_t dev)
+{
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+ uint16_t *data_ptr;
+ uint16_t ret = 0xffff;
+ uint8_t *byte_ret = (uint8_t *)&ret;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ if (chip->data.index < chip->data.size - 1) {
+ data_ptr =
+ (uint16_t *)&(chip->data.data_ptr[chip->data.index]);
+ ret = *data_ptr;
+ chip->data.index += 2;
+ byte_corrupt(chip, byte_ret);
+ byte_corrupt(chip, byte_ret + 1);
+ }
+ nandsim_log(chip, NANDSIM_LOG_DATA, "read %04x\n", ret);
+ }
+
+ return (ret);
+}
+
+static void
+nandsim_write_byte(device_t dev, uint8_t byte)
+{
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN) &&
+ (chip->data.index < chip->data.size)) {
+ byte_corrupt(chip, &byte);
+ chip->data.data_ptr[chip->data.index] &= byte;
+ chip->data.index++;
+ nandsim_log(chip, NANDSIM_LOG_DATA, "write %02x\n", byte);
+ }
+}
+
+static void
+nandsim_write_word(device_t dev, uint16_t word)
+{
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+ uint16_t *data_ptr;
+ uint8_t *byte_ptr = (uint8_t *)&word;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ if ((chip->data.index + 1) < chip->data.size) {
+ byte_corrupt(chip, byte_ptr);
+ byte_corrupt(chip, byte_ptr + 1);
+ data_ptr =
+ (uint16_t *)&(chip->data.data_ptr[chip->data.index]);
+ *data_ptr &= word;
+ chip->data.index += 2;
+ }
+
+ nandsim_log(chip, NANDSIM_LOG_DATA, "write %04x\n", word);
+ }
+}
+
+static void
+nandsim_read_buf(device_t dev, void *buf, uint32_t len)
+{
+ struct nandsim_softc *sc;
+ uint16_t *buf16 = (uint16_t *)buf;
+ uint8_t *buf8 = (uint8_t *)buf;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ if (sc->nand_dev.flags & NAND_16_BIT) {
+ for (i = 0; i < len / 2; i++)
+ buf16[i] = nandsim_read_word(dev);
+ } else {
+ for (i = 0; i < len; i++)
+ buf8[i] = nandsim_read_byte(dev);
+ }
+}
+
+static void
+nandsim_write_buf(device_t dev, void *buf, uint32_t len)
+{
+ struct nandsim_softc *sc;
+ uint16_t *buf16 = (uint16_t *)buf;
+ uint8_t *buf8 = (uint8_t *)buf;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ if (sc->nand_dev.flags & NAND_16_BIT) {
+ for (i = 0; i < len / 2; i++)
+ nandsim_write_word(dev, buf16[i]);
+ } else {
+ for (i = 0; i < len; i++)
+ nandsim_write_byte(dev, buf8[i]);
+ }
+}
+
+static void
+byte_corrupt(struct nandsim_chip *chip, uint8_t *byte)
+{
+ uint32_t rand;
+ uint8_t bit;
+
+ rand = random();
+ if ((rand % 1000000) < chip->error_ratio) {
+ bit = rand % 8;
+ if (*byte & (1 << bit))
+ *byte &= ~(1 << bit);
+ else
+ *byte |= (1 << bit);
+ }
+}
diff --git a/sys/dev/nand/nandsim_log.c b/sys/dev/nand/nandsim_log.c
new file mode 100644
index 000000000000..c51118f0dda8
--- /dev/null
+++ b/sys/dev/nand/nandsim_log.c
@@ -0,0 +1,186 @@
+/*-
+ * 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/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/alq.h>
+#include <sys/time.h>
+
+#include <machine/stdarg.h>
+
+#include <dev/nand/nandsim_log.h>
+
+int nandsim_log_level;
+int nandsim_log_output;
+int log_size = NANDSIM_RAM_LOG_SIZE;
+
+static int nandsim_entry_size = NANDSIM_ENTRY_SIZE;
+static int nandsim_entry_count = NANDSIM_ENTRY_COUNT;
+static int str_index = 0;
+static char string[NANDSIM_ENTRY_SIZE + 1] = {0};
+
+int
+nandsim_log_init(struct nandsim_softc *sc, char *filename)
+{
+ int error = 0;
+
+ if (nandsim_log_output == NANDSIM_OUTPUT_FILE) {
+ error = alq_open(&sc->alq, filename,
+ curthread->td_ucred, 0644,
+ nandsim_entry_size, nandsim_entry_count);
+ } else if (nandsim_log_output == NANDSIM_OUTPUT_RAM) {
+ sc->log_buff = malloc(log_size, M_NANDSIM, M_WAITOK | M_ZERO);
+ if (!sc->log_buff)
+ error = ENOMEM;
+ }
+
+ return (error);
+}
+
+void
+nandsim_log_close(struct nandsim_softc *sc)
+{
+
+ if (nandsim_log_output == NANDSIM_OUTPUT_FILE) {
+ memset(&string[str_index], 0, NANDSIM_ENTRY_SIZE - str_index);
+ alq_write(sc->alq, (void *) string, ALQ_NOWAIT);
+ str_index = 0;
+ string[0] = '\0';
+ alq_close(sc->alq);
+ } else if (nandsim_log_output == NANDSIM_OUTPUT_RAM) {
+ free(sc->log_buff, M_NANDSIM);
+ sc->log_buff = NULL;
+ }
+}
+
+void
+nandsim_log(struct nandsim_chip *chip, int level, const char *fmt, ...)
+{
+ char hdr[TIME_STR_SIZE];
+ char tmp[NANDSIM_ENTRY_SIZE];
+ struct nandsim_softc *sc;
+ struct timeval currtime;
+ va_list ap;
+ int hdr_len, len, rest;
+
+ if (nandsim_log_output == NANDSIM_OUTPUT_NONE)
+ return;
+
+ if (chip == NULL)
+ return;
+
+ sc = chip->sc;
+ if (!sc->alq && nandsim_log_output == NANDSIM_OUTPUT_FILE)
+ return;
+
+ if (level <= nandsim_log_level) {
+ microtime(&currtime);
+ hdr_len = sprintf(hdr, "%08jd.%08li [chip:%d, ctrl:%d]: ",
+ (intmax_t)currtime.tv_sec, currtime.tv_usec,
+ chip->chip_num, chip->ctrl_num);
+
+ switch(nandsim_log_output) {
+ case NANDSIM_OUTPUT_CONSOLE:
+ printf("%s", hdr);
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ break;
+ case NANDSIM_OUTPUT_RAM:
+ va_start(ap, fmt);
+ len = vsnprintf(tmp, NANDSIM_ENTRY_SIZE - 1, fmt, ap);
+ tmp[NANDSIM_ENTRY_SIZE - 1] = 0;
+ va_end(ap);
+
+ rest = log_size - sc->log_idx - 1;
+ if (rest >= hdr_len) {
+ bcopy(hdr, &sc->log_buff[sc->log_idx],
+ hdr_len);
+ sc->log_idx += hdr_len;
+ sc->log_buff[sc->log_idx] = 0;
+ } else {
+ bcopy(hdr, &sc->log_buff[sc->log_idx], rest);
+ bcopy(&hdr[rest], sc->log_buff,
+ hdr_len - rest);
+ sc->log_idx = hdr_len - rest;
+ sc->log_buff[sc->log_idx] = 0;
+ }
+
+ rest = log_size - sc->log_idx - 1;
+ if (rest >= len) {
+ bcopy(tmp, &sc->log_buff[sc->log_idx], len);
+ sc->log_idx += len;
+ sc->log_buff[sc->log_idx] = 0;
+ } else {
+ bcopy(tmp, &sc->log_buff[sc->log_idx], rest);
+ bcopy(&tmp[rest], sc->log_buff, len - rest);
+ sc->log_idx = len - rest;
+ sc->log_buff[sc->log_idx] = 0;
+ }
+
+ break;
+
+ case NANDSIM_OUTPUT_FILE:
+ va_start(ap, fmt);
+ len = vsnprintf(tmp, NANDSIM_ENTRY_SIZE - 1, fmt, ap);
+ tmp[NANDSIM_ENTRY_SIZE - 1] = 0;
+ va_end(ap);
+
+ rest = NANDSIM_ENTRY_SIZE - str_index;
+ if (rest >= hdr_len) {
+ strcat(string, hdr);
+ str_index += hdr_len;
+ } else {
+ strlcat(string, hdr, NANDSIM_ENTRY_SIZE + 1);
+ alq_write(sc->alq, (void *) string,
+ ALQ_NOWAIT);
+ strcpy(string, &hdr[rest]);
+ str_index = hdr_len - rest;
+ }
+ rest = NANDSIM_ENTRY_SIZE - str_index;
+ if (rest >= len) {
+ strcat(string, tmp);
+ str_index += len;
+ } else {
+ strlcat(string, tmp, NANDSIM_ENTRY_SIZE + 1);
+ alq_write(sc->alq, (void *) string,
+ ALQ_NOWAIT);
+ strcpy(string, &tmp[rest]);
+ str_index = len - rest;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/sys/dev/nand/nandsim_log.h b/sys/dev/nand/nandsim_log.h
new file mode 100644
index 000000000000..8cf108200df6
--- /dev/null
+++ b/sys/dev/nand/nandsim_log.h
@@ -0,0 +1,52 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NANDSIM_LOG_H
+#define _NANDSIM_LOG_H
+
+#include <dev/nand/nandsim_chip.h>
+
+#define NANDSIM_ENTRY_SIZE 128
+#define NANDSIM_ENTRY_COUNT 1024
+#define NANDSIM_RAM_LOG_SIZE 16384
+#define TIME_STR_SIZE 40
+
+#define NANDSIM_LOG_ERR 1
+#define NANDSIM_LOG_SM 5
+#define NANDSIM_LOG_EV 10
+#define NANDSIM_LOG_DATA 15
+
+extern int nandsim_log_level;
+extern int nandsim_log_output;
+
+int nandsim_log_init(struct nandsim_softc *, char *);
+void nandsim_log_close(struct nandsim_softc *);
+void nandsim_log(struct nandsim_chip *, int, const char *, ...);
+
+#endif /* _NANDSIM_LOG_H */
+
diff --git a/sys/dev/nand/nandsim_swap.c b/sys/dev/nand/nandsim_swap.c
new file mode 100644
index 000000000000..cc4201dbed88
--- /dev/null
+++ b/sys/dev/nand/nandsim_swap.c
@@ -0,0 +1,389 @@
+/*-
+ * 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/malloc.h>
+#include <sys/queue.h>
+#include <sys/fcntl.h>
+#include <sys/proc.h>
+#include <sys/namei.h>
+#include <sys/lock.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <dev/nand/nandsim_chip.h>
+#include <dev/nand/nandsim_swap.h>
+
+static int init_block_state(struct chip_swap *);
+static void destroy_block_state(struct chip_swap *);
+
+static int create_buffers(struct chip_swap *);
+static void destroy_buffers(struct chip_swap *);
+
+static int swap_file_open(struct chip_swap *, const char *);
+static void swap_file_close(struct chip_swap *);
+static int swap_file_write(struct chip_swap *, struct block_state *);
+static int swap_file_read(struct chip_swap *, struct block_state *);
+
+#define CHIP_SWAP_CMODE 0600
+#define CHIP_SWAP_BLOCKSPACES 2
+
+static int
+init_block_state(struct chip_swap *swap)
+{
+ struct block_state *blk_state;
+ int i;
+
+ if (swap == NULL)
+ return (-1);
+
+ blk_state = malloc(swap->nof_blks * sizeof(struct block_state),
+ M_NANDSIM, M_WAITOK | M_ZERO);
+
+ for (i = 0; i < swap->nof_blks; i++)
+ blk_state[i].offset = 0xffffffff;
+
+ swap->blk_state = blk_state;
+
+ return (0);
+}
+
+static void
+destroy_block_state(struct chip_swap *swap)
+{
+
+ if (swap == NULL)
+ return;
+
+ if (swap->blk_state != NULL)
+ free(swap->blk_state, M_NANDSIM);
+}
+
+static int
+create_buffers(struct chip_swap *swap)
+{
+ struct block_space *block_space;
+ void *block;
+ int i;
+
+ for (i = 0; i < CHIP_SWAP_BLOCKSPACES; i++) {
+ block_space = malloc(sizeof(*block_space), M_NANDSIM, M_WAITOK);
+ block = malloc(swap->blk_size, M_NANDSIM, M_WAITOK);
+ block_space->blk_ptr = block;
+ SLIST_INSERT_HEAD(&swap->free_bs, block_space, free_link);
+ nand_debug(NDBG_SIM,"created blk_space %p[%p]\n", block_space,
+ block);
+ }
+
+ if (i == 0)
+ return (-1);
+
+ return (0);
+}
+
+static void
+destroy_buffers(struct chip_swap *swap)
+{
+ struct block_space *blk_space;
+
+ if (swap == NULL)
+ return;
+
+ blk_space = SLIST_FIRST(&swap->free_bs);
+ while (blk_space) {
+ SLIST_REMOVE_HEAD(&swap->free_bs, free_link);
+ nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n",
+ blk_space, blk_space->blk_ptr);
+ free(blk_space->blk_ptr, M_NANDSIM);
+ free(blk_space, M_NANDSIM);
+ blk_space = SLIST_FIRST(&swap->free_bs);
+ }
+
+ blk_space = STAILQ_FIRST(&swap->used_bs);
+ while (blk_space) {
+ STAILQ_REMOVE_HEAD(&swap->used_bs, used_link);
+ nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n",
+ blk_space, blk_space->blk_ptr);
+ free(blk_space->blk_ptr, M_NANDSIM);
+ free(blk_space, M_NANDSIM);
+ blk_space = STAILQ_FIRST(&swap->used_bs);
+ }
+}
+
+static int
+swap_file_open(struct chip_swap *swap, const char *swap_file)
+{
+ struct nameidata nd;
+ int vfslocked, flags, error;
+
+ NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_SYSSPACE, swap_file,
+ curthread);
+
+ flags = FWRITE | FREAD | O_NOFOLLOW | O_CREAT | O_TRUNC;
+
+ error = vn_open(&nd, &flags, CHIP_SWAP_CMODE, NULL);
+ if (error) {
+ nand_debug(NDBG_SIM,"Cannot create swap file %s", swap_file);
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ return (error);
+ }
+
+ swap->swap_cred = crhold(curthread->td_ucred);
+ vfslocked = NDHASGIANT(&nd);
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+
+ /* We just unlock so we hold a reference */
+ VOP_UNLOCK(nd.ni_vp, 0);
+ VFS_UNLOCK_GIANT(vfslocked);
+
+ swap->swap_vp = nd.ni_vp;
+
+ return (0);
+}
+
+static void
+swap_file_close(struct chip_swap *swap)
+{
+
+ if (swap == NULL)
+ return;
+
+ if (swap->swap_vp == NULL)
+ return;
+
+ vn_close(swap->swap_vp, FWRITE, swap->swap_cred, curthread);
+ crfree(swap->swap_cred);
+}
+
+static int
+swap_file_write(struct chip_swap *swap, struct block_state *blk_state)
+{
+ struct block_space *blk_space;
+ struct thread *td;
+ struct mount *mp;
+ struct vnode *vp;
+ struct uio auio;
+ struct iovec aiov;
+ int vfslocked;
+
+ if (swap == NULL || blk_state == NULL)
+ return (-1);
+
+ blk_space = blk_state->blk_sp;
+ if (blk_state->offset == -1) {
+ blk_state->offset = swap->swap_offset;
+ swap->swap_offset += swap->blk_size;
+ }
+
+ nand_debug(NDBG_SIM,"saving %p[%p] at %x\n",
+ blk_space, blk_space->blk_ptr, blk_state->offset);
+
+ bzero(&aiov, sizeof(aiov));
+ bzero(&auio, sizeof(auio));
+
+ aiov.iov_base = blk_space->blk_ptr;
+ aiov.iov_len = swap->blk_size;
+ td = curthread;
+ vp = swap->swap_vp;
+
+ auio.uio_iov = &aiov;
+ auio.uio_offset = blk_state->offset;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = swap->blk_size;
+ auio.uio_td = td;
+
+ vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+ vn_start_write(vp, &mp, V_WAIT);
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+ VOP_WRITE(vp, &auio, IO_UNIT, swap->swap_cred);
+ VOP_UNLOCK(vp, 0);
+ vn_finished_write(mp);
+ VFS_UNLOCK_GIANT(vfslocked);
+
+ return (0);
+}
+
+static int
+swap_file_read(struct chip_swap *swap, struct block_state *blk_state)
+{
+ struct block_space *blk_space;
+ struct thread *td;
+ struct vnode *vp;
+ struct uio auio;
+ struct iovec aiov;
+ int vfslocked;
+
+ if (swap == NULL || blk_state == NULL)
+ return (-1);
+
+ blk_space = blk_state->blk_sp;
+
+ nand_debug(NDBG_SIM,"restore %p[%p] at %x\n",
+ blk_space, blk_space->blk_ptr, blk_state->offset);
+
+ bzero(&aiov, sizeof(aiov));
+ bzero(&auio, sizeof(auio));
+
+ aiov.iov_base = blk_space->blk_ptr;
+ aiov.iov_len = swap->blk_size;
+ td = curthread;
+ vp = swap->swap_vp;
+
+ auio.uio_iov = &aiov;
+ auio.uio_offset = blk_state->offset;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = swap->blk_size;
+ auio.uio_td = td;
+
+ vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+ VOP_READ(vp, &auio, 0, swap->swap_cred);
+ VOP_UNLOCK(vp, 0);
+ VFS_UNLOCK_GIANT(vfslocked);
+
+ return (0);
+}
+
+struct chip_swap *
+nandsim_swap_init(const char *swap_file, uint32_t nof_blks, uint32_t blk_size)
+{
+ struct chip_swap *swap;
+ int err = 0;
+
+ if ((swap_file == NULL) || (nof_blks == 0) || (blk_size == 0))
+ return (NULL);
+
+ swap = malloc(sizeof(*swap), M_NANDSIM, M_WAITOK | M_ZERO);
+
+ SLIST_INIT(&swap->free_bs);
+ STAILQ_INIT(&swap->used_bs);
+ swap->blk_size = blk_size;
+ swap->nof_blks = nof_blks;
+
+ err = init_block_state(swap);
+ if (err) {
+ nandsim_swap_destroy(swap);
+ return (NULL);
+ }
+
+ err = create_buffers(swap);
+ if (err) {
+ nandsim_swap_destroy(swap);
+ return (NULL);
+ }
+
+ err = swap_file_open(swap, swap_file);
+ if (err) {
+ nandsim_swap_destroy(swap);
+ return (NULL);
+ }
+
+ return (swap);
+}
+
+void
+nandsim_swap_destroy(struct chip_swap *swap)
+{
+
+ if (swap == NULL)
+ return;
+
+ destroy_block_state(swap);
+ destroy_buffers(swap);
+ swap_file_close(swap);
+ free(swap, M_NANDSIM);
+}
+
+struct block_space *
+get_bs(struct chip_swap *swap, uint32_t block, uint8_t writing)
+{
+ struct block_state *blk_state, *old_blk_state = NULL;
+ struct block_space *blk_space;
+
+ if (swap == NULL || (block >= swap->nof_blks))
+ return (NULL);
+
+ blk_state = &swap->blk_state[block];
+ nand_debug(NDBG_SIM,"blk_state %x\n", blk_state->status);
+
+ if (blk_state->status & BLOCK_ALLOCATED) {
+ blk_space = blk_state->blk_sp;
+ } else {
+ blk_space = SLIST_FIRST(&swap->free_bs);
+ if (blk_space) {
+ SLIST_REMOVE_HEAD(&swap->free_bs, free_link);
+ STAILQ_INSERT_TAIL(&swap->used_bs, blk_space,
+ used_link);
+ } else {
+ blk_space = STAILQ_FIRST(&swap->used_bs);
+ old_blk_state = blk_space->blk_state;
+ STAILQ_REMOVE_HEAD(&swap->used_bs, used_link);
+ STAILQ_INSERT_TAIL(&swap->used_bs, blk_space,
+ used_link);
+ if (old_blk_state->status & BLOCK_DIRTY) {
+ swap_file_write(swap, old_blk_state);
+ old_blk_state->status &= ~BLOCK_DIRTY;
+ old_blk_state->status |= BLOCK_SWAPPED;
+ }
+ }
+ }
+
+ if (blk_space == NULL)
+ return (NULL);
+
+ if (old_blk_state != NULL) {
+ old_blk_state->status &= ~BLOCK_ALLOCATED;
+ old_blk_state->blk_sp = NULL;
+ }
+
+ blk_state->blk_sp = blk_space;
+ blk_space->blk_state = blk_state;
+
+ if (!(blk_state->status & BLOCK_ALLOCATED)) {
+ if (blk_state->status & BLOCK_SWAPPED)
+ swap_file_read(swap, blk_state);
+ else
+ memset(blk_space->blk_ptr, 0xff, swap->blk_size);
+ blk_state->status |= BLOCK_ALLOCATED;
+ }
+
+ if (writing)
+ blk_state->status |= BLOCK_DIRTY;
+
+ nand_debug(NDBG_SIM,"get_bs returned %p[%p] state %x\n", blk_space,
+ blk_space->blk_ptr, blk_state->status);
+
+ return (blk_space);
+}
diff --git a/sys/dev/nand/nandsim_swap.h b/sys/dev/nand/nandsim_swap.h
new file mode 100644
index 000000000000..4fbae2a6827a
--- /dev/null
+++ b/sys/dev/nand/nandsim_swap.h
@@ -0,0 +1,64 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NANDSIM_SWAP_CHIP_H_
+#define _NANDSIM_SWAP_CHIP_H_
+
+struct block_space {
+ SLIST_ENTRY(block_space) free_link;
+ STAILQ_ENTRY(block_space) used_link;
+ struct block_state *blk_state;
+ uint8_t *blk_ptr;
+};
+
+#define BLOCK_ALLOCATED 0x1
+#define BLOCK_SWAPPED 0x2
+#define BLOCK_DIRTY 0x4
+
+struct block_state {
+ struct block_space *blk_sp;
+ uint32_t offset;
+ uint8_t status;
+};
+
+struct chip_swap {
+ struct block_state *blk_state;
+ SLIST_HEAD(,block_space) free_bs;
+ STAILQ_HEAD(,block_space) used_bs;
+ struct ucred *swap_cred;
+ struct vnode *swap_vp;
+ uint32_t swap_offset;
+ uint32_t blk_size;
+ uint32_t nof_blks;
+};
+
+struct chip_swap *nandsim_swap_init(const char *, uint32_t, uint32_t);
+void nandsim_swap_destroy(struct chip_swap *);
+struct block_space *get_bs(struct chip_swap *, uint32_t, uint8_t);
+
+#endif /* _NANDSIM_SWAP_CHIP_H_ */
diff --git a/sys/dev/nand/nfc_if.m b/sys/dev/nand/nfc_if.m
new file mode 100644
index 000000000000..a4e1099220ac
--- /dev/null
+++ b/sys/dev/nand/nfc_if.m
@@ -0,0 +1,165 @@
+#-
+# 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.
+#
+# $FreeBSD$
+
+# NAND controller interface description
+#
+
+#include <sys/bus.h>
+#include <dev/nand/nand.h>
+
+INTERFACE nfc;
+
+CODE {
+ static int nfc_default_method(device_t dev)
+ {
+ return (0);
+ }
+
+ static int nfc_softecc_get(device_t dev, void *buf, int pagesize,
+ void *ecc, int *needwrite)
+ {
+ *needwrite = 1;
+ return (nand_softecc_get(dev, buf, pagesize, ecc));
+ }
+
+ static int nfc_softecc_correct(device_t dev, void *buf, int pagesize,
+ void *readecc, void *calcecc)
+ {
+ return (nand_softecc_correct(dev, buf, pagesize, readecc,
+ calcecc));
+ }
+};
+
+# Send command to a NAND chip
+#
+# Return values:
+# 0: Success
+#
+METHOD int send_command {
+ device_t dev;
+ uint8_t command;
+};
+
+# Send address to a NAND chip
+#
+# Return values:
+# 0: Success
+#
+METHOD int send_address {
+ device_t dev;
+ uint8_t address;
+};
+
+# Read byte
+#
+# Return values:
+# byte read
+#
+METHOD uint8_t read_byte {
+ device_t dev;
+};
+
+# Write byte
+#
+METHOD void write_byte {
+ device_t dev;
+ uint8_t byte;
+};
+
+# Read word
+#
+# Return values:
+# word read
+#
+METHOD uint16_t read_word {
+ device_t dev;
+};
+
+# Write word
+#
+METHOD void write_word {
+ device_t dev;
+ uint16_t word;
+};
+
+# Read buf
+#
+METHOD void read_buf {
+ device_t dev;
+ void *buf;
+ uint32_t len;
+};
+
+# Write buf
+#
+METHOD void write_buf {
+ device_t dev;
+ void *buf;
+ uint32_t len;
+};
+
+# Select CS
+#
+METHOD int select_cs {
+ device_t dev;
+ uint8_t cs;
+};
+
+# Read ready/busy signal
+#
+METHOD int read_rnb {
+ device_t dev;
+};
+
+# Start command
+#
+# Return values:
+# 0: Success
+#
+METHOD int start_command {
+ device_t dev;
+} DEFAULT nfc_default_method;
+
+# Generate ECC or get it from H/W
+#
+METHOD int get_ecc {
+ device_t dev;
+ void *buf;
+ int pagesize;
+ void *ecc;
+ int *needwrite;
+} DEFAULT nfc_softecc_get;
+
+# Correct ECC
+#
+METHOD int correct_ecc {
+ device_t dev;
+ void *buf;
+ int pagesize;
+ void *readecc;
+ void *calcecc;
+} DEFAULT nfc_softecc_correct;
diff --git a/sys/dev/nand/nfc_mv.c b/sys/dev/nand/nfc_mv.c
new file mode 100644
index 000000000000..7f68e824cf90
--- /dev/null
+++ b/sys/dev/nand/nfc_mv.c
@@ -0,0 +1,236 @@
+/*-
+ * 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.
+ */
+
+/* Integrated NAND controller driver */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/time.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <arm/mv/mvvar.h>
+#include <arm/mv/mvwin.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include "nfc_if.h"
+
+#define MV_NAND_DATA (0x00)
+#define MV_NAND_COMMAND (0x01)
+#define MV_NAND_ADDRESS (0x02)
+
+struct mv_nand_softc {
+ struct nand_softc nand_dev;
+ bus_space_handle_t sc_handle;
+ bus_space_tag_t sc_tag;
+ struct resource *res;
+ int rid;
+};
+
+static int mv_nand_attach(device_t);
+static int mv_nand_probe(device_t);
+static int mv_nand_send_command(device_t, uint8_t);
+static int mv_nand_send_address(device_t, uint8_t);
+static uint8_t mv_nand_read_byte(device_t);
+static void mv_nand_read_buf(device_t, void *, uint32_t);
+static void mv_nand_write_buf(device_t, void *, uint32_t);
+static int mv_nand_select_cs(device_t, uint8_t);
+static int mv_nand_read_rnb(device_t);
+
+static device_method_t mv_nand_methods[] = {
+ DEVMETHOD(device_probe, mv_nand_probe),
+ DEVMETHOD(device_attach, mv_nand_attach),
+
+ DEVMETHOD(nfc_send_command, mv_nand_send_command),
+ DEVMETHOD(nfc_send_address, mv_nand_send_address),
+ DEVMETHOD(nfc_read_byte, mv_nand_read_byte),
+ DEVMETHOD(nfc_read_buf, mv_nand_read_buf),
+ DEVMETHOD(nfc_write_buf, mv_nand_write_buf),
+ DEVMETHOD(nfc_select_cs, mv_nand_select_cs),
+ DEVMETHOD(nfc_read_rnb, mv_nand_read_rnb),
+
+ { 0, 0 },
+};
+
+static driver_t mv_nand_driver = {
+ "nand",
+ mv_nand_methods,
+ sizeof(struct mv_nand_softc),
+};
+
+static devclass_t mv_nand_devclass;
+DRIVER_MODULE(mv_nand, localbus, mv_nand_driver, mv_nand_devclass, 0, 0);
+
+static int
+mv_nand_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "mrvl,nfc"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell NAND controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_nand_attach(device_t dev)
+{
+ struct mv_nand_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid,
+ RF_ACTIVE);
+ if (sc->res == NULL) {
+ device_printf(dev, "could not allocate resources!\n");
+ return (ENXIO);
+ }
+
+ sc->sc_tag = rman_get_bustag(sc->res);
+ sc->sc_handle = rman_get_bushandle(sc->res);
+
+ nand_init(&sc->nand_dev, dev, NAND_ECC_SOFT, 0, 0, NULL, NULL);
+
+ err = nandbus_create(dev);
+
+ return (err);
+}
+
+static int
+mv_nand_send_command(device_t dev, uint8_t command)
+{
+ struct mv_nand_softc *sc;
+
+ nand_debug(NDBG_DRV,"mv_nand: send command %x", command);
+
+ sc = device_get_softc(dev);
+ bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_COMMAND, command);
+ return (0);
+}
+
+static int
+mv_nand_send_address(device_t dev, uint8_t addr)
+{
+ struct mv_nand_softc *sc;
+
+ nand_debug(NDBG_DRV,"mv_nand: send address %x", addr);
+
+ sc = device_get_softc(dev);
+ bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_ADDRESS, addr);
+ return (0);
+}
+
+static uint8_t
+mv_nand_read_byte(device_t dev)
+{
+ struct mv_nand_softc *sc;
+ uint8_t data;
+
+ sc = device_get_softc(dev);
+ data = bus_space_read_1(sc->sc_tag, sc->sc_handle, MV_NAND_DATA);
+
+ nand_debug(NDBG_DRV,"mv_nand: read %x", data);
+
+ return (data);
+}
+
+static void
+mv_nand_read_buf(device_t dev, void* buf, uint32_t len)
+{
+ struct mv_nand_softc *sc;
+ int i;
+ uint8_t *b = (uint8_t*)buf;
+
+ sc = device_get_softc(dev);
+
+ for (i = 0; i < len; i++) {
+ b[i] = bus_space_read_1(sc->sc_tag, sc->sc_handle,
+ MV_NAND_DATA);
+#ifdef NAND_DEBUG
+ if (!(i % 16))
+ printf("%s", i == 0 ? "mv_nand:\n" : "\n");
+ printf(" %x", b[i]);
+ if (i == len - 1)
+ printf("\n");
+#endif
+ }
+}
+
+static void
+mv_nand_write_buf(device_t dev, void* buf, uint32_t len)
+{
+ struct mv_nand_softc *sc;
+ int i;
+ uint8_t *b = (uint8_t*)buf;
+
+ sc = device_get_softc(dev);
+
+ for (i = 0; i < len; i++) {
+#ifdef NAND_DEBUG
+ if (!(i % 16))
+ printf("%s", i == 0 ? "mv_nand:\n" : "\n");
+ printf(" %x", b[i]);
+ if (i == len - 1)
+ printf("\n");
+#endif
+ bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_DATA,
+ b[i]);
+ }
+}
+
+static int
+mv_nand_select_cs(device_t dev, uint8_t cs)
+{
+
+ if (cs > 0)
+ return (ENODEV);
+
+ return (0);
+}
+
+static int
+mv_nand_read_rnb(device_t dev)
+{
+
+ /* no-op */
+ return (0); /* ready */
+}