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