summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorUwe Stuehler <uwe@cvs.openbsd.org>2006-11-25 14:32:01 +0000
committerUwe Stuehler <uwe@cvs.openbsd.org>2006-11-25 14:32:01 +0000
commit3d181ecda67c9ab9633a36011ba4b76b3d1f2a7c (patch)
tree466f5b6aa1c21712a8fb87c85506e12bf088e6bc /sys
parent7f5337a570881bf9a7a29775b9ca02914568e566 (diff)
Initial NAND flash support for Zaurus, not enabled yet; prodded by many.
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/zaurus/conf/GENERIC3
-rw-r--r--sys/arch/zaurus/conf/files.zaurus7
-rw-r--r--sys/arch/zaurus/dev/zaurus_flash.c914
-rw-r--r--sys/conf/files3
-rw-r--r--sys/dev/flash.c1099
-rw-r--r--sys/dev/flashvar.h143
6 files changed, 2166 insertions, 3 deletions
diff --git a/sys/arch/zaurus/conf/GENERIC b/sys/arch/zaurus/conf/GENERIC
index a4a7b1d265f..d182a1c1d8c 100644
--- a/sys/arch/zaurus/conf/GENERIC
+++ b/sys/arch/zaurus/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.54 2006/09/27 08:54:44 grange Exp $
+# $OpenBSD: GENERIC,v 1.55 2006/11/25 14:31:59 uwe Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -195,6 +195,7 @@ zaudio0 at pxaip? # Zaurus I2S/I2C sound
audio* at zaudio?
zrc0 at pxaip? # Zaurus remote control
wskbd* at zrc? mux 1
+#flash0 at pxaip? # NAND flash memory
# 1-Wire devices
option ONEWIREVERBOSE
diff --git a/sys/arch/zaurus/conf/files.zaurus b/sys/arch/zaurus/conf/files.zaurus
index 4097fd2d0d9..29feac6abe4 100644
--- a/sys/arch/zaurus/conf/files.zaurus
+++ b/sys/arch/zaurus/conf/files.zaurus
@@ -1,4 +1,4 @@
-# $OpenBSD: files.zaurus,v 1.22 2006/09/27 06:33:03 grange Exp $
+# $OpenBSD: files.zaurus,v 1.23 2006/11/25 14:31:59 uwe Exp $
#
# First try for arm-specific configuration info
#
@@ -66,6 +66,11 @@ device zrc: wskbddev
attach zrc at pxaip
file arch/zaurus/dev/zaurus_remote.c zrc
+# NAND flash pseudo-disk device (Xilinx NAND flash controller)
+device flash: disk
+attach flash at pxaip with flash_pxaip
+file arch/zaurus/dev/zaurus_flash.c flash_pxaip
+
#
# Machine-independent ATA drivers
#
diff --git a/sys/arch/zaurus/dev/zaurus_flash.c b/sys/arch/zaurus/dev/zaurus_flash.c
new file mode 100644
index 00000000000..8dc584a5f32
--- /dev/null
+++ b/sys/arch/zaurus/dev/zaurus_flash.c
@@ -0,0 +1,914 @@
+/* $OpenBSD: zaurus_flash.c,v 1.1 2006/11/25 14:31:59 uwe Exp $ */
+
+/*
+ * Copyright (c) 2005 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Samsung NAND flash controlled by some unspecified CPLD device.
+ */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/device.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+
+#include <dev/flashvar.h>
+#include <dev/rndvar.h>
+
+#include <machine/zaurus_var.h>
+
+#include <arch/arm/xscale/pxa2x0var.h>
+
+#define DEBUG
+#ifdef DEBUG
+#define DPRINTF(x) printf x
+#else
+#define DPRINTF(x)
+#endif
+
+/* CPLD register definitions */
+#define CPLD_REG_ECCLPLB 0x00
+#define CPLD_REG_ECCLPUB 0x04
+#define CPLD_REG_ECCCP 0x08
+#define CPLD_REG_ECCCNTR 0x0c
+#define CPLD_REG_ECCCLRR 0x10
+#define CPLD_REG_FLASHIO 0x14
+#define CPLD_REG_FLASHCTL 0x18
+#define FLASHCTL_NCE0 (1<<0)
+#define FLASHCTL_CLE (1<<1)
+#define FLASHCTL_ALE (1<<2)
+#define FLASHCTL_NWP (1<<3)
+#define FLASHCTL_NCE1 (1<<4)
+#define FLASHCTL_RYBY (1<<5)
+#define FLASHCTL_NCE (FLASHCTL_NCE0|FLASHCTL_NCE1)
+
+/* CPLD register accesses */
+#define CPLD_READ(sc, r) \
+ bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (r))
+#define CPLD_WRITE(sc, r, v) \
+ bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (r), (v))
+#define CPLD_SET(sc, r, v) \
+ CPLD_WRITE((sc), (r), CPLD_READ((sc), (r)) | (v))
+#define CPLD_CLR(sc, r, v) \
+ CPLD_WRITE((sc), (r), CPLD_READ((sc), (r)) & ~(v))
+#define CPLD_SETORCLR(sc, r, m, v) \
+ ((v) ? CPLD_SET((sc), (r), (m)) : CPLD_CLR((sc), (r), (m)))
+
+/* Offsets into OOB data. */
+#define OOB_JFFS2_ECC0 0
+#define OOB_JFFS2_ECC1 1
+#define OOB_JFFS2_ECC2 2
+#define OOB_JFFS2_ECC3 3
+#define OOB_JFFS2_ECC4 6
+#define OOB_JFFS2_ECC5 7
+#define OOB_LOGADDR_0_LO 8
+#define OOB_LOGADDR_0_HI 9
+#define OOB_LOGADDR_1_LO 10
+#define OOB_LOGADDR_1_HI 11
+#define OOB_LOGADDR_2_LO 12
+#define OOB_LOGADDR_2_HI 13
+
+/*
+ * Structure for managing logical blocks in a partition; allocated on
+ * first use of each partition on a "safe" flash device.
+ */
+struct zflash_safe {
+ dev_t sp_dev;
+ u_long sp_pblks; /* physical block count */
+ u_long sp_lblks; /* logical block count */
+ u_int16_t *sp_phyuse; /* physical block usage */
+ u_int *sp_logmap; /* logical to physical */
+ u_int sp_pnext; /* next physical block */
+};
+
+struct zflash_softc {
+ struct flash_softc sc_flash;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ int sc_ioobbadblk;
+ int sc_ioobpostbadblk;
+ struct zflash_safe *sc_safe[MAXPARTITIONS];
+};
+
+int zflashmatch(struct device *, void *, void *);
+void zflashattach(struct device *, struct device *, void *);
+int zflashdetach(struct device *, int);
+
+u_int8_t zflash_reg8_read(void *, int);
+int zflash_regx_read_page(void *, caddr_t, caddr_t);
+void zflash_reg8_write(void *, int, u_int8_t);
+int zflash_regx_write_page(void *, caddr_t, caddr_t);
+void zflash_default_disklabel(void *, dev_t, struct disklabel *,
+ struct cpu_disklabel *);
+int zflash_safe_strategy(void *, struct buf *);
+
+int zflash_safe_start(struct zflash_softc *, dev_t);
+void zflash_safe_stop(struct zflash_softc *, dev_t);
+
+struct cfattach flash_pxaip_ca = {
+ sizeof(struct zflash_softc), zflashmatch, zflashattach,
+ zflashdetach, flashactivate
+};
+
+struct flash_ctl_tag zflash_ctl_tag = {
+ zflash_reg8_read,
+ zflash_regx_read_page,
+ zflash_reg8_write,
+ zflash_regx_write_page,
+ zflash_default_disklabel,
+ zflash_safe_strategy
+};
+
+int
+zflashmatch(struct device *parent, void *match, void *aux)
+{
+ /* XXX call flashprobe(), yet to be implemented */
+ return ZAURUS_ISC3000;
+}
+
+void
+zflashattach(struct device *parent, struct device *self, void *aux)
+{
+ struct zflash_softc *sc = (struct zflash_softc *)self;
+ struct pxaip_attach_args *pxa = aux;
+ bus_addr_t addr = pxa->pxa_addr;
+ bus_size_t size = pxa->pxa_size;
+
+ sc->sc_iot = pxa->pxa_iot;
+
+ if ((int)addr == -1 || (int)size == 0) {
+ addr = 0x0c000000;
+ size = 0x00001000;
+ }
+
+ if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) {
+ printf(": failed to map controller\n");
+ return;
+ }
+
+ /* Disable and write-protect the chip. */
+ CPLD_WRITE(sc, CPLD_REG_FLASHCTL, FLASHCTL_NCE);
+
+ flashattach(&sc->sc_flash, &zflash_ctl_tag, sc);
+
+ switch (sc->sc_flash.sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C: /* C3000 */
+ sc->sc_ioobpostbadblk = 4;
+ sc->sc_ioobbadblk = 5;
+ break;
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A: /* C3100 */
+ sc->sc_ioobpostbadblk = 4;
+ sc->sc_ioobbadblk = 0;
+ break;
+ }
+}
+
+int
+zflashdetach(struct device *self, int flags)
+{
+ struct zflash_softc *sc = (struct zflash_softc *)self;
+ int part;
+
+ for (part = 0; part < MAXPARTITIONS; part++)
+ zflash_safe_stop(sc, part);
+
+ return (flashdetach(self, flags));
+}
+
+u_int8_t
+zflash_reg8_read(void *arg, int reg)
+{
+ struct zflash_softc *sc = arg;
+ u_int8_t value;
+
+ switch (reg) {
+ case FLASH_REG_DATA:
+ value = CPLD_READ(sc, CPLD_REG_FLASHIO);
+ break;
+ case FLASH_REG_READY:
+ value = (CPLD_READ(sc, CPLD_REG_FLASHCTL) &
+ FLASHCTL_RYBY) != 0;
+ break;
+ default:
+#ifdef DIAGNOSTIC
+ printf("%s: read from pseudo-register %02x\n",
+ sc->sc_flash.sc_dev.dv_xname, reg);
+#endif
+ value = 0;
+ break;
+ }
+ return value;
+}
+
+void
+zflash_reg8_write(void *arg, int reg, u_int8_t value)
+{
+ struct zflash_softc *sc = arg;
+
+ switch (reg) {
+ case FLASH_REG_DATA:
+ case FLASH_REG_COL:
+ case FLASH_REG_ROW:
+ case FLASH_REG_CMD:
+ CPLD_WRITE(sc, CPLD_REG_FLASHIO, value);
+ break;
+ case FLASH_REG_ALE:
+ CPLD_SETORCLR(sc, CPLD_REG_FLASHCTL, FLASHCTL_ALE, value);
+ break;
+ case FLASH_REG_CLE:
+ CPLD_SETORCLR(sc, CPLD_REG_FLASHCTL, FLASHCTL_CLE, value);
+ break;
+ case FLASH_REG_CE:
+ CPLD_SETORCLR(sc, CPLD_REG_FLASHCTL, FLASHCTL_NCE, !value);
+ break;
+ case FLASH_REG_WP:
+ CPLD_SETORCLR(sc, CPLD_REG_FLASHCTL, FLASHCTL_NWP, !value);
+ break;
+#ifdef DIAGNOSTIC
+ default:
+ printf("%s: write to pseudo-register %02x\n",
+ sc->sc_flash.sc_dev.dv_xname, reg);
+#endif
+ }
+}
+
+int
+zflash_regx_read_page(void *arg, caddr_t data, caddr_t oob)
+{
+ struct zflash_softc *sc = arg;
+
+ if (oob == NULL || sc->sc_flash.sc_flashdev->pagesize != 512) {
+ flash_reg8_read_page(&sc->sc_flash, data, oob);
+ return 0;
+ }
+
+ flash_reg8_read_page(&sc->sc_flash, data, oob);
+
+ oob[OOB_JFFS2_ECC0] = 0xff;
+ oob[OOB_JFFS2_ECC1] = 0xff;
+ oob[OOB_JFFS2_ECC2] = 0xff;
+ oob[OOB_JFFS2_ECC3] = 0xff;
+ oob[OOB_JFFS2_ECC4] = 0xff;
+ oob[OOB_JFFS2_ECC5] = 0xff;
+ return 0;
+}
+
+int
+zflash_regx_write_page(void *arg, caddr_t data, caddr_t oob)
+{
+ struct zflash_softc *sc = arg;
+ int i;
+
+ if (oob == NULL || sc->sc_flash.sc_flashdev->pagesize != 512) {
+ flash_reg8_write_page(&sc->sc_flash, data, oob);
+ return 0;
+ }
+
+ if (oob[OOB_JFFS2_ECC0] != 0xff || oob[OOB_JFFS2_ECC1] != 0xff ||
+ oob[OOB_JFFS2_ECC2] != 0xff || oob[OOB_JFFS2_ECC3] != 0xff ||
+ oob[OOB_JFFS2_ECC4] != 0xff || oob[OOB_JFFS2_ECC5] != 0xff) {
+#ifdef DIAGNOSTIC
+ printf("%s: non-FF ECC bytes in OOB data\n",
+ sc->sc_flash.sc_dev.dv_xname);
+#endif
+ return EINVAL;
+ }
+
+ CPLD_WRITE(sc, CPLD_REG_ECCCLRR, 0x00);
+ for (i = 0; i < sc->sc_flash.sc_flashdev->pagesize / 2; i++)
+ flash_reg8_write(&sc->sc_flash, FLASH_REG_DATA, data[i]);
+
+ oob[OOB_JFFS2_ECC0] = ~CPLD_READ(sc, CPLD_REG_ECCLPUB);
+ oob[OOB_JFFS2_ECC1] = ~CPLD_READ(sc, CPLD_REG_ECCLPLB);
+ oob[OOB_JFFS2_ECC2] = (~CPLD_READ(sc, CPLD_REG_ECCCP) << 2) | 0x03;
+
+ if (CPLD_READ(sc, CPLD_REG_ECCCNTR) != 0) {
+ printf("%s: ECC failed\n", sc->sc_flash.sc_dev.dv_xname);
+ oob[OOB_JFFS2_ECC0] = 0xff;
+ oob[OOB_JFFS2_ECC1] = 0xff;
+ oob[OOB_JFFS2_ECC2] = 0xff;
+ return EIO;
+ }
+
+ CPLD_WRITE(sc, CPLD_REG_ECCCLRR, 0x00);
+ for (; i < sc->sc_flash.sc_flashdev->pagesize; i++)
+ flash_reg8_write(&sc->sc_flash, FLASH_REG_DATA, data[i]);
+
+ oob[OOB_JFFS2_ECC3] = ~CPLD_READ(sc, CPLD_REG_ECCLPUB);
+ oob[OOB_JFFS2_ECC4] = ~CPLD_READ(sc, CPLD_REG_ECCLPLB);
+ oob[OOB_JFFS2_ECC5] = (~CPLD_READ(sc, CPLD_REG_ECCCP) << 2) | 0x03;
+
+ if (CPLD_READ(sc, CPLD_REG_ECCCNTR) != 0) {
+ printf("%s: ECC failed\n", sc->sc_flash.sc_dev.dv_xname);
+ oob[OOB_JFFS2_ECC0] = 0xff;
+ oob[OOB_JFFS2_ECC1] = 0xff;
+ oob[OOB_JFFS2_ECC2] = 0xff;
+ oob[OOB_JFFS2_ECC3] = 0xff;
+ oob[OOB_JFFS2_ECC4] = 0xff;
+ oob[OOB_JFFS2_ECC5] = 0xff;
+ return EIO;
+ }
+
+ for (i = 0; i < sc->sc_flash.sc_flashdev->oobsize; i++)
+ flash_reg8_write(&sc->sc_flash, FLASH_REG_DATA, oob[i]);
+
+ oob[OOB_JFFS2_ECC0] = 0xff;
+ oob[OOB_JFFS2_ECC1] = 0xff;
+ oob[OOB_JFFS2_ECC2] = 0xff;
+ oob[OOB_JFFS2_ECC3] = 0xff;
+ oob[OOB_JFFS2_ECC4] = 0xff;
+ oob[OOB_JFFS2_ECC5] = 0xff;
+ return 0;
+}
+
+/*
+ * A default disklabel with only one RAW_PART spanning the whole
+ * device is passed to us. We add the partitions besides RAW_PART.
+ */
+void
+zflash_default_disklabel(void *arg, dev_t dev, struct disklabel *lp,
+ struct cpu_disklabel *clp)
+{
+ struct zflash_softc *sc = arg;
+ long bsize = sc->sc_flash.sc_flashdev->pagesize;
+
+ switch (sc->sc_flash.sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ lp->d_partitions[8].p_size = 7*1024*1024 / bsize;
+ lp->d_partitions[9].p_size = 5*1024*1024 / bsize;
+ lp->d_partitions[10].p_size = 4*1024*1024 / bsize;
+ break;
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ lp->d_partitions[8].p_size = 7*1024*1024 / bsize;
+ lp->d_partitions[9].p_size = 32*1024*1024 / bsize;
+ lp->d_partitions[10].p_size = 89*1024*1024 / bsize;
+ break;
+ default:
+ return;
+ }
+
+ /* The "smf" partition uses logical addressing. */
+ lp->d_partitions[8].p_offset = 0;
+ lp->d_partitions[8].p_fstype = FS_OTHER;
+
+ /* The "root" partition uses physical addressing. */
+ lp->d_partitions[9].p_offset = lp->d_partitions[8].p_size;
+ lp->d_partitions[9].p_fstype = FS_OTHER;
+
+ /* The "home" partition uses physical addressing. */
+ lp->d_partitions[10].p_offset = lp->d_partitions[9].p_offset +
+ lp->d_partitions[9].p_size;
+ lp->d_partitions[10].p_fstype = FS_OTHER;
+
+ lp->d_npartitions = 11;
+
+ /* Re-calculate the checksum. */
+ lp->d_checksum = dkcksum(lp);
+}
+
+/*
+ * Sharp's access strategy for bad blocks management and wear-leveling.
+ */
+
+#define PHYUSE_STATUS(v) ((v) & 0x00ff)
+#define P_BADBLOCK 0x0000
+#define P_POSTBADBLOCK 0x00f0
+#define P_NORMALBLOCK 0x00ff
+#define PHYUSE_WRITTEN(v) ((v) & 0xff00)
+#define P_DUST 0x0000
+#define P_LOGICAL 0x0100
+#define P_JFFS2 0x0300
+
+void zflash_write_strategy(struct zflash_softc *, struct buf *,
+ struct zflash_safe *, u_int, u_int);
+u_int zflash_safe_next_block(struct zflash_safe *);
+
+u_char zflash_oob_status_decode(u_char);
+u_int16_t zflash_oob_status(struct zflash_softc *, u_char *);
+u_int zflash_oob_logno(struct zflash_softc *, u_char *);
+void zflash_oob_set_status(struct zflash_softc *, u_char *, u_int16_t);
+void zflash_oob_set_logno(struct zflash_softc *, u_char *, u_int);
+
+int
+zflash_safe_strategy(void *arg, struct buf *bp)
+{
+ struct zflash_softc *sc = arg;
+ struct zflash_safe *sp;
+ u_int logno;
+ u_int blkofs;
+ u_int blkno;
+ int error;
+ int part;
+ int i;
+
+ /* Initialize logical blocks management on the fly. */
+ /* XXX toss everything when the disklabel has changed. */
+ if ((error = zflash_safe_start(sc, bp->b_dev)) != 0) {
+ bp->b_error = error;
+ bp->b_flags |= B_ERROR;
+ return 0;
+ }
+
+ part = flashpart(bp->b_dev);
+ sp = sc->sc_safe[part];
+
+ logno = bp->b_blkno / (sc->sc_flash.sc_flashdev->blkpages *
+ sc->sc_flash.sc_flashdev->pagesize / DEV_BSIZE);
+ blkofs = bp->b_blkno % (sc->sc_flash.sc_flashdev->blkpages *
+ sc->sc_flash.sc_flashdev->pagesize / DEV_BSIZE);
+
+ /* If exactly at end of logical flash, return EOF, else error. */
+ if (logno == sp->sp_lblks && blkofs == 0) {
+ bp->b_resid = bp->b_bcount;
+ return 0;
+ } else if (logno >= sp->sp_lblks) {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ return 0;
+ }
+
+ /* Writing is more complicated, so handle it separately. */
+ if ((bp->b_flags & B_READ) == 0) {
+ flash_chip_enable(&sc->sc_flash);
+ zflash_write_strategy(sc, bp, sp, logno, blkofs);
+ flash_chip_disable(&sc->sc_flash);
+ return 0;
+ }
+
+ /* Get the physical flash block number for this logical one. */
+ blkno = sp->sp_logmap[logno];
+
+ /* Unused logical blocks read as all 0xff. */
+ if ((bp->b_flags & B_READ) != 0 && blkno == UINT_MAX) {
+ for (i = 0; i < sc->sc_flash.sc_flashdev->pagesize; i++)
+ ((u_char *)bp->b_data)[i] = 0xff;
+ bp->b_resid = bp->b_bcount -
+ sc->sc_flash.sc_flashdev->pagesize;
+ return 0;
+ }
+
+ /* Update the block number in the buffer with the physical one. */
+ bp->b_blkno = blkno * (sc->sc_flash.sc_flashdev->blkpages *
+ sc->sc_flash.sc_flashdev->pagesize / DEV_BSIZE) + blkofs;
+
+ /* Process the modified transfer buffer normally. */
+ return 1;
+}
+
+void
+zflash_write_strategy(struct zflash_softc *sc, struct buf *bp,
+ struct zflash_safe *sp, u_int logno, u_int logofs)
+{
+ size_t bufsize;
+ u_char *buf = NULL;
+ size_t oobsize;
+ u_char *oob = NULL;
+ u_int oblkno;
+ u_int nblkno;
+ int error;
+
+ /* Not efficient, but we always transfer one page for now. */
+ if (bp->b_bcount < sc->sc_flash.sc_flashdev->pagesize) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+
+ /* Allocate a temporary buffer for one flash block. */
+ bufsize = sc->sc_flash.sc_flashdev->blkpages *
+ sc->sc_flash.sc_flashdev->pagesize;
+ buf = (u_char *)malloc(bufsize, M_DEVBUF, M_NOWAIT);
+ if (buf == NULL) {
+ bp->b_error = ENOMEM;
+ goto bad;
+ }
+
+ /* Allocate a temporary buffer for one spare area. */
+ oobsize = sc->sc_flash.sc_flashdev->oobsize;
+ oob = (u_char *)malloc(oobsize, M_DEVBUF, M_NOWAIT);
+ if (oob == NULL) {
+ bp->b_error = ENOMEM;
+ goto bad;
+ }
+
+ /* Read the old logical block into the temporary buffer. */
+ oblkno = sp->sp_logmap[logno];
+ if (oblkno != UINT_MAX) {
+ error = flash_chip_read_block(&sc->sc_flash, oblkno, buf);
+ if (error != 0) {
+ bp->b_error = error;
+ goto bad;
+ }
+ } else
+ /* Unused logical blocks read as all 0xff. */
+ memset(buf, 0xff, bufsize);
+
+ /* Transfer the page into the logical block buffer. */
+ bcopy(bp->b_data, buf + logofs * sc->sc_flash.sc_flashdev->pagesize,
+ sc->sc_flash.sc_flashdev->pagesize);
+
+ /* Generate OOB data for the spare area of this logical block. */
+ memset(oob, 0xff, oobsize);
+ zflash_oob_set_status(sc, oob, P_NORMALBLOCK);
+ zflash_oob_set_logno(sc, oob, logno);
+
+ while (1) {
+ /* Search for a free physical block. */
+ nblkno = zflash_safe_next_block(sp);
+ if (nblkno == UINT_MAX) {
+ printf("%s: no spare block, giving up on logical"
+ " block %u\n", sc->sc_flash.sc_dev.dv_xname,
+ logno);
+ bp->b_error = ENOSPC;
+ goto bad;
+ }
+
+#if 0
+ DPRINTF(("%s: moving logical block %u from physical %u to %u\n",
+ sc->sc_flash.sc_dev.dv_xname, logno, oblkno, nblkno));
+#endif
+
+ /* Erase the free physical block. */
+ if (flash_chip_erase_block(&sc->sc_flash, nblkno) != 0) {
+ printf("%s: can't erase block %u, retrying\n",
+ sc->sc_flash.sc_dev.dv_xname, nblkno);
+ sp->sp_phyuse[nblkno] = P_POSTBADBLOCK | P_DUST;
+ continue;
+ }
+
+ /* Write the logical block to the free physical block. */
+ if (flash_chip_write_block(&sc->sc_flash, nblkno, buf, oob)) {
+ printf("%s: can't write block %u, retrying\n",
+ sc->sc_flash.sc_dev.dv_xname, nblkno);
+ goto trynext;
+ }
+
+ /* Yeah, we re-wrote that logical block! */
+ break;
+ trynext:
+ sp->sp_phyuse[nblkno] = P_POSTBADBLOCK | P_DUST;
+ (void)flash_chip_erase_block(&sc->sc_flash, nblkno);
+ }
+
+ /* Map the new physical block. */
+ sp->sp_logmap[logno] = nblkno;
+ sp->sp_phyuse[nblkno] = PHYUSE_STATUS(sp->sp_phyuse[nblkno])
+ | P_LOGICAL;
+
+ /* Erase the old physical block. */
+ if (oblkno != UINT_MAX) {
+ sp->sp_phyuse[oblkno] = PHYUSE_STATUS(sp->sp_phyuse[oblkno])
+ | P_DUST;
+ error = flash_chip_erase_block(&sc->sc_flash, oblkno);
+ if (error != 0) {
+ printf("%s: can't erase old block %u\n",
+ sc->sc_flash.sc_dev.dv_xname, oblkno);
+ bp->b_error = error;
+ goto bad;
+ }
+ }
+
+ bp->b_resid = bp->b_bcount - sc->sc_flash.sc_flashdev->pagesize;
+ free(oob, M_DEVBUF);
+ free(buf, M_DEVBUF);
+ return;
+bad:
+ bp->b_flags |= B_ERROR;
+ if (oob != NULL)
+ free(oob, M_DEVBUF);
+ if (buf != NULL)
+ free(buf, M_DEVBUF);
+}
+
+int
+zflash_safe_start(struct zflash_softc *sc, dev_t dev)
+{
+ u_char oob[FLASH_MAXOOBSIZE];
+ struct disklabel *lp = sc->sc_flash.sc_dk.dk_label;
+ struct zflash_safe *sp;
+ u_int16_t *phyuse;
+ u_int *logmap;
+ u_int blksect;
+ u_int blkno;
+ u_int logno;
+ u_int unusable;
+ int part;
+
+ part = flashpart(dev);
+ if (sc->sc_safe[part] != NULL)
+ return 0;
+
+ /* We can only handle so much OOB data here. */
+ if (sc->sc_flash.sc_flashdev->oobsize > FLASH_MAXOOBSIZE)
+ return EIO;
+
+ /* Safe partitions must start on a flash block boundary. */
+ blksect = (sc->sc_flash.sc_flashdev->blkpages *
+ sc->sc_flash.sc_flashdev->pagesize) / lp->d_secsize;
+ if (lp->d_partitions[part].p_offset % blksect)
+ return EIO;
+
+ MALLOC(sp, struct zflash_safe *, sizeof(struct zflash_safe),
+ M_DEVBUF, M_NOWAIT);
+ if (sp == NULL)
+ return ENOMEM;
+
+ bzero(sp, sizeof(struct zflash_safe));
+ sp->sp_dev = dev;
+
+ sp->sp_pblks = lp->d_partitions[part].p_size / blksect;
+ sp->sp_lblks = sp->sp_pblks;
+
+ /* Try to reserve a number of spare physical blocks. */
+ switch (sc->sc_flash.sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ sp->sp_lblks -= 24; /* C3000 */
+ break;
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ sp->sp_lblks -= 4; /* C3100 */
+ break;
+ }
+
+ DPRINTF(("pblks %u lblks %u\n", sp->sp_pblks, sp->sp_lblks));
+
+ /* Next physical block to use; randomize for wear-leveling. */
+ sp->sp_pnext = arc4random() % sp->sp_pblks;
+
+ /* Allocate physical block usage map. */
+ phyuse = (u_int16_t *)malloc(sp->sp_pblks * sizeof(u_int16_t),
+ M_DEVBUF, M_NOWAIT);
+ if (phyuse == NULL) {
+ FREE(sp, M_DEVBUF);
+ return ENOMEM;
+ }
+ sp->sp_phyuse = phyuse;
+
+ /* Allocate logical to physical block map. */
+ logmap = (u_int *)malloc(sp->sp_lblks * sizeof(u_int),
+ M_DEVBUF, M_NOWAIT);
+ if (logmap == NULL) {
+ FREE(phyuse, M_DEVBUF);
+ FREE(sp, M_DEVBUF);
+ return ENOMEM;
+ }
+ sp->sp_logmap = logmap;
+
+ /* Initialize the physical and logical block maps. */
+ for (blkno = 0; blkno < sp->sp_pblks; blkno++)
+ phyuse[blkno] = P_BADBLOCK | P_DUST;
+ for (blkno = 0; blkno < sp->sp_lblks; blkno++)
+ logmap[blkno] = UINT_MAX;
+
+ /* Update physical block usage map with real data. */
+ unusable = 0;
+ flash_chip_enable(&sc->sc_flash);
+ for (blkno = 0; blkno < sp->sp_pblks; blkno++) {
+ long pageno;
+
+ pageno = blkno * sc->sc_flash.sc_flashdev->blkpages;
+ if (flash_chip_read_oob(&sc->sc_flash, pageno, oob) != 0) {
+ DPRINTF(("blkno %u: can't read oob data\n", blkno));
+ phyuse[blkno] = P_POSTBADBLOCK | P_DUST;
+ unusable++;
+ continue;
+ }
+
+ phyuse[blkno] = zflash_oob_status(sc, oob);
+ if (PHYUSE_STATUS(phyuse[blkno]) != P_NORMALBLOCK) {
+ DPRINTF(("blkno %u: badblock status %x\n", blkno,
+ PHYUSE_STATUS(phyuse[blkno])));
+ phyuse[blkno] |= P_DUST;
+ unusable++;
+ continue;
+ }
+
+ logno = zflash_oob_logno(sc, oob);
+ if (logno == UINT_MAX) {
+ DPRINTF(("blkno %u: can't read logno\n", blkno));
+ phyuse[blkno] |= P_JFFS2;
+ unusable++;
+ continue;
+ }
+
+ if (logno == USHRT_MAX) {
+ phyuse[blkno] |= P_DUST;
+ /* Block is usable and available. */
+ continue;
+ }
+
+ if (logno >= sp->sp_lblks) {
+ DPRINTF(("blkno %u: logno %u too big\n", blkno,
+ logno));
+ phyuse[blkno] |= P_JFFS2;
+ unusable++;
+ continue;
+ }
+
+ if (logmap[logno] == UINT_MAX) {
+ phyuse[blkno] |= P_LOGICAL;
+ logmap[logno] = blkno;
+ } else {
+ /* Duplicate logical block! */
+ DPRINTF(("blkno %u: duplicate logno %u\n", blkno,
+ logno));
+ phyuse[blkno] |= P_DUST;
+ }
+ }
+ flash_chip_disable(&sc->sc_flash);
+
+ if (unusable > 0)
+ printf("%s: %u unusable blocks\n",
+ sc->sc_flash.sc_dev.dv_xname, unusable);
+
+ sc->sc_safe[part] = sp;
+ return 0;
+}
+
+void
+zflash_safe_stop(struct zflash_softc *sc, dev_t dev)
+{
+ struct zflash_safe *sp;
+ int part;
+
+ part = flashpart(dev);
+ if (sc->sc_safe[part] == NULL)
+ return;
+
+ sp = sc->sc_safe[part];
+ free(sp->sp_phyuse, M_DEVBUF);
+ free(sp->sp_logmap, M_DEVBUF);
+ FREE(sp, M_DEVBUF);
+ sc->sc_safe[part] = NULL;
+}
+
+u_int
+zflash_safe_next_block(struct zflash_safe *sp)
+{
+ u_int blkno;
+
+ for (blkno = sp->sp_pnext; blkno < sp->sp_pblks; blkno++)
+ if (sp->sp_phyuse[blkno] == (P_NORMALBLOCK|P_DUST)) {
+ sp->sp_pnext = blkno + 1;
+ return blkno;
+ }
+
+ for (blkno = 0; blkno < sp->sp_pnext; blkno++)
+ if (sp->sp_phyuse[blkno] == (P_NORMALBLOCK|P_DUST)) {
+ sp->sp_pnext = blkno + 1;
+ return blkno;
+ }
+
+ return UINT_MAX;
+}
+
+/*
+ * Correct single bit errors in the block's status byte.
+ */
+u_char
+zflash_oob_status_decode(u_char status)
+{
+ u_char bit;
+ int count;
+
+ /* Speed-up. */
+ if (status == 0xff)
+ return 0xff;
+
+ /* Count the number of bits set in the byte. */
+ for (count = 0, bit = 0x01; bit != 0x00; bit <<= 1)
+ if ((status & bit) != 0)
+ count++;
+
+ return (count > 6) ? 0xff : 0x00;
+}
+
+/*
+ * Decode the block's status byte into a value for the phyuse map.
+ */
+u_int16_t
+zflash_oob_status(struct zflash_softc *sc, u_char *oob)
+{
+ u_char status;
+
+ status = zflash_oob_status_decode(oob[sc->sc_ioobbadblk]);
+ if (status != 0xff)
+ return P_BADBLOCK;
+
+ status = zflash_oob_status_decode(oob[sc->sc_ioobpostbadblk]);
+ if (status != 0xff)
+ return P_POSTBADBLOCK;
+
+ return P_NORMALBLOCK;
+}
+
+/*
+ * Extract the 16-bit logical block number corresponding to a physical
+ * block from the physical block's OOB data.
+ */
+u_int
+zflash_oob_logno(struct zflash_softc *sc, u_char *oob)
+{
+ int idx_lo, idx_hi;
+ u_int16_t word;
+ u_int16_t bit;
+ int parity;
+
+ /* Find a matching pair of high and low bytes. */
+ if (oob[OOB_LOGADDR_0_LO] == oob[OOB_LOGADDR_1_LO] &&
+ oob[OOB_LOGADDR_0_HI] == oob[OOB_LOGADDR_1_HI]) {
+ idx_lo = OOB_LOGADDR_0_LO;
+ idx_hi = OOB_LOGADDR_0_HI;
+ } else if (oob[OOB_LOGADDR_1_LO] == oob[OOB_LOGADDR_2_LO] &&
+ oob[OOB_LOGADDR_1_HI] == oob[OOB_LOGADDR_2_HI]) {
+ idx_lo = OOB_LOGADDR_1_LO;
+ idx_hi = OOB_LOGADDR_1_HI;
+ } else if (oob[OOB_LOGADDR_2_LO] == oob[OOB_LOGADDR_0_LO] &&
+ oob[OOB_LOGADDR_2_HI] == oob[OOB_LOGADDR_0_HI]) {
+ idx_lo = OOB_LOGADDR_2_LO;
+ idx_hi = OOB_LOGADDR_2_HI;
+ } else
+ /* Block's OOB data may be invalid. */
+ return UINT_MAX;
+
+ word = ((u_int16_t)oob[idx_lo] << 0) |
+ ((u_int16_t)oob[idx_hi] << 8);
+
+ /* Check for parity error in the logical block number. */
+ for (parity = 0, bit = 0x0001; bit != 0x0000; bit <<= 1)
+ if ((word & bit) != 0)
+ parity++;
+ if ((parity & 1) != 0)
+ return UINT_MAX;
+
+ /* No logical block number assigned to this block? */
+ if (word == USHRT_MAX)
+ return word;
+
+ /* Return the validated logical block number. */
+ return (word & 0x07fe) >> 1;
+}
+
+void
+zflash_oob_set_status(struct zflash_softc *sc, u_char *oob, u_int16_t phyuse)
+{
+ switch (PHYUSE_STATUS(phyuse)) {
+ case P_NORMALBLOCK:
+ oob[sc->sc_ioobbadblk] = 0xff;
+ oob[sc->sc_ioobpostbadblk] = 0xff;
+ break;
+ case P_BADBLOCK:
+ oob[sc->sc_ioobbadblk] = 0x00;
+ oob[sc->sc_ioobpostbadblk] = 0x00;
+ break;
+ case P_POSTBADBLOCK:
+ oob[sc->sc_ioobbadblk] = 0xff;
+ oob[sc->sc_ioobpostbadblk] = 0x00;
+ break;
+ }
+}
+
+void
+zflash_oob_set_logno(struct zflash_softc *sc, u_char *oob, u_int logno)
+{
+ u_int16_t word;
+ u_int16_t bit;
+ u_char lo;
+ u_char hi;
+ int parity;
+
+ /* Why do we set the most significant bit? */
+ word = ((logno & 0x03ff) << 1) | 0x1000;
+
+ /* Calculate the parity. */
+ for (bit = 0x0001; bit != 0x0000; bit <<= 1)
+ if ((word & bit) != 0)
+ parity++;
+ if ((parity & 1) != 0)
+ word |= 0x0001;
+
+ lo = word & 0x00ff;
+ hi = (word & 0xff00) >> 8;
+
+ oob[OOB_LOGADDR_0_LO] = lo;
+ oob[OOB_LOGADDR_0_HI] = hi;
+ oob[OOB_LOGADDR_1_LO] = lo;
+ oob[OOB_LOGADDR_1_HI] = hi;
+ oob[OOB_LOGADDR_2_LO] = lo;
+ oob[OOB_LOGADDR_2_HI] = hi;
+}
diff --git a/sys/conf/files b/sys/conf/files
index 25464cf0b65..8367c2dff7e 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.388 2006/11/17 18:58:30 mglocker Exp $
+# $OpenBSD: files,v 1.389 2006/11/25 14:31:59 uwe Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -518,6 +518,7 @@ file dev/audio.c audio needs-flag
file dev/ccd.c ccd needs-flag
file dev/cons.c
file dev/firmload.c firmload
+file dev/flash.c flash needs-flag
file dev/ic/ncr5380sbc.c ncr5380sbc
file dev/ic/ncr53c7xx.c ncr53c7xx
file dev/ic/pdq.c pdq
diff --git a/sys/dev/flash.c b/sys/dev/flash.c
new file mode 100644
index 00000000000..0fa721463e2
--- /dev/null
+++ b/sys/dev/flash.c
@@ -0,0 +1,1099 @@
+/* $OpenBSD: flash.c,v 1.1 2006/11/25 14:32:00 uwe Exp $ */
+
+/*
+ * Copyright (c) 2005 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#include <sys/kernel.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+
+#include <dev/flashvar.h>
+
+#include <ufs/ffs/fs.h> /* XXX */
+
+/* Samsung command set */
+#define SAMSUNG_CMD_PTRLO 0x00
+#define SAMSUNG_CMD_PTRHI 0x01
+#define SAMSUNG_CMD_PTROOB 0x50
+#define SAMSUNG_CMD_READ 0x30
+#define SAMSUNG_CMD_SEQIN 0x80
+#define SAMSUNG_CMD_WRITE 0x10
+#define SAMSUNG_CMD_ERASE0 0x60
+#define SAMSUNG_CMD_ERASE1 0xd0
+#define SAMSUNG_CMD_STATUS 0x70
+#define STATUS_FAIL (1<<0)
+#define STATUS_READY (1<<6)
+#define STATUS_NWP (1<<7)
+#define SAMSUNG_CMD_READID 0x90
+#define SAMSUNG_CMD_RESET 0xff
+
+int flash_wait_ready(struct flash_softc *);
+int flash_wait_complete(struct flash_softc *);
+
+/* XXX: these should go elsewhere */
+cdev_decl(flash);
+bdev_decl(flash);
+
+#define flashlock(sc) disk_lock(&(sc)->sc_dk)
+#define flashunlock(sc) disk_unlock(&(sc)->sc_dk)
+#define flashlookup(unit) \
+ (struct flash_softc *)device_lookup(&flash_cd, (unit))
+
+void flashminphys(struct buf *);
+void flashstart(struct flash_softc *);
+void _flashstart(struct flash_softc *, struct buf *);
+void flashdone(void *);
+
+int flashsafestrategy(struct flash_softc *, struct buf *);
+void flashgetdefaultlabel(dev_t, struct flash_softc *,
+ struct disklabel *);
+void flashgetdisklabel(dev_t, struct flash_softc *, struct disklabel *,
+ struct cpu_disklabel *, int);
+
+/*
+ * Driver attachment glue
+ */
+
+struct flashvendor {
+ u_int8_t vendor;
+ const char *name;
+};
+
+static const struct flashvendor flashvendors[] = {
+ { FLASH_VENDOR_SAMSUNG, "Samsung" }
+};
+#define FLASH_NVENDORS (sizeof(flashvendors) / sizeof(flashvendors[0]))
+
+static const struct flashdev flashdevs[] = {
+ { FLASH_DEVICE_SAMSUNG_K9F2808U0C, "K9F2808U0C 16Mx8 3.3V",
+ 512, 16, 32, 32768 },
+ { FLASH_DEVICE_SAMSUNG_K9F1G08U0A, "K9F1G08U0A 128Mx8 3.3V",
+ 2048, 64, 64, 65536 },
+};
+#define FLASH_NDEVS (sizeof(flashdevs) / sizeof(flashdevs[0]))
+
+struct cfdriver flash_cd = {
+ NULL, "flash", DV_DISK
+};
+
+struct dkdriver flashdkdriver = { flashstrategy };
+
+void
+flashattach(struct flash_softc *sc, struct flash_ctl_tag *tag,
+ void *cookie)
+{
+ u_int8_t vendor, device;
+ u_int16_t id;
+ int i;
+
+ sc->sc_tag = tag;
+ sc->sc_cookie = cookie;
+
+ if (sc->sc_maxwaitready <= 0)
+ sc->sc_maxwaitready = 1000; /* 1ms */
+ if (sc->sc_maxwaitcomplete <= 0)
+ sc->sc_maxwaitcomplete = 200000; /* 200ms */
+
+ flash_chip_enable(sc);
+
+ /* Identify the flash device. */
+ if (flash_chip_identify(sc, &vendor, &device) != 0) {
+ printf(": identification failed\n");
+ flash_chip_disable(sc);
+ return;
+ }
+ id = (vendor << 8) | device;
+
+ /* Look up device characteristics, abort if not recognized. */
+ for (i = 0; i < FLASH_NVENDORS; i++) {
+ if (flashvendors[i].vendor == vendor) {
+ printf(": %s", flashvendors[i].name);
+ break;
+ }
+ }
+ if (i == FLASH_NVENDORS)
+ printf(": vendor 0x%02x", vendor);
+ for (i = 0; i < FLASH_NDEVS; i++) {
+ if (flashdevs[i].id == id) {
+ printf(" %s\n", flashdevs[i].longname);
+ break;
+ }
+ }
+ if (i == FLASH_NDEVS) {
+ /* Need to add this device to flashdevs first. */
+ printf(" device 0x%02x\n", device);
+ flash_chip_disable(sc);
+ return;
+ }
+ sc->sc_flashdev = &flashdevs[i];
+
+ /* Check if the device really works or fail early. */
+ if (flash_chip_reset(sc) != 0) {
+ printf("%s: reset failed\n", sc->sc_dev.dv_xname);
+ flash_chip_disable(sc);
+ return;
+ }
+
+ flash_chip_disable(sc);
+
+ /*
+ * Initialize and attach the disk structure.
+ */
+ sc->sc_dk.dk_driver = &flashdkdriver;
+ sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
+ disk_attach(&sc->sc_dk);
+
+ /* XXX establish shutdown hook to finish any commands. */
+}
+
+int
+flashdetach(struct device *self, int flags)
+{
+ struct flash_softc *sc = (struct flash_softc *)self;
+
+ /* Detach disk. */
+ disk_detach(&sc->sc_dk);
+
+ /* XXX more resources need to be freed here. */
+ return 0;
+}
+
+int
+flashactivate(struct device *self, enum devact act)
+{
+ /* XXX anything to be done here? */
+ return 0;
+}
+
+/*
+ * Flash controller and chip functions
+ */
+
+u_int8_t
+flash_reg8_read(struct flash_softc *sc, int reg)
+{
+ return sc->sc_tag->reg8_read(sc->sc_cookie, reg);
+}
+
+void
+flash_reg8_read_page(struct flash_softc *sc, caddr_t data, caddr_t oob)
+{
+ int i;
+
+ for (i = 0; i < sc->sc_flashdev->pagesize; i++)
+ data[i] = flash_reg8_read(sc, FLASH_REG_DATA);
+
+ if (oob != NULL)
+ for (i = 0; i < sc->sc_flashdev->oobsize; i++)
+ oob[i] = flash_reg8_read(sc, FLASH_REG_DATA);
+}
+
+void
+flash_reg8_write(struct flash_softc *sc, int reg, u_int8_t value)
+{
+ sc->sc_tag->reg8_write(sc->sc_cookie, reg, value);
+}
+
+void
+flash_reg8_write_page(struct flash_softc *sc, caddr_t data, caddr_t oob)
+{
+ int i;
+
+ for (i = 0; i < sc->sc_flashdev->pagesize; i++)
+ flash_reg8_write(sc, FLASH_REG_DATA, data[i]);
+
+ if (oob != NULL)
+ for (i = 0; i < sc->sc_flashdev->oobsize; i++)
+ flash_reg8_write(sc, FLASH_REG_DATA, oob[i]);
+}
+
+/*
+ * Wait for the "Ready/Busy" signal to go high, indicating that the
+ * device is ready to accept another command.
+ */
+int
+flash_wait_ready(struct flash_softc *sc)
+{
+ int timo = sc->sc_maxwaitready;
+ u_int8_t ready;
+
+ ready = flash_reg8_read(sc, FLASH_REG_READY);
+ while (ready == 0 && timo-- > 0) {
+ delay(1);
+ ready = flash_reg8_read(sc, FLASH_REG_READY);
+ }
+ return (ready == 0 ? EIO : 0);
+}
+
+/*
+ * Similar to flash_wait_ready() but looks at IO 6 and IO 0 signals
+ * besides R/B to decide whether the last operation was successful.
+ */
+int
+flash_wait_complete(struct flash_softc *sc)
+{
+ int timo = sc->sc_maxwaitcomplete;
+ u_int8_t status;
+
+ (void)flash_wait_ready(sc);
+
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_STATUS);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+
+ status = flash_reg8_read(sc, FLASH_REG_DATA);
+ while ((status & STATUS_READY) == 0 && timo-- > 0) {
+ if (flash_reg8_read(sc, FLASH_REG_READY))
+ break;
+ delay(1);
+ status = flash_reg8_read(sc, FLASH_REG_DATA);
+ }
+
+ status = flash_reg8_read(sc, FLASH_REG_DATA);
+ return ((status & STATUS_FAIL) != 0 ? EIO : 0);
+}
+
+void
+flash_chip_enable(struct flash_softc *sc)
+{
+ /* XXX aquire the lock. */
+ flash_reg8_write(sc, FLASH_REG_CE, 1);
+}
+
+void
+flash_chip_disable(struct flash_softc *sc)
+{
+ flash_reg8_write(sc, FLASH_REG_CE, 0);
+ /* XXX release the lock. */
+}
+
+int
+flash_chip_reset(struct flash_softc *sc)
+{
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_RESET);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+
+ return flash_wait_ready(sc);
+}
+
+int
+flash_chip_identify(struct flash_softc *sc, u_int8_t *vendor,
+ u_int8_t *device)
+{
+ int error;
+
+ (void)flash_wait_ready(sc);
+
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_READID);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+
+ error = flash_wait_ready(sc);
+ if (error == 0) {
+ *vendor = flash_reg8_read(sc, FLASH_REG_DATA);
+ *device = flash_reg8_read(sc, FLASH_REG_DATA);
+ }
+ return error;
+}
+
+int
+flash_chip_erase_block(struct flash_softc *sc, long blkno)
+{
+ long pageno = blkno * sc->sc_flashdev->blkpages;
+ int error;
+
+ (void)flash_wait_ready(sc);
+
+ /* Disable write-protection. */
+ flash_reg8_write(sc, FLASH_REG_WP, 0);
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_ERASE0);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+ break;
+ }
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_ALE, 1);
+ flash_reg8_write(sc, FLASH_REG_ROW, pageno);
+ flash_reg8_write(sc, FLASH_REG_ROW, pageno >> 8);
+ flash_reg8_write(sc, FLASH_REG_ALE, 0);
+ break;
+ }
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_ERASE1);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+ break;
+ }
+
+ error = flash_wait_complete(sc);
+
+ /* Re-enable write-protection. */
+ flash_reg8_write(sc, FLASH_REG_WP, 1);
+
+ return error;
+}
+
+int
+flash_chip_read_block(struct flash_softc *sc, long blkno, caddr_t data)
+{
+ long pageno;
+ long blkend;
+ int error;
+
+ pageno = blkno * sc->sc_flashdev->blkpages;
+ blkend = pageno + sc->sc_flashdev->blkpages;
+
+ while (pageno < blkend) {
+ error = flash_chip_read_page(sc, pageno, data, NULL);
+ if (error != 0)
+ return error;
+ data += sc->sc_flashdev->pagesize;
+ pageno++;
+ }
+ return 0;
+}
+
+int
+flash_chip_read_page(struct flash_softc *sc, long pageno, caddr_t data,
+ caddr_t oob)
+{
+ int error;
+
+ (void)flash_wait_ready(sc);
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_PTRLO);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+ break;
+ }
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ flash_reg8_write(sc, FLASH_REG_ALE, 1);
+ flash_reg8_write(sc, FLASH_REG_COL, 0x00);
+ flash_reg8_write(sc, FLASH_REG_ALE, 0);
+ break;
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_ALE, 1);
+ flash_reg8_write(sc, FLASH_REG_COL, 0x00);
+ flash_reg8_write(sc, FLASH_REG_COL, 0x00);
+ flash_reg8_write(sc, FLASH_REG_ALE, 0);
+ break;
+ }
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_ALE, 1);
+ flash_reg8_write(sc, FLASH_REG_ROW, pageno);
+ flash_reg8_write(sc, FLASH_REG_ROW, pageno >> 8);
+ flash_reg8_write(sc, FLASH_REG_ALE, 0);
+ break;
+ }
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_READ);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+ break;
+ }
+
+ if ((error = flash_wait_ready(sc)) != 0)
+ return error;
+
+ /* Support hardware ECC calculation. */
+ if (sc->sc_tag->regx_read_page) {
+ error = sc->sc_tag->regx_read_page(sc->sc_cookie, data,
+ oob);
+ if (error != 0)
+ return error;
+ } else
+ flash_reg8_read_page(sc, data, oob);
+
+ return 0;
+}
+
+int
+flash_chip_read_oob(struct flash_softc *sc, long pageno, caddr_t oob)
+{
+ u_int8_t *p = (u_int8_t *)oob;
+ int error;
+ int i;
+
+ (void)flash_wait_ready(sc);
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_PTROOB);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+ break;
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_PTRLO);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+ break;
+ }
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ flash_reg8_write(sc, FLASH_REG_ALE, 1);
+ flash_reg8_write(sc, FLASH_REG_COL, 0x00);
+ flash_reg8_write(sc, FLASH_REG_ALE, 0);
+ break;
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_ALE, 1);
+ flash_reg8_write(sc, FLASH_REG_COL, 0x00);
+ flash_reg8_write(sc, FLASH_REG_COL, 0x08);
+ flash_reg8_write(sc, FLASH_REG_ALE, 0);
+ break;
+ }
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_ALE, 1);
+ flash_reg8_write(sc, FLASH_REG_ROW, pageno);
+ flash_reg8_write(sc, FLASH_REG_ROW, pageno >> 8);
+ flash_reg8_write(sc, FLASH_REG_ALE, 0);
+ break;
+ }
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_READ);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+ break;
+ }
+
+ if ((error = flash_wait_ready(sc)) != 0)
+ return error;
+
+ for (i = 0; i < sc->sc_flashdev->oobsize; i++)
+ p[i] = flash_reg8_read(sc, FLASH_REG_DATA);
+
+ return 0;
+}
+
+int
+flash_chip_write_block(struct flash_softc *sc, long blkno, caddr_t data,
+ caddr_t oob)
+{
+ long pageno;
+ long blkend;
+ caddr_t p;
+ int error;
+
+ pageno = blkno * sc->sc_flashdev->blkpages;
+ blkend = pageno + sc->sc_flashdev->blkpages;
+
+ p = data;
+ while (pageno < blkend) {
+ error = flash_chip_write_page(sc, pageno, p, oob);
+ if (error != 0)
+ return error;
+ p += sc->sc_flashdev->pagesize;
+ pageno++;
+ }
+
+ /* Verify the newly written block. */
+ return flash_chip_verify_block(sc, blkno, data, oob);
+}
+
+int
+flash_chip_write_page(struct flash_softc *sc, long pageno, caddr_t data,
+ caddr_t oob)
+{
+ int error;
+
+ (void)flash_wait_ready(sc);
+
+ /* Disable write-protection. */
+ flash_reg8_write(sc, FLASH_REG_WP, 0);
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_PTRLO);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+ break;
+ }
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_SEQIN);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+ break;
+ }
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ flash_reg8_write(sc, FLASH_REG_ALE, 1);
+ flash_reg8_write(sc, FLASH_REG_COL, 0x00);
+ flash_reg8_write(sc, FLASH_REG_ALE, 0);
+ break;
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_ALE, 1);
+ flash_reg8_write(sc, FLASH_REG_COL, 0x00);
+ flash_reg8_write(sc, FLASH_REG_COL, 0x00);
+ flash_reg8_write(sc, FLASH_REG_ALE, 0);
+ break;
+ }
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_ALE, 1);
+ flash_reg8_write(sc, FLASH_REG_ROW, pageno);
+ flash_reg8_write(sc, FLASH_REG_ROW, pageno >> 8);
+ flash_reg8_write(sc, FLASH_REG_ALE, 0);
+ break;
+ }
+
+ /* Support hardware ECC calculation. */
+ if (sc->sc_tag->regx_write_page) {
+ error = sc->sc_tag->regx_write_page(sc->sc_cookie, data,
+ oob);
+ if (error != 0)
+ return error;
+ } else
+ flash_reg8_write_page(sc, data, oob);
+
+ switch (sc->sc_flashdev->id) {
+ case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
+ case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
+ flash_reg8_write(sc, FLASH_REG_CLE, 1);
+ flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_WRITE);
+ flash_reg8_write(sc, FLASH_REG_CLE, 0);
+ break;
+ }
+
+ /*
+ * Wait for the write operation to complete although this can
+ * take up to 700 us for the K9F1G08U0A flash type.
+ */
+ error = flash_wait_complete(sc);
+
+ /* Re-enable write-protection. */
+ flash_reg8_write(sc, FLASH_REG_WP, 1);
+
+ return error;
+}
+
+int
+flash_chip_verify_block(struct flash_softc *sc, long blkno, caddr_t data,
+ caddr_t oob)
+{
+ long pageno;
+ long blkend;
+ int error;
+
+ pageno = blkno * sc->sc_flashdev->blkpages;
+ blkend = pageno + sc->sc_flashdev->blkpages;
+
+ while (pageno < blkend) {
+ error = flash_chip_verify_page(sc, pageno, data, oob);
+ if (error != 0) {
+ printf("block %d page %d verify failed\n",
+ blkno, pageno);
+ return error;
+ }
+ data += sc->sc_flashdev->pagesize;
+ pageno++;
+ }
+ return 0;
+}
+
+int
+flash_chip_verify_page(struct flash_softc *sc, long pageno, caddr_t data,
+ caddr_t oob)
+{
+ static u_char rbuf[FLASH_MAXPAGESIZE];
+ static u_char roob[FLASH_MAXOOBSIZE];
+ int error;
+
+ error = flash_chip_read_page(sc, pageno, rbuf,
+ oob == NULL ? NULL : roob);
+ if (error != 0)
+ return error;
+
+ if (memcmp((const void *)&rbuf[0], (const void *)data,
+ sc->sc_flashdev->pagesize) != 0)
+ return EIO;
+
+ if (oob != NULL && memcmp((const void *)&roob[0],
+ (const void *)oob, sc->sc_flashdev->oobsize) != 0)
+ return EIO;
+
+ return 0;
+}
+
+/*
+ * Block device functions
+ */
+
+int
+flashopen(dev_t dev, int oflags, int devtype, struct proc *p)
+{
+ struct flash_softc *sc;
+ int error;
+ int part;
+
+ sc = flashlookup(flashunit(dev));
+ if (sc == NULL)
+ return ENXIO;
+
+ if ((error = flashlock(sc)) != 0) {
+ device_unref(&sc->sc_dev);
+ return error;
+ }
+
+ /*
+ * If no partition is open load the partition info if it is
+ * not already valid. If partitions are already open, allow
+ * opens only for the same kind of device.
+ */
+ if (sc->sc_dk.dk_openmask == 0) {
+ if ((sc->sc_flags & FDK_LOADED) == 0 ||
+ ((sc->sc_flags & FDK_SAFE) == 0) !=
+ (flashsafe(dev) == 0)) {
+ sc->sc_flags &= ~FDK_SAFE;
+ sc->sc_flags |= FDK_LOADED;
+ if (flashsafe(dev))
+ sc->sc_flags |= FDK_SAFE;
+ flashgetdisklabel(dev, sc, sc->sc_dk.dk_label,
+ sc->sc_dk.dk_cpulabel, 0);
+ }
+ } else if (((sc->sc_flags & FDK_SAFE) == 0) !=
+ (flashsafe(dev) == 0)) {
+ flashunlock(sc);
+ device_unref(&sc->sc_dev);
+ return EBUSY;
+ }
+
+ /* Check that the partition exists. */
+ part = flashpart(dev);
+ if (part != RAW_PART &&
+ (part >= sc->sc_dk.dk_label->d_npartitions ||
+ sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
+ flashunlock(sc);
+ device_unref(&sc->sc_dev);
+ return ENXIO;
+ }
+
+ /* Prevent our unit from being deconfigured while open. */
+ switch (devtype) {
+ case S_IFCHR:
+ sc->sc_dk.dk_copenmask |= (1 << part);
+ break;
+ case S_IFBLK:
+ sc->sc_dk.dk_bopenmask |= (1 << part);
+ break;
+ }
+ sc->sc_dk.dk_openmask =
+ sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
+
+ flashunlock(sc);
+ device_unref(&sc->sc_dev);
+ return 0;
+}
+
+int
+flashclose(dev_t dev, int fflag, int devtype, struct proc *p)
+{
+ struct flash_softc *sc;
+ int error;
+ int part;
+
+ sc = flashlookup(flashunit(dev));
+ if (sc == NULL)
+ return ENXIO;
+
+ if ((error = flashlock(sc)) != 0) {
+ device_unref(&sc->sc_dev);
+ return error;
+ }
+
+ /* Close one open partition. */
+ part = flashpart(dev);
+ switch (devtype) {
+ case S_IFCHR:
+ sc->sc_dk.dk_copenmask &= ~(1 << part);
+ break;
+ case S_IFBLK:
+ sc->sc_dk.dk_bopenmask &= ~(1 << part);
+ break;
+ }
+ sc->sc_dk.dk_openmask =
+ sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
+
+ if (sc->sc_dk.dk_openmask == 0) {
+ /* XXX wait for I/O to complete? */
+ }
+
+ flashunlock(sc);
+ device_unref(&sc->sc_dev);
+ return 0;
+}
+
+/*
+ * Queue the transfer of one or more flash pages.
+ */
+void
+flashstrategy(struct buf *bp)
+{
+ struct flash_softc *sc;
+ int s;
+
+ sc = flashlookup(flashunit(bp->b_dev));
+ if (sc == NULL) {
+ bp->b_error = ENXIO;
+ goto bad;
+ }
+
+ /* Transfer only a multiple of the flash page size. */
+ if ((bp->b_bcount % sc->sc_flashdev->pagesize) != 0) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+
+ /* If the device has been invalidated, error out. */
+ if ((sc->sc_flags & FDK_LOADED) == 0) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+
+ /* Translate logical block numbers to physical. */
+ if (flashsafe(bp->b_dev) && flashsafestrategy(sc, bp) <= 0)
+ goto done;
+
+ /* Return immediately if it is a null transfer. */
+ if (bp->b_bcount == 0)
+ goto done;
+
+ /* Do bounds checking on partitions. */
+ if (flashpart(bp->b_dev) != RAW_PART &&
+ bounds_check_with_label(bp, sc->sc_dk.dk_label,
+ sc->sc_dk.dk_cpulabel, 0) <= 0)
+ goto done;
+
+ /* Queue the transfer. */
+ s = splbio();
+ disksort(&sc->sc_q, bp);
+ flashstart(sc);
+ splx(s);
+ device_unref(&sc->sc_dev);
+ return;
+
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+ if ((bp->b_flags & B_ERROR) != 0)
+ bp->b_resid = bp->b_bcount;
+ s = splbio();
+ biodone(bp);
+ splx(s);
+ if (sc != NULL)
+ device_unref(&sc->sc_dev);
+}
+
+int
+flashioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
+{
+ struct flash_softc *sc;
+ int error = 0;
+
+ sc = flashlookup(flashunit(dev));
+ if (sc == NULL)
+ return ENXIO;
+
+ if ((sc->sc_flags & FDK_LOADED) == 0) {
+ device_unref(&sc->sc_dev);
+ return EIO;
+ }
+
+ switch (cmd) {
+ case DIOCGDINFO:
+ *(struct disklabel *)data = *sc->sc_dk.dk_label;
+ break;
+ default:
+ error = ENOTTY;
+ break;
+ }
+
+ device_unref(&sc->sc_dev);
+ return error;
+}
+
+int
+flashdump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
+{
+ printf("flashdump\n");
+ return ENODEV;
+}
+
+int
+flashsize(dev_t dev)
+{
+ printf("flashsize\n");
+ return ENODEV;
+}
+
+void
+flashstart(struct flash_softc *sc)
+{
+ struct buf *dp, *bp;
+
+ while (1) {
+ /* Remove the next buffer from the queue or stop. */
+ dp = &sc->sc_q;
+ bp = dp->b_actf;
+ if (bp == NULL)
+ return;
+ dp->b_actf = bp->b_actf;
+
+ /* Transfer this buffer now. */
+ _flashstart(sc, bp);
+ }
+}
+
+void
+_flashstart(struct flash_softc *sc, struct buf *bp)
+{
+ int part;
+ long offset;
+ long pgno;
+
+ part = flashpart(bp->b_dev);
+ if (part != RAW_PART)
+ offset = sc->sc_dk.dk_label->d_partitions[part].p_offset;
+ else
+ offset = 0;
+
+ offset = offset + bp->b_blkno;
+ pgno = offset / (sc->sc_flashdev->pagesize / DEV_BSIZE);
+
+ /*
+ * If the requested page is exactly at the end of flash and it
+ * is an "unsafe" device, return EOF, else error out.
+ */
+ if (!flashsafe(bp->b_dev) && pgno == sc->sc_flashdev->capacity) {
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ return;
+ } else if (pgno >= sc->sc_flashdev->capacity) {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+
+ sc->sc_bp = bp;
+
+ /* Instrumentation. */
+ disk_busy(&sc->sc_dk);
+
+ /* XXX this should be done asynchronously. */
+ flash_chip_enable(sc);
+ if ((bp->b_flags & B_READ) != 0)
+ bp->b_error = flash_chip_read_page(sc, pgno, bp->b_data,
+ NULL);
+ else
+ bp->b_error = flash_chip_write_page(sc, pgno, bp->b_data,
+ NULL);
+ if (bp->b_error == 0)
+ bp->b_resid = bp->b_bcount - sc->sc_flashdev->pagesize;
+ flash_chip_disable(sc);
+ flashdone(sc);
+}
+
+void
+flashdone(void *v)
+{
+ struct flash_softc *sc = v;
+ struct buf *bp = sc->sc_bp;
+
+ /* Instrumentation. */
+ disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid,
+ (bp->b_flags & B_READ) != 0);
+
+ if (bp->b_error != 0)
+ bp->b_flags |= B_ERROR;
+
+ biodone(bp);
+ flashstart(sc);
+}
+
+void
+flashgetdefaultlabel(dev_t dev, struct flash_softc *sc,
+ struct disklabel *lp)
+{
+ size_t len;
+
+ bzero(lp, sizeof(struct disklabel));
+
+ lp->d_type = 0;
+ lp->d_subtype = 0;
+ strncpy(lp->d_typename, "NAND flash", sizeof(lp->d_typename));
+
+ /* Use the product name up to the first space. */
+ strncpy(lp->d_packname, sc->sc_flashdev->longname,
+ sizeof(lp->d_packname));
+ for (len = 0; len < sizeof(lp->d_packname); len++)
+ if (lp->d_packname[len] == ' ') {
+ lp->d_packname[len] = '\0';
+ break;
+ }
+
+ /* Fake the disk geometry. */
+ lp->d_ncylinders = 1;
+ lp->d_ntracks = 16;
+ lp->d_secsize = sc->sc_flashdev->pagesize;
+ lp->d_nsectors = sc->sc_flashdev->capacity / lp->d_ntracks
+ / lp->d_ncylinders;
+ lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
+ lp->d_secperunit = lp->d_ncylinders * lp->d_secpercyl;
+
+ /* Fake hardware characteristics. */
+ lp->d_rpm = 3600;
+ lp->d_interleave = 1;
+
+ /* XXX these values assume ffs. */
+ lp->d_bbsize = BBSIZE;
+ lp->d_sbsize = SBSIZE;
+
+ /* Fake the partition information. */
+ lp->d_npartitions = RAW_PART + 1;
+ lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
+ lp->d_partitions[RAW_PART].p_offset = 0;
+ lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
+
+ /* Wrap it up. */
+ lp->d_magic = DISKMAGIC;
+ lp->d_magic2 = DISKMAGIC;
+ lp->d_checksum = dkcksum(lp);
+}
+
+void
+flashgetdisklabel(dev_t dev, struct flash_softc *sc,
+ struct disklabel *lp, struct cpu_disklabel *clp, int spoofonly)
+{
+ char *errstring;
+ dev_t labeldev;
+
+ flashgetdefaultlabel(dev, sc, lp);
+ bzero(clp, sizeof(struct cpu_disklabel));
+
+ if (sc->sc_tag->default_disklabel != NULL)
+ sc->sc_tag->default_disklabel(sc->sc_cookie, dev, lp, clp);
+
+ /* Call the generic disklabel extraction routine. */
+ labeldev = flashlabeldev(dev);
+ errstring = readdisklabel(labeldev, flashstrategy, lp, clp,
+ spoofonly);
+ if (errstring != NULL)
+ printf("%s: %s\n", sc->sc_dev.dv_xname, errstring);
+}
+
+/*
+ * Character device functions
+ */
+
+void
+flashminphys(struct buf *bp)
+{
+ struct flash_softc *sc;
+
+ sc = flashlookup(flashunit(bp->b_dev));
+
+ if (bp->b_bcount > sc->sc_flashdev->pagesize)
+ bp->b_bcount = sc->sc_flashdev->pagesize;
+}
+
+int
+flashread(dev_t dev, struct uio *uio, int ioflag)
+{
+ return physio(flashstrategy, NULL, dev, B_READ, flashminphys, uio);
+}
+
+int
+flashwrite(dev_t dev, struct uio *uio, int ioflag)
+{
+ return physio(flashstrategy, NULL, dev, B_WRITE, flashminphys, uio);
+}
+
+/*
+ * Physical access strategy "fixup" routines for transparent bad
+ * blocks management, wear-leveling, etc.
+ */
+
+/*
+ * Call the machine-specific routine if there is any or use just a
+ * default strategy for bad blocks management.
+ */
+int
+flashsafestrategy(struct flash_softc *sc, struct buf *bp)
+{
+ if (sc->sc_tag->safe_strategy) {
+ return sc->sc_tag->safe_strategy(sc->sc_cookie, bp);
+ }
+
+ /* XXX no default bad blocks management strategy yet */
+ return 1;
+}
+
+void dumppage(u_char *);
+void dumppage(u_char *buf)
+{
+ int i;
+ for (i = 0; i < 512; i++) {
+ if ((i % 16) == 0)
+ printf("%04x: ", i);
+ if ((i % 16) == 8)
+ printf(" ");
+ printf(" %02x", buf[i]);
+ if ((i % 16) == 15)
+ printf("\n");
+ }
+ if ((i % 16) != 0)
+ printf("\n");
+}
diff --git a/sys/dev/flashvar.h b/sys/dev/flashvar.h
new file mode 100644
index 00000000000..dccf80d566d
--- /dev/null
+++ b/sys/dev/flashvar.h
@@ -0,0 +1,143 @@
+/* $OpenBSD: flashvar.h,v 1.1 2006/11/25 14:32:00 uwe Exp $ */
+
+/*
+ * Copyright (c) 2005 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FLASHVAR_H_
+#define _FLASHVAR_H_
+
+#ifdef _KERNEL
+
+/* Flash controller descriptor structure */
+struct flash_ctl_tag {
+ u_int8_t (*reg8_read)(void *, int);
+ int (*regx_read_page)(void *, caddr_t, caddr_t);
+ void (*reg8_write)(void *, int, u_int8_t);
+ int (*regx_write_page)(void *, caddr_t, caddr_t);
+ void (*default_disklabel)(void *, dev_t, struct disklabel *,
+ struct cpu_disklabel *);
+ int (*safe_strategy)(void *, struct buf *);
+};
+
+/*
+ * Pseudo-registers for a fictitious flash controller
+ *
+ * Note that logical levels are assumed for CE and WP bits.
+ * Signals corresponding to these bits are usually negated.
+ */
+#define FLASH_REG_DATA 0x00
+#define FLASH_REG_COL 0x01
+#define FLASH_REG_ROW 0x02
+#define FLASH_REG_CMD 0x03
+#define FLASH_REG_ALE 0x04
+#define FLASH_REG_CLE 0x05
+#define FLASH_REG_CE 0x06
+#define FLASH_REG_WP 0x07
+#define FLASH_REG_READY 0x0f
+
+/* Flash device descriptor structure */
+struct flashdev {
+ u_int16_t id;
+ const char *longname;
+ u_long pagesize; /* bytes per page */
+ u_long oobsize; /* OOB bytes per page */
+ u_long blkpages; /* pages per erasable block */
+ u_long capacity; /* pages per device */
+};
+
+#define FLASH_DEVICE(v,d) ((FLASH_VENDOR_##v << 8) | (d))
+
+/* Flash device vendors */
+#define FLASH_VENDOR_SAMSUNG 0xec
+
+/* Flash devices */
+#define FLASH_DEVICE_SAMSUNG_K9F2808U0C FLASH_DEVICE(SAMSUNG, 0x73)
+#define FLASH_DEVICE_SAMSUNG_K9F1G08U0A FLASH_DEVICE(SAMSUNG, 0xf1)
+
+/* Maximum sizes for all devices */
+#define FLASH_MAXPAGESIZE 2048
+#define FLASH_MAXOOBSIZE 64
+
+/*
+ * Should-be private softc structure for the generic flash driver.
+ */
+struct flash_softc {
+ struct device sc_dev;
+ /* Disk device information */
+ struct disk sc_dk;
+ struct buf sc_q;
+ struct buf *sc_bp;
+ int sc_flags;
+ /* Flash controller tag */
+ struct flash_ctl_tag *sc_tag;
+ void *sc_cookie;
+ /* Flash device characteristics */
+ const struct flashdev *sc_flashdev;
+ int sc_maxwaitready;
+ int sc_maxwaitcomplete;
+};
+
+/* Values for sc_flags */
+#define FDK_LOADED 0x00000001
+#define FDK_SAFE 0x00000002
+
+/*
+ * Similar to vnd(4) devices there are two kinds of flash devices.
+ * Both device kinds share the same disklabel.
+ *
+ * ``Safe'' devices have bit 11 set in the minor number and use the
+ * out-of-band page data to implement wear-leveling and transparent
+ * management of bad block information. Block erasing and rewriting
+ * is also handled transparently; arbitrary pages can be modified.
+ *
+ * ``Unsafe'' devices provide raw access to the flash pages. Access
+ * to OOB page data is possible via ioctl()s only with these devices.
+ * Erasing the containing flash block may be necessary before a page
+ * can be writting successfully, but the block erase command is only
+ * provided as an ioctl().
+ */
+#define flashsafe(x) (minor(x) & 0x800)
+#define flashunit(x) DISKUNIT(makedev(major(x), minor(x) & 0x7ff))
+#define flashpart(x) DISKPART(makedev(major(x), minor(x) & 0x7ff))
+#define flashlabeldev(x) (MAKEDISKDEV(major(x), flashunit(x), RAW_PART)\
+ |flashsafe(x))
+
+void flashattach(struct flash_softc *, struct flash_ctl_tag *, void *);
+int flashdetach(struct device *, int);
+int flashactivate(struct device *, enum devact);
+
+u_int8_t flash_reg8_read(struct flash_softc *, int);
+void flash_reg8_read_page(struct flash_softc *, caddr_t, caddr_t);
+void flash_reg8_write(struct flash_softc *, int, u_int8_t);
+void flash_reg8_write_page(struct flash_softc *, caddr_t, caddr_t);
+void flash_chip_enable(struct flash_softc *);
+void flash_chip_disable(struct flash_softc *);
+int flash_chip_reset(struct flash_softc *);
+int flash_chip_identify(struct flash_softc *, u_int8_t *, u_int8_t *);
+int flash_chip_erase_block(struct flash_softc *, long);
+int flash_chip_read_block(struct flash_softc *, long, caddr_t);
+int flash_chip_read_page(struct flash_softc *, long, caddr_t, caddr_t);
+int flash_chip_read_oob(struct flash_softc *, long, caddr_t);
+int flash_chip_write_block(struct flash_softc *, long, caddr_t, caddr_t);
+int flash_chip_write_page(struct flash_softc *, long, caddr_t, caddr_t);
+int flash_chip_verify_block(struct flash_softc *, long, caddr_t, caddr_t);
+int flash_chip_verify_page(struct flash_softc *, long, caddr_t, caddr_t);
+
+#endif /* _KERNEL */
+
+/* XXX: define ioctl commands for OOB page data access and block erase. */
+
+#endif /* _FLASHVAR_H_ */