From 4e9d4cca8619ad145038e97d64c0265398aa5d24 Mon Sep 17 00:00:00 2001 From: rwatson Date: Sat, 25 Aug 2012 11:19:20 +0000 Subject: 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 --- sys/dev/altera/sdcard/altera_sdcard.c | 402 +++++++++++++++++++++++++ sys/dev/altera/sdcard/altera_sdcard.h | 247 +++++++++++++++ sys/dev/altera/sdcard/altera_sdcard_disk.c | 185 ++++++++++++ sys/dev/altera/sdcard/altera_sdcard_io.c | 447 ++++++++++++++++++++++++++++ sys/dev/altera/sdcard/altera_sdcard_nexus.c | 116 ++++++++ 5 files changed, 1397 insertions(+) create mode 100644 sys/dev/altera/sdcard/altera_sdcard.c create mode 100644 sys/dev/altera/sdcard/altera_sdcard.h create mode 100644 sys/dev/altera/sdcard/altera_sdcard_disk.c create mode 100644 sys/dev/altera/sdcard/altera_sdcard_io.c create mode 100644 sys/dev/altera/sdcard/altera_sdcard_nexus.c (limited to 'sys/dev/altera/sdcard') 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +/* + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +/* + * 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); -- cgit v1.1