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