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
This commit is contained in:
Robert Watson 2012-08-25 11:19:20 +00:00
parent cf8248866d
commit d432e92a84
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=239675
8 changed files with 1520 additions and 0 deletions

View file

@ -33,6 +33,7 @@ MAN= aac.4 \
ale.4 \
alpm.4 \
altera_avgen.4 \
altera_sdcard.4 \
altq.4 \
amdpm.4 \
${_amdsbwd.4} \

View file

@ -0,0 +1,118 @@
.\"-
.\" 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$
.\"
.Dd August 18, 2012
.Dt ALTERA_SDCARD 4
.Os
.Sh NAME
.Nm altera_sdcard
.Nd driver for the Altera University Program Secure Data Card IP Core
.Sh SYNOPSIS
.Cd "device altera_sdcard"
.Pp
In
.Pa /boot/device.hints :
.Cd hint.altera_sdcardc.0.at="nexus0"
.Cd hint.altera_sdcardc.0.maddr=0x7f008000
.Cd hint.altera_sdcardc.0.msize=0x400
.Sh DESCRIPTION
The
.Nm
device driver provides support for the Altera University Program Secure Data
Card (SD Card) IP Core device.
A controller device,
.Li altera_sdcardcX ,
will be attached during boot.
Inserted disks are presented as
.Xr disk 9
devices,
.Li altera_sdcardX ,
corresponding to the controller number.
.Sh HARDWARE
The current version of the
.Nm
driver supports the SD Card IP core as described in the August 2011 version of
Altera's documentation.
The core supports only cards up to 2G (CSD 0); larger cards, or cards using
newer CSD versions, will not be detected.
The IP core has two key limitations: a lack of interrupt support, requiring
timer-driven polling to detect I/O completion, and support for only single
512-byte block read and write operations at a time.
The combined effect of those two limits is that the system clock rate,
.Dv HZ ,
must be set to at least 200 in order to accomplish the maximum 100KB/s data
rate supported by the IP core.
.Sh SEE ALSO
.Xr disk 9
.Rs
.%T Altera University Program Secure Data Card IP Core
.%D August 2011
.%I Altera Corporation - University Program
.%U ftp://ftp.altera.com/up/pub/Altera_Material/11.0/University_Program_IP_Cores/Memory/SD_Card_Interface_for_SoPC_Builder.pdf
.Re
.Sh HISTORY
The
.Nm
device driver first appeared in
.Fx 10.0 .
.Sh AUTHORS
The
.Nm
device driver and this manual page were
developed by SRI International and the University of Cambridge Computer
Laboratory under DARPA/AFRL contract
.Pq FA8750-10-C-0237
.Pq Do CTSRD Dc ,
as part of the DARPA CRASH research programme.
This device driver was written by
.An Robert N. M. Watson .
.Sh BUGS
.Nm
contains a number of work-arounds for IP core bugs.
Perhaps most critically,
.Nm
ignores the CRC error bit returned in the RR1 register, which appears to be
unexpectedly set by the IP core.
.Pp
.Nm
uses fixed polling intervals are used for card insertion/removal and
I/O completion detection; an adaptive strategy might improve performance by
reducing the latency to detecting completed I/O.
However, in our experiments, using polling rates greater than 200 times a
second did not improve performance.
.Pp
.Nm
supports only a
.Li nexus
bus attachment, which is appropriate for system-on-chip busses such as
Altera's Avalon bus.
If the IP core is configured off of another bus type, then additional bus
attachments will be required.

View file

@ -660,6 +660,10 @@ dev/aic7xxx/aic7xxx_pci.c optional ahc pci
dev/alc/if_alc.c optional alc pci
dev/ale/if_ale.c optional ale pci
dev/altera/avgen/altera_avgen.c optional altera_avgen
dev/altera/sdcard/altera_sdcard.c optional altera_sdcard
dev/altera/sdcard/altera_sdcard_disk.c optional altera_sdcard
dev/altera/sdcard/altera_sdcard_io.c optional altera_sdcard
dev/altera/sdcard/altera_sdcard_nexus.c optional altera_sdcard
dev/amr/amr.c optional amr
dev/amr/amr_cam.c optional amrp amr
dev/amr/amr_disk.c optional amr

View file

@ -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);
}

View file

@ -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_ */

View file

@ -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");
}

View file

@ -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);
}

View file

@ -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);