aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/files2
-rw-r--r--sys/dev/isf/isf.c740
-rw-r--r--sys/dev/isf/isf.h94
-rw-r--r--sys/dev/isf/isf_nexus.c120
-rw-r--r--sys/mips/conf/BERI_DE4.hints10
-rw-r--r--sys/mips/conf/BERI_DE4_MDROOT1
-rw-r--r--sys/mips/conf/BERI_DE4_SDROOT1
7 files changed, 968 insertions, 0 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 54100daf094b..a95e04564f00 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1380,6 +1380,8 @@ dev/iscsi/initiator/isc_cam.c optional iscsi_initiator scbus
dev/iscsi/initiator/isc_soc.c optional iscsi_initiator scbus
dev/iscsi/initiator/isc_sm.c optional iscsi_initiator scbus
dev/iscsi/initiator/isc_subr.c optional iscsi_initiator scbus
+dev/isf/isf.c optional isf
+dev/isf/isf_nexus.c optional isf
dev/isp/isp.c optional isp
dev/isp/isp_freebsd.c optional isp
dev/isp/isp_library.c optional isp
diff --git a/sys/dev/isf/isf.c b/sys/dev/isf/isf.c
new file mode 100644
index 000000000000..b3ac080a381f
--- /dev/null
+++ b/sys/dev/isf/isf.c
@@ -0,0 +1,740 @@
+/*-
+ * Copyright (c) 2012 Robert N. M. Watson
+ * Copyright (c) 2012 SRI International
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/bio.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <geom/geom_disk.h>
+
+#include <dev/isf/isf.h>
+
+/* Read Mode */
+#define ISF_CMD_RA 0xFF /* Read Array mode */
+#define ISF_CMD_RSR 0x70 /* Read Status Register mode */
+#define ISF_CMD_RDI 0x90 /* Read Device ID/Config Reg mode */
+#define ISF_CMD_RQ 0x98 /* Read Query mode */
+#define ISF_CMD_CSR 0x50 /* Clear Status Register */
+
+/* Write Mode */
+#define ISF_CMD_WPS 0x40 /* Word Program Setup */
+#define ISF_CMD_BPS 0xE8 /* Buffered Program Setup */
+#define ISF_CMD_BPC 0xD0 /* Buffered Program Confirm */
+
+/* Erase Mode */
+#define ISF_CMD_BES 0x20 /* Block Erase Setup */
+#define ISF_CMD_BEC 0xD0 /* Block Erase Confirm */
+
+/* Block Locking/Unlocking */
+#define ISF_CMD_LBS 0x60 /* Lock Block Setup */
+#define ISF_CMD_LB 0x01 /* Lock Block */
+#define ISF_CMD_UB 0xD0 /* Unlock Block */
+
+/*
+ * Read Device Identifier registers.
+ *
+ * NOTE: ISF_RDIR_BLC is relative to the block base address.
+ */
+#define ISF_REG_MC 0x00 /* Manufacture Code */
+#define ISF_REG_ID 0x01 /* Device ID Code */
+#define ISF_REG_BLC 0x02 /* Block Lock Configuration */
+#define ISF_REG_RCR 0x05 /* Read Configuration Register */
+
+/*
+ * Protection Registers
+ */
+#define ISF_REG_L0 0x80 /* Lock Register 0 */
+#define ISF_REG_FPP 0x81 /* 64-bit Factory Protection Register */
+#define ISF_REG_UPP 0x85 /* 64-bit User Protection Register */
+#define ISF_REG_L1 0x89 /* Lock Register 1 */
+#define ISF_REG_PP1 0x8A /* 128-bit Protection Register 1 */
+#define ISF_REG_PP2 0x92 /* 128-bit Protection Register 2 */
+#define ISF_REG_PP3 0x9A /* 128-bit Protection Register 3 */
+#define ISF_REG_PP4 0xA2 /* 128-bit Protection Register 4 */
+#define ISF_REG_PP5 0xAA /* 128-bit Protection Register 5 */
+#define ISF_REG_PP6 0xB2 /* 128-bit Protection Register 6 */
+#define ISF_REG_PP7 0xBA /* 128-bit Protection Register 7 */
+#define ISF_REG_PP8 0xC2 /* 128-bit Protection Register 8 */
+#define ISF_REG_PP9 0xCA /* 128-bit Protection Register 9 */
+#define ISF_REG_PP10 0xD2 /* 128-bit Protection Register 10 */
+#define ISF_REG_PP11 0xDA /* 128-bit Protection Register 11 */
+#define ISF_REG_PP12 0xE2 /* 128-bit Protection Register 12 */
+#define ISF_REG_PP13 0xEA /* 128-bit Protection Register 13 */
+#define ISF_REG_PP14 0xF2 /* 128-bit Protection Register 14 */
+#define ISF_REG_PP15 0xFA /* 128-bit Protection Register 15 */
+#define ISF_REG_PP16 0x102 /* 128-bit Protection Register 16 */
+
+#define ISF_SR_BWS (1 << 0) /* BEFP Status */
+#define ISF_SR_BLS (1 << 1) /* Block-Locked Status */
+#define ISF_SR_PSS (1 << 2) /* Program Suspend Status */
+#define ISF_SR_VPPS (1 << 3) /* Vpp Status */
+#define ISF_SR_PS (1 << 4) /* Program Status */
+#define ISF_SR_ES (1 << 5) /* Erase Status */
+#define ISF_SR_ESS (1 << 6) /* Erase Suspend Status */
+#define ISF_SR_DWS (1 << 7) /* Device Write Status */
+#define ISF_SR_FSC_MASK (ISF_SR_VPPS | ISF_SR_PS | ISF_SR_BLS)
+
+#define ISF_BUFFER_PROGRAM
+
+MALLOC_DEFINE(M_ISF, "isf_data", "Intel StrateFlash driver");
+static int isf_debug = 0;
+
+static struct isf_chips {
+ uint16_t chip_id;
+ size_t chip_size;
+ const char *chip_desc;
+} chip_ids[] = {
+ { 0x8817, 0x0800000, "64-Mbit Top Parameter" },
+ { 0x881A, 0x0800000, "64-Mbit Bottom Parameter" },
+ { 0x8818, 0x1000000, "128-Mbit Top Parameter" },
+ { 0x881B, 0x1000000, "128-Mbit Bottom Parameter" },
+ { 0x8919, 0x2000000, "256-Mbit Top Parameter" },
+ { 0x891C, 0x2000000, "256-Mbit Bottom Parameter" },
+ { 0x8961, 0x2000000, "512-Mbit package (half)" },
+ { 0x0000, 0x0000000, NULL }
+};
+
+static void isf_task(void *arg);
+
+/*
+ * Device driver for the Intel StrataFlash NOR flash device. This
+ * implementation is known to work with 256Mb instances of the device, but may
+ * also work with other 64/128/512Mb parts without much work. Multiple
+ * device instances should be used when multiple parts are in the same
+ * physical package, due to variable block size support in the StrataFlash
+ * part.
+ */
+
+static uint16_t
+isf_read_reg(struct isf_softc *sc, uint16_t reg)
+{
+
+ if (isf_debug)
+ device_printf(sc->isf_dev, "isf_read_reg(0x%02x)\n", reg);
+ return (le16toh(bus_read_2(sc->isf_res, reg * 2)));
+}
+
+static uint64_t
+isf_read_reg64(struct isf_softc *sc, uint16_t reg)
+{
+ uint64_t val;
+ uint16_t *val16 = (uint16_t *)&val;
+
+ if (isf_debug)
+ device_printf(sc->isf_dev, "isf_read_reg64(0x%02x)\n", reg);
+ val16[0] = bus_read_2(sc->isf_res, reg * 2);
+ val16[1] = bus_read_2(sc->isf_res, (reg+1) * 2);
+ val16[2] = bus_read_2(sc->isf_res, (reg+2) * 2);
+ val16[3] = bus_read_2(sc->isf_res, (reg+3) * 2);
+
+ return(le64toh(val));
+}
+
+static uint16_t
+isf_read_off(struct isf_softc *sc, off_t off)
+{
+
+ KASSERT(off >= 0, ("%s: negative offset\n", __func__));
+ KASSERT(off < sc->isf_disk->d_mediasize,
+ ("%s: offset out side address space 0x%08jx \n", __func__,
+ (intmax_t)off));
+
+ if (isf_debug)
+ device_printf(sc->isf_dev, "isf_read_off(0x%08jx)\n",
+ (intmax_t)off);
+ return (le16toh(bus_read_2(sc->isf_res, off)));
+}
+
+static void
+isf_write_cmd(struct isf_softc *sc, off_t off, uint16_t cmd)
+{
+
+ if (isf_debug)
+ device_printf(sc->isf_dev, "isf_write_cmd(0x%08jx, 0x%02x)\n",
+ off, cmd);
+ bus_write_2(sc->isf_res, off, htole16(cmd));
+}
+
+static uint16_t
+isf_read_status(struct isf_softc *sc, off_t off)
+{
+
+ isf_write_cmd(sc, off/2, ISF_CMD_RSR);
+ return isf_read_off(sc, off);
+}
+
+static void
+isf_clear_status(struct isf_softc *sc)
+{
+
+ isf_write_cmd(sc, 0, ISF_CMD_CSR);
+}
+
+static int
+isf_full_status_check(struct isf_softc *sc, off_t off)
+{
+ int error = 0;
+ uint16_t status;
+
+ status = isf_read_status(sc, off);
+ if (status & ISF_SR_VPPS) {
+ device_printf(sc->isf_dev, "Vpp Range Error\n");
+ error = EIO;
+ } else if (status & ISF_SR_PS) {
+ device_printf(sc->isf_dev, "Program Error\n");
+ error = EIO;
+ } else if (status & ISF_SR_BLS) {
+ device_printf(sc->isf_dev, "Device Protect Error\n");
+ error = EIO;
+ }
+ isf_clear_status(sc);
+
+ return(error);
+}
+
+static int
+isf_full_erase_status_check(struct isf_softc *sc, off_t off)
+{
+ int error = 0;
+ uint16_t status;
+
+ status = isf_read_status(sc, off);
+ if (status & ISF_SR_VPPS) {
+ device_printf(sc->isf_dev, "Vpp Range Error\n");
+ error = EIO;
+ } else if (status & (ISF_SR_PS|ISF_SR_ES)) {
+ device_printf(sc->isf_dev, "Command Sequence Error\n");
+ error = EIO;
+ } else if (status & ISF_SR_ES) {
+ device_printf(sc->isf_dev, "Block Erase Error\n");
+ error = EIO;
+ } else if (status & ISF_SR_BLS) {
+ device_printf(sc->isf_dev, "Block Locked Error\n");
+ error = EIO;
+ }
+ isf_clear_status(sc);
+
+ return(error);
+}
+
+static void
+isf_unlock_block(struct isf_softc *sc, off_t off)
+{
+
+ isf_write_cmd(sc, off, ISF_CMD_LBS);
+ isf_write_cmd(sc, off, ISF_CMD_UB);
+ isf_write_cmd(sc, off, ISF_CMD_RA);
+}
+
+static void
+isf_lock_block(struct isf_softc *sc, off_t off)
+{
+
+ isf_write_cmd(sc, off, ISF_CMD_LBS);
+ isf_write_cmd(sc, off, ISF_CMD_LB);
+ isf_write_cmd(sc, off, ISF_CMD_RA);
+}
+
+static void
+isf_read(struct isf_softc *sc, off_t off, void *data, size_t len)
+{
+
+ KASSERT((uintptr_t)data % 2 == 0,
+ ("%s: unaligned data %p", __func__, data));
+ KASSERT((len <= ISF_SECTORSIZE) && (len % 2 == 0),
+ ("%s: invalid length %ju", __func__, len));
+ KASSERT(off % ISF_SECTORSIZE == 0,
+ ("%s: invalid offset %ju\n", __func__, off));
+
+ /*
+ * It is not permitted to read blocks that are in the process of
+ * being erased, but we know they will be all 1's after the
+ * erase so just report that value if asked about a block that
+ * is being erased.
+ */
+ if (sc->isf_bstate[off / ISF_ERASE_BLOCK] == BS_ERASING)
+ memset(data, 0xFF, len);
+ else
+ bus_read_region_2(sc->isf_res, off, (uint16_t *)data, len / 2);
+}
+
+static int
+isf_write(struct isf_softc *sc, off_t off, void *data, size_t len)
+{
+ int cycles, error = 0;
+ uint16_t *dp;
+ uint16_t status;
+ off_t coff;
+
+ KASSERT((uintptr_t)data % 2 == 0,
+ ("%s: unaligned data %p", __func__, data));
+ KASSERT((len <= ISF_SECTORSIZE) && (len % 2 == 0),
+ ("%s: invalid length %ju", __func__, len));
+ KASSERT(off % ISF_SECTORSIZE == 0,
+ ("%s: invalid offset %ju\n", __func__, off));
+ KASSERT(!sc->isf_erasing,
+ ("%s: trying to write while erasing\n", __func__));
+ KASSERT(sc->isf_bstate[off / ISF_ERASE_BLOCK] != BS_ERASING,
+ ("%s: block being erased at %ju\n", __func__, off));
+
+ isf_unlock_block(sc, off);
+
+#ifdef ISF_BUFFER_PROGRAM
+ for (dp = data, coff = off; dp - (uint16_t *)data < len / 2;
+ dp += 32, coff += 64) {
+ isf_clear_status(sc);
+ isf_write_cmd(sc, coff, ISF_CMD_BPS);
+ cycles = 0xFFFF;
+ while ( !(isf_read_off(sc, coff) & ISF_SR_DWS) ) {
+ if (cycles-- == 0) {
+ device_printf(sc->isf_dev, "timeout waiting"
+ " for write to start at 0x08%jx\n",
+ (intmax_t)coff);
+ return (EIO);
+ }
+ isf_write_cmd(sc, coff, ISF_CMD_BPS);
+ }
+
+ /* When writing N blocks, send N-1 as the count */
+ isf_write_cmd(sc, coff, 31);
+ bus_write_region_2(sc->isf_res, coff, dp, 32);
+
+ isf_write_cmd(sc, coff, ISF_CMD_BPC);
+
+ status = isf_read_off(sc, coff);
+ cycles = 0xFFFFF;
+ while ( !(status & ISF_SR_DWS) ) {
+ if (cycles-- == 0) {
+ device_printf(sc->isf_dev, "timeout waiting"
+ " for write to complete at 0x08%jx\n",
+ (intmax_t)coff);
+ error = EIO;
+ break;
+ }
+ status = isf_read_off(sc, coff);
+ }
+ isf_full_status_check(sc, off);
+
+ isf_write_cmd(sc, coff, ISF_CMD_RA);
+ }
+#else
+ for (dp = data, coff = off; dp - (uint16_t *)data < len / 2;
+ dp++, coff += 2) {
+ isf_write_cmd(sc, coff, ISF_CMD_WPS);
+ bus_write_2(sc->isf_res, coff, *dp);
+ status = isf_read_off(sc, coff);
+ cycles=0xFFFFF;
+ while ( !(status & ISF_SR_DWS) ) {
+ if (cycles-- == 0) {
+ device_printf(sc->isf_dev, "timeout waiting"
+ " for write to complete at 0x08%jx\n",
+ (intmax_t)coff);
+ error = EIO;
+ break;
+ }
+ status = isf_read_off(sc, coff);
+ }
+
+ }
+ isf_full_status_check(sc, off);
+ isf_write_cmd(sc, coff, ISF_CMD_RA);
+#endif
+
+ isf_lock_block(sc, off);
+
+ return error;
+}
+
+static void
+isf_erase_at(struct isf_softc *sc, off_t off)
+{
+ int cycles;
+ uint16_t status;
+
+ isf_unlock_block(sc, off);
+ isf_clear_status(sc);
+
+ isf_write_cmd(sc, off, ISF_CMD_BES);
+ isf_write_cmd(sc, off, ISF_CMD_BEC);
+
+ cycles=0xFFFFFF;
+ status = isf_read_off(sc, off);
+ while ( !(status & ISF_SR_DWS) ) {
+#ifdef NOTYET
+ ISF_SLEEP(sc, sc, hz);
+#endif
+ if (cycles-- == 0) {
+ device_printf(sc->isf_dev,
+ "Giving up on erase\n");
+ break;
+ }
+ status = isf_read_off(sc, off);
+ }
+
+ isf_full_erase_status_check(sc, off);
+
+ isf_lock_block(sc, off);
+
+ isf_write_cmd(sc, off, ISF_CMD_RA);
+}
+
+static void
+isf_erase_range(struct isf_softc *sc, off_t blk_off, size_t size)
+{
+ off_t off;
+ off_t ms = sc->isf_disk->d_mediasize;
+
+ KASSERT(blk_off % ISF_ERASE_BLOCK == 0,
+ ("%s: invalid offset %ju\n", __func__, blk_off));
+
+ ISF_LOCK_ASSERT(sc);
+
+ for (off = blk_off; off < blk_off + size; off += ISF_ERASE_BLOCK) {
+ sc->isf_bstate[off / ISF_ERASE_BLOCK] = BS_ERASING;
+
+ /*
+ * The first or last 128K is four blocks depending which
+ * part this is. For now, just assume both are and
+ * erase four times.
+ */
+ if (off == 0 || ms - off == ISF_ERASE_BLOCK) {
+ isf_erase_at(sc, off);
+ isf_erase_at(sc, off + 0x08000);
+ isf_erase_at(sc, off + 0x10000);
+ isf_erase_at(sc, off + 0x18000);
+ } else
+ isf_erase_at(sc, off);
+
+ sc->isf_bstate[off / ISF_ERASE_BLOCK] = BS_STEADY;
+ }
+}
+
+/*
+ * disk(9) methods.
+ */
+static int
+isf_disk_ioctl(struct disk *disk, u_long cmd, void *data, int fflag,
+ struct thread *td)
+{
+ int error = 0;
+ struct isf_softc *sc = disk->d_drv1;
+ struct isf_range *ir;
+
+ switch (cmd) {
+ case ISF_ERASE:
+ ir = data;
+ if (ir->ir_off % ISF_ERASE_BLOCK != 0 ||
+ ir->ir_off >= disk->d_mediasize ||
+ ir->ir_size == 0 ||
+ ir->ir_size % ISF_ERASE_BLOCK != 0 ||
+ ir->ir_off + ir->ir_size > disk->d_mediasize) {
+ error = EINVAL;
+ break;
+ }
+ ISF_LOCK(sc);
+ if (sc->isf_erasing) {
+ ISF_UNLOCK(sc);
+ error = EBUSY;
+ break;
+ }
+ sc->isf_erasing = 1;
+ isf_erase_range(sc, ir->ir_off, ir->ir_size);
+ sc->isf_erasing = 0;
+ ISF_UNLOCK(sc);
+ break;
+ default:
+ error = EINVAL;
+ }
+
+ return (error);
+}
+
+static void
+isf_disk_strategy(struct bio *bp)
+{
+ struct isf_softc *sc = bp->bio_disk->d_drv1;;
+
+ /*
+ * We advertise a block size and maximum I/O size up the stack; catch
+ * any attempts to not follow the rules.
+ */
+ KASSERT(bp->bio_bcount == ISF_SECTORSIZE,
+ ("%s: I/O size not %d", __func__, ISF_SECTORSIZE));
+
+ ISF_LOCK(sc);
+ bioq_disksort(&sc->isf_bioq, bp);
+ ISF_WAKEUP(sc);
+ ISF_UNLOCK(sc);
+}
+
+static void
+isf_task(void *arg)
+{
+ struct isf_softc *sc = arg;
+ struct bio *bp;
+ int ss = sc->isf_disk->d_sectorsize;
+ int error, i;
+
+ for (;;) {
+ ISF_LOCK(sc);
+ do {
+ bp = bioq_first(&sc->isf_bioq);
+ if (bp == NULL) {
+ if (sc->isf_doomed)
+ kproc_exit(0);
+ else
+ ISF_SLEEP(sc, sc, 0);
+ }
+ } while (bp == NULL);
+ bioq_remove(&sc->isf_bioq, bp);
+
+ error = 0;
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ isf_read(sc, bp->bio_pblkno * ss, bp->bio_data,
+ bp->bio_bcount);
+ break;
+
+ case BIO_WRITE:
+ /*
+ * In principle one could suspend the in-progress
+ * erase, process any pending writes to other
+ * blocks and then proceed, but that seems
+ * overly complex for the likely usage modes.
+ */
+ if (sc->isf_erasing) {
+ error = EBUSY;
+ break;
+ }
+
+ /*
+ * Read in the block we want to write and check that
+ * we're only setting bits to 0. If an erase would
+ * be required return an I/O error.
+ */
+ isf_read(sc, bp->bio_pblkno * ss, sc->isf_rbuf,
+ bp->bio_bcount);
+ for (i = 0; i < bp->bio_bcount / 2; i++)
+ if ((sc->isf_rbuf[i] &
+ ((uint16_t *)bp->bio_data)[i]) !=
+ ((uint16_t *)bp->bio_data)[i]) {
+ device_printf(sc->isf_dev, "write"
+ " requires erase at 0x%08jx\n",
+ bp->bio_pblkno * ss);
+ error = EIO;
+ break;
+ }
+ if (error != 0)
+ break;
+
+ error = isf_write(sc, bp->bio_pblkno * ss,
+ bp->bio_data, bp->bio_bcount);
+ break;
+
+ default:
+ panic("%s: unsupported I/O operation %d", __func__,
+ bp->bio_cmd);
+ }
+ if (error == 0)
+ biodone(bp);
+ else
+ biofinish(bp, NULL, error);
+ ISF_UNLOCK(sc);
+ }
+}
+
+static void
+isf_dump_info(struct isf_softc *sc)
+{
+ int i;
+ int32_t reg;
+
+ isf_write_cmd(sc, 0, ISF_CMD_RDI);
+ device_printf(sc->isf_dev, "manufacturer code: 0x%04x\n",
+ isf_read_reg(sc, ISF_REG_MC));
+ device_printf(sc->isf_dev, "device id code: 0x%04x\n",
+ isf_read_reg(sc, ISF_REG_ID));
+ device_printf(sc->isf_dev, "read config register: 0x%04x\n",
+ isf_read_reg(sc, ISF_REG_RCR));
+
+ device_printf(sc->isf_dev, "lock register 0: 0x%04x\n",
+ isf_read_reg(sc, ISF_REG_L0));
+ device_printf(sc->isf_dev, "lock register 1: 0x%04x\n",
+ isf_read_reg(sc, ISF_REG_L1));
+
+ device_printf(sc->isf_dev, "factory PPR: 0x%016jx\n",
+ (uintmax_t)isf_read_reg64(sc, ISF_REG_FPP));
+ device_printf(sc->isf_dev, "user PPR (64-bit): 0x%016jx\n",
+ (uintmax_t)isf_read_reg64(sc, ISF_REG_UPP));
+
+ for (reg = ISF_REG_PP1, i = 1; reg <= ISF_REG_PP16; reg += 8, i++) {
+ /* XXX: big-endian ordering of uint64_t's */
+ device_printf(sc->isf_dev,
+ "user PPR [%02d]: 0x%016jx%016jx\n", i,
+ (uintmax_t)isf_read_reg64(sc, reg+4),
+ (uintmax_t)isf_read_reg64(sc, reg));
+ }
+
+ isf_write_cmd(sc, 0, ISF_CMD_RA);
+}
+
+static void
+isf_disk_insert(struct isf_softc *sc, off_t mediasize)
+{
+ struct disk *disk;
+
+ sc->isf_doomed = 0;
+ sc->isf_erasing = 0;
+ sc->isf_bstate = malloc(sizeof(*sc->isf_bstate) *
+ (mediasize / ISF_ERASE_BLOCK), M_ISF, M_ZERO | M_WAITOK);
+ kproc_create(&isf_task, sc, &sc->isf_proc, 0, 0, "isf");
+
+ disk = disk_alloc();
+ disk->d_drv1 = sc;
+ disk->d_name = "isf";
+ disk->d_unit = sc->isf_unit;
+ disk->d_strategy = isf_disk_strategy;
+ disk->d_ioctl = isf_disk_ioctl;
+ disk->d_sectorsize = ISF_SECTORSIZE;
+ disk->d_mediasize = mediasize;
+ disk->d_maxsize = ISF_SECTORSIZE;
+ sc->isf_disk = disk;
+
+ if (bootverbose)
+ isf_dump_info(sc);
+
+ disk_create(disk, DISK_VERSION);
+ device_printf(sc->isf_dev, "%juM flash device\n",
+ (uintmax_t)disk->d_mediasize / (1024 * 1024));
+
+}
+
+static void
+isf_disk_remove(struct isf_softc *sc)
+{
+ struct disk *disk;
+
+ ISF_LOCK_ASSERT(sc);
+ KASSERT(sc->isf_disk != NULL, ("%s: isf_disk NULL", __func__));
+
+ sc->isf_doomed = 1;
+ ISF_WAKEUP(sc);
+ ISF_SLEEP(sc, sc->isf_proc, 0);
+
+ /*
+ * XXXRW: Is it OK to call disk_destroy() under the mutex, or should
+ * we be deferring that to the calling context once it is released?
+ */
+ disk = sc->isf_disk;
+ disk_gone(disk);
+ disk_destroy(disk);
+ sc->isf_disk = NULL;
+ free(sc->isf_bstate, M_ISF);
+ device_printf(sc->isf_dev, "flash device removed\n");
+}
+
+int
+isf_attach(struct isf_softc *sc)
+{
+ uint16_t id;
+ u_long start, size;
+ struct isf_chips *cp = chip_ids;
+
+ start = rman_get_start(sc->isf_res);
+ if (start % 2 != 0) {
+ device_printf(sc->isf_dev,
+ "Unsupported flash start alignment %lu\n",
+ start);
+ return (ENXIO);
+ }
+
+ isf_write_cmd(sc, 0, ISF_CMD_RDI);
+ id = isf_read_reg(sc, ISF_REG_ID);
+ while (cp->chip_id != id)
+ cp++;
+ if (cp->chip_desc == NULL) {
+ device_printf(sc->isf_dev,
+ "Unsupported device ID 0x%04x\n", id);
+ return (ENXIO);
+ }
+ isf_write_cmd(sc, 0, ISF_CMD_RA);
+
+ size = rman_get_size(sc->isf_res);
+ if (size != cp->chip_size) {
+ device_printf(sc->isf_dev,
+ "Unsupported flash size %lu\n", size);
+ return (ENXIO);
+ }
+
+ bioq_init(&sc->isf_bioq);
+ ISF_LOCK_INIT(sc);
+ sc->isf_disk = NULL;
+ isf_disk_insert(sc, size);
+ return(0);
+}
+
+void
+isf_detach(struct isf_softc *sc)
+{
+
+ /*
+ * Simulate a disk removal if one is present to deal with any pending
+ * or queued I/O. This will occur as a result of a device driver
+ * detach -- the Intel StrataFlash has no notion of removal itself.
+ *
+ * XXXRW: Is the locking here right?
+ */
+ ISF_LOCK(sc);
+ isf_disk_remove(sc);
+ bioq_flush(&sc->isf_bioq, NULL, ENXIO);
+ KASSERT(bioq_first(&sc->isf_bioq) == NULL,
+ ("%s: non-empty bioq", __func__));
+ ISF_UNLOCK(sc);
+ ISF_LOCK_DESTROY(sc);
+}
diff --git a/sys/dev/isf/isf.h b/sys/dev/isf/isf.h
new file mode 100644
index 000000000000..f5cdc08fc31e
--- /dev/null
+++ b/sys/dev/isf/isf.h
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2012 Robert N. M. Watson
+ * Copyright (c) 2012 SRI International
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_ISF_H_
+#define _DEV_ISF_H_
+
+struct isf_range {
+ off_t ir_off; /* Offset of range to delete (set to 0xFF) */
+ size_t ir_size; /* Size of range */
+};
+
+#define ISF_ERASE _IOW('I', 1, struct isf_range)
+
+/*
+ * Ordinary read and write operations are limited to 512 bytes.
+ * We support erasing 128K blocks and ignore the fact that portions of the
+ * flash are in fact divided into 32K blocks.
+ */
+#define ISF_SECTORSIZE (512)
+#define ISF_ERASE_BLOCK (128 * 1024)
+
+#ifdef _KERNEL
+MALLOC_DECLARE(M_ISF);
+
+enum bstate {
+ BS_STEADY = 0,
+ BS_ERASING
+};
+
+struct isf_softc {
+ device_t isf_dev;
+ int isf_unit;
+ struct resource *isf_res;
+ int isf_rid;
+ struct mtx isf_lock;
+ struct disk *isf_disk;
+ struct proc *isf_proc;
+ int isf_doomed;
+
+ /*
+ * Fields relating to in-progress and pending I/O, if any.
+ */
+ struct bio_queue_head isf_bioq;
+ uint16_t isf_rbuf[ISF_SECTORSIZE / 2];
+ int isf_erasing;
+ enum bstate *isf_bstate;
+};
+
+#define ISF_LOCK(sc) mtx_lock(&(sc)->isf_lock)
+#define ISF_LOCK_ASSERT(sc) mtx_assert(&(sc)->isf_lock, MA_OWNED)
+#define ISF_LOCK_DESTROY(sc) mtx_destroy(&(sc)->isf_lock)
+#define ISF_LOCK_INIT(sc) mtx_init(&(sc)->isf_lock, "isf", NULL, \
+ MTX_DEF)
+#define ISF_SLEEP(sc, wait, timo) mtx_sleep((wait), \
+ &(sc)->isf_lock, PRIBIO, \
+ "isf", (timo))
+#define ISF_UNLOCK(sc) mtx_unlock(&(sc)->isf_lock)
+#define ISF_WAKEUP(sc) wakeup((sc))
+
+int isf_attach(struct isf_softc *sc);
+void isf_detach(struct isf_softc *sc);
+#endif /* _KERNEL */
+
+#endif /* _DEV_ISF_H_ */
diff --git a/sys/dev/isf/isf_nexus.c b/sys/dev/isf/isf_nexus.c
new file mode 100644
index 000000000000..af2f874411c3
--- /dev/null
+++ b/sys/dev/isf/isf_nexus.c
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 2012 Robert N. M. Watson
+ * Copyright (c) 2012 SRI International
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/conf.h>
+#include <sys/bio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <geom/geom_disk.h>
+
+#include <dev/isf/isf.h>
+
+/*
+ * Nexus bus attachment for the Intel Strata Flash devices. Appropriate for
+ * most Altera FPGA SoC-style configurations in which the part will be exposed
+ * to the processor via a memory-mapped Avalon bus.
+ */
+static int
+isf_nexus_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Intel StrataFlash NOR flash device");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+isf_nexus_attach(device_t dev)
+{
+ int error;
+ struct isf_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->isf_dev = dev;
+ sc->isf_unit = device_get_unit(dev);
+ sc->isf_rid = 0;
+ sc->isf_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->isf_rid, RF_ACTIVE);
+ if (sc->isf_res == NULL) {
+ device_printf(dev, "couldn't map memory\n");
+ return (ENXIO);
+ }
+ error = isf_attach(sc);
+ if (error)
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->isf_rid,
+ sc->isf_res);
+ return (error);
+}
+
+static int
+isf_nexus_detach(device_t dev)
+{
+ struct isf_softc *sc;
+
+ sc = device_get_softc(dev);
+ KASSERT(sc->isf_res != NULL, ("%s: resources not allocated",
+ __func__));
+ isf_detach(sc);
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->isf_rid, sc->isf_res);
+ return (0);
+}
+
+static device_method_t isf_nexus_methods[] = {
+ DEVMETHOD(device_probe, isf_nexus_probe),
+ DEVMETHOD(device_attach, isf_nexus_attach),
+ DEVMETHOD(device_detach, isf_nexus_detach),
+ { 0, 0 }
+};
+
+static driver_t isf_nexus_driver = {
+ "isf",
+ isf_nexus_methods,
+ sizeof(struct isf_softc),
+};
+
+static devclass_t isf_devclass;
+
+DRIVER_MODULE(isf, nexus, isf_nexus_driver, isf_devclass, 0, 0);
diff --git a/sys/mips/conf/BERI_DE4.hints b/sys/mips/conf/BERI_DE4.hints
index 319d0c2aa148..4ffe6c2f9572 100644
--- a/sys/mips/conf/BERI_DE4.hints
+++ b/sys/mips/conf/BERI_DE4.hints
@@ -46,6 +46,16 @@ hint.altera_avgen.0.devname="berirom"
#hint.altera_avgen.0.mmapio="rwx"
#hint.altera_avgen.0.devname="de4flash"
+#
+# General Intel StrataFlash driver
+#
+hint.isf.0.at="nexus0"
+hint.isf.0.maddr=0x74000000
+hint.isf.0.msize=0x2000000
+hint.isf.1.at="nexus0"
+hint.isf.1.maddr=0x76000000
+hint.isf.1.msize=0x2000000
+
# Reserved configuration blocks. Don't touch.
hint.map.0.at="isf0"
hint.map.0.start=0x00000000
diff --git a/sys/mips/conf/BERI_DE4_MDROOT b/sys/mips/conf/BERI_DE4_MDROOT
index 8903179a220f..658c925f78c7 100644
--- a/sys/mips/conf/BERI_DE4_MDROOT
+++ b/sys/mips/conf/BERI_DE4_MDROOT
@@ -25,4 +25,5 @@ device altera_avgen
device altera_jtag_uart
device altera_sdcard
+device isf
#device sc
diff --git a/sys/mips/conf/BERI_DE4_SDROOT b/sys/mips/conf/BERI_DE4_SDROOT
index dcd177b4ae09..b41e6ffca9d6 100644
--- a/sys/mips/conf/BERI_DE4_SDROOT
+++ b/sys/mips/conf/BERI_DE4_SDROOT
@@ -18,4 +18,5 @@ device altera_avgen
device altera_jtag_uart
device altera_sdcard
+device isf
#device sc