xref: /freebsd/sys/dev/altera/sdcard/altera_sdcard.c (revision 685dc743)
1d432e92aSRobert Watson /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4d432e92aSRobert Watson  * Copyright (c) 2012 Robert N. M. Watson
5d432e92aSRobert Watson  * All rights reserved.
6d432e92aSRobert Watson  *
7d432e92aSRobert Watson  * This software was developed by SRI International and the University of
8d432e92aSRobert Watson  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
9d432e92aSRobert Watson  * ("CTSRD"), as part of the DARPA CRASH research programme.
10d432e92aSRobert Watson  *
11d432e92aSRobert Watson  * Redistribution and use in source and binary forms, with or without
12d432e92aSRobert Watson  * modification, are permitted provided that the following conditions
13d432e92aSRobert Watson  * are met:
14d432e92aSRobert Watson  * 1. Redistributions of source code must retain the above copyright
15d432e92aSRobert Watson  *    notice, this list of conditions and the following disclaimer.
16d432e92aSRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
17d432e92aSRobert Watson  *    notice, this list of conditions and the following disclaimer in the
18d432e92aSRobert Watson  *    documentation and/or other materials provided with the distribution.
19d432e92aSRobert Watson  *
20d432e92aSRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21d432e92aSRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22d432e92aSRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23d432e92aSRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24d432e92aSRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25d432e92aSRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26d432e92aSRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27d432e92aSRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28d432e92aSRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29d432e92aSRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30d432e92aSRobert Watson  * SUCH DAMAGE.
31d432e92aSRobert Watson  */
32d432e92aSRobert Watson 
33d432e92aSRobert Watson #include <sys/cdefs.h>
34e9088043SBrooks Davis #include "opt_altera_sdcard.h"
35e9088043SBrooks Davis 
36d432e92aSRobert Watson #include <sys/param.h>
37d432e92aSRobert Watson #include <sys/bus.h>
38d432e92aSRobert Watson #include <sys/condvar.h>
39d432e92aSRobert Watson #include <sys/conf.h>
40d432e92aSRobert Watson #include <sys/bio.h>
41d432e92aSRobert Watson #include <sys/kernel.h>
42d432e92aSRobert Watson #include <sys/lock.h>
43d432e92aSRobert Watson #include <sys/malloc.h>
44d432e92aSRobert Watson #include <sys/module.h>
45d432e92aSRobert Watson #include <sys/mutex.h>
46d432e92aSRobert Watson #include <sys/rman.h>
47d432e92aSRobert Watson #include <sys/systm.h>
48d432e92aSRobert Watson #include <sys/taskqueue.h>
49d432e92aSRobert Watson 
50d432e92aSRobert Watson #include <machine/bus.h>
51d432e92aSRobert Watson #include <machine/resource.h>
52d432e92aSRobert Watson 
53d432e92aSRobert Watson #include <geom/geom_disk.h>
54d432e92aSRobert Watson 
55d432e92aSRobert Watson #include <dev/altera/sdcard/altera_sdcard.h>
56d432e92aSRobert Watson 
57d432e92aSRobert Watson /*
58d432e92aSRobert Watson  * Device driver for the Altera University Program Secure Data Card IP Core,
59d432e92aSRobert Watson  * as described in the similarly named SOPC Builder IP Core specification.
60d432e92aSRobert Watson  * This soft core is not a full SD host controller interface (SDHCI) but
61d432e92aSRobert Watson  * instead provides a set of memory mapped registers and memory buffer that
62d432e92aSRobert Watson  * mildly abstract the SD Card protocol, but without providing DMA or
63d432e92aSRobert Watson  * interrupts.  However, it does hide the details of voltage and
64d432e92aSRobert Watson  * communications negotiation.  This driver implements disk(9), but due to the
65d432e92aSRobert Watson  * lack of interrupt support, must rely on timer-driven polling to determine
66d432e92aSRobert Watson  * when I/Os have completed.
67d432e92aSRobert Watson  *
68d432e92aSRobert Watson  * TODO:
69d432e92aSRobert Watson  *
70d432e92aSRobert Watson  * 1. Implement DISKFLAG_CANDELETE / SD Card sector erase support.
71d432e92aSRobert Watson  * 2. Implement d_ident from SD Card CID serial number field.
72d432e92aSRobert Watson  * 3. Handle read-only SD Cards.
73d432e92aSRobert Watson  * 4. Tune timeouts based on real-world SD Card speeds.
74d432e92aSRobert Watson  */
75d432e92aSRobert Watson 
76d432e92aSRobert Watson void
altera_sdcard_attach(struct altera_sdcard_softc * sc)77d432e92aSRobert Watson altera_sdcard_attach(struct altera_sdcard_softc *sc)
78d432e92aSRobert Watson {
79d432e92aSRobert Watson 
80d432e92aSRobert Watson 	ALTERA_SDCARD_LOCK_INIT(sc);
81d432e92aSRobert Watson 	ALTERA_SDCARD_CONDVAR_INIT(sc);
82d432e92aSRobert Watson 	sc->as_disk = NULL;
83d432e92aSRobert Watson 	bioq_init(&sc->as_bioq);
84d432e92aSRobert Watson 	sc->as_currentbio = NULL;
85d432e92aSRobert Watson 	sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
86d432e92aSRobert Watson 	sc->as_taskqueue = taskqueue_create("altera_sdcardc taskq", M_WAITOK,
87d432e92aSRobert Watson 	    taskqueue_thread_enqueue, &sc->as_taskqueue);
88d432e92aSRobert Watson 	taskqueue_start_threads(&sc->as_taskqueue, 1, PI_DISK,
89d432e92aSRobert Watson 	    "altera_sdcardc%d taskqueue", sc->as_unit);
90d432e92aSRobert Watson 	TIMEOUT_TASK_INIT(sc->as_taskqueue, &sc->as_task, 0,
91d432e92aSRobert Watson 	    altera_sdcard_task, sc);
92d432e92aSRobert Watson 
93d432e92aSRobert Watson 	/*
94d432e92aSRobert Watson 	 * Kick off timer-driven processing with a manual poll so that we
95d432e92aSRobert Watson 	 * synchronously detect an already-inserted SD Card during the boot or
96d432e92aSRobert Watson 	 * other driver attach point.
97d432e92aSRobert Watson 	 */
98d432e92aSRobert Watson 	altera_sdcard_task(sc, 1);
99d432e92aSRobert Watson }
100d432e92aSRobert Watson 
101d432e92aSRobert Watson void
altera_sdcard_detach(struct altera_sdcard_softc * sc)102d432e92aSRobert Watson altera_sdcard_detach(struct altera_sdcard_softc *sc)
103d432e92aSRobert Watson {
104d432e92aSRobert Watson 
105d432e92aSRobert Watson 	KASSERT(sc->as_taskqueue != NULL, ("%s: taskqueue not present",
106d432e92aSRobert Watson 	    __func__));
107d432e92aSRobert Watson 
108d432e92aSRobert Watson 	/*
109d432e92aSRobert Watson 	 * Winding down the driver on detach is a bit complex.  Update the
110d432e92aSRobert Watson 	 * flags to indicate that a detach has been requested, and then wait
111d432e92aSRobert Watson 	 * for in-progress I/O to wind down before continuing.
112d432e92aSRobert Watson 	 */
113d432e92aSRobert Watson 	ALTERA_SDCARD_LOCK(sc);
114d432e92aSRobert Watson 	sc->as_flags |= ALTERA_SDCARD_FLAG_DETACHREQ;
115d432e92aSRobert Watson 	while (sc->as_state != ALTERA_SDCARD_STATE_DETACHED)
116d432e92aSRobert Watson 		ALTERA_SDCARD_CONDVAR_WAIT(sc);
117d432e92aSRobert Watson 	ALTERA_SDCARD_UNLOCK(sc);
118d432e92aSRobert Watson 
119d432e92aSRobert Watson 	/*
120d432e92aSRobert Watson 	 * Now wait for the possibly still executing taskqueue to drain.  In
121d432e92aSRobert Watson 	 * principle no more events will be scheduled as we've transitioned to
122d432e92aSRobert Watson 	 * a detached state, but there might still be a request in execution.
123d432e92aSRobert Watson 	 */
124d432e92aSRobert Watson 	while (taskqueue_cancel_timeout(sc->as_taskqueue, &sc->as_task, NULL))
125d432e92aSRobert Watson 		taskqueue_drain_timeout(sc->as_taskqueue, &sc->as_task);
126d432e92aSRobert Watson 
127d432e92aSRobert Watson 	/*
128d432e92aSRobert Watson 	 * Simulate a disk removal if one is present to deal with any pending
129d432e92aSRobert Watson 	 * or queued I/O.
130d432e92aSRobert Watson 	 */
131d432e92aSRobert Watson 	if (sc->as_disk != NULL)
132d432e92aSRobert Watson 		altera_sdcard_disk_remove(sc);
133d432e92aSRobert Watson 	KASSERT(bioq_first(&sc->as_bioq) == NULL,
134d432e92aSRobert Watson 	    ("%s: non-empty bioq", __func__));
135d432e92aSRobert Watson 
136d432e92aSRobert Watson 	/*
137d432e92aSRobert Watson 	 * Free any remaining allocated resources.
138d432e92aSRobert Watson 	 */
139d432e92aSRobert Watson 	taskqueue_free(sc->as_taskqueue);
140d432e92aSRobert Watson 	sc->as_taskqueue = NULL;
141d432e92aSRobert Watson 	ALTERA_SDCARD_CONDVAR_DESTROY(sc);
142d432e92aSRobert Watson 	ALTERA_SDCARD_LOCK_DESTROY(sc);
143d432e92aSRobert Watson }
144d432e92aSRobert Watson 
145d432e92aSRobert Watson /*
146d432e92aSRobert Watson  * Set up and start the next I/O.  Transition to the I/O state, but allow the
147d432e92aSRobert Watson  * caller to schedule the next timeout, as this may be called either from an
148d432e92aSRobert Watson  * initial attach context, or from the task queue, which requires different
149d432e92aSRobert Watson  * behaviour.
150d432e92aSRobert Watson  */
151d432e92aSRobert Watson static void
altera_sdcard_nextio(struct altera_sdcard_softc * sc)152d432e92aSRobert Watson altera_sdcard_nextio(struct altera_sdcard_softc *sc)
153d432e92aSRobert Watson {
154d432e92aSRobert Watson 	struct bio *bp;
155d432e92aSRobert Watson 
156d432e92aSRobert Watson 	ALTERA_SDCARD_LOCK_ASSERT(sc);
157d432e92aSRobert Watson 	KASSERT(sc->as_currentbio == NULL,
158d432e92aSRobert Watson 	    ("%s: bio already active", __func__));
159d432e92aSRobert Watson 
160d432e92aSRobert Watson 	bp = bioq_takefirst(&sc->as_bioq);
161d432e92aSRobert Watson 	if (bp == NULL)
162d432e92aSRobert Watson 		panic("%s: bioq empty", __func__);
163d432e92aSRobert Watson 	altera_sdcard_io_start(sc, bp);
164d432e92aSRobert Watson 	sc->as_state = ALTERA_SDCARD_STATE_IO;
165d432e92aSRobert Watson }
166d432e92aSRobert Watson 
167d432e92aSRobert Watson static void
altera_sdcard_task_nocard(struct altera_sdcard_softc * sc)168d432e92aSRobert Watson altera_sdcard_task_nocard(struct altera_sdcard_softc *sc)
169d432e92aSRobert Watson {
170d432e92aSRobert Watson 
171d432e92aSRobert Watson 	ALTERA_SDCARD_LOCK_ASSERT(sc);
172d432e92aSRobert Watson 
173d432e92aSRobert Watson 	/*
174d432e92aSRobert Watson 	 * Handle device driver detach.
175d432e92aSRobert Watson 	 */
176d432e92aSRobert Watson 	if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
177d432e92aSRobert Watson 		sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
178d432e92aSRobert Watson 		return;
179d432e92aSRobert Watson 	}
180d432e92aSRobert Watson 
181d432e92aSRobert Watson 	/*
182d432e92aSRobert Watson 	 * If there is no card insertion, remain in NOCARD.
183d432e92aSRobert Watson 	 */
184d432e92aSRobert Watson 	if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT))
185d432e92aSRobert Watson 		return;
186d432e92aSRobert Watson 
187d432e92aSRobert Watson 	/*
188d432e92aSRobert Watson 	 * Read the CSD -- it may contain values that the driver can't handle,
189d432e92aSRobert Watson 	 * either because of an unsupported version/feature, or because the
190d432e92aSRobert Watson 	 * card is misbehaving.  This triggers a transition to
191d432e92aSRobert Watson 	 * ALTERA_SDCARD_STATE_BADCARD.  We rely on the CSD read to print a
192d432e92aSRobert Watson 	 * banner about how the card is problematic, since it has more
193d432e92aSRobert Watson 	 * information.  The bad card state allows us to print that banner
194d432e92aSRobert Watson 	 * once rather than each time we notice the card is there, and still
195d432e92aSRobert Watson 	 * bad.
196d432e92aSRobert Watson 	 */
197d432e92aSRobert Watson 	if (altera_sdcard_read_csd(sc) != 0) {
198d432e92aSRobert Watson 		sc->as_state = ALTERA_SDCARD_STATE_BADCARD;
199d432e92aSRobert Watson 		return;
200d432e92aSRobert Watson 	}
201d432e92aSRobert Watson 
202d432e92aSRobert Watson 	/*
203d432e92aSRobert Watson 	 * Process card insertion and upgrade to the IDLE state.
204d432e92aSRobert Watson 	 */
205d432e92aSRobert Watson 	altera_sdcard_disk_insert(sc);
206d432e92aSRobert Watson 	sc->as_state = ALTERA_SDCARD_STATE_IDLE;
207d432e92aSRobert Watson }
208d432e92aSRobert Watson 
209d432e92aSRobert Watson static void
altera_sdcard_task_badcard(struct altera_sdcard_softc * sc)210d432e92aSRobert Watson altera_sdcard_task_badcard(struct altera_sdcard_softc *sc)
211d432e92aSRobert Watson {
212d432e92aSRobert Watson 
213d432e92aSRobert Watson 	ALTERA_SDCARD_LOCK_ASSERT(sc);
214d432e92aSRobert Watson 
215d432e92aSRobert Watson 	/*
216d432e92aSRobert Watson 	 * Handle device driver detach.
217d432e92aSRobert Watson 	 */
218d432e92aSRobert Watson 	if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
219d432e92aSRobert Watson 		sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
220d432e92aSRobert Watson 		return;
221d432e92aSRobert Watson 	}
222d432e92aSRobert Watson 
223d432e92aSRobert Watson 	/*
224d432e92aSRobert Watson 	 * Handle safe card removal -- no teardown is required, just a state
225d432e92aSRobert Watson 	 * transition.
226d432e92aSRobert Watson 	 */
227d432e92aSRobert Watson 	if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT))
228d432e92aSRobert Watson 		sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
229d432e92aSRobert Watson }
230d432e92aSRobert Watson 
231d432e92aSRobert Watson static void
altera_sdcard_task_idle(struct altera_sdcard_softc * sc)232d432e92aSRobert Watson altera_sdcard_task_idle(struct altera_sdcard_softc *sc)
233d432e92aSRobert Watson {
234d432e92aSRobert Watson 
235d432e92aSRobert Watson 	ALTERA_SDCARD_LOCK_ASSERT(sc);
236d432e92aSRobert Watson 
237d432e92aSRobert Watson 	/*
238d432e92aSRobert Watson 	 * Handle device driver detach.
239d432e92aSRobert Watson 	 */
240d432e92aSRobert Watson 	if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
241d432e92aSRobert Watson 		sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
242d432e92aSRobert Watson 		return;
243d432e92aSRobert Watson 	}
244d432e92aSRobert Watson 
245d432e92aSRobert Watson 	/*
246d432e92aSRobert Watson 	 * Handle safe card removal.
247d432e92aSRobert Watson 	 */
248d432e92aSRobert Watson 	if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT)) {
249d432e92aSRobert Watson 		altera_sdcard_disk_remove(sc);
250d432e92aSRobert Watson 		sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
251d432e92aSRobert Watson 	}
252d432e92aSRobert Watson }
253d432e92aSRobert Watson 
254d432e92aSRobert Watson static void
altera_sdcard_task_io(struct altera_sdcard_softc * sc)255d432e92aSRobert Watson altera_sdcard_task_io(struct altera_sdcard_softc *sc)
256d432e92aSRobert Watson {
257d432e92aSRobert Watson 	uint16_t asr;
258d432e92aSRobert Watson 
259d432e92aSRobert Watson 	ALTERA_SDCARD_LOCK_ASSERT(sc);
260d432e92aSRobert Watson 	KASSERT(sc->as_currentbio != NULL, ("%s: no current I/O", __func__));
261d432e92aSRobert Watson 
262e9088043SBrooks Davis #ifdef ALTERA_SDCARD_FAST_SIM
263e9088043SBrooks Davis recheck:
264e9088043SBrooks Davis #endif
265d432e92aSRobert Watson 	asr = altera_sdcard_read_asr(sc);
266d432e92aSRobert Watson 
267d432e92aSRobert Watson 	/*
268d432e92aSRobert Watson 	 * Check for unexpected card removal during an I/O.
269d432e92aSRobert Watson 	 */
270d432e92aSRobert Watson 	if (!(asr & ALTERA_SDCARD_ASR_CARDPRESENT)) {
271d432e92aSRobert Watson 		altera_sdcard_disk_remove(sc);
272d432e92aSRobert Watson 		if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ)
273d432e92aSRobert Watson 			sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
274d432e92aSRobert Watson 		else
275d432e92aSRobert Watson 			sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
276d432e92aSRobert Watson 		return;
277d432e92aSRobert Watson 	}
278d432e92aSRobert Watson 
279d432e92aSRobert Watson 	/*
280d432e92aSRobert Watson 	 * If the I/O isn't complete, remain in the IO state without further
281d432e92aSRobert Watson 	 * action, even if DETACHREQ is in flight.
282d432e92aSRobert Watson 	 */
283d432e92aSRobert Watson 	if (asr & ALTERA_SDCARD_ASR_CMDINPROGRESS)
284d432e92aSRobert Watson 		return;
285d432e92aSRobert Watson 
286d432e92aSRobert Watson 	/*
287d432e92aSRobert Watson 	 * Handle various forms of I/O completion, successful and otherwise.
288d432e92aSRobert Watson 	 * The I/O layer may restart the transaction if an error occurred, in
289d432e92aSRobert Watson 	 * which case remain in the IO state and reschedule.
290d432e92aSRobert Watson 	 */
291d432e92aSRobert Watson 	if (!altera_sdcard_io_complete(sc, asr))
292d432e92aSRobert Watson 		return;
293d432e92aSRobert Watson 
294d432e92aSRobert Watson 	/*
295d432e92aSRobert Watson 	 * Now that I/O is complete, process detach requests in preference to
296d432e92aSRobert Watson 	 * starting new I/O.
297d432e92aSRobert Watson 	 */
298d432e92aSRobert Watson 	if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
299d432e92aSRobert Watson 		sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
300d432e92aSRobert Watson 		return;
301d432e92aSRobert Watson 	}
302d432e92aSRobert Watson 
303d432e92aSRobert Watson 	/*
304d432e92aSRobert Watson 	 * Finally, either start the next I/O or transition to the IDLE state.
305d432e92aSRobert Watson 	 */
306e9088043SBrooks Davis 	if (bioq_first(&sc->as_bioq) != NULL) {
307d432e92aSRobert Watson 		altera_sdcard_nextio(sc);
308e9088043SBrooks Davis #ifdef ALTERA_SDCARD_FAST_SIM
309e9088043SBrooks Davis 		goto recheck;
310e9088043SBrooks Davis #endif
311e9088043SBrooks Davis 	} else
312d432e92aSRobert Watson 		sc->as_state = ALTERA_SDCARD_STATE_IDLE;
313d432e92aSRobert Watson }
314d432e92aSRobert Watson 
315d432e92aSRobert Watson static void
altera_sdcard_task_rechedule(struct altera_sdcard_softc * sc)316d432e92aSRobert Watson altera_sdcard_task_rechedule(struct altera_sdcard_softc *sc)
317d432e92aSRobert Watson {
318d432e92aSRobert Watson 	int interval;
319d432e92aSRobert Watson 
320d432e92aSRobert Watson 	/*
321d432e92aSRobert Watson 	 * Reschedule based on new state.  Or not, if detaching the device
322d432e92aSRobert Watson 	 * driver.  Treat a bad card as though it were no card at all.
323d432e92aSRobert Watson 	 */
324d432e92aSRobert Watson 	switch (sc->as_state) {
325d432e92aSRobert Watson 	case ALTERA_SDCARD_STATE_NOCARD:
326d432e92aSRobert Watson 	case ALTERA_SDCARD_STATE_BADCARD:
327d432e92aSRobert Watson 		interval = ALTERA_SDCARD_TIMEOUT_NOCARD;
328d432e92aSRobert Watson 		break;
329d432e92aSRobert Watson 
330d432e92aSRobert Watson 	case ALTERA_SDCARD_STATE_IDLE:
331d432e92aSRobert Watson 		interval = ALTERA_SDCARD_TIMEOUT_IDLE;
332d432e92aSRobert Watson 		break;
333d432e92aSRobert Watson 
334d432e92aSRobert Watson 	case ALTERA_SDCARD_STATE_IO:
335d432e92aSRobert Watson 		if (sc->as_flags & ALTERA_SDCARD_FLAG_IOERROR)
336d432e92aSRobert Watson 			interval = ALTERA_SDCARD_TIMEOUT_IOERROR;
337d432e92aSRobert Watson 		else
338d432e92aSRobert Watson 			interval = ALTERA_SDCARD_TIMEOUT_IO;
339d432e92aSRobert Watson 		break;
340d432e92aSRobert Watson 
341d432e92aSRobert Watson 	default:
342d432e92aSRobert Watson 		panic("%s: invalid exit state %d", __func__, sc->as_state);
343d432e92aSRobert Watson 	}
344d432e92aSRobert Watson 	taskqueue_enqueue_timeout(sc->as_taskqueue, &sc->as_task, interval);
345d432e92aSRobert Watson }
346d432e92aSRobert Watson 
347d432e92aSRobert Watson /*
348d432e92aSRobert Watson  * Because the Altera SD Card IP Core doesn't support interrupts, we do all
349d432e92aSRobert Watson  * asynchronous work from a timeout.  Poll at two different rates -- an
350d432e92aSRobert Watson  * infrequent check for card insertion status changes, and a frequent one for
351d432e92aSRobert Watson  * I/O completion.  The task should never start in DETACHED, as that would
352d432e92aSRobert Watson  * imply that a previous instance failed to cancel rather than reschedule.
353d432e92aSRobert Watson  */
354d432e92aSRobert Watson void
altera_sdcard_task(void * arg,int pending)355d432e92aSRobert Watson altera_sdcard_task(void *arg, int pending)
356d432e92aSRobert Watson {
357d432e92aSRobert Watson 	struct altera_sdcard_softc *sc;
358d432e92aSRobert Watson 
359d432e92aSRobert Watson 	sc = arg;
360d432e92aSRobert Watson 	KASSERT(sc->as_state != ALTERA_SDCARD_STATE_DETACHED,
361d432e92aSRobert Watson 	    ("%s: already in detached", __func__));
362d432e92aSRobert Watson 
363d432e92aSRobert Watson 	ALTERA_SDCARD_LOCK(sc);
364d432e92aSRobert Watson 	switch (sc->as_state) {
365d432e92aSRobert Watson 	case ALTERA_SDCARD_STATE_NOCARD:
366d432e92aSRobert Watson 		altera_sdcard_task_nocard(sc);
367d432e92aSRobert Watson 		break;
368d432e92aSRobert Watson 
369d432e92aSRobert Watson 	case ALTERA_SDCARD_STATE_BADCARD:
370d432e92aSRobert Watson 		altera_sdcard_task_badcard(sc);
371d432e92aSRobert Watson 		break;
372d432e92aSRobert Watson 
373d432e92aSRobert Watson 	case ALTERA_SDCARD_STATE_IDLE:
374d432e92aSRobert Watson 		altera_sdcard_task_idle(sc);
375d432e92aSRobert Watson 		break;
376d432e92aSRobert Watson 
377d432e92aSRobert Watson 	case ALTERA_SDCARD_STATE_IO:
378d432e92aSRobert Watson 		altera_sdcard_task_io(sc);
379d432e92aSRobert Watson 		break;
380d432e92aSRobert Watson 
381d432e92aSRobert Watson 	default:
382d432e92aSRobert Watson 		panic("%s: invalid enter state %d", __func__, sc->as_state);
383d432e92aSRobert Watson 	}
384d432e92aSRobert Watson 
385d432e92aSRobert Watson 	/*
386d432e92aSRobert Watson 	 * If we have transitioned to DETACHED, signal the detach thread and
387d432e92aSRobert Watson 	 * cancel the timeout-driven task.  Otherwise reschedule on an
388d432e92aSRobert Watson 	 * appropriate timeout.
389d432e92aSRobert Watson 	 */
390d432e92aSRobert Watson 	if (sc->as_state == ALTERA_SDCARD_STATE_DETACHED)
391d432e92aSRobert Watson 		ALTERA_SDCARD_CONDVAR_SIGNAL(sc);
392d432e92aSRobert Watson 	else
393d432e92aSRobert Watson 		altera_sdcard_task_rechedule(sc);
394d432e92aSRobert Watson 	ALTERA_SDCARD_UNLOCK(sc);
395d432e92aSRobert Watson }
396d432e92aSRobert Watson 
397d432e92aSRobert Watson void
altera_sdcard_start(struct altera_sdcard_softc * sc)398d432e92aSRobert Watson altera_sdcard_start(struct altera_sdcard_softc *sc)
399d432e92aSRobert Watson {
400d432e92aSRobert Watson 
401d432e92aSRobert Watson 	ALTERA_SDCARD_LOCK_ASSERT(sc);
402d432e92aSRobert Watson 
403d432e92aSRobert Watson 	KASSERT(sc->as_state == ALTERA_SDCARD_STATE_IDLE,
404d432e92aSRobert Watson 	    ("%s: starting when not IDLE", __func__));
405d432e92aSRobert Watson 
406d432e92aSRobert Watson 	taskqueue_cancel_timeout(sc->as_taskqueue, &sc->as_task, NULL);
407d432e92aSRobert Watson 	altera_sdcard_nextio(sc);
408e9088043SBrooks Davis #ifdef ALTERA_SDCARD_FAST_SIM
409e9088043SBrooks Davis 	altera_sdcard_task_io(sc);
410e9088043SBrooks Davis #endif
411e9088043SBrooks Davis 	altera_sdcard_task_rechedule(sc);
412d432e92aSRobert Watson }
413