summaryrefslogtreecommitdiffstats
path: root/sys/dev/altera
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2012-08-25 11:19:20 +0000
committerrwatson <rwatson@FreeBSD.org>2012-08-25 11:19:20 +0000
commit4e9d4cca8619ad145038e97d64c0265398aa5d24 (patch)
tree91e0a4a0eef199b9e535844b58bff1840822f03e /sys/dev/altera
parent57bdf1f3160c3bad78968872120cea0e37a94bf1 (diff)
downloadFreeBSD-src-4e9d4cca8619ad145038e97d64c0265398aa5d24.zip
FreeBSD-src-4e9d4cca8619ad145038e97d64c0265398aa5d24.tar.gz
Add a device driver for the Altera University Program SD Card IP Core,
which can be synthesised in Altera FPGAs. An altera_sdcardc device probes during the boot, and /dev/altera_sdcard devices come and go as inserted and removed. The device driver attaches directly to the Nexus, as is common for system-on-chip device drivers. This IP core suffers a number of significant limitations, including a lack of interrupt-driven I/O -- we must implement timer-driven polling, only CSD 0 cards (up to 2G) are supported, there are serious memory access issues that require the driver to verify writes to memory-mapped buffers, undocumented alignment requirements, and erroneous error returns. The driver must therefore work quite hard, despite a fairly simple hardware-software interface. The IP core also supports at most one outstanding I/O at a time, so is not a speed demon. However, with the above workarounds, and subject to performance problems, it works quite reliably in practice, and we can use it for read-write mounts of root file systems, etc. Sponsored by: DARPA, AFRL
Diffstat (limited to 'sys/dev/altera')
-rw-r--r--sys/dev/altera/sdcard/altera_sdcard.c402
-rw-r--r--sys/dev/altera/sdcard/altera_sdcard.h247
-rw-r--r--sys/dev/altera/sdcard/altera_sdcard_disk.c185
-rw-r--r--sys/dev/altera/sdcard/altera_sdcard_io.c447
-rw-r--r--sys/dev/altera/sdcard/altera_sdcard_nexus.c116
5 files changed, 1397 insertions, 0 deletions
diff --git a/sys/dev/altera/sdcard/altera_sdcard.c b/sys/dev/altera/sdcard/altera_sdcard.c
new file mode 100644
index 0000000..4572206
--- /dev/null
+++ b/sys/dev/altera/sdcard/altera_sdcard.c
@@ -0,0 +1,402 @@
+/*-
+ * Copyright (c) 2012 Robert N. M. Watson
+ * 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/altera/sdcard/altera_sdcard.h>
+
+/*
+ * Device driver for the Altera University Program Secure Data Card IP Core,
+ * as described in the similarly named SOPC Builder IP Core specification.
+ * This soft core is not a full SD host controller interface (SDHCI) but
+ * instead provides a set of memory mapped registers and memory buffer that
+ * mildly abstract the SD Card protocol, but without providing DMA or
+ * interrupts. However, it does hide the details of voltage and
+ * communications negotiation. This driver implements disk(9), but due to the
+ * lack of interrupt support, must rely on timer-driven polling to determine
+ * when I/Os have completed.
+ *
+ * TODO:
+ *
+ * 1. Implement DISKFLAG_CANDELETE / SD Card sector erase support.
+ * 2. Implement d_ident from SD Card CID serial number field.
+ * 3. Handle read-only SD Cards.
+ * 4. Tune timeouts based on real-world SD Card speeds.
+ */
+
+void
+altera_sdcard_attach(struct altera_sdcard_softc *sc)
+{
+
+ ALTERA_SDCARD_LOCK_INIT(sc);
+ ALTERA_SDCARD_CONDVAR_INIT(sc);
+ sc->as_disk = NULL;
+ bioq_init(&sc->as_bioq);
+ sc->as_currentbio = NULL;
+ sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
+ sc->as_taskqueue = taskqueue_create("altera_sdcardc taskq", M_WAITOK,
+ taskqueue_thread_enqueue, &sc->as_taskqueue);
+ taskqueue_start_threads(&sc->as_taskqueue, 1, PI_DISK,
+ "altera_sdcardc%d taskqueue", sc->as_unit);
+ TIMEOUT_TASK_INIT(sc->as_taskqueue, &sc->as_task, 0,
+ altera_sdcard_task, sc);
+
+ /*
+ * Kick off timer-driven processing with a manual poll so that we
+ * synchronously detect an already-inserted SD Card during the boot or
+ * other driver attach point.
+ */
+ altera_sdcard_task(sc, 1);
+}
+
+void
+altera_sdcard_detach(struct altera_sdcard_softc *sc)
+{
+
+ KASSERT(sc->as_taskqueue != NULL, ("%s: taskqueue not present",
+ __func__));
+
+ /*
+ * Winding down the driver on detach is a bit complex. Update the
+ * flags to indicate that a detach has been requested, and then wait
+ * for in-progress I/O to wind down before continuing.
+ */
+ ALTERA_SDCARD_LOCK(sc);
+ sc->as_flags |= ALTERA_SDCARD_FLAG_DETACHREQ;
+ while (sc->as_state != ALTERA_SDCARD_STATE_DETACHED)
+ ALTERA_SDCARD_CONDVAR_WAIT(sc);
+ ALTERA_SDCARD_UNLOCK(sc);
+
+ /*
+ * Now wait for the possibly still executing taskqueue to drain. In
+ * principle no more events will be scheduled as we've transitioned to
+ * a detached state, but there might still be a request in execution.
+ */
+ while (taskqueue_cancel_timeout(sc->as_taskqueue, &sc->as_task, NULL))
+ taskqueue_drain_timeout(sc->as_taskqueue, &sc->as_task);
+
+ /*
+ * Simulate a disk removal if one is present to deal with any pending
+ * or queued I/O.
+ */
+ if (sc->as_disk != NULL)
+ altera_sdcard_disk_remove(sc);
+ KASSERT(bioq_first(&sc->as_bioq) == NULL,
+ ("%s: non-empty bioq", __func__));
+
+ /*
+ * Free any remaining allocated resources.
+ */
+ taskqueue_free(sc->as_taskqueue);
+ sc->as_taskqueue = NULL;
+ ALTERA_SDCARD_CONDVAR_DESTROY(sc);
+ ALTERA_SDCARD_LOCK_DESTROY(sc);
+}
+
+/*
+ * Set up and start the next I/O. Transition to the I/O state, but allow the
+ * caller to schedule the next timeout, as this may be called either from an
+ * initial attach context, or from the task queue, which requires different
+ * behaviour.
+ */
+static void
+altera_sdcard_nextio(struct altera_sdcard_softc *sc)
+{
+ struct bio *bp;
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+ KASSERT(sc->as_currentbio == NULL,
+ ("%s: bio already active", __func__));
+
+ bp = bioq_takefirst(&sc->as_bioq);
+ if (bp == NULL)
+ panic("%s: bioq empty", __func__);
+ altera_sdcard_io_start(sc, bp);
+ sc->as_state = ALTERA_SDCARD_STATE_IO;
+}
+
+static void
+altera_sdcard_task_nocard(struct altera_sdcard_softc *sc)
+{
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+
+ /*
+ * Handle device driver detach.
+ */
+ if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
+ sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
+ return;
+ }
+
+ /*
+ * If there is no card insertion, remain in NOCARD.
+ */
+ if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT))
+ return;
+
+ /*
+ * Read the CSD -- it may contain values that the driver can't handle,
+ * either because of an unsupported version/feature, or because the
+ * card is misbehaving. This triggers a transition to
+ * ALTERA_SDCARD_STATE_BADCARD. We rely on the CSD read to print a
+ * banner about how the card is problematic, since it has more
+ * information. The bad card state allows us to print that banner
+ * once rather than each time we notice the card is there, and still
+ * bad.
+ */
+ if (altera_sdcard_read_csd(sc) != 0) {
+ sc->as_state = ALTERA_SDCARD_STATE_BADCARD;
+ return;
+ }
+
+ /*
+ * Process card insertion and upgrade to the IDLE state.
+ */
+ altera_sdcard_disk_insert(sc);
+ sc->as_state = ALTERA_SDCARD_STATE_IDLE;
+}
+
+static void
+altera_sdcard_task_badcard(struct altera_sdcard_softc *sc)
+{
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+
+ /*
+ * Handle device driver detach.
+ */
+ if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
+ sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
+ return;
+ }
+
+ /*
+ * Handle safe card removal -- no teardown is required, just a state
+ * transition.
+ */
+ if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT))
+ sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
+}
+
+static void
+altera_sdcard_task_idle(struct altera_sdcard_softc *sc)
+{
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+
+ /*
+ * Handle device driver detach.
+ */
+ if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
+ sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
+ return;
+ }
+
+ /*
+ * Handle safe card removal.
+ */
+ if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT)) {
+ altera_sdcard_disk_remove(sc);
+ sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
+ }
+}
+
+static void
+altera_sdcard_task_io(struct altera_sdcard_softc *sc)
+{
+ uint16_t asr;
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+ KASSERT(sc->as_currentbio != NULL, ("%s: no current I/O", __func__));
+
+ asr = altera_sdcard_read_asr(sc);
+
+ /*
+ * Check for unexpected card removal during an I/O.
+ */
+ if (!(asr & ALTERA_SDCARD_ASR_CARDPRESENT)) {
+ altera_sdcard_disk_remove(sc);
+ if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ)
+ sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
+ else
+ sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
+ return;
+ }
+
+ /*
+ * If the I/O isn't complete, remain in the IO state without further
+ * action, even if DETACHREQ is in flight.
+ */
+ if (asr & ALTERA_SDCARD_ASR_CMDINPROGRESS)
+ return;
+
+ /*
+ * Handle various forms of I/O completion, successful and otherwise.
+ * The I/O layer may restart the transaction if an error occurred, in
+ * which case remain in the IO state and reschedule.
+ */
+ if (!altera_sdcard_io_complete(sc, asr))
+ return;
+
+ /*
+ * Now that I/O is complete, process detach requests in preference to
+ * starting new I/O.
+ */
+ if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
+ sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
+ return;
+ }
+
+ /*
+ * Finally, either start the next I/O or transition to the IDLE state.
+ */
+ if (bioq_first(&sc->as_bioq) != NULL)
+ altera_sdcard_nextio(sc);
+ else
+ sc->as_state = ALTERA_SDCARD_STATE_IDLE;
+}
+
+static void
+altera_sdcard_task_rechedule(struct altera_sdcard_softc *sc)
+{
+ int interval;
+
+ /*
+ * Reschedule based on new state. Or not, if detaching the device
+ * driver. Treat a bad card as though it were no card at all.
+ */
+ switch (sc->as_state) {
+ case ALTERA_SDCARD_STATE_NOCARD:
+ case ALTERA_SDCARD_STATE_BADCARD:
+ interval = ALTERA_SDCARD_TIMEOUT_NOCARD;
+ break;
+
+ case ALTERA_SDCARD_STATE_IDLE:
+ interval = ALTERA_SDCARD_TIMEOUT_IDLE;
+ break;
+
+ case ALTERA_SDCARD_STATE_IO:
+ if (sc->as_flags & ALTERA_SDCARD_FLAG_IOERROR)
+ interval = ALTERA_SDCARD_TIMEOUT_IOERROR;
+ else
+ interval = ALTERA_SDCARD_TIMEOUT_IO;
+ break;
+
+ default:
+ panic("%s: invalid exit state %d", __func__, sc->as_state);
+ }
+ taskqueue_enqueue_timeout(sc->as_taskqueue, &sc->as_task, interval);
+}
+
+/*
+ * Because the Altera SD Card IP Core doesn't support interrupts, we do all
+ * asynchronous work from a timeout. Poll at two different rates -- an
+ * infrequent check for card insertion status changes, and a frequent one for
+ * I/O completion. The task should never start in DETACHED, as that would
+ * imply that a previous instance failed to cancel rather than reschedule.
+ */
+void
+altera_sdcard_task(void *arg, int pending)
+{
+ struct altera_sdcard_softc *sc;
+
+ sc = arg;
+ KASSERT(sc->as_state != ALTERA_SDCARD_STATE_DETACHED,
+ ("%s: already in detached", __func__));
+
+ ALTERA_SDCARD_LOCK(sc);
+ switch (sc->as_state) {
+ case ALTERA_SDCARD_STATE_NOCARD:
+ altera_sdcard_task_nocard(sc);
+ break;
+
+ case ALTERA_SDCARD_STATE_BADCARD:
+ altera_sdcard_task_badcard(sc);
+ break;
+
+ case ALTERA_SDCARD_STATE_IDLE:
+ altera_sdcard_task_idle(sc);
+ break;
+
+ case ALTERA_SDCARD_STATE_IO:
+ altera_sdcard_task_io(sc);
+ break;
+
+ default:
+ panic("%s: invalid enter state %d", __func__, sc->as_state);
+ }
+
+ /*
+ * If we have transitioned to DETACHED, signal the detach thread and
+ * cancel the timeout-driven task. Otherwise reschedule on an
+ * appropriate timeout.
+ */
+ if (sc->as_state == ALTERA_SDCARD_STATE_DETACHED)
+ ALTERA_SDCARD_CONDVAR_SIGNAL(sc);
+ else
+ altera_sdcard_task_rechedule(sc);
+ ALTERA_SDCARD_UNLOCK(sc);
+}
+
+void
+altera_sdcard_start(struct altera_sdcard_softc *sc)
+{
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+
+ KASSERT(sc->as_state == ALTERA_SDCARD_STATE_IDLE,
+ ("%s: starting when not IDLE", __func__));
+
+ taskqueue_cancel_timeout(sc->as_taskqueue, &sc->as_task, NULL);
+ altera_sdcard_nextio(sc);
+ taskqueue_enqueue_timeout(sc->as_taskqueue, &sc->as_task,
+ ALTERA_SDCARD_TIMEOUT_IO);
+}
diff --git a/sys/dev/altera/sdcard/altera_sdcard.h b/sys/dev/altera/sdcard/altera_sdcard.h
new file mode 100644
index 0000000..4a491c4
--- /dev/null
+++ b/sys/dev/altera/sdcard/altera_sdcard.h
@@ -0,0 +1,247 @@
+/*-
+ * Copyright (c) 2012 Robert N. M. Watson
+ * 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_ALTERA_SDCARD_H_
+#define _DEV_ALTERA_SDCARD_H_
+
+#define ALTERA_SDCARD_CSD_SIZE 16
+struct altera_sdcard_csd {
+ uint8_t csd_data[ALTERA_SDCARD_CSD_SIZE];
+} __aligned(2); /* CSD is read in 16-bit chunks, so align to match. */
+
+struct altera_sdcard_softc {
+ device_t as_dev;
+ int as_unit;
+ struct resource *as_res;
+ int as_rid;
+ struct mtx as_lock;
+ struct cv as_condvar;
+ int as_state;
+ int as_flags;
+ struct disk *as_disk;
+ struct taskqueue *as_taskqueue;
+ struct timeout_task as_task;
+
+ /*
+ * Fields relating to in-progress and pending I/O, if any.
+ */
+ struct bio_queue_head as_bioq;
+ struct bio *as_currentbio;
+ u_int as_retriesleft;
+
+ /*
+ * Infrequently changing fields cached from the SD Card IP Core.
+ */
+ struct altera_sdcard_csd as_csd;
+ uint8_t as_csd_structure; /* CSD version. */
+ uint64_t as_mediasize;
+};
+
+#define ALTERA_SDCARD_LOCK(sc) mtx_lock(&(sc)->as_lock)
+#define ALTERA_SDCARD_LOCK_ASSERT(sc) mtx_assert(&(sc)->as_lock, MA_OWNED)
+#define ALTERA_SDCARD_LOCK_DESTROY(sc) mtx_destroy(&(sc)->as_lock)
+#define ALTERA_SDCARD_LOCK_INIT(sc) mtx_init(&(sc)->as_lock, \
+ "altera_sdcard", NULL, MTX_DEF)
+#define ALTERA_SDCARD_UNLOCK(sc) mtx_unlock(&(sc)->as_lock)
+
+#define ALTERA_SDCARD_CONDVAR_DESTROY(sc) cv_destroy(&(sc)->as_condvar)
+#define ALTERA_SDCARD_CONDVAR_INIT(sc) cv_init(&(sc)->as_condvar, \
+ "altera_sdcard_detach_wait")
+#define ALTERA_SDCARD_CONDVAR_SIGNAL(dc) cv_signal(&(sc)->as_condvar)
+#define ALTERA_SDCARD_CONDVAR_WAIT(sc) cv_wait(&(sc)->as_condvar, \
+ &(sc)->as_lock)
+
+/*
+ * States an instance can be in at any given moment.
+ */
+#define ALTERA_SDCARD_STATE_NOCARD 1 /* No card inserted. */
+#define ALTERA_SDCARD_STATE_BADCARD 2 /* Card bad/not supported. */
+#define ALTERA_SDCARD_STATE_IDLE 3 /* Card present but idle. */
+#define ALTERA_SDCARD_STATE_IO 4 /* Card in I/O currently. */
+#define ALTERA_SDCARD_STATE_DETACHED 5 /* Driver is detaching. */
+
+/*
+ * Different timeout intervals based on state. When just looking for a card
+ * status change, check twice a second. When we're actively waiting on I/O
+ * completion, check every millisecond.
+ */
+#define ALTERA_SDCARD_TIMEOUT_NOCARD (hz/2)
+#define ALTERA_SDCARD_TIMEOUT_IDLE (hz/2)
+#define ALTERA_SDCARD_TIMEOUT_IO (1)
+#define ALTERA_SDCARD_TIMEOUT_IOERROR (hz/5)
+
+/*
+ * Maximum number of retries on an I/O.
+ */
+#define ALTERA_SDCARD_RETRY_LIMIT 10
+
+/*
+ * Driver status flags.
+ */
+#define ALTERA_SDCARD_FLAG_DETACHREQ 0x00000001 /* Detach requested. */
+#define ALTERA_SDCARD_FLAG_IOERROR 0x00000002 /* Error in progress. */
+
+/*
+ * Functions for performing low-level register and memory I/O to/from the SD
+ * Card IP Core. In general, only code in altera_sdcard_io.c is aware of the
+ * hardware interface.
+ */
+uint16_t altera_sdcard_read_asr(struct altera_sdcard_softc *sc);
+int altera_sdcard_read_csd(struct altera_sdcard_softc *sc);
+
+int altera_sdcard_io_complete(struct altera_sdcard_softc *sc,
+ uint16_t asr);
+void altera_sdcard_io_start(struct altera_sdcard_softc *sc,
+ struct bio *bp);
+
+/*
+ * Constants for interpreting the SD Card Card Specific Data (CSD) register.
+ */
+#define ALTERA_SDCARD_CSD_STRUCTURE_BYTE 15
+#define ALTERA_SDCARD_CSD_STRUCTURE_MASK 0xc0 /* 2 bits */
+#define ALTERA_SDCARD_CSD_STRUCTURE_RSHIFT 6
+
+#define ALTERA_SDCARD_CSD_READ_BL_LEN_BYTE 10
+#define ALTERA_SDCARD_CSD_READ_BL_LEN_MASK 0x0f /* 4 bits */
+
+/*
+ * C_SIZE is a 12-bit field helpfully split over three differe bytes of CSD
+ * data. Software ease of use was not a design consideration.
+ */
+#define ALTERA_SDCARD_CSD_C_SIZE_BYTE0 7
+#define ALTERA_SDCARD_CSD_C_SIZE_MASK0 0xc /* top 2 bits */
+#define ALTERA_SDCARD_CSD_C_SIZE_RSHIFT0 6
+
+#define ALTERA_SDCARD_CSD_C_SIZE_BYTE1 8
+#define ALTERA_SDCARD_CSD_C_SIZE_MASK1 0xff /* 8 bits */
+#define ALTERA_SDCARD_CSD_C_SIZE_LSHIFT1 2
+
+#define ALTERA_SDCARD_CSD_C_SIZE_BYTE2 9
+#define ALTERA_SDCARD_CSD_C_SIZE_MASK2 0x03 /* bottom 2 bits */
+#define ALTERA_SDCARD_CSD_C_SIZE_LSHIFT2 10
+
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE0 5
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK0 0x80 /* top 1 bit */
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_RSHIFT0 7
+
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE1 6
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK1 0x03 /* bottom 2 bits */
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_LSHIFT1 1
+
+/*
+ * I/O register/buffer offsets, from Table 4.1.1 in the Altera University
+ * Program SD Card IP Core specification.
+ */
+#define ALTERA_SDCARD_OFF_RXTX_BUFFER 0 /* 512-byte I/O buffer */
+#define ALTERA_SDCARD_OFF_CID 512 /* 16-byte Card ID number */
+#define ALTERA_SDCARD_OFF_CSD 528 /* 16-byte Card Specific Data */
+#define ALTERA_SDCARD_OFF_OCR 544 /* Operating Conditions Reg */
+#define ALTERA_SDCARD_OFF_SR 548 /* SD Card Status Register */
+#define ALTERA_SDCARD_OFF_RCA 552 /* Relative Card Address Reg */
+#define ALTERA_SDCARD_OFF_CMD_ARG 556 /* Command Argument Register */
+#define ALTERA_SDCARD_OFF_CMD 560 /* Command Register */
+#define ALTERA_SDCARD_OFF_ASR 564 /* Auxiliary Status Register */
+#define ALTERA_SDCARD_OFF_RR1 568 /* Response R1 */
+
+/*
+ * The Altera IP Core provides a 16-bit "Additional Status Register" (ASR)
+ * beyond those described in the SD Card specification that captures IP Core
+ * transaction state, such as whether the last command is in progress, the
+ * card has been removed, etc.
+ */
+#define ALTERA_SDCARD_ASR_CMDVALID 0x0001
+#define ALTERA_SDCARD_ASR_CARDPRESENT 0x0002
+#define ALTERA_SDCARD_ASR_CMDINPROGRESS 0x0004
+#define ALTERA_SDCARD_ASR_SRVALID 0x0008
+#define ALTERA_SDCARD_ASR_CMDTIMEOUT 0x0010
+#define ALTERA_SDCARD_ASR_CMDDATAERROR 0x0020
+
+/*
+ * The Altera IP Core claims to provide a 16-bit "Response R1" register (RR1)
+ * to provide more detailed error reporting when a read or write fails.
+ *
+ * XXXRW: The specification claims that this field is 16-bit, but then
+ * proceeds to define values as though it is 32-bit. In practice, 16-bit
+ * seems more likely as the register is not 32-bit aligned.
+ */
+#define ALTERA_SDCARD_RR1_INITPROCRUNNING 0x0100
+#define ALTERA_SDCARD_RR1_ERASEINTERRUPTED 0x0200
+#define ALTERA_SDCARD_RR1_ILLEGALCOMMAND 0x0400
+#define ALTERA_SDCARD_RR1_COMMANDCRCFAILED 0x0800
+#define ALTERA_SDCARD_RR1_ADDRESSMISALIGNED 0x1000
+#define ALTERA_SDCARD_RR1_ADDRBLOCKRANGE 0x2000
+
+/*
+ * Not all RR1 values are "errors" per se -- check only for the ones that are
+ * when performing error handling.
+ */
+#define ALTERA_SDCARD_RR1_ERRORMASK \
+ (ALTERA_SDCARD_RR1_ERASEINTERRUPTED | ALTERA_SDCARD_RR1_ILLEGALCOMMAND | \
+ ALTERA_SDCARD_RR1_COMMANDCRCFAILED | ALTERA_SDCARD_RR1_ADDRESSMISALIGNED |\
+ ALTERA_SDCARD_RR1_ADDRBLOCKRANGE)
+
+/*
+ * Although SD Cards may have various sector sizes, the Altera IP Core
+ * requires that I/O be done in 512-byte chunks.
+ */
+#define ALTERA_SDCARD_SECTORSIZE 512
+
+/*
+ * SD Card commands used in this driver.
+ */
+#define ALTERA_SDCARD_CMD_SEND_RCA 0x03 /* Retrieve card RCA. */
+#define ALTERA_SDCARD_CMD_SEND_CSD 0x09 /* Retrieve CSD register. */
+#define ALTERA_SDCARD_CMD_SEND_CID 0x0A /* Retrieve CID register. */
+#define ALTERA_SDCARD_CMD_READ_BLOCK 0x11 /* Read block from disk. */
+#define ALTERA_SDCARD_CMD_WRITE_BLOCK 0x18 /* Write block to disk. */
+
+/*
+ * Functions exposed by the device driver core to newbus(9) bus attachment
+ * implementations.
+ */
+void altera_sdcard_attach(struct altera_sdcard_softc *sc);
+void altera_sdcard_detach(struct altera_sdcard_softc *sc);
+void altera_sdcard_task(void *arg, int pending);
+
+/*
+ * Functions exposed by the device driver core to the disk(9) front-end.
+ */
+void altera_sdcard_start(struct altera_sdcard_softc *sc);
+
+/*
+ * Functions relating to the implementation of disk(9) KPIs for the SD Card
+ * driver.
+ */
+void altera_sdcard_disk_insert(struct altera_sdcard_softc *sc);
+void altera_sdcard_disk_remove(struct altera_sdcard_softc *sc);
+
+#endif /* _DEV_ALTERA_SDCARD_H_ */
diff --git a/sys/dev/altera/sdcard/altera_sdcard_disk.c b/sys/dev/altera/sdcard/altera_sdcard_disk.c
new file mode 100644
index 0000000..cae3a82
--- /dev/null
+++ b/sys/dev/altera/sdcard/altera_sdcard_disk.c
@@ -0,0 +1,185 @@
+/*-
+ * Copyright (c) 2012 Robert N. M. Watson
+ * 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/altera/sdcard/altera_sdcard.h>
+
+static int
+altera_sdcard_disk_dump(void *arg, void *virtual, vm_offset_t physical,
+ off_t offset, size_t length)
+{
+
+ panic("%s: not yet", __func__);
+}
+
+static int
+altera_sdcard_disk_ioctl(struct disk *disk, u_long cmd, void *data, int fflag,
+ struct thread *td)
+{
+
+ /* XXXRW: more here? */
+ return (EINVAL);
+}
+
+static void
+altera_sdcard_disk_strategy(struct bio *bp)
+{
+ struct altera_sdcard_softc *sc;
+
+ /*
+ * Although the SD Card doesn't need sorting, we don't want to
+ * introduce barriers, so use bioq_disksort().
+ */
+ sc = bp->bio_disk->d_drv1;
+ ALTERA_SDCARD_LOCK(sc);
+ switch (sc->as_state) {
+ case ALTERA_SDCARD_STATE_NOCARD:
+ device_printf(sc->as_dev, "%s: unexpected I/O on NOCARD",
+ __func__);
+ biofinish(bp, NULL, ENXIO);
+ break;
+
+ case ALTERA_SDCARD_STATE_BADCARD:
+ device_printf(sc->as_dev, "%s: unexpected I/O on BADCARD",
+ __func__);
+ biofinish(bp, NULL, ENXIO);
+ break;
+
+ case ALTERA_SDCARD_STATE_DETACHED:
+ device_printf(sc->as_dev, "%s: unexpected I/O on DETACHED",
+ __func__);
+ biofinish(bp, NULL, ENXIO);
+
+ case ALTERA_SDCARD_STATE_IDLE:
+ bioq_disksort(&sc->as_bioq, bp);
+ altera_sdcard_start(sc);
+ break;
+
+ case ALTERA_SDCARD_STATE_IO:
+ bioq_disksort(&sc->as_bioq, bp);
+ break;
+
+ default:
+ panic("%s: invalid state %d", __func__, sc->as_state);
+ }
+ ALTERA_SDCARD_UNLOCK(sc);
+}
+
+void
+altera_sdcard_disk_insert(struct altera_sdcard_softc *sc)
+{
+ struct disk *disk;
+ uint64_t size;
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+
+ /*
+ * Because the disk insertion routine occupies the driver instance's
+ * task queue thread, and the disk(9) instance isn't hooked up yet by
+ * definition, the only other source of events of concern is a thread
+ * initiating driver detach. That thread has to issue a detach
+ * request and await an ACK from the taskqueue thread. It is
+ * therefore safe to drop the lock here.
+ */
+ ALTERA_SDCARD_UNLOCK(sc);
+ disk = disk_alloc();
+ disk->d_drv1 = sc;
+ disk->d_name = "altera_sdcard";
+ disk->d_unit = sc->as_unit;
+ disk->d_strategy = altera_sdcard_disk_strategy;
+ disk->d_dump = altera_sdcard_disk_dump;
+ disk->d_ioctl = altera_sdcard_disk_ioctl;
+ disk->d_sectorsize = ALTERA_SDCARD_SECTORSIZE;
+ disk->d_mediasize = sc->as_mediasize;
+ disk->d_maxsize = ALTERA_SDCARD_SECTORSIZE;
+ sc->as_disk = disk;
+ disk_create(disk, DISK_VERSION);
+ ALTERA_SDCARD_LOCK(sc);
+
+ /*
+ * Print a pretty-ish card insertion string. We could stand to
+ * decorate this further, e.g., with card vendor information.
+ */
+ size = sc->as_mediasize / (1000 * 1000);
+ device_printf(sc->as_dev, "%juM SD Card inserted\n", (uintmax_t)size);
+}
+
+void
+altera_sdcard_disk_remove(struct altera_sdcard_softc *sc)
+{
+ struct disk *disk;
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+ KASSERT(sc->as_disk != NULL, ("%s: as_disk NULL", __func__));
+
+ /*
+ * sc->as_state will be updated by the caller.
+ *
+ * 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->as_disk;
+ disk_gone(disk);
+ disk_destroy(disk);
+ sc->as_disk = NULL;
+
+ /*
+ * Cancel all outstanding I/O on the SD Card.
+ */
+ if (sc->as_currentbio != NULL) {
+ device_printf(sc->as_dev, "%s: SD Card removed during I/O",
+ __func__);
+ biofinish(sc->as_currentbio, NULL, ENXIO);
+ sc->as_currentbio = NULL;
+ }
+ bioq_flush(&sc->as_bioq, NULL, ENXIO);
+ device_printf(sc->as_dev, "SD Card removed\n");
+}
diff --git a/sys/dev/altera/sdcard/altera_sdcard_io.c b/sys/dev/altera/sdcard/altera_sdcard_io.c
new file mode 100644
index 0000000..adbd5d1
--- /dev/null
+++ b/sys/dev/altera/sdcard/altera_sdcard_io.c
@@ -0,0 +1,447 @@
+/*-
+ * Copyright (c) 2012 Robert N. M. Watson
+ * 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/endian.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/altera/sdcard/altera_sdcard.h>
+
+int altera_sdcard_ignore_crc_errors = 1;
+int altera_sdcard_verify_rxtx_writes = 1;
+
+/*
+ * Low-level I/O routines for the Altera SD Card University IP Core driver.
+ *
+ * XXXRW: Throughout, it is assumed that the IP Core handles multibyte
+ * registers as little endian, as is the case for other Altera IP cores.
+ * However, the specification makes no reference to endianness, so this
+ * assumption might not always be correct.
+ */
+uint16_t
+altera_sdcard_read_asr(struct altera_sdcard_softc *sc)
+{
+
+ return (le16toh(bus_read_2(sc->as_res, ALTERA_SDCARD_OFF_ASR)));
+}
+
+static int
+altera_sdcard_process_csd0(struct altera_sdcard_softc *sc)
+{
+ uint64_t c_size, c_size_mult, read_bl_len;
+ uint8_t byte0, byte1, byte2;
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+
+ /*-
+ * Compute card capacity per SD Card interface description as follows:
+ *
+ * Memory capacity = BLOCKNR * BLOCK_LEN
+ *
+ * Where:
+ *
+ * BLOCKNR = (C_SIZE + 1) * MULT
+ * MULT = 2^(C_SIZE_MULT+2)
+ * BLOCK_LEN = 2^READ_BL_LEN
+ */
+ read_bl_len = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_READ_BL_LEN_BYTE];
+ read_bl_len &= ALTERA_SDCARD_CSD_READ_BL_LEN_MASK;
+
+ byte0 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE0];
+ byte0 &= ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK0;
+ byte1 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE1];
+ byte1 &= ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK1;
+ c_size_mult = (byte0 >> ALTERA_SDCARD_CSD_C_SIZE_MULT_RSHIFT0) |
+ (byte0 << ALTERA_SDCARD_CSD_C_SIZE_MULT_LSHIFT1);
+
+ byte0 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_BYTE0];
+ byte0 &= ALTERA_SDCARD_CSD_C_SIZE_MASK0;
+ byte1 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_BYTE1];
+ byte2 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_BYTE2];
+ byte2 &= ALTERA_SDCARD_CSD_C_SIZE_MASK2;
+ c_size = (byte0 >> ALTERA_SDCARD_CSD_C_SIZE_RSHIFT0) |
+ (byte1 << ALTERA_SDCARD_CSD_C_SIZE_LSHIFT1) |
+ (byte2 << ALTERA_SDCARD_CSD_C_SIZE_LSHIFT2);
+
+ byte0 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE0];
+ byte0 &= ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK0;
+ byte1 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE1];
+ byte1 &= ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK1;
+ c_size_mult = (byte0 >> ALTERA_SDCARD_CSD_C_SIZE_MULT_RSHIFT0) |
+ (byte1 << ALTERA_SDCARD_CSD_C_SIZE_MULT_LSHIFT1);
+
+ /*
+ * If we're just getting back zero's, mark the card as bad, even
+ * though it could just mean a Very Small Disk Indeed.
+ */
+ if (c_size == 0 && c_size_mult == 0 && read_bl_len == 0) {
+ device_printf(sc->as_dev, "Ignored zero-size card\n");
+ return (ENXIO);
+ }
+ sc->as_mediasize = (c_size + 1) * (1 << (c_size_mult + 2)) *
+ (1 << read_bl_len);
+ return (0);
+}
+
+int
+altera_sdcard_read_csd(struct altera_sdcard_softc *sc)
+{
+ uint8_t csd_structure;
+ int error;
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+
+ /*
+ * XXXRW: Assume for now that when the SD Card IP Core negotiates
+ * voltage/speed/etc, it must use the CSD register, and therefore
+ * populates the SD Card IP Core's cache of the register value. This
+ * means that we can read it without issuing further SD Card commands.
+ * If this assumption proves false, we will (a) get back garbage and
+ * (b) need to add additional states in the driver state machine in
+ * order to query card properties before I/O can start.
+ *
+ * XXXRW: Treating this as an array of bytes, so no byte swapping --
+ * is that a safe assumption?
+ */
+ KASSERT(((uintptr_t)&sc->as_csd.csd_data) % 2 == 0,
+ ("%s: CSD buffer unaligned", __func__));
+ bus_read_region_2(sc->as_res, ALTERA_SDCARD_OFF_CSD,
+ (uint16_t *)sc->as_csd.csd_data, sizeof(sc->as_csd) / 2);
+
+ /*
+ * Interpret the loaded CSD, extracting certain fields and copying
+ * them into the softc for easy software access.
+ *
+ * Currently, we support only CSD Version 1.0. If we detect a newer
+ * version, suppress card detection.
+ */
+ csd_structure = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_STRUCTURE_BYTE];
+ csd_structure &= ALTERA_SDCARD_CSD_STRUCTURE_MASK;
+ csd_structure >>= ALTERA_SDCARD_CSD_STRUCTURE_RSHIFT;
+ sc->as_csd_structure = csd_structure;
+
+ /*
+ * Interpret the CSD field based on its version. Extract fields,
+ * especially mediasize.
+ *
+ * XXXRW: Desirable to support further CSD versions here.
+ */
+ switch (sc->as_csd_structure) {
+ case 0:
+ error = altera_sdcard_process_csd0(sc);
+ if (error)
+ return (error);
+ break;
+
+ default:
+ device_printf(sc->as_dev,
+ "Ignored disk with unsupported CSD structure (%d)\n",
+ sc->as_csd_structure);
+ return (ENXIO);
+ }
+ return (0);
+}
+
+/*
+ * XXXRW: The Altera IP Core specification indicates that RR1 is a 16-bit
+ * register, but all bits it identifies are >16 bit. Most likely, RR1 is a
+ * 32-bit register?
+ */
+static uint16_t
+altera_sdcard_read_rr1(struct altera_sdcard_softc *sc)
+{
+
+ return (le16toh(bus_read_2(sc->as_res, ALTERA_SDCARD_OFF_RR1)));
+}
+
+static void
+altera_sdcard_write_cmd_arg(struct altera_sdcard_softc *sc, uint32_t cmd_arg)
+{
+
+ bus_write_4(sc->as_res, ALTERA_SDCARD_OFF_CMD_ARG, htole32(cmd_arg));
+}
+
+static void
+altera_sdcard_write_cmd(struct altera_sdcard_softc *sc, uint16_t cmd)
+{
+
+ bus_write_2(sc->as_res, ALTERA_SDCARD_OFF_CMD, htole16(cmd));
+}
+
+static void
+altera_sdcard_read_rxtx_buffer(struct altera_sdcard_softc *sc, void *data,
+ size_t len)
+{
+
+ KASSERT((uintptr_t)data % 2 == 0,
+ ("%s: unaligned data %p", __func__, data));
+ KASSERT((len <= ALTERA_SDCARD_SECTORSIZE) && (len % 2 == 0),
+ ("%s: invalid length %ju", __func__, len));
+
+ bus_read_region_2(sc->as_res, ALTERA_SDCARD_OFF_RXTX_BUFFER,
+ (uint16_t *)data, len / 2);
+}
+
+static void
+altera_sdcard_write_rxtx_buffer(struct altera_sdcard_softc *sc, void *data,
+ size_t len)
+{
+ u_int corrections, differences, i, retry_counter;
+ uint16_t d, v;
+
+ KASSERT((uintptr_t)data % 2 == 0,
+ ("%s: unaligned data %p", __func__, data));
+ KASSERT((len <= ALTERA_SDCARD_SECTORSIZE) && (len % 2 == 0),
+ ("%s: invalid length %ju", __func__, len));
+
+ retry_counter = 0;
+ do {
+ bus_write_region_2(sc->as_res, ALTERA_SDCARD_OFF_RXTX_BUFFER,
+ (uint16_t *)data, len / 2);
+
+ /*
+ * XXXRW: Due to a possible hardware bug, the above call to
+ * bus_write_region_2() might not succeed. If the workaround
+ * is enabled, verify each write and retry until it succeeds.
+ *
+ * XXXRW: Do we want a limit counter for retries here?
+ */
+recheck:
+ corrections = 0;
+ differences = 0;
+ if (altera_sdcard_verify_rxtx_writes) {
+ for (i = 0; i < ALTERA_SDCARD_SECTORSIZE; i += 2) {
+ v = bus_read_2(sc->as_res,
+ ALTERA_SDCARD_OFF_RXTX_BUFFER + i);
+ d = *(uint16_t *)((uint8_t *)data + i);
+ if (v != d) {
+ if (retry_counter == 0) {
+ bus_write_2(sc->as_res,
+ ALTERA_SDCARD_OFF_RXTX_BUFFER + i,
+ d);
+ v = bus_read_2(sc->as_res,
+ ALTERA_SDCARD_OFF_RXTX_BUFFER + i);
+ if (v == d) {
+ corrections++;
+ device_printf(sc->as_dev,
+ "%s: single word rewrite worked"
+ " at offset %u\n",
+ __func__, i);
+ continue;
+ }
+ }
+ differences++;
+ device_printf(sc->as_dev,
+ "%s: retrying write -- difference"
+ " %u at offset %u, retry %u\n",
+ __func__, differences, i,
+ retry_counter);
+ }
+ }
+ if (differences != 0) {
+ retry_counter++;
+ if (retry_counter == 1 &&
+ corrections == differences)
+ goto recheck;
+ }
+ }
+ } while (differences != 0);
+ if (retry_counter)
+ device_printf(sc->as_dev, "%s: succeeded after %u retries\n",
+ __func__, retry_counter);
+}
+
+static void
+altera_sdcard_io_start_internal(struct altera_sdcard_softc *sc, struct bio *bp)
+{
+
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ altera_sdcard_write_cmd_arg(sc, bp->bio_pblkno *
+ ALTERA_SDCARD_SECTORSIZE);
+ altera_sdcard_write_cmd(sc, ALTERA_SDCARD_CMD_READ_BLOCK);
+ break;
+
+ case BIO_WRITE:
+ altera_sdcard_write_rxtx_buffer(sc, bp->bio_data,
+ bp->bio_bcount);
+ altera_sdcard_write_cmd_arg(sc, bp->bio_pblkno *
+ ALTERA_SDCARD_SECTORSIZE);
+ altera_sdcard_write_cmd(sc, ALTERA_SDCARD_CMD_WRITE_BLOCK);
+ break;
+
+ default:
+ panic("%s: unsupported I/O operation %d", __func__,
+ bp->bio_cmd);
+ }
+}
+
+void
+altera_sdcard_io_start(struct altera_sdcard_softc *sc, struct bio *bp)
+{
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+ KASSERT(sc->as_currentbio == NULL,
+ ("%s: bio already started", __func__));
+
+ /*
+ * We advertise a block size and maximum I/O size up the stack of the
+ * SD Card IP Core sector size. Catch any attempts to not follow the
+ * rules.
+ */
+ KASSERT(bp->bio_bcount == ALTERA_SDCARD_SECTORSIZE,
+ ("%s: I/O size not %d", __func__, ALTERA_SDCARD_SECTORSIZE));
+ altera_sdcard_io_start_internal(sc, bp);
+ sc->as_currentbio = bp;
+ sc->as_retriesleft = ALTERA_SDCARD_RETRY_LIMIT;
+}
+
+/*
+ * Handle completed I/O. ASR is passed in to avoid reading it more than once.
+ * Return 1 if the I/O is actually complete (success, or retry limit
+ * exceeded), or 0 if not.
+ */
+int
+altera_sdcard_io_complete(struct altera_sdcard_softc *sc, uint16_t asr)
+{
+ struct bio *bp;
+ uint16_t rr1, mask;
+ int error;
+
+ ALTERA_SDCARD_LOCK_ASSERT(sc);
+ KASSERT(!(asr & ALTERA_SDCARD_ASR_CMDINPROGRESS),
+ ("%s: still in progress", __func__));
+ KASSERT(asr & ALTERA_SDCARD_ASR_CARDPRESENT,
+ ("%s: card removed", __func__));
+
+ bp = sc->as_currentbio;
+
+ /*-
+ * Handle I/O retries if an error is returned by the device. Various
+ * quirks handled in the process:
+ *
+ * 1. ALTERA_SDCARD_ASR_CMDDATAERROR is ignored for BIO_WRITE.
+ * 2. ALTERA_SDCARD_RR1_COMMANDCRCFAILED is optionally ignored for
+ * BIO_READ.
+ */
+ error = 0;
+ rr1 = altera_sdcard_read_rr1(sc);
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ mask = ALTERA_SDCARD_RR1_ERRORMASK;
+ if (altera_sdcard_ignore_crc_errors)
+ mask &= ~ALTERA_SDCARD_RR1_COMMANDCRCFAILED;
+ if (asr & ALTERA_SDCARD_ASR_CMDTIMEOUT)
+ error = EIO;
+ else if ((asr & ALTERA_SDCARD_ASR_CMDDATAERROR) &&
+ (rr1 & mask))
+ error = EIO;
+ else
+ error = 0;
+ break;
+
+ case BIO_WRITE:
+ if (asr & ALTERA_SDCARD_ASR_CMDTIMEOUT)
+ error = EIO;
+ else
+ error = 0;
+ break;
+
+ default:
+ break;
+ }
+ if (error) {
+ /*
+ * This attempt experienced an error; possibly retry.
+ */
+ sc->as_retriesleft--;
+ if (sc->as_retriesleft != 0) {
+ sc->as_flags |= ALTERA_SDCARD_FLAG_IOERROR;
+ altera_sdcard_io_start_internal(sc, bp);
+ return (0);
+ }
+ device_printf(sc->as_dev, "%s: %s operation block %ju length "
+ "%ju failed; asr 0x%08x (rr1: 0x%04x)\n", __func__,
+ bp->bio_cmd == BIO_READ ? "BIO_READ" :
+ (bp->bio_cmd == BIO_WRITE ? "BIO_WRITE" : "unknown"),
+ bp->bio_pblkno, bp->bio_bcount, asr, rr1);
+ sc->as_flags &= ~ALTERA_SDCARD_FLAG_IOERROR;
+ } else {
+ /*
+ * Successful I/O completion path.
+ */
+ if (sc->as_flags & ALTERA_SDCARD_FLAG_IOERROR) {
+ device_printf(sc->as_dev, "%s: %s operation block %ju"
+ " length %ju succeeded after %d retries\n",
+ __func__, bp->bio_cmd == BIO_READ ? "BIO_READ" :
+ (bp->bio_cmd == BIO_WRITE ? "write" : "unknown"),
+ bp->bio_pblkno, bp->bio_bcount,
+ ALTERA_SDCARD_RETRY_LIMIT - sc->as_retriesleft);
+ sc->as_flags &= ~ALTERA_SDCARD_FLAG_IOERROR;
+ }
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ altera_sdcard_read_rxtx_buffer(sc, bp->bio_data,
+ bp->bio_bcount);
+ break;
+
+ case BIO_WRITE:
+ break;
+
+ default:
+ panic("%s: unsupported I/O operation %d", __func__,
+ bp->bio_cmd);
+ }
+ bp->bio_resid = 0;
+ error = 0;
+ }
+ biofinish(bp, NULL, error);
+ sc->as_currentbio = NULL;
+ return (1);
+}
diff --git a/sys/dev/altera/sdcard/altera_sdcard_nexus.c b/sys/dev/altera/sdcard/altera_sdcard_nexus.c
new file mode 100644
index 0000000..e0fddcb
--- /dev/null
+++ b/sys/dev/altera/sdcard/altera_sdcard_nexus.c
@@ -0,0 +1,116 @@
+/*-
+ * Copyright (c) 2012 Robert N. M. Watson
+ * 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/altera/sdcard/altera_sdcard.h>
+
+/*
+ * Nexus bus attachment for the Altera SD Card IP core. Appropriate for most
+ * Altera FPGA SoC-style configurations in which the IP core will be exposed
+ * to the processor via a memory-mapped Avalon bus.
+ */
+static int
+altera_sdcard_nexus_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Altera Secure Data Card IP Core");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+altera_sdcard_nexus_attach(device_t dev)
+{
+ struct altera_sdcard_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->as_dev = dev;
+ sc->as_unit = device_get_unit(dev);
+ sc->as_rid = 0;
+ sc->as_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->as_rid, RF_ACTIVE);
+ if (sc->as_res == NULL) {
+ device_printf(dev, "couldn't map memory\n");
+ return (ENXIO);
+ }
+ altera_sdcard_attach(sc);
+ return (0);
+}
+
+static int
+altera_sdcard_nexus_detach(device_t dev)
+{
+ struct altera_sdcard_softc *sc;
+
+ sc = device_get_softc(dev);
+ KASSERT(sc->as_res != NULL, ("%s: resources not allocated",
+ __func__));
+ altera_sdcard_detach(sc);
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->as_rid, sc->as_res);
+ return (0);
+}
+
+static device_method_t altera_sdcard_nexus_methods[] = {
+ DEVMETHOD(device_probe, altera_sdcard_nexus_probe),
+ DEVMETHOD(device_attach, altera_sdcard_nexus_attach),
+ DEVMETHOD(device_detach, altera_sdcard_nexus_detach),
+ { 0, 0 }
+};
+
+static driver_t altera_sdcard_nexus_driver = {
+ "altera_sdcardc",
+ altera_sdcard_nexus_methods,
+ sizeof(struct altera_sdcard_softc),
+};
+
+static devclass_t altera_sdcard_devclass;
+
+DRIVER_MODULE(altera_sdcard, nexus, altera_sdcard_nexus_driver,
+ altera_sdcard_devclass, 0, 0);
OpenPOWER on IntegriCloud