summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorahoka <ahoka@NetBSD.org>2011-02-26 18:07:13 +0000
committerahoka <ahoka@NetBSD.org>2011-02-26 18:07:13 +0000
commitb84aee73028fe564bfa78c0c2c568359ca7288a8 (patch)
treefe512736abd91f2864d5b0021de9e4af277634cd /sys/dev
parent11ce3c28dd5240489a3d69366d65cc854b754c35 (diff)
Import the Flash and NAND subsytem code contributed by the University
of Szeged, Hungary. The commit includes: - Flash layer, which gives a common API to access flash devices - NAND controller subsystem for the flash layer - An example OMAP driver which is used on BeagleBoard or alike ARM boards
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/flash/files.flash9
-rw-r--r--sys/dev/flash/flash.c695
-rw-r--r--sys/dev/flash/flash.h168
-rw-r--r--sys/dev/nand/files.nand16
-rw-r--r--sys/dev/nand/hamming.c232
-rw-r--r--sys/dev/nand/hamming.h56
-rw-r--r--sys/dev/nand/nand.c1478
-rw-r--r--sys/dev/nand/nand.h465
-rw-r--r--sys/dev/nand/nand_bbt.c250
-rw-r--r--sys/dev/nand/nand_bbt.h58
-rw-r--r--sys/dev/nand/nand_crc.c63
-rw-r--r--sys/dev/nand/nand_crc.h42
-rw-r--r--sys/dev/nand/nand_io.c373
-rw-r--r--sys/dev/nand/nandemulator.c779
-rw-r--r--sys/dev/nand/nandemulator.h5
-rw-r--r--sys/dev/nand/onfi.h180
16 files changed, 4869 insertions, 0 deletions
diff --git a/sys/dev/flash/files.flash b/sys/dev/flash/files.flash
new file mode 100644
index 00000000000..7871e2d9031
--- /dev/null
+++ b/sys/dev/flash/files.flash
@@ -0,0 +1,9 @@
+# $NetBSD: files.flash,v 1.1 2011/02/26 18:07:30 ahoka Exp $
+
+define flashbus { [offset = 0], [size = 0], [readonly = 0] }
+
+device flash
+attach flash at flashbus
+file dev/flash/flash.c flash
+
+defflag opt_flash.h FLASH_STATIC_PARTITIONS
diff --git a/sys/dev/flash/flash.c b/sys/dev/flash/flash.c
new file mode 100644
index 00000000000..33007cfe339
--- /dev/null
+++ b/sys/dev/flash/flash.c
@@ -0,0 +1,695 @@
+/* $NetBSD: flash.c,v 1.1 2011/02/26 18:07:30 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2011 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
+ * Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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 ``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 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.
+ */
+
+/*-
+ * Framework for storage devices based on Flash technology
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: flash.c,v 1.1 2011/02/26 18:07:30 ahoka Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+
+#include <sys/atomic.h>
+#include <sys/buf.h>
+#include <sys/bufq.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/malloc.h>
+#include <sys/reboot.h>
+
+#include <sys/flashio.h>
+#include "flash.h"
+
+#define FLASH_DEBUG 1
+#ifdef FLASH_DEBUG
+#define DPRINTF(x) if (flashdebug) printf x
+#define DPRINTFN(n,x) if (flashdebug>(n)) printf x
+int flashdebug = FLASH_DEBUG;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+extern struct cfdriver flash_cd;
+
+dev_type_open(flashopen);
+dev_type_close(flashclose);
+dev_type_read(flashread);
+dev_type_write(flashwrite);
+dev_type_ioctl(flashioctl);
+dev_type_strategy(flashstrategy);
+dev_type_dump(flashdump);
+dev_type_size(flashsize);
+
+int flash_print(void *aux, const char *pnp);
+
+bool flash_shutdown(device_t dev, int how);
+int flash_nsectors(struct buf *bp);
+int flash_sector(struct buf *bp);
+
+static inline off_t flash_get_part_offset(struct flash_softc *fl,
+ size_t poffset);
+
+int flash_match(device_t parent, cfdata_t match, void *aux);
+void flash_attach(device_t parent, device_t self, void *aux);
+int flash_detach(device_t device, int flags);
+
+CFATTACH_DECL_NEW(flash, sizeof(struct flash_softc),
+ flash_match, flash_attach, flash_detach, NULL);
+
+/**
+ * Block device's operation
+ */
+const struct bdevsw flash_bdevsw = {
+ .d_open = flashopen,
+ .d_close = flashclose,
+ .d_strategy = flashstrategy,
+ .d_ioctl = flashioctl,
+ .d_dump = flashdump,
+ .d_psize = flashsize,
+ .d_flag = D_DISK | D_MPSAFE
+};
+
+/**
+ * Character device's operations
+ */
+const struct cdevsw flash_cdevsw = {
+ .d_open = flashopen,
+ .d_close = flashclose,
+ .d_read = flashread,
+ .d_write = flashwrite,
+ .d_ioctl = flashioctl,
+ .d_stop = nostop,
+ .d_tty = notty,
+ .d_poll = nopoll,
+ .d_mmap = nommap,
+ .d_kqfilter = nokqfilter,
+ .d_flag = D_DISK | D_MPSAFE
+};
+
+/* ARGSUSED */
+int
+flash_match(device_t parent, cfdata_t match, void *aux)
+{
+ /* pseudo device, always attaches */
+ return 1;
+}
+
+/* ARGSUSED */
+void
+flash_attach(device_t parent, device_t self, void *aux)
+{
+ struct flash_softc *sc = device_private(self);
+ struct flash_attach_args *faa = aux;
+ char pbuf[2][sizeof("9999 KB")];
+
+ sc->sc_dev = self;
+ sc->sc_parent_dev = parent;
+ sc->flash_if = faa->flash_if;
+ sc->hw_softc = device_private(parent);
+
+ format_bytes(pbuf[0], sizeof(pbuf[0]), sc->flash_if->size);
+ format_bytes(pbuf[1], sizeof(pbuf[1]), sc->flash_if->erasesize);
+
+ aprint_naive("\n");
+
+ switch (sc->flash_if->type) {
+ case FLASH_TYPE_NOR:
+ aprint_normal(": %s NOR flash\n", pbuf[0]);
+ break;
+
+ case FLASH_TYPE_NAND:
+ aprint_normal(": %s NAND flash\n", pbuf[0]);
+ break;
+
+ default:
+ aprint_normal(": %s unknown flash\n", pbuf[0]);
+ }
+
+ aprint_normal_dev(sc->sc_dev,
+ "size: 0x%jx, offset: 0x%jx",
+ (uintmax_t )sc->flash_if->partition.part_size,
+ (uintmax_t )sc->flash_if->partition.part_offset);
+
+ if (sc->flash_if->partition.part_flags & FLASH_PART_READONLY) {
+ sc->sc_readonly = true;
+ aprint_normal(", read only");
+ } else {
+ sc->sc_readonly = false;
+ }
+
+ aprint_normal("\n");
+
+ if (sc->flash_if->partition.part_size == 0) {
+ aprint_error_dev(self,
+ "partition size must be larger than 0\n");
+ return;
+ }
+
+ switch (sc->flash_if->type) {
+ case FLASH_TYPE_NOR:
+ aprint_normal_dev(sc->sc_dev,
+ "erase size %s bytes, write size %d bytes\n",
+ pbuf[1], sc->flash_if->writesize);
+ break;
+
+ case FLASH_TYPE_NAND:
+ default:
+ aprint_normal_dev(sc->sc_dev,
+ "erase size %s, page size %d bytes, write size %d bytes\n",
+ pbuf[1], sc->flash_if->page_size,
+ sc->flash_if->writesize);
+ break;
+ }
+
+ if (!pmf_device_register1(sc->sc_dev, NULL, NULL, flash_shutdown))
+ aprint_error_dev(sc->sc_dev,
+ "couldn't establish power handler\n");
+}
+
+int
+flash_detach(device_t device, int flags)
+{
+ struct flash_softc *sc = device_private(device);
+
+ pmf_device_deregister(sc->sc_dev);
+
+ /* freeing flash_if is our responsibility */
+ printf("freeing flash_if...");
+ kmem_free(sc->flash_if, sizeof(*sc->flash_if));
+ printf("done!\n");
+
+ return 0;
+}
+
+int
+flash_print(void *aux, const char *pnp)
+{
+ struct flash_attach_args *arg;
+ const char *type;
+
+ if (pnp != NULL) {
+ arg = aux;
+ switch (arg->flash_if->type) {
+ case FLASH_TYPE_NOR:
+ type = "NOR";
+ break;
+ case FLASH_TYPE_NAND:
+ type = "NAND";
+ break;
+ default:
+ panic("flash_print: unknown type %d",
+ arg->flash_if->type);
+ }
+ aprint_normal("%s flash at %s", type, pnp);
+ }
+ return UNCONF;
+}
+
+device_t
+flash_attach_mi(struct flash_interface *flash_if, device_t device)
+{
+ struct flash_attach_args arg;
+
+#ifdef DIAGNOSTIC
+ if (flash_if == NULL) {
+ aprint_error("flash_attach_mi: NULL\n");
+ return 0;
+ }
+#endif
+ arg.flash_if = flash_if;
+
+ return config_found_ia(device, "flashbus", &arg, flash_print);
+}
+
+/**
+ * flash_open - open the character device
+ * Checks if there is a driver registered to the minor number of the open
+ * request.
+ */
+int
+flashopen(dev_t dev, int flags, int fmt, struct lwp *l)
+{
+ int unit = minor(dev);
+ struct flash_softc *sc;
+
+ DPRINTFN(1, ("flash: opening device unit %d\n", unit));
+
+ if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+ return ENXIO;
+
+ /* TODO return eperm if want to open for writing a read only dev */
+
+ /* reset buffer length */
+// sc->sc_cache->fc_len = 0;
+
+ return 0;
+}
+
+/**
+ * flash_close - close device
+ * We don't have to release any resources, so just return 0.
+ */
+int
+flashclose(dev_t dev, int flags, int fmt, struct lwp *l)
+{
+ int unit = minor(dev);
+ struct flash_softc *sc;
+ int err;
+
+ DPRINTFN(1, ("flash: closing flash device unit %d\n", unit));
+
+ if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+ return ENXIO;
+
+ if (!sc->sc_readonly) {
+ err = flash_sync(sc->sc_dev);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * flash_read - read from character device
+ * This function uses the registered driver's read function to read the requested length to
+ * a buffer and then moves this buffer to userspace.
+ */
+int
+flashread(dev_t dev, struct uio *uio, int flag)
+{
+ return physio(flashstrategy, NULL, dev, B_READ, minphys, uio);
+}
+
+/**
+ * flash_write - write to character device
+ * This function moves the data into a buffer from userspace to kernel space,
+ * then uses the registered driver's write function to write out the data to
+ * the media.
+ */
+int
+flashwrite(dev_t dev, struct uio *uio, int flag)
+{
+ return physio(flashstrategy, NULL, dev, B_WRITE, minphys, uio);
+}
+
+void
+flashstrategy(struct buf *bp)
+{
+ struct flash_softc *sc;
+ const struct flash_interface *flash_if;
+ const struct flash_partition *part;
+ int unit, device_blks;
+
+ unit = minor(bp->b_dev);
+ sc = device_lookup_private(&flash_cd, unit);
+ if (sc == NULL) {
+ bp->b_error = ENXIO;
+ goto done;
+ }
+
+ flash_if = sc->flash_if;
+ part = &flash_if->partition;
+
+ /* divider */
+ KASSERT(flash_if->writesize != 0);
+
+ aprint_debug_dev(sc->sc_dev, "flash_strategy()\n");
+
+ if (!(bp->b_flags & B_READ) && sc->sc_readonly) {
+ bp->b_error = EACCES;
+ goto done;
+ }
+
+ /* check if length is not negative */
+ if (bp->b_blkno < 0) {
+ bp->b_error = EINVAL;
+ goto done;
+ }
+
+ /* zero lenght i/o */
+ if (bp->b_bcount == 0) {
+ goto done;
+ }
+
+ device_blks = sc->flash_if->size / DEV_BSIZE;
+ KASSERT(part->part_offset % DEV_BSIZE == 0);
+ bp->b_rawblkno = bp->b_blkno + (part->part_offset / DEV_BSIZE);
+
+ if (bounds_check_with_mediasize(bp, DEV_BSIZE, device_blks) <= 0) {
+ goto done;
+ }
+
+ bp->b_resid = bp->b_bcount;
+ flash_if->submit(sc->sc_parent_dev, bp);
+
+ return;
+done:
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+}
+
+/*
+ * Handle the ioctl for the device
+ */
+int
+flashioctl(dev_t dev, u_long command, void *data, int flags, struct lwp *l)
+{
+ struct flash_erase_params *ep;
+ struct flash_info_params *ip;
+ struct flash_dump_params *dp;
+ struct flash_badblock_params *bbp;
+ struct flash_erase_instruction ei;
+ struct flash_softc *sc;
+ int unit, err;
+ size_t retlen;
+ flash_addr_t offset;
+
+ unit = minor(dev);
+ if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+ return ENXIO;
+
+ err = 0;
+ switch (command) {
+ case FLASH_ERASE_BLOCK:
+ /**
+ * Set up an erase instruction then call the registered
+ * driver's erase operation.
+ */
+ ep = data;
+
+ if (sc->sc_readonly) {
+ return EACCES;
+ }
+
+ ei.ei_addr = ep->ep_addr;
+ ei.ei_len = ep->ep_len;
+ ei.ei_callback = NULL;
+
+ err = flash_erase(sc->sc_dev, &ei);
+ if (err) {
+ return err;
+ }
+
+ break;
+ case FLASH_BLOCK_ISBAD:
+ /**
+ * Set up an erase instruction then call the registered
+ * driver's erase operation.
+ */
+ bbp = data;
+
+ err = flash_block_isbad(sc->sc_dev, bbp->bbp_addr);
+ if (err == EIO) {
+ bbp->bbp_isbad = true;
+ err = 0;
+ } else if (err) {
+ return err;
+ } else {
+ bbp->bbp_isbad = false;
+ }
+
+ break;
+ case FLASH_BLOCK_MARKBAD:
+ bbp = data;
+
+ err = flash_block_markbad(sc->sc_dev, bbp->bbp_addr);
+
+ break;
+ case FLASH_DUMP:
+ dp = data;
+ offset = dp->dp_block * sc->flash_if->erasesize;
+ DPRINTF(("Reading from block: %jd len: %jd\n",
+ (intmax_t )dp->dp_block, (intmax_t )dp->dp_len));
+ err = flash_read(sc->sc_parent_dev, offset, dp->dp_len,
+ &retlen, dp->dp_buf);
+ if (err)
+ return err;
+ if (retlen != dp->dp_len) {
+ dp->dp_len = -1;
+ dp->dp_buf = NULL;
+ }
+
+ break;
+ case FLASH_GET_INFO:
+ ip = data;
+
+ ip->ip_page_size = sc->flash_if->page_size;
+ ip->ip_erase_size = sc->flash_if->erasesize;
+ ip->ip_flash_type = sc->flash_if->type;
+ ip->ip_flash_size = sc->flash_if->size;
+ break;
+ default:
+ err = ENODEV;
+ }
+
+ return err;
+}
+int
+flashsize(dev_t dev)
+{
+ return -1;
+}
+
+int
+flashdump(dev_t dev, daddr_t blkno, void *va, size_t size)
+{
+ return EACCES;
+}
+
+bool
+flash_shutdown(device_t self, int how)
+{
+ struct flash_softc *sc = device_private(self);
+
+ if ((how & RB_NOSYNC) == 0 && !sc->sc_readonly)
+ flash_sync(self);
+
+ return true;
+}
+
+const struct flash_interface *
+flash_get_interface(dev_t dev)
+{
+ struct flash_softc *sc;
+ int unit;
+
+ unit = minor(dev);
+ if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+ return NULL;
+
+ return sc->flash_if;
+}
+
+const struct flash_softc *
+flash_get_softc(dev_t dev)
+{
+ struct flash_softc *sc;
+ int unit;
+
+ unit = minor(dev);
+ sc = device_lookup_private(&flash_cd, unit);
+
+ return sc;
+}
+
+device_t
+flash_get_device(dev_t dev)
+{
+ struct flash_softc *sc;
+ int unit;
+
+ unit = minor(dev);
+ sc = device_lookup_private(&flash_cd, unit);
+
+ return sc->sc_dev;
+}
+
+static inline off_t
+flash_get_part_offset(struct flash_softc *fl, size_t poffset)
+{
+ return fl->flash_if->partition.part_offset + poffset;
+}
+
+int
+flash_erase(device_t self, struct flash_erase_instruction *ei)
+{
+ struct flash_softc *sc = device_private(self);
+ KASSERT(ei != NULL);
+ struct flash_erase_instruction e = *ei;
+
+ if (sc->sc_readonly)
+ return EACCES;
+
+ /* adjust for flash partition */
+ e.ei_addr += sc->flash_if->partition.part_offset;
+
+ /* bounds check for flash partition */
+ if (e.ei_addr + e.ei_len > sc->flash_if->partition.part_size +
+ sc->flash_if->partition.part_offset)
+ return EINVAL;
+
+ return sc->flash_if->erase(device_parent(self), &e);
+}
+
+int
+flash_read(device_t self,
+ off_t offset, size_t len, size_t *retlen, uint8_t *buf)
+{
+ struct flash_softc *sc = device_private(self);
+
+ offset += sc->flash_if->partition.part_offset;
+
+ if (offset + len > sc->flash_if->partition.part_size +
+ sc->flash_if->partition.part_offset)
+ return EINVAL;
+
+ return sc->flash_if->read(device_parent(self),
+ offset, len, retlen, buf);
+}
+
+int
+flash_write(device_t self,
+ off_t offset, size_t len, size_t *retlen, const uint8_t *buf)
+{
+ struct flash_softc *sc = device_private(self);
+
+ if (sc->sc_readonly)
+ return EACCES;
+
+ offset += sc->flash_if->partition.part_offset;
+
+ if (offset + len > sc->flash_if->partition.part_size +
+ sc->flash_if->partition.part_offset)
+ return EINVAL;
+
+ return sc->flash_if->write(device_parent(self),
+ offset, len, retlen, buf);
+}
+
+int
+flash_block_markbad(device_t self, uint64_t offset)
+{
+ struct flash_softc *sc = device_private(self);
+
+ if (sc->sc_readonly)
+ return EACCES;
+
+ offset += sc->flash_if->partition.part_offset;
+
+ if (offset + sc->flash_if->erasesize >=
+ sc->flash_if->partition.part_size +
+ sc->flash_if->partition.part_offset)
+ return EINVAL;
+
+ return sc->flash_if->block_markbad(device_parent(self), offset);
+}
+
+int
+flash_block_isbad(device_t self, uint64_t offset)
+{
+ struct flash_softc *sc = device_private(self);
+
+ offset += sc->flash_if->partition.part_offset;
+
+ if (offset + sc->flash_if->erasesize >
+ sc->flash_if->partition.part_size +
+ sc->flash_if->partition.part_offset)
+ return EINVAL;
+
+ return sc->flash_if->block_isbad(device_parent(self), offset);
+}
+
+int
+flash_sync(device_t self)
+{
+ struct flash_softc *sc = device_private(self);
+
+ if (sc->sc_readonly)
+ return EACCES;
+
+ /* noop now TODO: implement */
+ return 0;
+}
+
+MODULE(MODULE_CLASS_DRIVER, flash, NULL);
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+flash_modcmd(modcmd_t cmd, void *opaque)
+{
+ int error = 0;
+#ifdef _MODULE
+ int bmaj = -1, cmaj = -1;
+#endif
+
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+#ifdef _MODULE
+ error = config_init_component(cfdriver_ioconf_flash,
+ cfattach_ioconf_flash, cfdata_ioconf_flash);
+ if (error)
+ return error;
+ error = devsw_attach("flash", &flash_bdevsw, &bmaj,
+ &flash_cdevsw, &cmaj);
+ if (error)
+ config_fini_component(cfdriver_ioconf_flash,
+ cfattach_ioconf_flash, cfdata_ioconf_flash);
+#endif
+ return error;
+ case MODULE_CMD_FINI:
+#ifdef _MODULE
+ devsw_detach(&flash_bdevsw, &flash_cdevsw);
+ error = config_fini_component(cfdriver_ioconf_flash,
+ cfattach_ioconf_flash, cfdata_ioconf_flash);
+#endif
+ return error;
+ default:
+ return ENOTTY;
+ }
+}
diff --git a/sys/dev/flash/flash.h b/sys/dev/flash/flash.h
new file mode 100644
index 00000000000..ead0b3cda27
--- /dev/null
+++ b/sys/dev/flash/flash.h
@@ -0,0 +1,168 @@
+/* $NetBSD: flash.h,v 1.1 2011/02/26 18:07:30 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2011 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
+ * Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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 ``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 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.
+ */
+
+#ifndef _FLASH_H_
+#define _FLASH_H_
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/buf.h>
+#include <sys/flashio.h>
+
+/**
+ * flash_softc - private structure for flash layer driver
+ */
+
+struct flash_softc {
+ device_t sc_dev;
+ device_t sc_parent_dev; /* Hardware (parent) device */
+ void *hw_softc; /* Hardware device private softc */
+ struct flash_interface *flash_if; /* Hardware interface */
+
+ bool sc_readonly; /* read only flash device */
+};
+
+struct flash_attach_args {
+ struct flash_interface *flash_if; /* Hardware interface */
+};
+
+device_t flash_attach_mi(struct flash_interface *, device_t);
+const struct flash_interface *flash_get_interface(dev_t);
+const struct flash_softc *flash_get_softc(dev_t);
+device_t flash_get_device(dev_t);
+
+/**
+ * struct erase_instruction - instructions to erase a flash eraseblock
+ * @fd: flash descriptor
+ * @addr: start address of the erase operation
+ * @len: the erase length
+ * @callback: callback operation, called when erase finished
+ * @priv: private data
+ * @state: the erase operation's result
+ */
+struct flash_erase_instruction {
+ flash_addr_t ei_addr;
+ flash_addr_t ei_len;
+ void (*ei_callback) (struct flash_erase_instruction *);
+ u_long ei_priv;
+ u_char ei_state;
+};
+
+enum {
+ FLASH_PART_READONLY = (1<<0),
+ FLASH_PART_FILESYSTEM = (1<<2)
+};
+
+struct flash_partition {
+ flash_addr_t part_offset;
+ flash_addr_t part_size;
+ int part_flags;
+};
+
+/**
+ * struct flash_interface - interface for flash operations
+ * @type: type of flash device
+ * @size: size of flash
+ * @page_size: page size of flash
+ * @erasesize: erase size of flash
+ * @writesize: minimum write size of flash
+ * @minor: minor number of the character device attached to this driver
+ * @erase: erase operation of the flash
+ * @read: read operation of the flash
+ * @write: write operation of the flash
+ * @block_markbad: marks a block as bad on the flash
+ * @block_isbad: checks if a block is bad on flash
+ */
+struct flash_interface {
+ int (*erase) (device_t, struct flash_erase_instruction *);
+ int (*read) (device_t, off_t, size_t, size_t *, uint8_t *);
+ int (*write) (device_t, off_t, size_t, size_t *, const uint8_t *);
+ int (*block_markbad)(device_t, uint64_t);
+ int (*block_isbad)(device_t, uint64_t);
+ int (*sync) (device_t);
+
+ int (*submit)(device_t, struct buf *);
+
+ /* storage for partition info */
+ struct flash_partition partition;
+
+ /* total size of mtd */
+ flash_addr_t size;
+ uint32_t page_size;
+ uint32_t erasesize;
+ uint32_t writesize;
+ uint32_t minor;
+ uint8_t type;
+};
+
+/**
+ * struct cache - for caching writes on block device
+ */
+struct flash_cache {
+ size_t fc_len;
+ flash_addr_t fc_block;
+ uint8_t *fc_data;
+};
+
+/* flash operations should be used through these */
+int flash_erase(device_t, struct flash_erase_instruction *);
+int flash_read(device_t, off_t, size_t, size_t *, uint8_t *);
+int flash_write(device_t, off_t, size_t, size_t *, const uint8_t *);
+int flash_block_markbad(device_t, uint64_t);
+int flash_block_isbad(device_t, uint64_t);
+int flash_sync(device_t);
+
+/*
+ * check_pattern - checks the buffer only contains the byte pattern
+ * @buf: the buffer to check
+ * @patt: the pattern to match
+ * @offset: the starting byte number, the matching starts from here
+ * @size: the buffer size
+ *
+ * This functions checks if the buffer only contains a specified byte pattern.
+ * Returns %0 if found something else, %1 otherwise.
+ */
+static inline int
+check_pattern(const void *buf, uint8_t patt, size_t offset, size_t size)
+{
+ size_t i;
+ for (i = offset; i < size; i++) {
+ if (((const uint8_t *) buf)[i] != patt)
+ return 0;
+ }
+ return 1;
+}
+
+#endif /* _FLASH_H_ */
diff --git a/sys/dev/nand/files.nand b/sys/dev/nand/files.nand
new file mode 100644
index 00000000000..4ab562e4103
--- /dev/null
+++ b/sys/dev/nand/files.nand
@@ -0,0 +1,16 @@
+# $NetBSD: files.nand,v 1.1 2011/02/26 18:07:31 ahoka Exp $
+
+define nandbus { }
+
+device nand: flashbus
+attach nand at nandbus
+file dev/nand/nand.c nand
+file dev/nand/nand_io.c nand
+file dev/nand/hamming.c nand
+file dev/nand/nand_bbt.c nand
+file dev/nand/nand_crc.c nand
+
+defpseudodev nandemulator: nandbus
+file dev/nand/nandemulator.c nandemulator
+
+defflag opt_nand.h NAND_BBT
diff --git a/sys/dev/nand/hamming.c b/sys/dev/nand/hamming.c
new file mode 100644
index 00000000000..eb2cc7ab253
--- /dev/null
+++ b/sys/dev/nand/hamming.c
@@ -0,0 +1,232 @@
+/* $NetBSD: hamming.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */
+
+/*
+ * Copyright (c) 2008, Atmel Corporation
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the disclaimer below.
+ *
+ * Atmel's name may not be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * DISCLAIMED. IN NO EVENT SHALL ATMEL 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>
+__KERNEL_RCSID(0, "$NetBSD: hamming.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $");
+
+#include <sys/param.h>
+#include <lib/libkern/libkern.h>
+#include "hamming.h"
+
+/**
+ * Calculates the 22-bit hamming code for a 256-bytes block of data.
+ * \param data Data buffer to calculate code for.
+ * \param code Pointer to a buffer where the code should be stored.
+ */
+void
+hamming_compute_256(const uint8_t *data, uint8_t *code)
+{
+ unsigned int i;
+ uint8_t column_sum = 0;
+ uint8_t even_line_code = 0;
+ uint8_t odd_line_code = 0;
+ uint8_t even_column_code = 0;
+ uint8_t odd_column_code = 0;
+
+ /*-
+ * Xor all bytes together to get the column sum;
+ * At the same time, calculate the even and odd line codes
+ */
+ for (i = 0; i < 256; i++) {
+ column_sum ^= data[i];
+
+ /*-
+ * If the xor sum of the byte is 0, then this byte has no
+ * incidence on the computed code; so check if the sum is 1.
+ */
+ if ((popcount(data[i]) & 1) == 1) {
+ /*-
+ * Parity groups are formed by forcing a particular
+ * index bit to 0 (even) or 1 (odd).
+ * Example on one byte:
+ *
+ * bits (dec) 7 6 5 4 3 2 1 0
+ * (bin) 111 110 101 100 011 010 001 000
+ * '---'---'---'----------.
+ * |
+ * groups P4' ooooooooooooooo eeeeeeeeeeeeeee P4 |
+ * P2' ooooooo eeeeeee ooooooo eeeeeee P2 |
+ * P1' ooo eee ooo eee ooo eee ooo eee P1 |
+ * |
+ * We can see that: |
+ * - P4 -> bit 2 of index is 0 --------------------'
+ * - P4' -> bit 2 of index is 1.
+ * - P2 -> bit 1 of index if 0.
+ * - etc...
+ * We deduce that a bit position has an impact on all
+ * even Px if the log2(x)nth bit of its index is 0
+ * ex: log2(4) = 2,
+ * bit2 of the index must be 0 (-> 0 1 2 3)
+ * and on all odd Px' if the log2(x)nth bit
+ * of its index is 1
+ * ex: log2(2) = 1,
+ * bit1 of the index must be 1 (-> 0 1 4 5)
+ *
+ * As such, we calculate all the possible Px and Px'
+ * values at the same time in two variables,
+ * even_line_code and odd_line_code, such as
+ * even_line_code bits: P128 P64 P32
+ * P16 P8 P4 P2 P1
+ * odd_line_code bits: P128' P64' P32' P16'
+ * P8' P4' P2' P1'
+ */
+ even_line_code ^= (255 - i);
+ odd_line_code ^= i;
+ }
+ }
+
+ /*-
+ * At this point, we have the line parities, and the column sum.
+ * First, We must caculate the parity group values on the column sum.
+ */
+ for (i = 0; i < 8; i++) {
+ if (column_sum & 1) {
+ even_column_code ^= (7 - i);
+ odd_column_code ^= i;
+ }
+ column_sum >>= 1;
+ }
+
+ /*-
+ * Now, we must interleave the parity values,
+ * to obtain the following layout:
+ * Code[0] = Line1
+ * Code[1] = Line2
+ * Code[2] = Column
+ * Line = Px' Px P(x-1)- P(x-1) ...
+ * Column = P4' P4 P2' P2 P1' P1 PadBit PadBit
+ */
+ code[0] = 0;
+ code[1] = 0;
+ code[2] = 0;
+
+ for (i = 0; i < 4; i++) {
+ code[0] <<= 2;
+ code[1] <<= 2;
+ code[2] <<= 2;
+
+ /* Line 1 */
+ if ((odd_line_code & 0x80) != 0) {
+
+ code[0] |= 2;
+ }
+ if ((even_line_code & 0x80) != 0) {
+
+ code[0] |= 1;
+ }
+
+ /* Line 2 */
+ if ((odd_line_code & 0x08) != 0) {
+
+ code[1] |= 2;
+ }
+ if ((even_line_code & 0x08) != 0) {
+
+ code[1] |= 1;
+ }
+
+ /* Column */
+ if ((odd_column_code & 0x04) != 0) {
+
+ code[2] |= 2;
+ }
+ if ((even_column_code & 0x04) != 0) {
+
+ code[2] |= 1;
+ }
+
+ odd_line_code <<= 1;
+ even_line_code <<= 1;
+ odd_column_code <<= 1;
+ even_column_code <<= 1;
+ }
+
+ /* Invert codes (linux compatibility) */
+ code[0] = ~code[0];
+ code[1] = ~code[1];
+ code[2] = ~code[2];
+}
+
+/**
+ * Verifies and corrects a 256-bytes block of data using the given 22-bits
+ * hamming code.
+ * Returns 0 if there is no error, otherwise returns a HAMMING_ERROR code.
+ * param data Data buffer to check.
+ * \param original_code Hamming code to use for verifying the data.
+ */
+uint8_t
+hamming_correct_256(uint8_t *data, const uint8_t *original_code,
+ const uint8_t *computed_code)
+{
+ /* Calculate new code */
+ /* we allocate 4 bytes so we can use popcount32 in one step */
+ uint8_t correction_code[4];
+
+ /* this byte should remain zero all the time */
+ correction_code[3] = 0;
+
+ /* Xor both codes together */
+ correction_code[0] = computed_code[0] ^ original_code[0];
+ correction_code[1] = computed_code[1] ^ original_code[1];
+ correction_code[2] = computed_code[2] ^ original_code[2];
+
+ /* If all bytes are 0, there is no error */
+ if (*(uint32_t *)correction_code == 0) {
+ return 0;
+ }
+ /* If there is a single bit error, there are 11 bits set to 1 */
+ if (popcount32(*(uint32_t *)correction_code) == 11) {
+ /* Get byte and bit indexes */
+ uint8_t byte = correction_code[0] & 0x80;
+ byte |= (correction_code[0] << 1) & 0x40;
+ byte |= (correction_code[0] << 2) & 0x20;
+ byte |= (correction_code[0] << 3) & 0x10;
+
+ byte |= (correction_code[1] >> 4) & 0x08;
+ byte |= (correction_code[1] >> 3) & 0x04;
+ byte |= (correction_code[1] >> 2) & 0x02;
+ byte |= (correction_code[1] >> 1) & 0x01;
+
+ uint8_t bit = (correction_code[2] >> 5) & 0x04;
+ bit |= (correction_code[2] >> 4) & 0x02;
+ bit |= (correction_code[2] >> 3) & 0x01;
+
+ /* Correct bit */
+ data[byte] ^= (1 << bit);
+
+ return HAMMING_ERROR_SINGLEBIT;
+ }
+ /* Check if ECC has been corrupted */
+ if (popcount32(*(uint32_t *)correction_code) == 1) {
+ return HAMMING_ERROR_ECC;
+ } else {
+ /* Otherwise, this is a multi-bit error */
+ return HAMMING_ERROR_MULTIPLEBITS;
+ }
+}
+
diff --git a/sys/dev/nand/hamming.h b/sys/dev/nand/hamming.h
new file mode 100644
index 00000000000..4c3d569c9cf
--- /dev/null
+++ b/sys/dev/nand/hamming.h
@@ -0,0 +1,56 @@
+/* $NetBSD: hamming.h,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */
+
+/*
+ * Copyright (c) 2008, Atmel Corporation
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the disclaimer below.
+ *
+ * Atmel's name may not be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
+ */
+
+#ifndef HAMMING_H
+#define HAMMING_H
+
+#include "nand.h"
+
+/*-
+ * These are the possible errors when trying to verify a block of data encoded
+ * using a Hamming code:
+ *
+ * - HAMMING_ERROR_SINGLEBIT
+ * - HAMMING_ERROR_ECC
+ * - HAMMING_ERROR_MULTIPLEBITS
+ */
+
+/* A single bit was incorrect but has been recovered. */
+#define HAMMING_ERROR_SINGLEBIT NAND_ECC_CORRECTED
+
+/* The original code has been corrupted. */
+#define HAMMING_ERROR_ECC NAND_ECC_INVALID
+
+/* Multiple bits are incorrect in the data and they cannot be corrected. */
+#define HAMMING_ERROR_MULTIPLEBITS NAND_ECC_TWOBIT
+
+uint8_t hamming_correct_256(uint8_t *, const uint8_t *, const uint8_t *);
+void hamming_compute_256(const uint8_t *data, uint8_t *code);
+
+#endif /* HAMMING_H */
+
diff --git a/sys/dev/nand/nand.c b/sys/dev/nand/nand.c
new file mode 100644
index 00000000000..e999b28fd73
--- /dev/null
+++ b/sys/dev/nand/nand.c
@@ -0,0 +1,1478 @@
+/* $NetBSD: nand.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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 ``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 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.
+ */
+
+/* Common driver for NAND chips implementing the ONFI 2.2 specification */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: nand.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $");
+
+#include "locators.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/device.h>
+#include <sys/kmem.h>
+#include <sys/sysctl.h>
+
+#include <dev/flash/flash.h>
+#include <dev/nand/nand.h>
+#include <dev/nand/onfi.h>
+#include <dev/nand/hamming.h>
+#include <dev/nand/nand_bbt.h>
+#include <dev/nand/nand_crc.h>
+
+#include "opt_nand.h"
+
+int nand_match(device_t parent, cfdata_t match, void *aux);
+void nand_attach(device_t parent, device_t self, void *aux);
+int nand_detach(device_t device, int flags);
+bool nand_shutdown(device_t self, int howto);
+
+int nand_print(void *aux, const char *pnp);
+
+static int nand_search(device_t parent, cfdata_t cf, const int *ldesc,
+ void *aux);
+static void nand_address_row(device_t self, size_t row);
+static void nand_address_column(device_t self, size_t row, size_t column);
+static void nand_readid(device_t self, struct nand_chip *chip);
+static void nand_read_parameter_page(device_t self, struct nand_chip *chip);
+static const char *nand_midtoname(int id);
+static int nand_scan_media(device_t self, struct nand_chip *chip);
+static bool nand_check_wp(device_t self);
+
+CFATTACH_DECL_NEW(nand, sizeof(struct nand_softc),
+ nand_match, nand_attach, nand_detach, NULL);
+
+#ifdef NAND_DEBUG
+int nanddebug = NAND_DEBUG;
+#endif
+
+int nand_cachesync_timeout = 1;
+int nand_cachesync_nodenum;
+
+const struct nand_manufacturer nand_mfrs[] = {
+ { NAND_MFR_AMD, "AMD" },
+ { NAND_MFR_FUJITSU, "Fujitsu" },
+ { NAND_MFR_RENESAS, "Renesas" },
+ { NAND_MFR_STMICRO, "ST Micro" },
+ { NAND_MFR_MICRON, "Micron" },
+ { NAND_MFR_NATIONAL, "National" },
+ { NAND_MFR_TOSHIBA, "Toshiba" },
+ { NAND_MFR_HYNIX, "Hynix" },
+ { NAND_MFR_SAMSUNG, "Samsung" },
+ { NAND_MFR_UNKNOWN, "Unknown" }
+};
+
+/* ARGSUSED */
+int
+nand_match(device_t parent, cfdata_t match, void *aux)
+{
+ /* pseudo device, always attaches */
+ return 1;
+}
+
+void
+nand_attach(device_t parent, device_t self, void *aux)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_attach_args *naa = aux;
+ struct nand_chip *chip = &sc->sc_chip;
+// struct flash_interface *flash_if;
+// int i;
+
+ sc->sc_dev = self;
+ sc->nand_dev = parent;
+ sc->nand_if = naa->naa_nand_if;
+
+// sc->nand_softc = device_private(parent);
+
+ aprint_naive("\n");
+// aprint_normal(": NAND flash memory\n");
+
+ if (nand_check_wp(self)) {
+ aprint_error("NAND chip is write protected!\n");
+ return;
+ }
+ if (nand_scan_media(self, chip))
+ return;
+
+ /* allocate cache */
+ chip->nc_oob_cache = kmem_alloc(chip->nc_spare_size, KM_SLEEP);
+ chip->nc_page_cache = kmem_alloc(chip->nc_page_size, KM_SLEEP);
+
+ mutex_init(&sc->sc_device_lock, MUTEX_DEFAULT, IPL_NONE);
+
+ if (nand_sync_thread_start(self)) {
+ goto error;
+ }
+
+ if (!pmf_device_register1(sc->sc_dev, NULL, NULL, nand_shutdown))
+ aprint_error_dev(sc->sc_dev,
+ "couldn't establish power handler\n");
+
+#ifdef NAND_BBT
+ nand_bbt_init(self);
+ nand_bbt_scan(self);
+#endif
+
+ /*
+ * Attach all our devices
+ */
+ config_search_ia(nand_search, self, NULL, NULL);
+
+ return;
+error:
+ kmem_free(chip->nc_oob_cache, chip->nc_spare_size);
+ kmem_free(chip->nc_page_cache, chip->nc_page_size);
+ mutex_destroy(&sc->sc_device_lock);
+}
+
+static int
+nand_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
+{
+ struct nand_softc *sc = device_private(parent);
+ struct nand_chip *chip = &sc->sc_chip;
+ struct flash_interface *flash_if;
+ struct flash_attach_args faa;
+
+ flash_if = kmem_alloc(sizeof(*flash_if), KM_SLEEP);
+
+ flash_if->type = FLASH_TYPE_NAND;
+
+ flash_if->read = nand_flash_read;
+ flash_if->write = nand_flash_write;
+ flash_if->erase = nand_flash_erase;
+ flash_if->block_isbad = nand_flash_isbad;
+ flash_if->block_markbad = nand_flash_markbad;
+
+ flash_if->submit = nand_io_submit;
+
+ flash_if->erasesize = chip->nc_block_size;
+ flash_if->page_size = chip->nc_page_size;
+ flash_if->writesize = chip->nc_page_size;
+
+ flash_if->partition.part_offset = cf->cf_loc[FLASHBUSCF_OFFSET];
+
+ if (cf->cf_loc[FLASHBUSCF_SIZE] == 0) {
+ flash_if->size = chip->nc_size -
+ flash_if->partition.part_offset;
+ flash_if->partition.part_size = flash_if->size;
+ } else {
+ flash_if->size = cf->cf_loc[FLASHBUSCF_SIZE];
+ flash_if->partition.part_size = cf->cf_loc[FLASHBUSCF_SIZE];
+ }
+
+ if (cf->cf_loc[FLASHBUSCF_READONLY])
+ flash_if->partition.part_flags = FLASH_PART_READONLY;
+ else
+ flash_if->partition.part_flags = 0;
+
+ faa.flash_if = flash_if;
+
+ if (config_match(parent, cf, &faa)) {
+ config_attach(parent, cf, &faa, nand_print);
+ return 0;
+ } else {
+ kmem_free(flash_if, sizeof(*flash_if));
+ }
+
+ return 1;
+}
+
+int
+nand_detach(device_t self, int flags)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ int ret = 0;
+
+#ifdef NAND_BBT
+ nand_bbt_detach(self);
+#endif
+ nand_sync_thread_stop(self);
+
+ /* free oob cache */
+ kmem_free(chip->nc_oob_cache, chip->nc_spare_size);
+ kmem_free(chip->nc_page_cache, chip->nc_page_size);
+ kmem_free(chip->nc_ecc_cache, chip->nc_ecc->necc_size);
+
+ mutex_destroy(&sc->sc_device_lock);
+
+ pmf_device_deregister(sc->sc_dev);
+
+ return ret;
+}
+
+int
+nand_print(void *aux, const char *pnp)
+{
+ if (pnp != NULL)
+ aprint_normal("nand at %s\n", pnp);
+
+ return UNCONF;
+}
+
+device_t
+nand_attach_mi(struct nand_interface *nand_if, device_t parent)
+{
+ struct nand_attach_args arg;
+
+ KASSERT(nand_if != NULL);
+
+ arg.naa_nand_if = nand_if;
+ return config_found_ia(parent, "nandbus", &arg, nand_print);
+}
+
+static const char *
+nand_midtoname(int id)
+{
+ int i;
+
+ for (i = 0; nand_mfrs[i].id != 0; i++) {
+ if (nand_mfrs[i].id == id)
+ return nand_mfrs[i].name;
+ }
+
+ KASSERT(nand_mfrs[i].id == 0);
+
+ return nand_mfrs[i].name;
+}
+
+#if 0
+/* handle quirks here */
+static void
+nand_quirks(device_t self, struct nand_chip *chip)
+{
+ /* this is an example only! */
+ switch (chip->nc_manf_id) {
+ case NAND_MFR_SAMSUNG:
+ if (chip->nc_dev_id == 0x00) {
+ /* do something only samsung chips need */
+ /* or */
+ /* chip->nc_quirks |= NC_QUIRK_NO_READ_START */
+ }
+ }
+
+ return;
+}
+#endif
+
+/**
+ * scan media to determine the chip's properties
+ * this function resets the device
+ */
+static int
+nand_scan_media(device_t self, struct nand_chip *chip)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_ecc *ecc;
+ uint8_t onfi_signature[4];
+
+ nand_select(self, true);
+ nand_command(self, ONFI_RESET);
+ nand_select(self, false);
+
+ nand_select(self, true);
+ nand_command(self, ONFI_READ_ID);
+ nand_address(self, 0x20);
+ nand_read_byte(self, &onfi_signature[0]);
+ nand_read_byte(self, &onfi_signature[1]);
+ nand_read_byte(self, &onfi_signature[2]);
+ nand_read_byte(self, &onfi_signature[3]);
+ nand_select(self, false);
+
+ if (onfi_signature[0] != 'O' || onfi_signature[1] != 'N' ||
+ onfi_signature[2] != 'F' || onfi_signature[3] != 'I') {
+ aprint_error_dev(self,
+ "device does not support the ONFI specification\n");
+
+ return 1;
+ }
+
+ nand_readid(self, chip);
+
+ aprint_normal(": NAND Flash\n");
+
+ aprint_debug_dev(self,
+ "manufacturer id: 0x%.2x (%s), device id: 0x%.2x\n",
+ chip->nc_manf_id,
+ nand_midtoname(chip->nc_manf_id),
+ chip->nc_dev_id);
+
+ nand_read_parameter_page(self, chip);
+
+ ecc = chip->nc_ecc = &sc->nand_if->ecc;
+
+ /*
+ * calculate the place of ecc data in oob
+ * we try to be compatible with Linux here
+ */
+ switch (chip->nc_spare_size) {
+ case 8:
+ ecc->necc_offset = 0;
+ break;
+ case 16:
+ ecc->necc_offset = 0;
+ break;
+ case 64:
+ ecc->necc_offset = 40;
+ break;
+ case 128:
+ ecc->necc_offset = 80;
+ break;
+ default:
+ panic("OOB size is unexpected");
+ }
+
+ ecc->necc_steps = chip->nc_page_size / ecc->necc_block_size;
+ ecc->necc_size = ecc->necc_steps * ecc->necc_code_size;
+
+ /* check if we fit in oob */
+ if (ecc->necc_offset + ecc->necc_size > chip->nc_spare_size) {
+ panic("NAND ECC bits dont fit in OOB");
+ }
+
+ /* TODO: mark free oob area available for file systems */
+
+ chip->nc_ecc_cache = kmem_zalloc(ecc->necc_size, KM_SLEEP);
+
+ /*
+ * calculate badblock marker offset in oob
+ * we try to be compatible with linux here
+ */
+ if (chip->nc_page_size > 512)
+ chip->nc_badmarker_offs = 0;
+ else
+ chip->nc_badmarker_offs = 5;
+
+ /* Calculate page shift and mask */
+ chip->nc_page_shift = ffs(chip->nc_page_size) - 1;
+ chip->nc_page_mask = ~(chip->nc_page_size - 1);
+ /* same for block */
+ chip->nc_block_shift = ffs(chip->nc_block_size) - 1;
+ chip->nc_block_mask = ~(chip->nc_block_size - 1);
+
+ /* look for quirks here if needed in future */
+ /* nand_quirks(self, chip); */
+
+ return 0;
+}
+
+static void
+nand_readid(device_t self, struct nand_chip *chip)
+{
+ nand_select(self, true);
+ nand_command(self, ONFI_READ_ID);
+ nand_address(self, 0x00);
+ nand_read_byte(self, &chip->nc_manf_id);
+ nand_read_byte(self, &chip->nc_dev_id);
+ nand_select(self, false);
+}
+
+/* read the parameter page. TODO: check CRC! */
+static void
+nand_read_parameter_page(device_t self, struct nand_chip *chip)
+{
+ struct onfi_parameter_page params;
+ uint8_t *bufp;
+ uint8_t vendor[13], model[21];
+ uint16_t crc;
+ int i;
+
+ KASSERT(sizeof(params) == 256);
+
+ nand_select(self, true);
+ nand_command(self, ONFI_READ_PARAMETER_PAGE);
+ nand_address(self, 0x00);
+
+ nand_busy(self);
+
+ bufp = (uint8_t *)&params;
+ for (i = 0; i < 256; i++) {
+ nand_read_byte(self, &bufp[i]);
+ }
+ nand_select(self, false);
+
+ /* validate the parameter page with the crc */
+ crc = nand_crc16(bufp, 254);
+
+ if (crc != params.param_integrity_crc) {
+ aprint_error_dev(self, "parameter page crc check failed\n");
+ /* TODO: we should read the next parameter page copy */
+ }
+
+ /* strip manufacturer and model string */
+ strlcpy(vendor, params.param_manufacturer, sizeof(vendor));
+ for (i = 11; i > 0 && vendor[i] == ' '; i--)
+ vendor[i] = 0;
+ strlcpy(model, params.param_model, sizeof(model));
+ for (i = 19; i > 0 && model[i] == ' '; i--)
+ model[i] = 0;
+
+ aprint_normal_dev(self, "vendor: %s, model: %s\n", vendor, model);
+
+ aprint_normal_dev(self,
+ "page size: %u bytes, spare size: %u bytes, block size: %u bytes\n",
+ params.param_pagesize, params.param_sparesize,
+ params.param_blocksize * params.param_pagesize);
+
+ aprint_normal_dev(self,
+ "LUN size: %u blocks, LUNs: %u, total storage size: %u MB\n",
+ params.param_lunsize, params.param_numluns,
+ params.param_blocksize * params.param_pagesize *
+ params.param_lunsize * params.param_numluns / 1024 / 1024);
+
+ /* XXX TODO multiple LUNs */
+ if (__predict_false(params.param_numluns != 1))
+ panic("more than one LUNs are not supported yet!\n");
+
+ chip->nc_size = params.param_pagesize * params.param_blocksize *
+ params.param_lunsize * params.param_numluns;
+
+ chip->nc_page_size = params.param_pagesize;
+ chip->nc_block_pages = params.param_blocksize;
+ chip->nc_block_size = params.param_blocksize * params.param_pagesize;
+ chip->nc_spare_size = params.param_sparesize;
+
+ /* the lower 4 bits contain the row address cycles */
+ chip->nc_addr_cycles_row = params.param_addr_cycles & 0x07;
+ /* the upper 4 bits contain the column address cycles */
+ chip->nc_addr_cycles_column = (params.param_addr_cycles & ~0x07) >> 4;
+
+#ifdef NAND_VERBOSE
+ aprint_normal_dev(self, "column cycles: %d, row cycles: %d\n",
+ chip->nc_addr_cycles_column, chip->nc_addr_cycles_row);
+#endif
+
+ if (params.param_features & ONFI_FEATURE_16BIT)
+ chip->nc_flags |= NC_BUSWIDTH_16;
+
+ if (params.param_features & ONFI_FEATURE_EXTENDED_PARAM)
+ chip->nc_flags |= NC_EXTENDED_PARAM;
+}
+
+/* ARGSUSED */
+bool
+nand_shutdown(device_t self, int howto)
+{
+ return true;
+}
+
+static void
+nand_address_column(device_t self, size_t row, size_t column)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ uint8_t i;
+
+ DPRINTF(("addressing row: 0x%jx column: %zu\n",
+ (uintmax_t )row, column));
+
+ /* XXX TODO */
+ row >>= chip->nc_page_shift;
+// DPRINTF(("row address is: 0x%jx\n", (uintmax_t )row));
+
+ /* Write the column (subpage) address */
+ if (chip->nc_flags & NC_BUSWIDTH_16)
+ column >>= 1;
+ for (i = 0; i < chip->nc_addr_cycles_column; i++, column >>= 8)
+ nand_address(self, column & 0xff);
+
+ /* Write the row (page) address */
+ for (i = 0; i < chip->nc_addr_cycles_row; i++, row >>= 8)
+ nand_address(self, row & 0xff);
+}
+
+static void
+nand_address_row(device_t self, size_t row)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ off_t i;
+
+// DPRINTF(("addressing row: %zu\n", row));
+
+ /* XXX TODO */
+ row >>= chip->nc_page_shift;
+
+ /* Write the row (page) address */
+ for (i = 0; i < chip->nc_addr_cycles_row; i++, row >>= 8)
+ nand_address(self, row & 0xff);
+}
+
+static inline uint8_t
+nand_get_status(device_t self)
+{
+ uint8_t status;
+
+ nand_command(self, ONFI_READ_STATUS);
+ nand_busy(self);
+ nand_read_byte(self, &status);
+
+ return status;
+}
+
+static bool
+nand_check_wp(device_t self)
+{
+ if (nand_get_status(self) & 0x80)
+ return false;
+ else
+ return true;
+}
+
+static void
+nand_prepare_read(device_t self, flash_addr_t row, flash_addr_t column)
+{
+ nand_command(self, ONFI_READ);
+ nand_address_column(self, row, column);
+ nand_command(self, ONFI_READ_START);
+
+ nand_busy(self);
+}
+
+/* read a page with ecc correction */
+int
+nand_read_page(device_t self, size_t offset, uint8_t *data)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ size_t b, bs, e, cs;
+ uint8_t *ecc;
+ int result;
+
+ DPRINTF(("nand read page\n"));
+
+ nand_prepare_read(self, offset, 0);
+
+ bs = chip->nc_ecc->necc_block_size;
+ cs = chip->nc_ecc->necc_code_size;
+
+ /* decide if we access by 8 or 16 bits */
+ if (chip->nc_flags & NC_BUSWIDTH_16) {
+ for (b = 0, e = 0; b < chip->nc_page_size; b += bs, e += cs) {
+ nand_ecc_prepare(self, NAND_ECC_READ);
+ nand_read_buf_word(self, data + b, bs);
+ nand_ecc_compute(self, data + b,
+ chip->nc_ecc_cache + e);
+ }
+ } else {
+ for (b = 0, e = 0; b < chip->nc_page_size; b += bs, e += cs) {
+ nand_ecc_prepare(self, NAND_ECC_READ);
+ nand_read_buf_byte(self, data + b, bs);
+ nand_ecc_compute(self, data + b,
+ chip->nc_ecc_cache + e);
+ }
+ }
+
+// nand_dump_data("page", data, chip->nc_page_size);
+
+ nand_read_oob(self, offset, chip->nc_oob_cache);
+ ecc = chip->nc_oob_cache + chip->nc_ecc->necc_offset;
+
+ /* useful for debugging new ecc drivers */
+#if 0
+ printf("dumping ecc %d\n--------------\n", chip->nc_ecc->necc_steps);
+ for (e = 0; e < chip->nc_ecc->necc_steps; e++) {
+ printf("0x");
+ for (b = 0; b < cs; b++) {
+ printf("%.2hhx", ecc[e+b]);
+ }
+ printf(" 0x");
+ for (b = 0; b < cs; b++) {
+ printf("%.2hhx", chip->nc_ecc_cache[e+b]);
+ }
+ printf("\n");
+ }
+ printf("--------------\n");
+#endif
+
+ for (b = 0, e = 0; b < chip->nc_page_size; b += bs, e += cs) {
+ result = nand_ecc_correct(self, data + b, ecc + e,
+ chip->nc_ecc_cache + e);
+
+ switch (result) {
+ case NAND_ECC_OK:
+ break;
+ case NAND_ECC_CORRECTED:
+ aprint_error_dev(self,
+ "data corrected with ECC at page offset 0x%jx "
+ "block %zu\n", (uintmax_t)offset, b);
+ break;
+ case NAND_ECC_TWOBIT:
+ aprint_error_dev(self,
+ "uncorrectable ECC error at page offset 0x%jx "
+ "block %zu\n", (uintmax_t)offset, b);
+ return EIO;
+ break;
+ case NAND_ECC_INVALID:
+ aprint_error_dev(self,
+ "invalid ECC in oob at page offset 0x%jx "
+ "block %zu\n", (uintmax_t)offset, b);
+ return EIO;
+ break;
+ default:
+ panic("invalid ECC correction errno");
+ }
+ }
+
+ return 0;
+}
+
+static int
+nand_program_page(device_t self, size_t page, const uint8_t *data)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ size_t bs, cs, e, b;
+ uint8_t status;
+ uint8_t *ecc;
+
+ nand_command(self, ONFI_PAGE_PROGRAM);
+ nand_address_column(self, page, 0);
+
+ nand_busy(self);
+
+ bs = chip->nc_ecc->necc_block_size;
+ cs = chip->nc_ecc->necc_code_size;
+ ecc = chip->nc_oob_cache + chip->nc_ecc->necc_offset;
+
+ /* XXX code duplication */
+ /* decide if we access by 8 or 16 bits */
+ if (chip->nc_flags & NC_BUSWIDTH_16) {
+ for (b = 0, e = 0; b < chip->nc_page_size; b += bs, e += cs) {
+ nand_ecc_prepare(self, NAND_ECC_WRITE);
+ nand_write_buf_word(self, data + b, bs);
+ nand_ecc_compute(self, data + b, ecc + e);
+ }
+ /* write oob with ecc correction code */
+ nand_write_buf_word(self, chip->nc_oob_cache,
+ chip->nc_spare_size);
+ } else {
+ for (b = 0, e = 0; b < chip->nc_page_size; b += bs, e += cs) {
+ nand_ecc_prepare(self, NAND_ECC_WRITE);
+ nand_write_buf_byte(self, data + b, bs);
+ nand_ecc_compute(self, data + b, ecc + e);
+ }
+ /* write oob with ecc correction code */
+ nand_write_buf_byte(self, chip->nc_oob_cache,
+ chip->nc_spare_size);
+ }
+
+ nand_command(self, ONFI_PAGE_PROGRAM_START);
+
+ nand_busy(self);
+
+#if 0
+ printf("dumping ecc %d\n--------------\n", chip->nc_ecc->necc_steps);
+ for (e = 0; e < chip->nc_ecc->necc_steps; e++) {
+ printf("0x");
+ for (b = 0; b < cs; b++) {
+ printf("%.2hhx", ecc[e+b]);
+ }
+ printf("\n");
+ }
+ printf("--------------\n");
+#endif
+
+ status = nand_get_status(self);
+ KASSERT(status & ONFI_STATUS_RDY);
+ if (status & ONFI_STATUS_FAIL) {
+ aprint_error_dev(self, "page program failed!\n");
+ return EIO;
+ }
+
+ return 0;
+}
+
+int
+nand_read_oob(device_t self, size_t page, void *oob)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+
+ nand_prepare_read(self, page, chip->nc_page_size);
+
+ if (chip->nc_flags & NC_BUSWIDTH_16)
+ nand_read_buf_word(self, oob, chip->nc_spare_size);
+ else
+ nand_read_buf_byte(self, oob, chip->nc_spare_size);
+
+// nand_dump_data("oob", oob, chip->nc_spare_size);
+
+ return 0;
+}
+
+static int
+nand_write_oob(device_t self, size_t offset, const void *oob)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ uint8_t status;
+
+ nand_command(self, ONFI_PAGE_PROGRAM);
+ nand_address_column(self, offset, chip->nc_page_size);
+ nand_command(self, ONFI_PAGE_PROGRAM_START);
+
+ nand_busy(self);
+
+ if (chip->nc_flags & NC_BUSWIDTH_16)
+ nand_write_buf_word(self, oob, chip->nc_spare_size);
+ else
+ nand_write_buf_byte(self, oob, chip->nc_spare_size);
+
+ status = nand_get_status(self);
+ KASSERT(status & ONFI_STATUS_RDY);
+ if (status & ONFI_STATUS_FAIL)
+ return EIO;
+ else
+ return 0;
+}
+
+void
+nand_markbad(device_t self, size_t offset)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ flash_addr_t blockoffset, marker;
+#ifdef NAND_BBT
+ flash_addr_t block;
+
+ block = offset / chip->nc_block_size;
+
+ nand_bbt_block_markbad(self, block);
+#endif
+ blockoffset = offset & chip->nc_block_mask;
+ marker = chip->nc_badmarker_offs & ~0x01;
+
+ /* check if it is already marked bad */
+ if (nand_isbad(self, blockoffset))
+ return;
+
+ nand_read_oob(self, blockoffset, chip->nc_oob_cache);
+
+ chip->nc_oob_cache[chip->nc_badmarker_offs] = 0x00;
+ chip->nc_oob_cache[chip->nc_badmarker_offs + 1] = 0x00;
+
+ nand_write_oob(self, blockoffset, chip->nc_oob_cache);
+}
+
+bool
+nand_isfactorybad(device_t self, flash_addr_t offset)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ flash_addr_t block, first_page, last_page, page;
+ int i;
+
+ /* Check for factory bad blocks first
+ * Factory bad blocks are marked in the first or last
+ * page of the blocks, see: ONFI 2.2, 3.2.2.
+ */
+ block = offset / chip->nc_block_size;
+ first_page = block * chip->nc_block_size;
+ last_page = (block + 1) * chip->nc_block_size
+ - chip->nc_page_size;
+
+ for (i = 0, page = first_page; i < 2; i++, page = last_page) {
+ /* address OOB */
+ nand_prepare_read(self, page, chip->nc_page_size);
+
+ if (chip->nc_flags & NC_BUSWIDTH_16) {
+ uint16_t word;
+ nand_read_word(self, &word);
+ if (word == 0x0000)
+ return true;
+ } else {
+ uint8_t byte;
+ nand_read_byte(self, &byte);
+ if (byte == 0x00)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+nand_iswornoutbad(device_t self, flash_addr_t offset)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ flash_addr_t block;
+
+ /* we inspect the first page of the block */
+ block = offset & chip->nc_block_mask;
+
+ /* Linux/u-boot compatible badblock handling */
+ if (chip->nc_flags & NC_BUSWIDTH_16) {
+ uint16_t word, mark;
+
+ nand_prepare_read(self, block,
+ chip->nc_page_size + (chip->nc_badmarker_offs & 0xfe));
+
+ nand_read_word(self, &word);
+ mark = htole16(word);
+ if (chip->nc_badmarker_offs & 0x01)
+ mark >>= 8;
+ if ((mark & 0xff) != 0xff)
+ return true;
+ } else {
+ uint8_t byte;
+
+ nand_prepare_read(self, block,
+ chip->nc_page_size + chip->nc_badmarker_offs);
+
+ nand_read_byte(self, &byte);
+ if (byte != 0xff)
+ return true;
+ }
+
+ return false;
+}
+
+bool
+nand_isbad(device_t self, flash_addr_t offset)
+{
+#ifdef NAND_BBT
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ flash_addr_t block;
+
+ block = offset / chip->nc_block_size;
+
+ return nand_bbt_block_isbad(self, block);
+#else
+ /* ONFI host requirement */
+ if (nand_isfactorybad(self, offset))
+ return true;
+
+ /* Look for Linux/U-Boot compatible bad marker */
+ if (nand_iswornoutbad(self, offset))
+ return true;
+
+ return false;
+#endif
+}
+
+int
+nand_erase_block(device_t self, size_t offset)
+{
+// struct nand_softc *sc = device_private(self);
+// struct nand_chip *chip = &sc->sc_chip;
+ uint8_t status;
+
+ /* xxx calculate first page of block for address? */
+
+ nand_command(self, ONFI_BLOCK_ERASE);
+ nand_address_row(self, offset);
+ nand_command(self, ONFI_BLOCK_ERASE_START);
+
+ nand_busy(self);
+
+ status = nand_get_status(self);
+ KASSERT(status & ONFI_STATUS_RDY);
+ if (status & ONFI_STATUS_FAIL) {
+ aprint_error_dev(self, "block erase failed!\n");
+ nand_markbad(self, offset);
+ return EIO;
+ } else {
+ return 0;
+ }
+}
+
+/* default functions for driver development */
+
+/* default ECC using hamming code of 256 byte chunks */
+int
+nand_default_ecc_compute(device_t self, const uint8_t *data, uint8_t *code)
+{
+ hamming_compute_256(data, code);
+
+ return 0;
+}
+
+int
+nand_default_ecc_correct(device_t self, uint8_t *data, const uint8_t *origcode,
+ const uint8_t *compcode)
+{
+ return hamming_correct_256(data, origcode, compcode);
+}
+
+void
+nand_default_select(device_t self, bool enable)
+{
+ /* do nothing */
+ return;
+}
+
+/* implementation of the block device API */
+
+/*
+ * handle (page) unaligned write to nand
+ */
+static int
+nand_flash_write_unaligned(device_t self, off_t offset, size_t len,
+ size_t *retlen, const uint8_t *buf)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ flash_addr_t first, last, firstoff;
+ const uint8_t *bufp;
+ flash_addr_t addr;
+ size_t left, count;
+ int error, i;
+
+ /* to debug chfs */
+// printf("unaligned write to nand\n");
+
+ first = offset & chip->nc_page_mask;
+ firstoff = offset & ~chip->nc_page_mask;
+ /* XXX check if this should be len - 1 */
+ last = (offset + len) & chip->nc_page_mask;
+ count = last - first + 1;
+
+ addr = first;
+ *retlen = 0;
+
+ if (count == 1) {
+ if (nand_isbad(self, addr)) {
+ aprint_error_dev(self,
+ "nand_flash_write_unaligned: "
+ "bad block encountered\n");
+ return EIO;
+ }
+
+ error = nand_read_page(self, addr, chip->nc_page_cache);
+ if (error)
+ return error;
+
+ memcpy(chip->nc_page_cache + firstoff, buf, len);
+
+ error = nand_program_page(self, addr, chip->nc_page_cache);
+ if (error)
+ return error;
+
+ *retlen = len;
+ return 0;
+ }
+
+ bufp = buf;
+ left = len;
+
+ for (i = 0; i < count && left != 0; i++) {
+ if (nand_isbad(self, addr)) {
+ aprint_error_dev(self,
+ "nand_flash_write_unaligned: "
+ "bad block encountered\n");
+ return EIO;
+ }
+
+ if (i == 0) {
+ error = nand_read_page(self,
+ addr, chip->nc_page_cache);
+ if (error)
+ return error;
+
+ memcpy(chip->nc_page_cache + firstoff,
+ bufp, chip->nc_page_size - firstoff);
+
+ printf("program page: %s: %d\n", __FILE__, __LINE__);
+ error = nand_program_page(self,
+ addr, chip->nc_page_cache);
+ if (error)
+ return error;
+
+ bufp += chip->nc_page_size - firstoff;
+ left -= chip->nc_page_size - firstoff;
+ *retlen += chip->nc_page_size - firstoff;
+
+ } else if (i == count - 1) {
+ error = nand_read_page(self,
+ addr, chip->nc_page_cache);
+ if (error)
+ return error;
+
+ memcpy(chip->nc_page_cache, bufp, left);
+
+ error = nand_program_page(self,
+ addr, chip->nc_page_cache);
+ if (error)
+ return error;
+
+ *retlen += left;
+ KASSERT(left < chip->nc_page_size);
+
+ } else {
+ /* XXX debug */
+ if (left > chip->nc_page_size) {
+ printf("left: %zu, i: %d, count: %zu\n",
+ (size_t )left, i, count);
+ }
+ KASSERT(left > chip->nc_page_size);
+
+ error = nand_program_page(self, addr, bufp);
+ if (error)
+ return error;
+
+ bufp += chip->nc_page_size;
+ left -= chip->nc_page_size;
+ *retlen += chip->nc_page_size;
+ }
+
+ addr += chip->nc_page_size;
+ }
+
+ KASSERT(*retlen == len);
+
+ return 0;
+}
+
+int
+nand_flash_write(device_t self, off_t offset, size_t len, size_t *retlen,
+ const uint8_t *buf)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ const uint8_t *bufp;
+ size_t pages, page;
+ daddr_t addr;
+ int error = 0;
+
+ if ((offset + len) > chip->nc_size) {
+ DPRINTF(("nand_flash_write: write (off: 0x%jx, len: %ju),"
+ " is over device size (0x%jx)\n",
+ (uintmax_t)offset, (uintmax_t)len,
+ (uintmax_t)chip->nc_size));
+ return EINVAL;
+ }
+
+ if (len % chip->nc_page_size != 0 ||
+ offset % chip->nc_page_size != 0) {
+ return nand_flash_write_unaligned(self,
+ offset, len, retlen, buf);
+ }
+
+ pages = len / chip->nc_page_size;
+ KASSERT(pages != 0);
+ *retlen = 0;
+
+ addr = offset;
+ bufp = buf;
+
+ mutex_enter(&sc->sc_device_lock);
+ for (page = 0; page < pages; page++) {
+ /* do we need this check here? */
+ if (nand_isbad(self, addr)) {
+ aprint_error_dev(self,
+ "nand_flash_write: bad block encountered\n");
+
+ error = EIO;
+ goto out;
+ }
+
+ error = nand_program_page(self, addr, bufp);
+ if (error)
+ goto out;
+
+ addr += chip->nc_page_size;
+ bufp += chip->nc_page_size;
+ *retlen += chip->nc_page_size;
+ }
+out:
+ mutex_exit(&sc->sc_device_lock);
+ DPRINTF(("page programming: retlen: %zu, len: %zu\n", *retlen, len));
+
+ return error;
+}
+
+/*
+ * handle (page) unaligned read from nand
+ */
+static int
+nand_flash_read_unaligned(device_t self, size_t offset,
+ size_t len, size_t *retlen, uint8_t *buf)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ daddr_t first, last, count, firstoff;
+ uint8_t *bufp;
+ daddr_t addr;
+ size_t left;
+ int error = 0, i;
+
+ /* to debug chfs */
+// printf("unaligned read from nand\n");
+
+ first = offset & chip->nc_page_mask;
+ firstoff = offset & ~chip->nc_page_mask;
+ last = (offset + len) & chip->nc_page_mask;
+// lastoff = chip->nc_page_size - (offset + len) & ~chip->nc_page_mask;
+ count = (last - first) / chip->nc_page_size + 1;
+
+ addr = first;
+ bufp = buf;
+ left = len;
+ *retlen = 0;
+
+ mutex_enter(&sc->sc_device_lock);
+ if (count == 1) {
+ error = nand_read_page(self, addr, chip->nc_page_cache);
+ if (error)
+ goto out;
+
+ memcpy(bufp, chip->nc_page_cache + firstoff, len);
+
+ *retlen = len;
+ goto out;
+ }
+
+ for (i = 0; i < count && left != 0; i++) {
+ error = nand_read_page(self, addr, chip->nc_page_cache);
+ if (error)
+ goto out;
+
+ if (i == 0) {
+ memcpy(bufp, chip->nc_page_cache + firstoff,
+ chip->nc_page_size - firstoff);
+
+ bufp += chip->nc_page_size - firstoff;
+ left -= chip->nc_page_size - firstoff;
+ *retlen += chip->nc_page_size - firstoff;
+
+ } else if (i == count - 1) {
+ memcpy(bufp, chip->nc_page_cache, left);
+ *retlen += left;
+ KASSERT(left < chip->nc_page_size);
+
+ } else {
+ memcpy(bufp, chip->nc_page_cache, chip->nc_page_size);
+
+ bufp += chip->nc_page_size;
+ left -= chip->nc_page_size;
+ *retlen += chip->nc_page_size;
+ }
+
+ addr += chip->nc_page_size;
+ }
+
+ KASSERT(*retlen == len);
+
+out:
+ mutex_exit(&sc->sc_device_lock);
+
+ return error;
+}
+
+int
+nand_flash_read(device_t self, off_t offset, size_t len, size_t *retlen,
+ uint8_t *buf)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ uint8_t *bufp;
+ size_t addr;
+ size_t i, pages;
+ int error = 0;
+
+ *retlen = 0;
+
+ DPRINTF(("nand_flash_read: off: 0x%jx, len: %zu\n",
+ (uintmax_t)offset, len));
+
+ if (__predict_false((offset + len) > chip->nc_size)) {
+ DPRINTF(("nand_flash_read: read (off: 0x%jx, len: %zu),"
+ " is over device size (%ju)\n", (uintmax_t)offset,
+ len, (uintmax_t)chip->nc_size));
+ return EINVAL;
+ }
+
+ /* Handle unaligned access, shouldnt be needed when using the
+ * block device, as strategy handles it, so only low level
+ * accesses will use this path
+ */
+// if (len < chip->nc_page_size)
+// panic("TODO page size is larger than read size");
+
+ if (len % chip->nc_page_size != 0 ||
+ offset % chip->nc_page_size != 0) {
+ return nand_flash_read_unaligned(self,
+ offset, len, retlen, buf);
+ }
+
+ bufp = buf;
+ addr = offset;
+ pages = len / chip->nc_page_size;
+
+ mutex_enter(&sc->sc_device_lock);
+ for (i = 0; i < pages; i++) {
+ /* do we need this check here? */
+ if (nand_isbad(self, addr)) {
+ aprint_error_dev(self, "bad block encountered\n");
+ error = EIO;
+ goto out;
+ }
+ error = nand_read_page(self, addr, bufp);
+ if (error)
+ goto out;
+
+ bufp += chip->nc_page_size;
+ addr += chip->nc_page_size;
+ *retlen += chip->nc_page_size;
+ }
+
+out:
+ mutex_exit(&sc->sc_device_lock);
+
+ return error;
+}
+
+int
+nand_flash_isbad(device_t self, uint64_t ofs)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ bool result;
+// uint64_t block_num;
+
+ if (ofs > chip->nc_size) {
+ DPRINTF(("nand_flash_isbad: offset 0x%jx is larger than"
+ " device size (0x%jx)\n", (uintmax_t)ofs,
+ (uintmax_t)chip->nc_size));
+ return EINVAL;
+ }
+
+ if (ofs % chip->nc_block_size != 0) {
+ panic("offset (0x%jx) is not the multiple of block size (%ju)",
+ (uintmax_t)ofs, (uintmax_t)chip->nc_block_size);
+ }
+// block_num = ofs / fl->flash_if.erasesize;
+
+ mutex_enter(&sc->sc_device_lock);
+ result = nand_isbad(self, ofs);
+ mutex_exit(&sc->sc_device_lock);
+
+ if (result)
+ return 1;
+ else
+ return 0;
+}
+
+int
+nand_flash_markbad(device_t self, uint64_t ofs)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+
+// uint64_t block_num;
+
+ if (ofs > chip->nc_size) {
+ DPRINTF(("nand_flash_markbad: offset 0x%jx is larger than"
+ " device size (0x%jx)\n", ofs,
+ (uintmax_t)chip->nc_size));
+ return EINVAL;
+ }
+
+ if (ofs % chip->nc_block_size != 0) {
+ panic("offset (%ju) is not the multiple of block size (%ju)",
+ (uintmax_t)ofs, (uintmax_t)chip->nc_block_size);
+ }
+
+// block_num = ofs / fl->flash_if->erasesize;
+
+ mutex_enter(&sc->sc_device_lock);
+ nand_markbad(self, ofs);
+ mutex_exit(&sc->sc_device_lock);
+
+ return 0;
+}
+
+int
+nand_flash_erase(device_t self,
+ struct flash_erase_instruction *ei)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ flash_addr_t addr;
+ int error;
+// off_t block_num;
+
+// if (FLASH_CLOSED == sc->sc_flash.status)
+// return FLASH_CLOSED;
+
+ if (ei->ei_addr < 0 || ei->ei_len < chip->nc_block_size)
+ return EINVAL;
+
+ if (ei->ei_addr + ei->ei_len > chip->nc_size) {
+ DPRINTF(("nand_flash_erase: erase address is over the end"
+ " of the device\n"));
+ return EINVAL;
+ }
+
+ if (ei->ei_addr % chip->nc_block_size != 0) {
+ aprint_error_dev(self,
+ "nand_flash_erase: ei_addr (%ju) is not"
+ "the multiple of block size (%ju)",
+ (uintmax_t)ei->ei_addr,
+ (uintmax_t)chip->nc_block_size);
+ return EINVAL;
+ }
+
+ if (ei->ei_len % chip->nc_block_size != 0) {
+ aprint_error_dev(self,
+ "nand_flash_erase: ei_len (%ju) is not"
+ "the multiple of block size (%ju)",
+ (uintmax_t)ei->ei_addr,
+ (uintmax_t)chip->nc_block_size);
+ return EINVAL;
+ }
+
+ mutex_enter(&sc->sc_device_lock);
+ addr = ei->ei_addr;
+ while (addr < ei->ei_addr + ei->ei_len) {
+ if (nand_isbad(self, addr)) {
+ mutex_exit(&sc->sc_device_lock);
+ aprint_error_dev(self, "bad block encountered\n");
+ ei->ei_state = FLASH_ERASE_FAILED;
+ return EIO;
+ }
+
+ error = nand_erase_block(self, addr);
+ if (error) {
+ mutex_exit(&sc->sc_device_lock);
+ ei->ei_state = FLASH_ERASE_FAILED;
+ return error;
+ }
+
+ addr += chip->nc_block_size;
+ }
+ mutex_exit(&sc->sc_device_lock);
+
+ ei->ei_state = FLASH_ERASE_DONE;
+ if (ei->ei_callback != NULL)
+ ei->ei_callback(ei);
+
+ return 0;
+}
+
+static int
+sysctl_nand_verify(SYSCTLFN_ARGS)
+{
+ int error, t;
+ struct sysctlnode node;
+
+ node = *rnode;
+ t = *(int *)rnode->sysctl_data;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ if (node.sysctl_num == nand_cachesync_nodenum) {
+ if (t <= 0 || t > 60)
+ return EINVAL;
+ } else {
+ return EINVAL;
+ }
+
+ *(int *)rnode->sysctl_data = t;
+
+ return 0;
+}
+
+SYSCTL_SETUP(sysctl_nand, "sysctl nand subtree setup")
+{
+ int rc, nand_root_num;
+ const struct sysctlnode *node;
+
+ if ((rc = sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
+ NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0) {
+ goto error;
+ }
+
+ if ((rc = sysctl_createv(clog, 0, NULL, &node,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "nand",
+ SYSCTL_DESCR("NAND driver controls"),
+ NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) {
+ goto error;
+ }
+
+ nand_root_num = node->sysctl_num;
+
+ if ((rc = sysctl_createv(clog, 0, NULL, &node,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+ CTLTYPE_INT, "cache_sync_timeout",
+ SYSCTL_DESCR("NAND write cache sync timeout in seconds"),
+ sysctl_nand_verify, 0, &nand_cachesync_timeout,
+ 0, CTL_HW, nand_root_num, CTL_CREATE,
+ CTL_EOL)) != 0) {
+ goto error;
+ }
+
+ nand_cachesync_nodenum = node->sysctl_num;
+
+ return;
+
+error:
+ aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
+}
+
+MODULE(MODULE_CLASS_DRIVER, nand, "flash");
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+nand_modcmd(modcmd_t cmd, void *opaque)
+{
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+#ifdef _MODULE
+ return config_init_component(cfdriver_ioconf_nand,
+ cfattach_ioconf_nand, cfdata_ioconf_nand);
+#else
+ return 0;
+#endif
+ case MODULE_CMD_FINI:
+#ifdef _MODULE
+ return config_fini_component(cfdriver_ioconf_nand,
+ cfattach_ioconf_nand, cfdata_ioconf_nand);
+#else
+ return 0;
+#endif
+ default:
+ return ENOTTY;
+ }
+}
diff --git a/sys/dev/nand/nand.h b/sys/dev/nand/nand.h
new file mode 100644
index 00000000000..a9e8c1f295a
--- /dev/null
+++ b/sys/dev/nand/nand.h
@@ -0,0 +1,465 @@
+/* $NetBSD: nand.h,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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 ``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 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.
+ */
+
+#ifndef _NAND_H_
+#define _NAND_H_
+
+#include <sys/param.h>
+#include <sys/cdefs.h>
+
+#include <sys/bufq.h>
+#include <sys/buf.h>
+#include <sys/time.h>
+
+#include <dev/flash/flash.h>
+
+/* flash interface implementation */
+int nand_flash_isbad(device_t, uint64_t);
+int nand_flash_markbad(device_t, uint64_t);
+int nand_flash_write(device_t, off_t, size_t, size_t *, const u_char *);
+int nand_flash_read(device_t, off_t, size_t, size_t *, uint8_t *);
+int nand_flash_erase(device_t, struct flash_erase_instruction *);
+
+/* nand specific functions */
+int nand_erase_block(device_t, size_t);
+
+int nand_io_submit(device_t, struct buf *);
+void nand_sync_thread(void *);
+int nand_sync_thread_start(device_t);
+void nand_sync_thread_stop(device_t);
+
+bool nand_isfactorybad(device_t, flash_addr_t);
+bool nand_iswornoutbad(device_t, flash_addr_t);
+bool nand_isbad(device_t, flash_addr_t);
+void nand_markbad(device_t, size_t);
+
+int nand_read_page(device_t, size_t, uint8_t *);
+int nand_read_oob(device_t self, size_t page, void *oob);
+
+/*
+ * default functions for driver development
+ */
+void nand_default_select(device_t, bool);
+int nand_default_ecc_compute(device_t, const uint8_t *, uint8_t *);
+int nand_default_ecc_correct(device_t, uint8_t *, const uint8_t *,
+ const uint8_t *);
+
+static inline void nand_busy(device_t);
+static inline void nand_select(device_t, bool);
+static inline void nand_command(device_t, uint8_t);
+static inline void nand_address(device_t, uint32_t);
+static inline void nand_read_buf_byte(device_t, void *, size_t);
+static inline void nand_read_buf_word(device_t, void *, size_t);
+static inline void nand_read_byte(device_t, uint8_t *);
+static inline void nand_write_buf_byte(device_t, const void *, size_t);
+static inline void nand_write_buf_word(device_t, const void *, size_t);
+//static inline bool nand_block_isbad(device_t, off_t);
+//static inline void nand_block_markbad(device_t, off_t);
+//static inline bool nand_isbusy(device_t);
+
+//#define NAND_DEBUG 1
+#ifdef NAND_DEBUG
+#define DPRINTF(x) if (nanddebug) printf x
+#define DPRINTFN(n,x) if (nanddebug>(n)) printf x
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define NAND_VERBOSE
+
+/* same as in linux for compatibility */
+enum {
+ NAND_BAD_MARKER_OFFSET = 0,
+ NAND_BAD_MARKER_OFFSET_SMALL = 5
+};
+
+/* feature flags use in nc_flags */
+enum {
+ NC_BUSWIDTH_16 = (1<<0),
+ NC_SOURCE_SYNC = (1<<2),
+ NC_INTERLEAVED_PE = (1<<1),
+ NC_INTERLEAVED_R = (1<<3),
+ NC_EXTENDED_PARAM = (1<<4)
+};
+
+/* various quirks used in nc_quirks */
+enum {
+ NC_QUIRK_NO_READ_START = (1<<0)
+};
+
+enum {
+ NAND_ECC_READ,
+ NAND_ECC_WRITE
+};
+
+enum {
+ NAND_ECC_OK,
+ NAND_ECC_CORRECTED,
+ NAND_ECC_INVALID,
+ NAND_ECC_TWOBIT
+};
+
+enum {
+ NAND_ECC_TYPE_HW,
+ NAND_ECC_TYPE_SW
+};
+
+struct nand_bbt {
+ uint8_t *nbbt_bitmap;
+ size_t nbbt_size;
+};
+
+struct nand_ecc {
+ size_t necc_offset; /* offset of ecc data in oob */
+ size_t necc_size; /* size of ecc data in oob */
+ size_t necc_block_size; /* block size used in ecc calc */
+ size_t necc_code_size; /* reduntant bytes per block */
+ int necc_steps; /* pagesize / code size */
+ int necc_type; /* type of the ecc engine */
+};
+
+/**
+ * nand_chip: structure containing the required information
+ * about the NAND chip.
+ */
+struct nand_chip {
+ uint8_t *nc_oob_cache; /* buffer for oob cache */
+ uint8_t *nc_page_cache; /* buffer for page cache */
+ uint8_t *nc_ecc_cache;
+ size_t nc_size; /* storage size in bytes */
+ size_t nc_page_size; /* page size in bytes */
+ size_t nc_block_pages; /* block size in pages */
+ size_t nc_block_size; /* block size in bytes */
+ size_t nc_spare_size; /* spare (oob) size in bytes */
+ uint32_t nc_flags; /* bitfield flags */
+ uint32_t nc_quirks; /* bitfield quirks */
+ unsigned int nc_page_shift; /* page shift for page alignment */
+ unsigned int nc_page_mask; /* page mask for page alignment */
+ unsigned int nc_block_shift; /* write shift */
+ unsigned int nc_block_mask; /* write mask */
+ uint8_t nc_manf_id; /* manufacturer id */
+ uint8_t nc_dev_id; /* device id */
+ uint8_t nc_addr_cycles_row; /* row cycles for addressing */
+ uint8_t nc_addr_cycles_column; /* column cycles for addressing */
+ uint8_t nc_badmarker_offs; /* offset for marking bad blocks */
+
+ struct nand_ecc *nc_ecc;
+};
+
+struct nand_write_cache {
+ struct bintime nwc_creation;
+ struct bintime nwc_last_write;
+ struct bufq_state *nwc_bufq;
+ uint8_t *nwc_data;
+ daddr_t nwc_block;
+ kmutex_t nwc_lock;
+ bool nwc_write_pending;
+};
+
+/* driver softc for nand */
+struct nand_softc {
+ device_t sc_dev;
+ device_t nand_dev;
+ struct nand_interface *nand_if;
+ void *nand_softc;
+ struct nand_chip sc_chip;
+ struct nand_bbt sc_bbt;
+ size_t sc_part_offset;
+ size_t sc_part_size;
+ kmutex_t sc_device_lock; /* serialize access to chip */
+
+ /* for the i/o thread */
+ struct lwp *sc_sync_thread;
+ struct nand_write_cache sc_cache;
+ kmutex_t sc_io_lock;
+ kmutex_t sc_waitq_lock;
+ kcondvar_t sc_io_cv;
+ bool sc_io_running;
+};
+
+/* structure holding the nand api */
+struct nand_interface
+{
+ void (*select) (device_t, bool);
+ void (*command) (device_t, uint8_t);
+ void (*address) (device_t, uint8_t);
+ void (*read_buf_byte) (device_t, void *, size_t);
+ void (*read_buf_word) (device_t, void *, size_t);
+ void (*read_byte) (device_t, uint8_t *);
+ void (*read_word) (device_t, uint16_t *);
+ void (*write_buf_byte) (device_t, const void *, size_t);
+ void (*write_buf_word) (device_t, const void *, size_t);
+ void (*write_byte) (device_t, uint8_t);
+ void (*write_word) (device_t, uint16_t);
+ void (*busy) (device_t);
+
+ /* functions specific to ecc computation */
+ int (*ecc_prepare)(device_t, int);
+ int (*ecc_compute)(device_t, const uint8_t *, uint8_t *);
+ int (*ecc_correct)(device_t, uint8_t *, const uint8_t *,
+ const uint8_t *);
+
+ struct nand_ecc ecc;
+
+ /* flash partition information */
+ const struct flash_partition *part_info;
+ int part_num;
+};
+
+/* attach args */
+struct nand_attach_args {
+ struct nand_interface *naa_nand_if;
+};
+
+device_t nand_attach_mi(struct nand_interface *nand_if, device_t dev);
+
+static inline void
+nand_busy(device_t device)
+{
+ struct nand_softc *sc = device_private(device);
+
+ KASSERT(sc->nand_if->select != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->select(sc->nand_dev, true);
+
+ if (sc->nand_if->busy != NULL) {
+ sc->nand_if->busy(sc->nand_dev);
+ }
+
+ sc->nand_if->select(sc->nand_dev, false);
+}
+
+static inline void
+nand_select(device_t self, bool enable)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->select != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->select(sc->nand_dev, enable);
+}
+
+static inline void
+nand_address(device_t self, uint32_t address)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->address != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->address(sc->nand_dev, address);
+}
+
+static inline void
+nand_command(device_t self, uint8_t command)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->command != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->command(sc->nand_dev, command);
+}
+
+static inline void
+nand_read_byte(device_t self, uint8_t *data)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->read_byte != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->read_byte(sc->nand_dev, data);
+}
+
+static inline void
+nand_write_byte(device_t self, uint8_t data)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->write_byte != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->write_byte(sc->nand_dev, data);
+}
+
+static inline void
+nand_read_word(device_t self, uint16_t *data)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->read_word != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->read_word(sc->nand_dev, data);
+}
+
+static inline void
+nand_write_word(device_t self, uint16_t data)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->write_word != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->write_word(sc->nand_dev, data);
+}
+
+static inline void
+nand_read_buf_byte(device_t self, void *buf, size_t size)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->read_buf_byte != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->read_buf_byte(sc->nand_dev, buf, size);
+}
+
+static inline void
+nand_read_buf_word(device_t self, void *buf, size_t size)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->read_buf_word != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->read_buf_word(sc->nand_dev, buf, size);
+}
+
+static inline void
+nand_write_buf_byte(device_t self, const void *buf, size_t size)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->write_buf_byte != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->write_buf_byte(sc->nand_dev, buf, size);
+}
+
+static inline void
+nand_write_buf_word(device_t self, const void *buf, size_t size)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->write_buf_word != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->write_buf_word(sc->nand_dev, buf, size);
+}
+
+static inline int
+nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldcode,
+ const uint8_t *newcode)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->ecc_correct != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ return sc->nand_if->ecc_correct(sc->nand_dev, data, oldcode, newcode);
+}
+
+static inline void
+nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *code)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->ecc_compute != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ sc->nand_if->ecc_compute(sc->nand_dev, data, code);
+}
+
+static inline void
+nand_ecc_prepare(device_t self, int mode)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_dev != NULL);
+
+ if (sc->nand_if->ecc_prepare != NULL)
+ sc->nand_if->ecc_prepare(sc->nand_dev, mode);
+}
+
+#if 0
+static inline bool
+nand_block_isbad(device_t self, off_t block)
+{
+ struct nand_softc *sc = device_private(self);
+
+ KASSERT(sc->nand_if->block_isbad != NULL);
+ KASSERT(sc->nand_dev != NULL);
+
+ return sc->nand_if->block_isbad(sc->nand_dev, block);
+}
+#endif
+
+/* Manufacturer IDs defined by JEDEC */
+enum {
+ NAND_MFR_UNKNOWN = 0x00,
+ NAND_MFR_AMD = 0x01,
+ NAND_MFR_FUJITSU = 0x04,
+ NAND_MFR_RENESAS = 0x07,
+ NAND_MFR_STMICRO = 0x20,
+ NAND_MFR_MICRON = 0x2c,
+ NAND_MFR_NATIONAL = 0x8f,
+ NAND_MFR_TOSHIBA = 0x98,
+ NAND_MFR_HYNIX = 0xad,
+ NAND_MFR_SAMSUNG = 0xec
+};
+
+struct nand_manufacturer {
+ int id;
+ const char *name;
+};
+
+extern const struct nand_manufacturer nand_mfrs[];
+
+static inline void
+nand_dump_data(const char *name, void *data, size_t len)
+{
+ printf("dumping %s\n--------------\n", name);
+ uint8_t *dump = data;
+ for (int i = 0; i < len; i++) {
+ printf("0x%.2hhx ", *dump);
+ dump++;
+ }
+ printf("\n--------------\n");
+}
+
+#endif /* _NAND_H_ */
diff --git a/sys/dev/nand/nand_bbt.c b/sys/dev/nand/nand_bbt.c
new file mode 100644
index 00000000000..362a1d52378
--- /dev/null
+++ b/sys/dev/nand/nand_bbt.c
@@ -0,0 +1,250 @@
+/* $NetBSD: nand_bbt.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2011 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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 ``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 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.
+ */
+
+/* Support for Bad Block Tables (BBTs) */
+
+#include <sys/param.h>
+#include <sys/kmem.h>
+
+#include "nand.h"
+#include "nand_bbt.h"
+
+void
+nand_bbt_init(device_t self)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ struct nand_bbt *bbt = &sc->sc_bbt;
+
+ bbt->nbbt_size = chip->nc_size / chip->nc_block_size / 4;
+ bbt->nbbt_bitmap = kmem_alloc(bbt->nbbt_size, KM_SLEEP);
+
+ memset(bbt->nbbt_bitmap, 0xff, bbt->nbbt_size);
+}
+
+void
+nand_bbt_detach(device_t self)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_bbt *bbt = &sc->sc_bbt;
+
+ printf("freeing bbt bitmap...");
+ kmem_free(bbt->nbbt_bitmap, bbt->nbbt_size);
+ printf("done!\n");
+}
+
+void
+nand_bbt_scan(device_t self)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ flash_addr_t i, blocks, addr;
+
+ blocks = chip->nc_size / chip->nc_block_size;
+
+ aprint_normal_dev(self, "scanning for bad blocks\n");
+
+ addr = 0;
+ for (i = 0; i < blocks; i++) {
+ if (nand_isfactorybad(self, addr)) {
+ nand_bbt_block_markfactorybad(self, i);
+ } else if (nand_iswornoutbad(self, addr)) {
+ nand_bbt_block_markbad(self, i);
+ }
+
+ addr += chip->nc_block_size;
+ }
+}
+
+bool
+nand_bbt_update(device_t self)
+{
+ return true;
+}
+
+static bool
+nand_bbt_page_has_bbt(device_t self, flash_addr_t addr) {
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ uint8_t *oob = chip->nc_oob_cache;
+
+ nand_read_oob(self, addr, oob);
+
+ if (oob[NAND_BBT_OFFSET] == 'B' &&
+ oob[NAND_BBT_OFFSET + 1] == 'b' &&
+ oob[NAND_BBT_OFFSET + 2] == 't') {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static bool
+nand_bbt_get_bbt_from_page(device_t self, flash_addr_t addr)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ struct nand_bbt *bbt = &sc->sc_bbt;
+ uint8_t *bbtp, *buf = chip->nc_page_cache;
+ size_t left, bbt_pages, i;
+
+ bbt_pages = bbt->nbbt_size / chip->nc_page_size;
+ if (bbt->nbbt_size % chip->nc_page_size)
+ bbt_pages++;
+
+ if (nand_isbad(self, addr)) {
+ return false;
+ }
+
+ if (nand_bbt_page_has_bbt(self, addr)) {
+ bbtp = bbt->nbbt_bitmap;
+ left = bbt->nbbt_size;
+
+ for (i = 0; i < bbt_pages; i++) {
+ nand_read_page(self, addr, buf);
+
+ if (i == bbt_pages - 1) {
+ KASSERT(left <= chip->nc_page_size);
+ memcpy(bbtp, buf, left);
+ } else {
+ memcpy(bbtp, buf, chip->nc_page_size);
+ }
+
+ bbtp += chip->nc_page_size;
+ left -= chip->nc_page_size;
+ addr += chip->nc_page_size;
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool
+nand_bbt_load(device_t self)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ flash_addr_t blockaddr;
+ int n;
+
+ blockaddr = chip->nc_size - chip->nc_block_size;
+ /* XXX currently we check the last 4 blocks */
+ for (n = 0; n < 4; n++) {
+ if (nand_bbt_get_bbt_from_page(self, blockaddr)) {
+ break;
+ } else {
+ blockaddr -= chip->nc_block_size;
+ }
+ }
+
+ return true;
+}
+
+void
+nand_bbt_block_markbad(device_t self, flash_addr_t block)
+{
+ if (nand_bbt_block_isbad(self, block)) {
+ aprint_error_dev(self,
+ "trying to mark block bad already marked in bbt\n");
+ }
+ /* XXX check if this is the correct marker */
+ nand_bbt_block_mark(self, block, NAND_BBT_MARKER_WORNOUT_BAD);
+}
+
+void
+nand_bbt_block_markfactorybad(device_t self, flash_addr_t block)
+{
+ if (nand_bbt_block_isbad(self, block)) {
+ aprint_error_dev(self,
+ "trying to mark block factory bad already"
+ " marked in bbt\n");
+ }
+ nand_bbt_block_mark(self, block, NAND_BBT_MARKER_FACTORY_BAD);
+}
+
+void
+nand_bbt_block_mark(device_t self, flash_addr_t block, uint8_t marker)
+{
+ struct nand_softc *sc = device_private(self);
+#ifdef DIAGNOSTIC
+ struct nand_chip *chip = &sc->sc_chip;
+#endif
+ struct nand_bbt *bbt = &sc->sc_bbt;
+ uint8_t clean;
+
+ KASSERT(block < chip->nc_size / chip->nc_block_size);
+
+ clean = (~0x03 << ((block % 4) * 2));
+ marker = (marker << ((block % 4) * 2));
+
+ /* set byte containing the 2 bit marker for this block */
+ bbt->nbbt_bitmap[block / 4] &= clean;
+ bbt->nbbt_bitmap[block / 4] |= marker;
+}
+
+bool
+nand_bbt_block_isbad(device_t self, flash_addr_t block)
+{
+ struct nand_softc *sc = device_private(self);
+#ifdef DIAGNOSTIC
+ struct nand_chip *chip = &sc->sc_chip;
+#endif
+ struct nand_bbt *bbt = &sc->sc_bbt;
+ uint8_t byte, marker;
+ bool result;
+
+ KASSERT(block < chip->nc_size / chip->nc_block_size);
+
+ /* get byte containing the 2 bit marker for this block */
+ byte = bbt->nbbt_bitmap[block / 4];
+
+ /* extract the 2 bit marker from the byte */
+ marker = (byte >> ((block % 4) * 2)) & 0x03;
+
+ switch (marker) {
+ case NAND_BBT_MARKER_FACTORY_BAD:
+ case NAND_BBT_MARKER_WORNOUT_BAD:
+ case NAND_BBT_MARKER_RESERVED:
+ result = true;
+ break;
+ case NAND_BBT_MARKER_GOOD:
+ result = false;
+ break;
+ default:
+ panic("error in marker extraction");
+ }
+
+ return result;
+}
diff --git a/sys/dev/nand/nand_bbt.h b/sys/dev/nand/nand_bbt.h
new file mode 100644
index 00000000000..dec73fff465
--- /dev/null
+++ b/sys/dev/nand/nand_bbt.h
@@ -0,0 +1,58 @@
+/* $NetBSD: nand_bbt.h,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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 ``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 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.
+ */
+
+#ifndef _NAND_BBT_H_
+#define _NAND_BBT_H_
+
+enum {
+ NAND_BBT_MARKER_FACTORY_BAD = 0x00, /* 00 */
+ NAND_BBT_MARKER_WORNOUT_BAD = 0x01, /* 01 */
+ NAND_BBT_MARKER_RESERVED = 0x02, /* 10 */
+ NAND_BBT_MARKER_GOOD = 0x03 /* 11 */
+};
+
+enum {
+ NAND_BBT_OFFSET = 0x0e
+};
+
+void nand_bbt_init(device_t);
+void nand_bbt_detach(device_t);
+void nand_bbt_scan(device_t);
+bool nand_bbt_update(device_t);
+bool nand_bbt_load(device_t);
+void nand_bbt_block_mark(device_t, flash_addr_t, uint8_t);
+void nand_bbt_block_markbad(device_t, flash_addr_t);
+void nand_bbt_block_markfactorybad(device_t, flash_addr_t);
+bool nand_bbt_block_isbad(device_t, flash_addr_t);
+
+#endif
diff --git a/sys/dev/nand/nand_crc.c b/sys/dev/nand/nand_crc.c
new file mode 100644
index 00000000000..490c07bc77c
--- /dev/null
+++ b/sys/dev/nand/nand_crc.c
@@ -0,0 +1,63 @@
+/* $NetBSD: nand_crc.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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 ``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 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.
+ */
+
+/* Implements CRC-16 as required by the ONFI 2.3 specification */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: nand_crc.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $");
+
+#include "nand_crc.h"
+
+uint16_t
+nand_crc16(uint8_t *data, size_t len)
+{
+ const uint16_t init = 0x4f4e;
+ const uint16_t polynom = 0x8005;
+ const uint16_t highbit = 0x0001 << 15;
+ uint16_t crc = init;
+ size_t i;
+ int j;
+
+ for (i = 0; i < len; i++) {
+ crc ^= data[i] << 8;
+
+ for (j = 0; j < 8; j++) {
+ if ((crc & highbit) != 0x00)
+ crc = (crc << 1) ^ polynom;
+ else
+ crc <<= 1;
+ }
+ }
+
+ return crc;
+}
diff --git a/sys/dev/nand/nand_crc.h b/sys/dev/nand/nand_crc.h
new file mode 100644
index 00000000000..eea450c32eb
--- /dev/null
+++ b/sys/dev/nand/nand_crc.h
@@ -0,0 +1,42 @@
+/* $NetBSD: nand_crc.h,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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 ``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 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.
+ */
+
+#ifndef _NAND_CRC_H_
+#define _NAND_CRC_H_
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+uint16_t nand_crc16(uint8_t *, size_t);
+
+#endif
diff --git a/sys/dev/nand/nand_io.c b/sys/dev/nand/nand_io.c
new file mode 100644
index 00000000000..b883943bfd4
--- /dev/null
+++ b/sys/dev/nand/nand_io.c
@@ -0,0 +1,373 @@
+/* $NetBSD: nand_io.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2011 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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 ``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 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.
+ */
+
+/* Inspired by the similar code in the NetBSD SPI driver, but I
+ * decided to do a rewrite from scratch to be suitable for NAND.
+ */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/bufq.h>
+#include <sys/kernel.h>
+#include <sys/kmem.h>
+#include <sys/kthread.h>
+#include <sys/mutex.h>
+
+#include <dev/flash/flash.h>
+#include "nand.h"
+
+extern int nanddebug;
+extern int nand_cachesync_timeout;
+
+void nand_io_read(device_t, struct buf *);
+void nand_io_write(device_t, struct buf *);
+void nand_io_done(device_t, int error, struct buf *);
+int nand_io_cache_write(device_t, daddr_t, struct buf *);
+void nand_io_cache_sync(device_t);
+
+static int
+nand_timestamp_diff(struct bintime *bt, struct bintime *b2)
+{
+ struct bintime b1 = *bt;
+ struct timeval tv;
+
+ bintime_sub(&b1, b2);
+ bintime2timeval(&b1, &tv);
+
+ return tvtohz(&tv);
+}
+
+static daddr_t
+nand_io_getblock(device_t self, struct buf *bp)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ flash_addr_t block, last;
+
+ /* get block number of first byte */
+ block = bp->b_rawblkno * DEV_BSIZE / chip->nc_block_size;
+
+ /* block of the last bite */
+ last = (bp->b_rawblkno * DEV_BSIZE + bp->b_resid - 1)
+ / chip->nc_block_size;
+
+ /* spans trough multiple blocks, needs special handling */
+ if (last != block) {
+ printf("0x%jx -> 0x%jx\n",
+ bp->b_rawblkno * DEV_BSIZE,
+ bp->b_rawblkno * DEV_BSIZE + bp->b_resid - 1);
+ panic("TODO: multiple block write. last: %jd, current: %jd",
+ (intmax_t )last, (intmax_t )block);
+ }
+
+ return block;
+}
+
+int
+nand_sync_thread_start(device_t self)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ struct nand_write_cache *wc = &sc->sc_cache;
+ int error;
+
+ DPRINTF(("starting nand io thread\n"));
+
+ sc->sc_cache.nwc_data = kmem_alloc(chip->nc_block_size, KM_SLEEP);
+
+ mutex_init(&sc->sc_io_lock, MUTEX_DEFAULT, IPL_NONE);
+ mutex_init(&wc->nwc_lock, MUTEX_DEFAULT, IPL_NONE);
+ cv_init(&sc->sc_io_cv, "nandcv");
+
+ error = bufq_alloc(&wc->nwc_bufq, "fcfs", BUFQ_SORT_RAWBLOCK);
+ if (error)
+ goto err_bufq;
+
+ sc->sc_io_running = true;
+ wc->nwc_write_pending = false;
+
+ /* arrange to allocate the kthread */
+ error = kthread_create(PRI_NONE, KTHREAD_JOINABLE | KTHREAD_MPSAFE,
+ NULL, nand_sync_thread, self, &sc->sc_sync_thread, "nandio");
+
+ if (!error)
+ return 0;
+
+ bufq_free(wc->nwc_bufq);
+err_bufq:
+ cv_destroy(&sc->sc_io_cv);
+
+ mutex_destroy(&sc->sc_io_lock);
+ mutex_destroy(&wc->nwc_lock);
+
+ kmem_free(sc->sc_cache.nwc_data, chip->nc_block_size);
+
+ return error;
+}
+
+void
+nand_sync_thread_stop(device_t self)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_chip *chip = &sc->sc_chip;
+ struct nand_write_cache *wc = &sc->sc_cache;
+
+ DPRINTF(("stopping nand io thread\n"));
+
+ kmem_free(wc->nwc_data, chip->nc_block_size);
+
+ sc->sc_io_running = false;
+ kthread_join(sc->sc_sync_thread);
+
+ bufq_free(wc->nwc_bufq);
+
+ mutex_destroy(&sc->sc_io_lock);
+ mutex_destroy(&sc->sc_waitq_lock);
+ mutex_destroy(&wc->nwc_lock);
+
+ cv_destroy(&sc->sc_io_cv);
+}
+
+int
+nand_io_submit(device_t self, struct buf *bp)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_write_cache *wc = &sc->sc_cache;
+
+ DPRINTF(("submitting job to nand io thread: %p\n", bp));
+
+ if (BUF_ISREAD(bp)) {
+ DPRINTF(("we have a read job\n"));
+
+ mutex_enter(&wc->nwc_lock);
+ if (wc->nwc_write_pending)
+ nand_io_cache_sync(self);
+ mutex_exit(&wc->nwc_lock);
+
+ nand_io_read(self, bp);
+ } else {
+ DPRINTF(("we have a write job\n"));
+
+ nand_io_write(self, bp);
+ }
+
+ return 0;
+}
+
+int
+nand_io_cache_write(device_t self, daddr_t block, struct buf *bp)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_write_cache *wc = &sc->sc_cache;
+ struct nand_chip *chip = &sc->sc_chip;
+ size_t retlen;
+ daddr_t base, offset;
+ int error;
+
+ KASSERT(chip->nc_block_size != 0);
+
+ base = block * chip->nc_block_size;
+ offset = bp->b_rawblkno * DEV_BSIZE - base;
+
+ DPRINTF(("io cache write, offset: %jd\n", (intmax_t )offset));
+
+ if (!wc->nwc_write_pending) {
+ wc->nwc_block = block;
+ /*
+ * fill the cache with data from flash,
+ * so we dont have to bother with gaps later
+ */
+ DPRINTF(("filling buffer from offset %ju\n", (uintmax_t)base));
+ error = nand_flash_read(self,
+ base, chip->nc_block_size,
+ &retlen, wc->nwc_data);
+ DPRINTF(("cache filled\n"));
+
+ if (error)
+ return error;
+
+ wc->nwc_write_pending = true;
+ /* save creation time for aging */
+ binuptime(&sc->sc_cache.nwc_creation);
+ }
+ /* copy data to cache */
+ memcpy(wc->nwc_data + offset, bp->b_data, bp->b_resid);
+ bufq_put(wc->nwc_bufq, bp);
+
+ /* update timestamp */
+ binuptime(&wc->nwc_last_write);
+
+ return 0;
+}
+
+/* must be called with nwc_lock hold */
+void
+nand_io_cache_sync(device_t self)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_write_cache *wc = &sc->sc_cache;
+ struct nand_chip *chip = &sc->sc_chip;
+ struct flash_erase_instruction ei;
+ struct buf *bp;
+ size_t retlen;
+ daddr_t base;
+ int error;
+
+ if (!wc->nwc_write_pending) {
+ DPRINTF(("trying to sync with an invalid buffer\n"));
+ return;
+ }
+
+ base = wc->nwc_block * chip->nc_block_size;
+
+ DPRINTF(("eraseing block at 0x%jx\n", (uintmax_t )base));
+ ei.ei_addr = base;
+ ei.ei_len = chip->nc_block_size;
+ ei.ei_callback = NULL;
+ error = nand_flash_erase(self, &ei);
+
+ if (error) {
+ aprint_error_dev(self, "cannot erase nand flash!\n");
+ goto out;
+ }
+
+ DPRINTF(("writing %zu bytes to 0x%jx\n",
+ chip->nc_block_size, (uintmax_t )base));
+
+ error = nand_flash_write(self,
+ base, chip->nc_block_size, &retlen, wc->nwc_data);
+
+ if (error || retlen != chip->nc_block_size) {
+ aprint_error_dev(self, "can't sync write cache: %d\n", error);
+ goto out;
+ }
+
+out:
+ while ((bp = bufq_get(wc->nwc_bufq)) != NULL)
+ nand_io_done(self, error, bp);
+
+ wc->nwc_block = -1;
+ wc->nwc_write_pending = false;
+}
+
+void
+nand_sync_thread(void * arg)
+{
+ device_t self = arg;
+ struct nand_softc *sc = device_private(self);
+ struct nand_write_cache *wc = &sc->sc_cache;
+ struct bintime now;
+
+ /* sync thread waking in every seconds */
+ while (sc->sc_io_running) {
+ mutex_enter(&sc->sc_io_lock);
+ cv_timedwait_sig(&sc->sc_io_cv, &sc->sc_io_lock, hz / 4);
+ mutex_exit(&sc->sc_io_lock);
+
+ mutex_enter(&wc->nwc_lock);
+
+ if (!wc->nwc_write_pending) {
+ mutex_exit(&wc->nwc_lock);
+ continue;
+ }
+
+ /* see if the cache is older than 3 seconds (safety limit),
+ * or if we havent touched the cache since more than 1 ms
+ */
+ binuptime(&now);
+ if (nand_timestamp_diff(&now, &wc->nwc_last_write)
+ > hz / 5 ||
+ nand_timestamp_diff(&now, &wc->nwc_creation)
+ > 3 * hz) {
+ printf("syncing write cache after timeout\n");
+ nand_io_cache_sync(self);
+ }
+ mutex_exit(&wc->nwc_lock);
+ }
+
+ kthread_exit(0);
+}
+
+void
+nand_io_read(device_t self, struct buf *bp)
+{
+ size_t retlen;
+ daddr_t offset;
+ int error;
+
+ DPRINTF(("nand io read\n"));
+
+ offset = bp->b_rawblkno * DEV_BSIZE;
+
+ error = nand_flash_read(self, offset, bp->b_resid,
+ &retlen, bp->b_data);
+
+ nand_io_done(self, error, bp);
+}
+
+void
+nand_io_write(device_t self, struct buf *bp)
+{
+ struct nand_softc *sc = device_private(self);
+ struct nand_write_cache *wc = &sc->sc_cache;
+ daddr_t block;
+
+ DPRINTF(("nand io write\n"));
+
+ block = nand_io_getblock(self, bp);
+ DPRINTF(("write to block %jd\n", (intmax_t )block));
+
+ mutex_enter(&wc->nwc_lock);
+
+ if (wc->nwc_write_pending && wc->nwc_block != block) {
+ DPRINTF(("writing to new block, syncing caches\n"));
+ nand_io_cache_sync(self);
+ }
+
+ nand_io_cache_write(self, block, bp);
+
+ mutex_exit(&wc->nwc_lock);
+}
+
+void
+nand_io_done(device_t self, int error, struct buf *bp)
+{
+ DPRINTF(("io done: %p\n", bp));
+
+ if (error == 0)
+ bp->b_resid = 0;
+
+ bp->b_error = error;
+
+ biodone(bp);
+}
diff --git a/sys/dev/nand/nandemulator.c b/sys/dev/nand/nandemulator.c
new file mode 100644
index 00000000000..51bbb65a8e2
--- /dev/null
+++ b/sys/dev/nand/nandemulator.c
@@ -0,0 +1,779 @@
+/* $NetBSD: nandemulator.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2011 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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 ``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 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>
+__KERNEL_RCSID(0, "$NetBSD: nandemulator.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $");
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/kernel.h>
+
+#include "nandemulator.h"
+
+#include <dev/nand/nand.h>
+#include <dev/nand/onfi.h>
+#include <dev/nand/nand_crc.h>
+
+extern struct cfdriver nandemulator_cd;
+void nandemulatorattach(int n);
+
+static int nandemulator_match(device_t, cfdata_t, void *);
+static void nandemulator_attach(device_t, device_t, void *);
+static int nandemulator_detach(device_t, int);
+
+static void nandemulator_device_reset(device_t);
+static void nandemulator_command(device_t, uint8_t);
+static void nandemulator_address(device_t, uint8_t);
+static void nandemulator_busy(device_t);
+static void nandemulator_read_byte(device_t, uint8_t *);
+static void nandemulator_write_byte(device_t, uint8_t);
+static void nandemulator_read_word(device_t, uint16_t *);
+static void nandemulator_write_word(device_t, uint16_t);
+static void nandemulator_read_buf_byte(device_t, void *, size_t);
+static void nandemulator_read_buf_word(device_t, void *, size_t);
+static void nandemulator_write_buf_byte(device_t, const void *, size_t);
+static void nandemulator_write_buf_word(device_t, const void *, size_t);
+
+static size_t nandemulator_address_to_page(device_t);
+static size_t nandemulator_page_to_backend_offset(device_t, size_t);
+static size_t nandemulator_column_address_to_subpage(device_t);
+/*
+#define NANDEMULATOR_DEBUG 1
+
+#ifdef NANDEMULATOR_DEBUG
+#warning debug enabled
+#define DPRINTF(x) if (nandemulatordebug) printf x
+#define DPRINTFN(n,x) if (nandemulatordebug>(n)) printf x
+#else
+#error no debug
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#ifdef NANDEMULATOR_DEBUG
+int nandemulatordebug = NANDEMULATOR_DEBUG;
+#endif
+*/
+
+extern int nanddebug;
+
+enum {
+ NANDEMULATOR_8BIT,
+ NANDEMULATOR_16BIT
+};
+
+struct nandemulator_softc {
+ device_t sc_dev;
+ device_t sc_nanddev;
+
+ int sc_buswidth;
+
+ struct nand_interface sc_nand_if;
+
+ uint8_t sc_command;
+ size_t sc_io_len;
+ uint8_t *sc_io_pointer;
+ uint64_t sc_address;
+
+ uint8_t *sc_backend;
+ size_t sc_backend_size;
+ size_t sc_device_size;
+ bool sc_register_writable;
+
+ uint8_t sc_status_register;
+ uint8_t sc_ids[2];
+ uint8_t sc_onfi[4];
+
+ size_t sc_page_size;
+ size_t sc_block_size;
+ size_t sc_spare_size;
+ size_t sc_lun_size;
+ uint8_t sc_row_cycles;
+ uint8_t sc_column_cycles;
+ uint64_t sc_row_mask;
+
+ int sc_address_counter;
+
+ struct onfi_parameter_page *sc_parameter_page;
+};
+
+CFATTACH_DECL_NEW(nandemulator, sizeof(struct nandemulator_softc),
+ nandemulator_match, nandemulator_attach, nandemulator_detach, NULL);
+
+void
+nandemulatorattach(int n)
+{
+ int i, err;
+ cfdata_t cf;
+
+ aprint_debug("nandemulator: requested %d units\n", n);
+
+ err = config_cfattach_attach(nandemulator_cd.cd_name,
+ &nandemulator_ca);
+ if (err) {
+ aprint_error("%s: couldn't register cfattach: %d\n",
+ nandemulator_cd.cd_name, err);
+ config_cfdriver_detach(&nandemulator_cd);
+ return;
+ }
+ for (i = 0; i < n; i++) {
+ cf = kmem_alloc(sizeof(struct cfdata), KM_NOSLEEP);
+ if (cf == NULL) {
+ aprint_error("%s: couldn't allocate cfdata\n",
+ nandemulator_cd.cd_name);
+ continue;
+ }
+ cf->cf_name = nandemulator_cd.cd_name;
+ cf->cf_atname = nandemulator_cd.cd_name;
+ cf->cf_unit = i;
+ cf->cf_fstate = FSTATE_STAR;
+
+ (void)config_attach_pseudo(cf);
+ }
+}
+
+/* ARGSUSED */
+static int
+nandemulator_match(device_t parent, cfdata_t match, void *aux)
+{
+ /* pseudo device, always attaches */
+ return 1;
+}
+
+static void
+nandemulator_attach(device_t parent, device_t self, void *aux)
+{
+ struct nandemulator_softc *sc = device_private(self);
+ int i;
+
+ aprint_normal_dev(self, "NAND emulator\n");
+
+ sc->sc_dev = self;
+
+ sc->sc_nand_if.select = &nand_default_select;
+ sc->sc_nand_if.command = &nandemulator_command;
+ sc->sc_nand_if.address = &nandemulator_address;
+ sc->sc_nand_if.read_buf_byte = &nandemulator_read_buf_byte;
+ sc->sc_nand_if.read_buf_word = &nandemulator_read_buf_word;
+ sc->sc_nand_if.read_byte = &nandemulator_read_byte;
+ sc->sc_nand_if.read_word = &nandemulator_read_word;
+ sc->sc_nand_if.write_buf_byte = &nandemulator_write_buf_byte;
+ sc->sc_nand_if.write_buf_word = &nandemulator_write_buf_word;
+ sc->sc_nand_if.write_byte = &nandemulator_write_byte;
+ sc->sc_nand_if.write_word = &nandemulator_write_word;
+ sc->sc_nand_if.busy = &nandemulator_busy;
+
+ sc->sc_nand_if.ecc_compute = &nand_default_ecc_compute;
+ sc->sc_nand_if.ecc_correct = &nand_default_ecc_correct;
+ sc->sc_nand_if.ecc_prepare = NULL;
+ sc->sc_nand_if.ecc.necc_code_size = 3;
+ sc->sc_nand_if.ecc.necc_block_size = 256;
+ sc->sc_nand_if.ecc.necc_type = NAND_ECC_TYPE_SW;
+
+ if (!pmf_device_register1(sc->sc_dev, NULL, NULL, NULL))
+ aprint_error_dev(sc->sc_dev,
+ "couldn't establish power handler\n");
+
+ sc->sc_buswidth = NANDEMULATOR_16BIT; /* 16bit for now */
+
+ /* hardcode these now, make it configurable later */
+ sc->sc_device_size = 32 * 1024 * 1024; /* 32MB */
+ sc->sc_page_size = 2048;
+ sc->sc_block_size = 64;
+ sc->sc_lun_size =
+ sc->sc_device_size / (sc->sc_page_size * sc->sc_block_size);
+ KASSERT(sc->sc_device_size %
+ (sc->sc_page_size * sc->sc_block_size) == 0);
+ sc->sc_spare_size = 64;
+
+ sc->sc_column_cycles = 2;
+ sc->sc_row_cycles = 3;
+
+ /* init the emulator data structures */
+ sc->sc_backend_size =
+ sc->sc_device_size +
+ sc->sc_device_size / sc->sc_page_size * sc->sc_spare_size;
+
+ sc->sc_backend = kmem_alloc(sc->sc_backend_size, KM_SLEEP);
+ memset(sc->sc_backend, 0xff, sc->sc_backend_size);
+
+ sc->sc_parameter_page =
+ kmem_zalloc(sizeof(struct onfi_parameter_page) * 4, KM_SLEEP);
+
+ struct onfi_parameter_page *opp;
+ uint8_t sig[4] = { 'O', 'N', 'F', 'I' };
+
+ for (i = 0; i < 4; i++) {
+ opp = &sc->sc_parameter_page[i];
+
+ opp->param_signature = *(uint32_t *)sig;
+ opp->param_pagesize = sc->sc_page_size;
+ opp->param_blocksize = sc->sc_block_size;
+ opp->param_sparesize = sc->sc_spare_size;
+ opp->param_lunsize = sc->sc_lun_size;
+ opp->param_numluns = 1;
+
+ opp->param_manufacturer_id = 0x00;
+ memcpy(opp->param_manufacturer,
+ "NETBSD", strlen("NETBSD"));
+ memcpy(opp->param_model,
+ "NANDEMULATOR", strlen("NANDEMULATOR"));
+
+ opp->param_features = ONFI_FEATURE_16BIT;
+
+ /* the lower 4 bits contain the row address cycles
+ * the upper 4 bits contain the column address cycles
+ */
+ opp->param_addr_cycles = sc->sc_row_cycles;
+ opp->param_addr_cycles |= (sc->sc_column_cycles << 4);
+
+ opp->param_integrity_crc = nand_crc16((uint8_t *)opp, 254);
+ }
+
+ sc->sc_ids[0] = 0x00;
+ sc->sc_ids[1] = 0x00;
+
+ sc->sc_onfi[0] = 'O';
+ sc->sc_onfi[1] = 'N';
+ sc->sc_onfi[2] = 'F';
+ sc->sc_onfi[3] = 'I';
+
+ sc->sc_row_mask = 0x00;
+ for (i = 0; i < sc->sc_row_cycles; i++) {
+ sc->sc_row_mask <<= 8;
+ sc->sc_row_mask |= 0xff;
+ }
+
+ nandemulator_device_reset(self);
+
+ sc->sc_nanddev = nand_attach_mi(&sc->sc_nand_if, sc->sc_dev);
+}
+
+static int
+nandemulator_detach(device_t self, int flags)
+{
+ struct nandemulator_softc *sc = device_private(self);
+ int ret = 0;
+
+ aprint_normal_dev(sc->sc_dev, "detaching emulator\n");
+
+ pmf_device_deregister(sc->sc_dev);
+
+ if (sc->sc_nanddev != NULL)
+ ret = config_detach(sc->sc_nanddev, flags);
+
+ kmem_free(sc->sc_backend, sc->sc_backend_size);
+ kmem_free(sc->sc_parameter_page,
+ sizeof(struct onfi_parameter_page) * 4);
+
+ return ret;
+}
+
+/**
+ * bring the emulated device to a known state
+ */
+static void
+nandemulator_device_reset(device_t self)
+{
+ struct nandemulator_softc *sc = device_private(self);
+
+ sc->sc_command = 0;
+ sc->sc_register_writable = false;
+ sc->sc_io_len = 0;
+ sc->sc_io_pointer = NULL;
+ sc->sc_address = 0;
+ sc->sc_address_counter = 0;
+
+ sc->sc_status_register = ONFI_STATUS_RDY | ONFI_STATUS_WP;
+}
+
+static void
+nandemulator_address_chip(device_t self)
+{
+ struct nandemulator_softc *sc = device_private(self);
+ size_t page, offset;
+
+ if (sc->sc_address_counter !=
+ sc->sc_column_cycles + sc->sc_row_cycles) {
+ aprint_error_dev(self, "incorrect number of address cycles\n");
+ aprint_error_dev(self, "cc: %d, rc: %d, ac: %d\n",
+ sc->sc_column_cycles, sc->sc_row_cycles,
+ sc->sc_address_counter);
+ }
+
+ page = nandemulator_address_to_page(self);
+ offset = sc->sc_page_size * page;
+
+ DPRINTF(("READ/PROGRAM; page: 0x%jx (row addr: 0x%jx)\n",
+ (uintmax_t )page,
+ (uintmax_t )offset));
+
+ if (offset >= sc->sc_device_size) {
+ aprint_error_dev(self, "address > device size!\n");
+ sc->sc_io_len = 0;
+ } else {
+ size_t addr =
+ nandemulator_page_to_backend_offset(self, page);
+ size_t pageoff =
+ nandemulator_column_address_to_subpage(self);
+
+ DPRINTF(("subpage: 0x%jx\n", (uintmax_t )pageoff));
+
+ KASSERT(pageoff <
+ sc->sc_page_size + sc->sc_spare_size);
+ KASSERT(addr < sc->sc_backend_size);
+
+ sc->sc_io_pointer = sc->sc_backend + addr + pageoff;
+ sc->sc_io_len =
+ sc->sc_page_size + sc->sc_spare_size - pageoff;
+ }
+}
+
+static void
+nandemulator_command(device_t self, uint8_t command)
+{
+ struct nandemulator_softc *sc = device_private(self);
+ size_t offset, page;
+
+ sc->sc_command = command;
+ sc->sc_register_writable = false;
+
+ DPRINTF(("nandemulator command: 0x%hhx\n", command));
+
+ switch (command) {
+ case ONFI_READ_STATUS:
+ sc->sc_io_pointer = &sc->sc_status_register;
+ sc->sc_io_len = 1;
+ break;
+ case ONFI_RESET:
+ nandemulator_device_reset(self);
+ break;
+ case ONFI_PAGE_PROGRAM:
+ sc->sc_register_writable = true;
+ case ONFI_READ:
+ case ONFI_BLOCK_ERASE:
+ sc->sc_address_counter = 0;
+ case ONFI_READ_ID:
+ case ONFI_READ_PARAMETER_PAGE:
+ sc->sc_io_len = 0;
+ sc->sc_address = 0;
+ break;
+ case ONFI_PAGE_PROGRAM_START:
+ /* XXX the program should only happen here */
+ break;
+ case ONFI_READ_START:
+ nandemulator_address_chip(self);
+ break;
+ case ONFI_BLOCK_ERASE_START:
+ page = nandemulator_address_to_page(self);
+ offset = sc->sc_page_size * page;
+
+ KASSERT(offset %
+ (sc->sc_block_size * sc->sc_page_size) == 0);
+
+ if (offset >= sc->sc_device_size) {
+ aprint_error_dev(self, "address > device size!\n");
+ } else {
+ size_t addr =
+ nandemulator_page_to_backend_offset(self, page);
+
+ size_t blocklen =
+ sc->sc_block_size *
+ (sc->sc_page_size + sc->sc_spare_size);
+
+ KASSERT(addr < sc->sc_backend_size);
+ uint8_t *block = sc->sc_backend + addr;
+
+ DPRINTF(("erasing block at 0x%jx\n",
+ (uintmax_t )offset));
+
+ memset(block, 0xff, blocklen);
+ }
+ sc->sc_io_len = 0;
+ break;
+ default:
+ aprint_error_dev(self,
+ "invalid nand command (0x%hhx)\n", command);
+ sc->sc_io_len = 0;
+ }
+};
+
+static void
+nandemulator_address(device_t self, uint8_t address)
+{
+ struct nandemulator_softc *sc = device_private(self);
+
+ /**
+ * we have to handle read id/parameter page here,
+ * as we can read right after giving the address.
+ */
+ switch (sc->sc_command) {
+ case ONFI_READ_ID:
+ if (address == 0x00) {
+ sc->sc_io_len = 2;
+ sc->sc_io_pointer = sc->sc_ids;
+ } else if (address == 0x20) {
+ sc->sc_io_len = 4;
+ sc->sc_io_pointer = sc->sc_onfi;
+ } else {
+ sc->sc_io_len = 0;
+ }
+ break;
+ case ONFI_READ_PARAMETER_PAGE:
+ if (address == 0x00) {
+ sc->sc_io_len = sizeof(struct onfi_parameter_page) * 4;
+ sc->sc_io_pointer = (uint8_t *)sc->sc_parameter_page;
+ } else {
+ sc->sc_io_len = 0;
+ }
+ break;
+ case ONFI_PAGE_PROGRAM:
+ sc->sc_address <<= 8;
+ sc->sc_address |= address;
+ sc->sc_address_counter++;
+
+ if (sc->sc_address_counter ==
+ sc->sc_column_cycles + sc->sc_row_cycles) {
+ nandemulator_address_chip(self);
+ }
+ break;
+ default:
+ sc->sc_address <<= 8;
+ sc->sc_address |= address;
+ sc->sc_address_counter++;
+ }
+};
+
+static void
+nandemulator_busy(device_t self)
+{
+#ifdef NANDEMULATOR_DELAYS
+ struct nandemulator_softc *sc = device_private(self);
+
+ /* do some delay depending on command */
+ switch (sc->sc_command) {
+ case ONFI_PAGE_PROGRAM_START:
+ case ONFI_BLOCK_ERASE_START:
+ DELAY(10);
+ break;
+ case ONFI_READ_START:
+ default:
+ DELAY(1);
+ }
+#endif
+}
+
+static void
+nandemulator_read_byte(device_t self, uint8_t *data)
+{
+ struct nandemulator_softc *sc = device_private(self);
+
+ if (sc->sc_io_len > 0) {
+ *data = *sc->sc_io_pointer;
+
+ sc->sc_io_pointer++;
+ sc->sc_io_len--;
+ } else {
+ aprint_error_dev(self, "reading byte from invalid location\n");
+ *data = 0xff;
+ }
+}
+
+static void
+nandemulator_write_byte(device_t self, uint8_t data)
+{
+ struct nandemulator_softc *sc = device_private(self);
+
+ if (!sc->sc_register_writable) {
+ aprint_error_dev(self,
+ "trying to write read only location without effect\n");
+ return;
+ }
+
+ if (sc->sc_io_len > 0) {
+ *sc->sc_io_pointer = data;
+
+ sc->sc_io_pointer++;
+ sc->sc_io_len--;
+ } else {
+ aprint_error_dev(self, "write to invalid location\n");
+ }
+}
+
+static void
+nandemulator_read_word(device_t self, uint16_t *data)
+{
+ struct nandemulator_softc *sc = device_private(self);
+
+ if (sc->sc_buswidth != NANDEMULATOR_16BIT) {
+ aprint_error_dev(self,
+ "trying to read a word on an 8bit chip\n");
+ return;
+ }
+
+ if (sc->sc_io_len > 1) {
+ *data = *(uint16_t *)sc->sc_io_pointer;
+
+ sc->sc_io_pointer += 2;
+ sc->sc_io_len -= 2;
+ } else {
+ aprint_error_dev(self, "reading word from invalid location\n");
+ *data = 0xffff;
+ }
+}
+
+static void
+nandemulator_write_word(device_t self, uint16_t data)
+{
+ struct nandemulator_softc *sc = device_private(self);
+
+ if (!sc->sc_register_writable) {
+ aprint_error_dev(self,
+ "trying to write read only location without effect\n");
+ return;
+ }
+
+ if (sc->sc_buswidth != NANDEMULATOR_16BIT) {
+ aprint_error_dev(self,
+ "trying to write a word to an 8bit chip");
+ return;
+ }
+
+ if (sc->sc_io_len > 1) {
+ *(uint16_t *)sc->sc_io_pointer = data;
+
+ sc->sc_io_pointer += 2;
+ sc->sc_io_len -= 2;
+ } else {
+ aprint_error_dev(self, "writing to invalid location");
+ }
+}
+
+static void
+nandemulator_read_buf_byte(device_t self, void *buf, size_t len)
+{
+ uint8_t *addr;
+
+ KASSERT(buf != NULL);
+ KASSERT(len >= 1);
+
+ addr = buf;
+ while (len > 0) {
+ nandemulator_read_byte(self, addr);
+ addr++, len--;
+ }
+}
+
+static void
+nandemulator_read_buf_word(device_t self, void *buf, size_t len)
+{
+ uint16_t *addr;
+
+ KASSERT(buf != NULL);
+ KASSERT(len >= 2);
+ KASSERT(!(len & 0x01));
+
+ addr = buf;
+ len /= 2;
+ while (len > 0) {
+ nandemulator_read_word(self, addr);
+ addr++, len--;
+ }
+}
+
+static void
+nandemulator_write_buf_byte(device_t self, const void *buf, size_t len)
+{
+ const uint8_t *addr;
+
+ KASSERT(buf != NULL);
+ KASSERT(len >= 1);
+
+ addr = buf;
+ while (len > 0) {
+ nandemulator_write_byte(self, *addr);
+ addr++, len--;
+ }
+}
+
+static void
+nandemulator_write_buf_word(device_t self, const void *buf, size_t len)
+{
+ const uint16_t *addr;
+
+ KASSERT(buf != NULL);
+ KASSERT(len >= 2);
+ KASSERT(!(len & 0x01));
+
+ addr = buf;
+ len /= 2;
+ while (len > 0) {
+ nandemulator_write_word(self, *addr);
+ addr++, len--;
+ }
+}
+
+static size_t
+nandemulator_address_to_page(device_t self)
+{
+ struct nandemulator_softc *sc = device_private(self);
+ uint64_t address, offset;
+ int i;
+
+ address = htole64(sc->sc_address);
+ address &= sc->sc_row_mask;
+
+ offset = 0;
+ for (i = 0; i < sc->sc_row_cycles; i++) {
+ offset <<= 8;
+ offset |= (address & 0xff);
+ address >>= 8;
+ }
+
+ return le64toh(offset);
+}
+
+static size_t
+nandemulator_column_address_to_subpage(device_t self)
+{
+ struct nandemulator_softc *sc = device_private(self);
+ uint64_t address, offset;
+ int i;
+
+ address = htole64(sc->sc_address);
+ address >>= (8 * sc->sc_row_cycles);
+
+ offset = 0;
+ for (i = 0; i < sc->sc_column_cycles; i++) {
+ offset <<= 8;
+ offset |= (address & 0xff);
+ address >>= 8;
+ }
+
+ if (sc->sc_buswidth == NANDEMULATOR_16BIT)
+ return (size_t )le64toh(offset << 1);
+ else
+ return (size_t )le64toh(offset);
+}
+
+static size_t
+nandemulator_page_to_backend_offset(device_t self, size_t page)
+{
+ struct nandemulator_softc *sc = device_private(self);
+
+ return (sc->sc_page_size + sc->sc_spare_size) * page;
+}
+
+#ifdef _MODULE
+
+MODULE(MODULE_CLASS_DRIVER, nandemulator, "nand");
+
+static const struct cfiattrdata nandbuscf_iattrdata = {
+ "nandbus", 0, { { NULL, NULL, 0 }, }
+};
+static const struct cfiattrdata * const nandemulator_attrs[] = {
+ &nandbuscf_iattrdata, NULL
+};
+
+CFDRIVER_DECL(nandemulator, DV_DULL, nandemulator_attrs);
+extern struct cfattach nandemulator_ca;
+static int nandemulatorloc[] = { -1, -1 };
+
+static struct cfdata nandemulator_cfdata[] = {
+ {
+ .cf_name = "nandemulator",
+ .cf_atname = "nandemulator",
+ .cf_unit = 0,
+ .cf_fstate = FSTATE_STAR,
+ .cf_loc = nandemulatorloc,
+ .cf_flags = 0,
+ .cf_pspec = NULL,
+ },
+ { NULL, NULL, 0, 0, NULL, 0, NULL }
+};
+
+static int
+nandemulator_modcmd(modcmd_t cmd, void *arg)
+{
+ int error;
+
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+ error = config_cfdriver_attach(&nandemulator_cd);
+ if (error) {
+ return error;
+ }
+
+ error = config_cfattach_attach(nandemulator_cd.cd_name,
+ &nandemulator_ca);
+ if (error) {
+ config_cfdriver_detach(&nandemulator_cd);
+ aprint_error("%s: unable to register cfattach\n",
+ nandemulator_cd.cd_name);
+
+ return error;
+ }
+
+ error = config_cfdata_attach(nandemulator_cfdata, 1);
+ if (error) {
+ config_cfattach_detach(nandemulator_cd.cd_name,
+ &nandemulator_ca);
+ config_cfdriver_detach(&nandemulator_cd);
+ aprint_error("%s: unable to register cfdata\n",
+ nandemulator_cd.cd_name);
+
+ return error;
+ }
+
+ (void)config_attach_pseudo(nandemulator_cfdata);
+
+ return 0;
+ case MODULE_CMD_FINI:
+ error = config_cfdata_detach(nandemulator_cfdata);
+ if (error) {
+ return error;
+ }
+
+ config_cfattach_detach(nandemulator_cd.cd_name,
+ &nandemulator_ca);
+ config_cfdriver_detach(&nandemulator_cd);
+
+ return 0;
+ default:
+ return ENOTTY;
+ }
+}
+
+#endif
diff --git a/sys/dev/nand/nandemulator.h b/sys/dev/nand/nandemulator.h
new file mode 100644
index 00000000000..155921e943e
--- /dev/null
+++ b/sys/dev/nand/nandemulator.h
@@ -0,0 +1,5 @@
+#ifndef _NANDEMULATOR_H_
+#define _NANDEMULATOR_H_
+#else
+
+#endif
diff --git a/sys/dev/nand/onfi.h b/sys/dev/nand/onfi.h
new file mode 100644
index 00000000000..113edc7f629
--- /dev/null
+++ b/sys/dev/nand/onfi.h
@@ -0,0 +1,180 @@
+/* $NetBSD: onfi.h,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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 ``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 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.
+ */
+
+#ifndef _ONFI_H_
+#define _ONFI_H_
+
+/**
+ * ONFI 2.2, Section 5.1: Command Set
+ *
+ * Indented ones are 2nd or 3rd cycle commands.
+ */
+
+enum {
+ ONFI_READ = 0x00, /* M */
+ ONFI_READ_START = 0x30, /* M */
+ ONFI_READ_INTERLEAVED = 0x32, /* O */
+ ONFI_READ_COPYBACK = 0x35, /* O */
+ ONFI_READ_CACHE_RANDOM = 0x31, /* O */
+
+ ONFI_CHANGE_READ_COLUMN = 0x05, /* M */
+ ONFI_CHANGE_READ_COLUMN_ENHANCED = 0x06, /* O */
+ ONFI_CHANGE_READ_COLUMN_START = 0xe0, /* M */
+
+ ONFI_READ_CACHE_SEQUENTIAL = 0x31, /* O */
+ ONFI_READ_CACHE_END = 0x3f, /* O */
+
+ ONFI_BLOCK_ERASE = 0x60, /* M */
+ ONFI_BLOCK_ERASE_START = 0xd0, /* M */
+ ONFI_BLOCK_ERASE_INTERLEAVED = 0xd1, /* O */
+
+ ONFI_READ_STATUS = 0x70, /* M */
+ ONFI_READ_STATUS_ENHANCED = 0x78, /* O */
+
+ ONFI_PAGE_PROGRAM = 0x80, /* M */
+ ONFI_PAGE_PROGRAM_START = 0x10, /* M */
+ ONFI_PAGE_PROGRAM_INTERLEAVED = 0x11, /* O */
+ ONFI_PAGE_CACHE_PROGRAM = 0x15, /* O */
+
+ ONFI_COPYBACK_PROGRAM = 0x85, /* O */
+ ONFI_COPYBACK_PROGRAM_START = 0x10, /* O */
+ ONFI_COPYBACK_PROGRAM_INTERLEAVED = 0x11, /* O */
+/*-
+ * Small Data's first opcode may be 80h if the operation is a program only
+ * with no data output. For the last second cycle of a Small Data Move,
+ * it is a 10h command to confirm the Program or Copyback operation
+ */
+ ONFI_SMALL_DATA_MOVE = 0x85, /* O */
+ ONFI_SMALL_DATA_MOVE_START = 0x11, /* O */
+
+ ONFI_CHANGE_WRITE_COLUMN = 0x85, /* M */
+ ONFI_CHANGE_ROW_ADDRESS = 0x85, /* O */
+
+ ONFI_READ_ID = 0x90, /* M */
+ ONFI_READ_PARAMETER_PAGE = 0xec, /* M */
+ ONFI_READ_UNIQUE_ID = 0xed, /* O */
+ ONFI_GET_FEATURES = 0xee, /* O */
+ ONFI_SET_FEATURES = 0xef, /* O */
+ ONFI_RESET_LUN = 0xfa, /* O */
+ ONFI_SYNCHRONOUS_RESET = 0xfc, /* O */
+ ONFI_RESET = 0xff /* M */
+};
+
+/**
+ * status codes from ONFI_READ_STATUS
+ */
+enum {
+ ONFI_STATUS_FAIL = (1<<0),
+ ONFI_STATUS_FAILC = (1<<1),
+ ONFI_STATUS_R = (1<<2),
+ ONFI_STATUS_CSP = (1<<3),
+ ONFI_STATUS_VSP = (1<<4),
+ ONFI_STATUS_ARDY = (1<<5),
+ ONFI_STATUS_RDY = (1<<6),
+ ONFI_STATUS_WP = (1<<7)
+};
+
+enum {
+ ONFI_FEATURE_16BIT = (1<<0),
+ ONFI_FEATURE_EXTENDED_PARAM = (1<<7)
+};
+
+/* 5.7.1. Parameter Page Data Structure Definition */
+struct onfi_parameter_page {
+ /* Revision information and features block */
+ uint32_t param_signature; /* M: onfi signature ({'O','N','F','I'}) */
+ uint16_t param_revision; /* M: revision number */
+ uint16_t param_features; /* M: features supported */
+ uint16_t param_optional_cmds; /* M: optional commands */
+ uint16_t param_reserved_1; /* R: reserved */
+ uint16_t param_extended_len; /* O: extended parameter page lenght */
+ uint8_t param_num_param_pg; /* O: number of parameter pages */
+ uint8_t param_reserved_2[17]; /* R: reserved */
+ /* Manufacturer information block */
+ uint8_t param_manufacturer[12]; /* M: device manufacturer (ASCII) */
+ uint8_t param_model[20]; /* M: device model (ASCII) */
+ uint8_t param_manufacturer_id; /* M: JEDEC ID of manufacturer */
+ uint16_t param_date; /* O: date code (BCD) */
+ uint8_t param_reserved_3[13]; /* R: reserved */
+ /* Memory organization block */
+ uint32_t param_pagesize; /* M: number of data bytes per page */
+ uint16_t param_sparesize; /* M: number of spare bytes per page */
+ uint32_t param_part_pagesize; /* O: obsolete */
+ uint16_t param_part_sparesize; /* O: obsolete */
+ uint32_t param_blocksize; /* M: number of pages per block */
+ uint32_t param_lunsize; /* M: number of blocks per LUN */
+ uint8_t param_numluns; /* M: number of LUNs */
+ uint8_t param_addr_cycles; /* M: address cycles:
+ col: 4-7 (high), row: 0-3 (low) */
+ uint8_t param_cellsize; /* M: number of bits per cell */
+ uint16_t param_lun_maxbad; /* M: maximum badblocks per LUN */
+ uint16_t param_block_endurance; /* M: block endurance */
+ uint8_t param_guaranteed_blocks; /* M: guaranteed valid blocks at
+ begginning of target */
+ uint16_t param_guaranteed_endurance; /* M: block endurance of
+ guranteed blocks */
+ uint8_t param_programs_per_page; /* M: number of programs per page */
+ uint8_t param_partial_programming_attr; /* O: obsolete */
+ uint8_t param_ecc_correctable_bits; /* M: number of bits
+ ECC correctability */
+ uint8_t param_interleaved_addr_bits; /* M: num of interleaved address
+ bits (only low half is valid) */
+ uint8_t param_interleaved_op_attrs; /* O: obsolete */
+ uint8_t param_reserved_4[13]; /* R: reserved */
+ /* Electrical parameters block */
+ uint8_t param_io_c_max; /* M: I/O pin capacitance, maximum */
+ uint16_t param_async_timing_mode; /* M: async timing mode support */
+ uint16_t param_async_progcache_timing_mode; /* O: obsolete */
+ uint16_t param_t_prog; /* M: maximum page program time (us) */
+ uint16_t param_t_bers; /* M: maximum block erase time (us) */
+ uint16_t param_t_r; /* M: maximum page read time (us) */
+ uint16_t param_ccs; /* M: minimum change column setup time (ns) */
+ uint16_t param_sync_timing_mode; /* source sync timing mode support */
+ uint8_t param_sync_features; /* M: source sync features */
+ uint16_t param_clk_input_c; /* O: CLK input pin cap., typical */
+ uint16_t param_io_c; /* O: I/O pin capacitance, typical */
+ uint16_t param_input_c; /* O: input pin capacitance, typical */
+ uint8_t param_input_c_max; /* M: input pin capacitance, maximum */
+ uint8_t param_driver_strength; /* M: driver strength support */
+ uint16_t param_t_r_interleaved; /* O: maximum interleaved
+ page read time (us) */
+ uint16_t param_t_adl; /* O: program page register clear enhancement
+ tADL value (ns) */
+ uint8_t param_reserved_5[8]; /* R: reserved */
+ /* Vendor block */
+ uint16_t param_vendor_revision; /* M: vendor specific rev number */
+ uint8_t param_vendor_specific[88]; /* vendor specific information */
+ uint16_t param_integrity_crc; /* M: integrity CRC */
+} __packed;
+
+#endif /* _ONFI_H_ */