altera_sdcard.c revision 303975
1227825Stheraven/*- 2227825Stheraven * Copyright (c) 2012 Robert N. M. Watson 3227825Stheraven * All rights reserved. 4227825Stheraven * 5227825Stheraven * This software was developed by SRI International and the University of 6227825Stheraven * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 7227825Stheraven * ("CTSRD"), as part of the DARPA CRASH research programme. 8227825Stheraven * 9227825Stheraven * Redistribution and use in source and binary forms, with or without 10227825Stheraven * modification, are permitted provided that the following conditions 11227825Stheraven * are met: 12227825Stheraven * 1. Redistributions of source code must retain the above copyright 13227825Stheraven * notice, this list of conditions and the following disclaimer. 14227825Stheraven * 2. Redistributions in binary form must reproduce the above copyright 15227825Stheraven * notice, this list of conditions and the following disclaimer in the 16227825Stheraven * documentation and/or other materials provided with the distribution. 17227825Stheraven * 18227825Stheraven * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19227825Stheraven * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20227825Stheraven * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21227825Stheraven * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22227825Stheraven * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23227825Stheraven * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24227825Stheraven * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25227825Stheraven * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26227825Stheraven * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27227825Stheraven * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28227825Stheraven * SUCH DAMAGE. 29227825Stheraven */ 30227825Stheraven 31227825Stheraven#include <sys/cdefs.h> 32227825Stheraven__FBSDID("$FreeBSD: releng/11.0/sys/dev/altera/sdcard/altera_sdcard.c 256744 2013-10-18 15:27:11Z brooks $"); 33227825Stheraven 34227825Stheraven#include "opt_altera_sdcard.h" 35227825Stheraven 36227825Stheraven#include <sys/param.h> 37227825Stheraven#include <sys/bus.h> 38227825Stheraven#include <sys/condvar.h> 39227825Stheraven#include <sys/conf.h> 40227825Stheraven#include <sys/bio.h> 41227825Stheraven#include <sys/kernel.h> 42227825Stheraven#include <sys/lock.h> 43227825Stheraven#include <sys/malloc.h> 44227825Stheraven#include <sys/module.h> 45227825Stheraven#include <sys/mutex.h> 46227825Stheraven#include <sys/rman.h> 47227825Stheraven#include <sys/systm.h> 48227825Stheraven#include <sys/taskqueue.h> 49227825Stheraven 50227825Stheraven#include <machine/bus.h> 51227825Stheraven#include <machine/resource.h> 52227825Stheraven 53227825Stheraven#include <geom/geom_disk.h> 54227825Stheraven 55227825Stheraven#include <dev/altera/sdcard/altera_sdcard.h> 56227825Stheraven 57227825Stheraven/* 58227825Stheraven * Device driver for the Altera University Program Secure Data Card IP Core, 59227825Stheraven * as described in the similarly named SOPC Builder IP Core specification. 60227825Stheraven * This soft core is not a full SD host controller interface (SDHCI) but 61227825Stheraven * instead provides a set of memory mapped registers and memory buffer that 62227825Stheraven * mildly abstract the SD Card protocol, but without providing DMA or 63227825Stheraven * interrupts. However, it does hide the details of voltage and 64227825Stheraven * communications negotiation. This driver implements disk(9), but due to the 65227825Stheraven * lack of interrupt support, must rely on timer-driven polling to determine 66227825Stheraven * when I/Os have completed. 67227825Stheraven * 68227825Stheraven * TODO: 69227825Stheraven * 70227825Stheraven * 1. Implement DISKFLAG_CANDELETE / SD Card sector erase support. 71227825Stheraven * 2. Implement d_ident from SD Card CID serial number field. 72227825Stheraven * 3. Handle read-only SD Cards. 73227825Stheraven * 4. Tune timeouts based on real-world SD Card speeds. 74227825Stheraven */ 75227825Stheravendevclass_t altera_sdcard_devclass; 76227825Stheraven 77227825Stheravenvoid 78227825Stheravenaltera_sdcard_attach(struct altera_sdcard_softc *sc) 79227825Stheraven{ 80227825Stheraven 81227825Stheraven ALTERA_SDCARD_LOCK_INIT(sc); 82227825Stheraven ALTERA_SDCARD_CONDVAR_INIT(sc); 83227825Stheraven sc->as_disk = NULL; 84227825Stheraven bioq_init(&sc->as_bioq); 85227825Stheraven sc->as_currentbio = NULL; 86227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_NOCARD; 87227825Stheraven sc->as_taskqueue = taskqueue_create("altera_sdcardc taskq", M_WAITOK, 88227825Stheraven taskqueue_thread_enqueue, &sc->as_taskqueue); 89227825Stheraven taskqueue_start_threads(&sc->as_taskqueue, 1, PI_DISK, 90227825Stheraven "altera_sdcardc%d taskqueue", sc->as_unit); 91227825Stheraven TIMEOUT_TASK_INIT(sc->as_taskqueue, &sc->as_task, 0, 92227825Stheraven altera_sdcard_task, sc); 93227825Stheraven 94227825Stheraven /* 95227825Stheraven * Kick off timer-driven processing with a manual poll so that we 96227825Stheraven * synchronously detect an already-inserted SD Card during the boot or 97227825Stheraven * other driver attach point. 98227825Stheraven */ 99227825Stheraven altera_sdcard_task(sc, 1); 100227825Stheraven} 101227825Stheraven 102227825Stheravenvoid 103227825Stheravenaltera_sdcard_detach(struct altera_sdcard_softc *sc) 104227825Stheraven{ 105227825Stheraven 106227825Stheraven KASSERT(sc->as_taskqueue != NULL, ("%s: taskqueue not present", 107227825Stheraven __func__)); 108227825Stheraven 109227825Stheraven /* 110227825Stheraven * Winding down the driver on detach is a bit complex. Update the 111227825Stheraven * flags to indicate that a detach has been requested, and then wait 112227825Stheraven * for in-progress I/O to wind down before continuing. 113227825Stheraven */ 114227825Stheraven ALTERA_SDCARD_LOCK(sc); 115227825Stheraven sc->as_flags |= ALTERA_SDCARD_FLAG_DETACHREQ; 116227825Stheraven while (sc->as_state != ALTERA_SDCARD_STATE_DETACHED) 117227825Stheraven ALTERA_SDCARD_CONDVAR_WAIT(sc); 118227825Stheraven ALTERA_SDCARD_UNLOCK(sc); 119227825Stheraven 120227825Stheraven /* 121227825Stheraven * Now wait for the possibly still executing taskqueue to drain. In 122227825Stheraven * principle no more events will be scheduled as we've transitioned to 123227825Stheraven * a detached state, but there might still be a request in execution. 124227825Stheraven */ 125227825Stheraven while (taskqueue_cancel_timeout(sc->as_taskqueue, &sc->as_task, NULL)) 126227825Stheraven taskqueue_drain_timeout(sc->as_taskqueue, &sc->as_task); 127227825Stheraven 128227825Stheraven /* 129227825Stheraven * Simulate a disk removal if one is present to deal with any pending 130227825Stheraven * or queued I/O. 131227825Stheraven */ 132227825Stheraven if (sc->as_disk != NULL) 133227825Stheraven altera_sdcard_disk_remove(sc); 134227825Stheraven KASSERT(bioq_first(&sc->as_bioq) == NULL, 135227825Stheraven ("%s: non-empty bioq", __func__)); 136227825Stheraven 137227825Stheraven /* 138227825Stheraven * Free any remaining allocated resources. 139227825Stheraven */ 140227825Stheraven taskqueue_free(sc->as_taskqueue); 141227825Stheraven sc->as_taskqueue = NULL; 142227825Stheraven ALTERA_SDCARD_CONDVAR_DESTROY(sc); 143227825Stheraven ALTERA_SDCARD_LOCK_DESTROY(sc); 144227825Stheraven} 145227825Stheraven 146227825Stheraven/* 147227825Stheraven * Set up and start the next I/O. Transition to the I/O state, but allow the 148227825Stheraven * caller to schedule the next timeout, as this may be called either from an 149227825Stheraven * initial attach context, or from the task queue, which requires different 150227825Stheraven * behaviour. 151227825Stheraven */ 152227825Stheravenstatic void 153227825Stheravenaltera_sdcard_nextio(struct altera_sdcard_softc *sc) 154227825Stheraven{ 155227825Stheraven struct bio *bp; 156227825Stheraven 157227825Stheraven ALTERA_SDCARD_LOCK_ASSERT(sc); 158227825Stheraven KASSERT(sc->as_currentbio == NULL, 159227825Stheraven ("%s: bio already active", __func__)); 160227825Stheraven 161227825Stheraven bp = bioq_takefirst(&sc->as_bioq); 162227825Stheraven if (bp == NULL) 163227825Stheraven panic("%s: bioq empty", __func__); 164227825Stheraven altera_sdcard_io_start(sc, bp); 165227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_IO; 166227825Stheraven} 167227825Stheraven 168227825Stheravenstatic void 169227825Stheravenaltera_sdcard_task_nocard(struct altera_sdcard_softc *sc) 170227825Stheraven{ 171227825Stheraven 172227825Stheraven ALTERA_SDCARD_LOCK_ASSERT(sc); 173227825Stheraven 174227825Stheraven /* 175227825Stheraven * Handle device driver detach. 176227825Stheraven */ 177227825Stheraven if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) { 178227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_DETACHED; 179227825Stheraven return; 180227825Stheraven } 181227825Stheraven 182227825Stheraven /* 183227825Stheraven * If there is no card insertion, remain in NOCARD. 184227825Stheraven */ 185227825Stheraven if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT)) 186227825Stheraven return; 187227825Stheraven 188227825Stheraven /* 189227825Stheraven * Read the CSD -- it may contain values that the driver can't handle, 190227825Stheraven * either because of an unsupported version/feature, or because the 191227825Stheraven * card is misbehaving. This triggers a transition to 192227825Stheraven * ALTERA_SDCARD_STATE_BADCARD. We rely on the CSD read to print a 193227825Stheraven * banner about how the card is problematic, since it has more 194227825Stheraven * information. The bad card state allows us to print that banner 195227825Stheraven * once rather than each time we notice the card is there, and still 196227825Stheraven * bad. 197227825Stheraven */ 198227825Stheraven if (altera_sdcard_read_csd(sc) != 0) { 199227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_BADCARD; 200227825Stheraven return; 201227825Stheraven } 202227825Stheraven 203227825Stheraven /* 204227825Stheraven * Process card insertion and upgrade to the IDLE state. 205227825Stheraven */ 206227825Stheraven altera_sdcard_disk_insert(sc); 207227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_IDLE; 208227825Stheraven} 209227825Stheraven 210227825Stheravenstatic void 211227825Stheravenaltera_sdcard_task_badcard(struct altera_sdcard_softc *sc) 212227825Stheraven{ 213227825Stheraven 214227825Stheraven ALTERA_SDCARD_LOCK_ASSERT(sc); 215227825Stheraven 216227825Stheraven /* 217227825Stheraven * Handle device driver detach. 218227825Stheraven */ 219227825Stheraven if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) { 220227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_DETACHED; 221227825Stheraven return; 222227825Stheraven } 223227825Stheraven 224227825Stheraven /* 225227825Stheraven * Handle safe card removal -- no teardown is required, just a state 226227825Stheraven * transition. 227227825Stheraven */ 228227825Stheraven if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT)) 229227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_NOCARD; 230227825Stheraven} 231227825Stheraven 232227825Stheravenstatic void 233227825Stheravenaltera_sdcard_task_idle(struct altera_sdcard_softc *sc) 234227825Stheraven{ 235227825Stheraven 236227825Stheraven ALTERA_SDCARD_LOCK_ASSERT(sc); 237227825Stheraven 238227825Stheraven /* 239227825Stheraven * Handle device driver detach. 240227825Stheraven */ 241227825Stheraven if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) { 242227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_DETACHED; 243227825Stheraven return; 244227825Stheraven } 245227825Stheraven 246227825Stheraven /* 247227825Stheraven * Handle safe card removal. 248227825Stheraven */ 249227825Stheraven if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT)) { 250227825Stheraven altera_sdcard_disk_remove(sc); 251227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_NOCARD; 252227825Stheraven } 253227825Stheraven} 254227825Stheraven 255227825Stheravenstatic void 256227825Stheravenaltera_sdcard_task_io(struct altera_sdcard_softc *sc) 257227825Stheraven{ 258227825Stheraven uint16_t asr; 259227825Stheraven 260227825Stheraven ALTERA_SDCARD_LOCK_ASSERT(sc); 261227825Stheraven KASSERT(sc->as_currentbio != NULL, ("%s: no current I/O", __func__)); 262227825Stheraven 263227825Stheraven#ifdef ALTERA_SDCARD_FAST_SIM 264227825Stheravenrecheck: 265227825Stheraven#endif 266227825Stheraven asr = altera_sdcard_read_asr(sc); 267227825Stheraven 268227825Stheraven /* 269227825Stheraven * Check for unexpected card removal during an I/O. 270227825Stheraven */ 271227825Stheraven if (!(asr & ALTERA_SDCARD_ASR_CARDPRESENT)) { 272227825Stheraven altera_sdcard_disk_remove(sc); 273227825Stheraven if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) 274227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_DETACHED; 275227825Stheraven else 276227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_NOCARD; 277227825Stheraven return; 278227825Stheraven } 279227825Stheraven 280227825Stheraven /* 281227825Stheraven * If the I/O isn't complete, remain in the IO state without further 282227825Stheraven * action, even if DETACHREQ is in flight. 283227825Stheraven */ 284227825Stheraven if (asr & ALTERA_SDCARD_ASR_CMDINPROGRESS) 285227825Stheraven return; 286227825Stheraven 287227825Stheraven /* 288227825Stheraven * Handle various forms of I/O completion, successful and otherwise. 289227825Stheraven * The I/O layer may restart the transaction if an error occurred, in 290227825Stheraven * which case remain in the IO state and reschedule. 291227825Stheraven */ 292227825Stheraven if (!altera_sdcard_io_complete(sc, asr)) 293227825Stheraven return; 294227825Stheraven 295227825Stheraven /* 296227825Stheraven * Now that I/O is complete, process detach requests in preference to 297227825Stheraven * starting new I/O. 298227825Stheraven */ 299227825Stheraven if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) { 300227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_DETACHED; 301227825Stheraven return; 302227825Stheraven } 303227825Stheraven 304227825Stheraven /* 305227825Stheraven * Finally, either start the next I/O or transition to the IDLE state. 306227825Stheraven */ 307227825Stheraven if (bioq_first(&sc->as_bioq) != NULL) { 308227825Stheraven altera_sdcard_nextio(sc); 309227825Stheraven#ifdef ALTERA_SDCARD_FAST_SIM 310227825Stheraven goto recheck; 311227825Stheraven#endif 312227825Stheraven } else 313227825Stheraven sc->as_state = ALTERA_SDCARD_STATE_IDLE; 314227825Stheraven} 315227825Stheraven 316227825Stheravenstatic void 317227825Stheravenaltera_sdcard_task_rechedule(struct altera_sdcard_softc *sc) 318227825Stheraven{ 319227825Stheraven int interval; 320227825Stheraven 321227825Stheraven /* 322227825Stheraven * Reschedule based on new state. Or not, if detaching the device 323227825Stheraven * driver. Treat a bad card as though it were no card at all. 324227825Stheraven */ 325227825Stheraven switch (sc->as_state) { 326227825Stheraven case ALTERA_SDCARD_STATE_NOCARD: 327227825Stheraven case ALTERA_SDCARD_STATE_BADCARD: 328227825Stheraven interval = ALTERA_SDCARD_TIMEOUT_NOCARD; 329227825Stheraven break; 330227825Stheraven 331227825Stheraven case ALTERA_SDCARD_STATE_IDLE: 332227825Stheraven interval = ALTERA_SDCARD_TIMEOUT_IDLE; 333227825Stheraven break; 334227825Stheraven 335227825Stheraven case ALTERA_SDCARD_STATE_IO: 336227825Stheraven if (sc->as_flags & ALTERA_SDCARD_FLAG_IOERROR) 337227825Stheraven interval = ALTERA_SDCARD_TIMEOUT_IOERROR; 338227825Stheraven else 339227825Stheraven interval = ALTERA_SDCARD_TIMEOUT_IO; 340227825Stheraven break; 341227825Stheraven 342227825Stheraven default: 343227825Stheraven panic("%s: invalid exit state %d", __func__, sc->as_state); 344227825Stheraven } 345227825Stheraven taskqueue_enqueue_timeout(sc->as_taskqueue, &sc->as_task, interval); 346227825Stheraven} 347227825Stheraven 348227825Stheraven/* 349227825Stheraven * Because the Altera SD Card IP Core doesn't support interrupts, we do all 350227825Stheraven * asynchronous work from a timeout. Poll at two different rates -- an 351227825Stheraven * infrequent check for card insertion status changes, and a frequent one for 352227825Stheraven * I/O completion. The task should never start in DETACHED, as that would 353227825Stheraven * imply that a previous instance failed to cancel rather than reschedule. 354227825Stheraven */ 355227825Stheravenvoid 356227825Stheravenaltera_sdcard_task(void *arg, int pending) 357227825Stheraven{ 358227825Stheraven struct altera_sdcard_softc *sc; 359227825Stheraven 360227825Stheraven sc = arg; 361227825Stheraven KASSERT(sc->as_state != ALTERA_SDCARD_STATE_DETACHED, 362227825Stheraven ("%s: already in detached", __func__)); 363227825Stheraven 364227825Stheraven ALTERA_SDCARD_LOCK(sc); 365227825Stheraven switch (sc->as_state) { 366227825Stheraven case ALTERA_SDCARD_STATE_NOCARD: 367227825Stheraven altera_sdcard_task_nocard(sc); 368262801Sdim break; 369227825Stheraven 370227825Stheraven case ALTERA_SDCARD_STATE_BADCARD: 371227825Stheraven altera_sdcard_task_badcard(sc); 372227825Stheraven break; 373227825Stheraven 374227825Stheraven case ALTERA_SDCARD_STATE_IDLE: 375227825Stheraven altera_sdcard_task_idle(sc); 376227825Stheraven break; 377227825Stheraven 378227825Stheraven case ALTERA_SDCARD_STATE_IO: 379227825Stheraven altera_sdcard_task_io(sc); 380227825Stheraven break; 381227825Stheraven 382227825Stheraven default: 383227825Stheraven panic("%s: invalid enter state %d", __func__, sc->as_state); 384227825Stheraven } 385227825Stheraven 386227825Stheraven /* 387227825Stheraven * If we have transitioned to DETACHED, signal the detach thread and 388227825Stheraven * cancel the timeout-driven task. Otherwise reschedule on an 389227825Stheraven * appropriate timeout. 390227825Stheraven */ 391227825Stheraven if (sc->as_state == ALTERA_SDCARD_STATE_DETACHED) 392227825Stheraven ALTERA_SDCARD_CONDVAR_SIGNAL(sc); 393227825Stheraven else 394227825Stheraven altera_sdcard_task_rechedule(sc); 395227825Stheraven ALTERA_SDCARD_UNLOCK(sc); 396227825Stheraven} 397227825Stheraven 398227825Stheravenvoid 399227825Stheravenaltera_sdcard_start(struct altera_sdcard_softc *sc) 400227825Stheraven{ 401227825Stheraven 402227825Stheraven ALTERA_SDCARD_LOCK_ASSERT(sc); 403227825Stheraven 404227825Stheraven KASSERT(sc->as_state == ALTERA_SDCARD_STATE_IDLE, 405227825Stheraven ("%s: starting when not IDLE", __func__)); 406227825Stheraven 407227825Stheraven taskqueue_cancel_timeout(sc->as_taskqueue, &sc->as_task, NULL); 408227825Stheraven altera_sdcard_nextio(sc); 409227825Stheraven#ifdef ALTERA_SDCARD_FAST_SIM 410227825Stheraven altera_sdcard_task_io(sc); 411227825Stheraven#endif 412227825Stheraven altera_sdcard_task_rechedule(sc); 413227825Stheraven} 414227825Stheraven