altera_sdcard.c revision 245380
1239675Srwatson/*-
2239675Srwatson * Copyright (c) 2012 Robert N. M. Watson
3239675Srwatson * All rights reserved.
4239675Srwatson *
5239675Srwatson * This software was developed by SRI International and the University of
6239675Srwatson * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7239675Srwatson * ("CTSRD"), as part of the DARPA CRASH research programme.
8239675Srwatson *
9239675Srwatson * Redistribution and use in source and binary forms, with or without
10239675Srwatson * modification, are permitted provided that the following conditions
11239675Srwatson * are met:
12239675Srwatson * 1. Redistributions of source code must retain the above copyright
13239675Srwatson *    notice, this list of conditions and the following disclaimer.
14239675Srwatson * 2. Redistributions in binary form must reproduce the above copyright
15239675Srwatson *    notice, this list of conditions and the following disclaimer in the
16239675Srwatson *    documentation and/or other materials provided with the distribution.
17239675Srwatson *
18239675Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19239675Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20239675Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21239675Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22239675Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23239675Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24239675Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25239675Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26239675Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27239675Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28239675Srwatson * SUCH DAMAGE.
29239675Srwatson */
30239675Srwatson
31239675Srwatson#include <sys/cdefs.h>
32239675Srwatson__FBSDID("$FreeBSD: head/sys/dev/altera/sdcard/altera_sdcard.c 245380 2013-01-13 16:57:11Z rwatson $");
33239675Srwatson
34239675Srwatson#include <sys/param.h>
35239675Srwatson#include <sys/bus.h>
36239675Srwatson#include <sys/condvar.h>
37239675Srwatson#include <sys/conf.h>
38239675Srwatson#include <sys/bio.h>
39239675Srwatson#include <sys/kernel.h>
40239675Srwatson#include <sys/lock.h>
41239675Srwatson#include <sys/malloc.h>
42239675Srwatson#include <sys/module.h>
43239675Srwatson#include <sys/mutex.h>
44239675Srwatson#include <sys/rman.h>
45239675Srwatson#include <sys/systm.h>
46239675Srwatson#include <sys/taskqueue.h>
47239675Srwatson
48239675Srwatson#include <machine/bus.h>
49239675Srwatson#include <machine/resource.h>
50239675Srwatson
51239675Srwatson#include <geom/geom_disk.h>
52239675Srwatson
53239675Srwatson#include <dev/altera/sdcard/altera_sdcard.h>
54239675Srwatson
55239675Srwatson/*
56239675Srwatson * Device driver for the Altera University Program Secure Data Card IP Core,
57239675Srwatson * as described in the similarly named SOPC Builder IP Core specification.
58239675Srwatson * This soft core is not a full SD host controller interface (SDHCI) but
59239675Srwatson * instead provides a set of memory mapped registers and memory buffer that
60239675Srwatson * mildly abstract the SD Card protocol, but without providing DMA or
61239675Srwatson * interrupts.  However, it does hide the details of voltage and
62239675Srwatson * communications negotiation.  This driver implements disk(9), but due to the
63239675Srwatson * lack of interrupt support, must rely on timer-driven polling to determine
64239675Srwatson * when I/Os have completed.
65239675Srwatson *
66239675Srwatson * TODO:
67239675Srwatson *
68239675Srwatson * 1. Implement DISKFLAG_CANDELETE / SD Card sector erase support.
69239675Srwatson * 2. Implement d_ident from SD Card CID serial number field.
70239675Srwatson * 3. Handle read-only SD Cards.
71239675Srwatson * 4. Tune timeouts based on real-world SD Card speeds.
72239675Srwatson */
73245380Srwatsondevclass_t	altera_sdcard_devclass;
74239675Srwatson
75239675Srwatsonvoid
76239675Srwatsonaltera_sdcard_attach(struct altera_sdcard_softc *sc)
77239675Srwatson{
78239675Srwatson
79239675Srwatson	ALTERA_SDCARD_LOCK_INIT(sc);
80239675Srwatson	ALTERA_SDCARD_CONDVAR_INIT(sc);
81239675Srwatson	sc->as_disk = NULL;
82239675Srwatson	bioq_init(&sc->as_bioq);
83239675Srwatson	sc->as_currentbio = NULL;
84239675Srwatson	sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
85239675Srwatson	sc->as_taskqueue = taskqueue_create("altera_sdcardc taskq", M_WAITOK,
86239675Srwatson	    taskqueue_thread_enqueue, &sc->as_taskqueue);
87239675Srwatson	taskqueue_start_threads(&sc->as_taskqueue, 1, PI_DISK,
88239675Srwatson	    "altera_sdcardc%d taskqueue", sc->as_unit);
89239675Srwatson	TIMEOUT_TASK_INIT(sc->as_taskqueue, &sc->as_task, 0,
90239675Srwatson	    altera_sdcard_task, sc);
91239675Srwatson
92239675Srwatson	/*
93239675Srwatson	 * Kick off timer-driven processing with a manual poll so that we
94239675Srwatson	 * synchronously detect an already-inserted SD Card during the boot or
95239675Srwatson	 * other driver attach point.
96239675Srwatson	 */
97239675Srwatson	altera_sdcard_task(sc, 1);
98239675Srwatson}
99239675Srwatson
100239675Srwatsonvoid
101239675Srwatsonaltera_sdcard_detach(struct altera_sdcard_softc *sc)
102239675Srwatson{
103239675Srwatson
104239675Srwatson	KASSERT(sc->as_taskqueue != NULL, ("%s: taskqueue not present",
105239675Srwatson	    __func__));
106239675Srwatson
107239675Srwatson	/*
108239675Srwatson	 * Winding down the driver on detach is a bit complex.  Update the
109239675Srwatson	 * flags to indicate that a detach has been requested, and then wait
110239675Srwatson	 * for in-progress I/O to wind down before continuing.
111239675Srwatson	 */
112239675Srwatson	ALTERA_SDCARD_LOCK(sc);
113239675Srwatson	sc->as_flags |= ALTERA_SDCARD_FLAG_DETACHREQ;
114239675Srwatson	while (sc->as_state != ALTERA_SDCARD_STATE_DETACHED)
115239675Srwatson		ALTERA_SDCARD_CONDVAR_WAIT(sc);
116239675Srwatson	ALTERA_SDCARD_UNLOCK(sc);
117239675Srwatson
118239675Srwatson	/*
119239675Srwatson	 * Now wait for the possibly still executing taskqueue to drain.  In
120239675Srwatson	 * principle no more events will be scheduled as we've transitioned to
121239675Srwatson	 * a detached state, but there might still be a request in execution.
122239675Srwatson	 */
123239675Srwatson	while (taskqueue_cancel_timeout(sc->as_taskqueue, &sc->as_task, NULL))
124239675Srwatson		taskqueue_drain_timeout(sc->as_taskqueue, &sc->as_task);
125239675Srwatson
126239675Srwatson	/*
127239675Srwatson	 * Simulate a disk removal if one is present to deal with any pending
128239675Srwatson	 * or queued I/O.
129239675Srwatson	 */
130239675Srwatson	if (sc->as_disk != NULL)
131239675Srwatson		altera_sdcard_disk_remove(sc);
132239675Srwatson	KASSERT(bioq_first(&sc->as_bioq) == NULL,
133239675Srwatson	    ("%s: non-empty bioq", __func__));
134239675Srwatson
135239675Srwatson	/*
136239675Srwatson	 * Free any remaining allocated resources.
137239675Srwatson	 */
138239675Srwatson	taskqueue_free(sc->as_taskqueue);
139239675Srwatson	sc->as_taskqueue = NULL;
140239675Srwatson	ALTERA_SDCARD_CONDVAR_DESTROY(sc);
141239675Srwatson	ALTERA_SDCARD_LOCK_DESTROY(sc);
142239675Srwatson}
143239675Srwatson
144239675Srwatson/*
145239675Srwatson * Set up and start the next I/O.  Transition to the I/O state, but allow the
146239675Srwatson * caller to schedule the next timeout, as this may be called either from an
147239675Srwatson * initial attach context, or from the task queue, which requires different
148239675Srwatson * behaviour.
149239675Srwatson */
150239675Srwatsonstatic void
151239675Srwatsonaltera_sdcard_nextio(struct altera_sdcard_softc *sc)
152239675Srwatson{
153239675Srwatson	struct bio *bp;
154239675Srwatson
155239675Srwatson	ALTERA_SDCARD_LOCK_ASSERT(sc);
156239675Srwatson	KASSERT(sc->as_currentbio == NULL,
157239675Srwatson	    ("%s: bio already active", __func__));
158239675Srwatson
159239675Srwatson	bp = bioq_takefirst(&sc->as_bioq);
160239675Srwatson	if (bp == NULL)
161239675Srwatson		panic("%s: bioq empty", __func__);
162239675Srwatson	altera_sdcard_io_start(sc, bp);
163239675Srwatson	sc->as_state = ALTERA_SDCARD_STATE_IO;
164239675Srwatson}
165239675Srwatson
166239675Srwatsonstatic void
167239675Srwatsonaltera_sdcard_task_nocard(struct altera_sdcard_softc *sc)
168239675Srwatson{
169239675Srwatson
170239675Srwatson	ALTERA_SDCARD_LOCK_ASSERT(sc);
171239675Srwatson
172239675Srwatson	/*
173239675Srwatson	 * Handle device driver detach.
174239675Srwatson	 */
175239675Srwatson	if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
176239675Srwatson		sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
177239675Srwatson		return;
178239675Srwatson	}
179239675Srwatson
180239675Srwatson	/*
181239675Srwatson	 * If there is no card insertion, remain in NOCARD.
182239675Srwatson	 */
183239675Srwatson	if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT))
184239675Srwatson		return;
185239675Srwatson
186239675Srwatson	/*
187239675Srwatson	 * Read the CSD -- it may contain values that the driver can't handle,
188239675Srwatson	 * either because of an unsupported version/feature, or because the
189239675Srwatson	 * card is misbehaving.  This triggers a transition to
190239675Srwatson	 * ALTERA_SDCARD_STATE_BADCARD.  We rely on the CSD read to print a
191239675Srwatson	 * banner about how the card is problematic, since it has more
192239675Srwatson	 * information.  The bad card state allows us to print that banner
193239675Srwatson	 * once rather than each time we notice the card is there, and still
194239675Srwatson	 * bad.
195239675Srwatson	 */
196239675Srwatson	if (altera_sdcard_read_csd(sc) != 0) {
197239675Srwatson		sc->as_state = ALTERA_SDCARD_STATE_BADCARD;
198239675Srwatson		return;
199239675Srwatson	}
200239675Srwatson
201239675Srwatson	/*
202239675Srwatson	 * Process card insertion and upgrade to the IDLE state.
203239675Srwatson	 */
204239675Srwatson	altera_sdcard_disk_insert(sc);
205239675Srwatson	sc->as_state = ALTERA_SDCARD_STATE_IDLE;
206239675Srwatson}
207239675Srwatson
208239675Srwatsonstatic void
209239675Srwatsonaltera_sdcard_task_badcard(struct altera_sdcard_softc *sc)
210239675Srwatson{
211239675Srwatson
212239675Srwatson	ALTERA_SDCARD_LOCK_ASSERT(sc);
213239675Srwatson
214239675Srwatson	/*
215239675Srwatson	 * Handle device driver detach.
216239675Srwatson	 */
217239675Srwatson	if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
218239675Srwatson		sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
219239675Srwatson		return;
220239675Srwatson	}
221239675Srwatson
222239675Srwatson	/*
223239675Srwatson	 * Handle safe card removal -- no teardown is required, just a state
224239675Srwatson	 * transition.
225239675Srwatson	 */
226239675Srwatson	if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT))
227239675Srwatson		sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
228239675Srwatson}
229239675Srwatson
230239675Srwatsonstatic void
231239675Srwatsonaltera_sdcard_task_idle(struct altera_sdcard_softc *sc)
232239675Srwatson{
233239675Srwatson
234239675Srwatson	ALTERA_SDCARD_LOCK_ASSERT(sc);
235239675Srwatson
236239675Srwatson	/*
237239675Srwatson	 * Handle device driver detach.
238239675Srwatson	 */
239239675Srwatson	if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
240239675Srwatson		sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
241239675Srwatson		return;
242239675Srwatson	}
243239675Srwatson
244239675Srwatson	/*
245239675Srwatson	 * Handle safe card removal.
246239675Srwatson	 */
247239675Srwatson	if (!(altera_sdcard_read_asr(sc) & ALTERA_SDCARD_ASR_CARDPRESENT)) {
248239675Srwatson		altera_sdcard_disk_remove(sc);
249239675Srwatson		sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
250239675Srwatson	}
251239675Srwatson}
252239675Srwatson
253239675Srwatsonstatic void
254239675Srwatsonaltera_sdcard_task_io(struct altera_sdcard_softc *sc)
255239675Srwatson{
256239675Srwatson	uint16_t asr;
257239675Srwatson
258239675Srwatson	ALTERA_SDCARD_LOCK_ASSERT(sc);
259239675Srwatson	KASSERT(sc->as_currentbio != NULL, ("%s: no current I/O", __func__));
260239675Srwatson
261239675Srwatson	asr = altera_sdcard_read_asr(sc);
262239675Srwatson
263239675Srwatson	/*
264239675Srwatson	 * Check for unexpected card removal during an I/O.
265239675Srwatson	 */
266239675Srwatson	if (!(asr & ALTERA_SDCARD_ASR_CARDPRESENT)) {
267239675Srwatson		altera_sdcard_disk_remove(sc);
268239675Srwatson		if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ)
269239675Srwatson			sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
270239675Srwatson		else
271239675Srwatson			sc->as_state = ALTERA_SDCARD_STATE_NOCARD;
272239675Srwatson		return;
273239675Srwatson	}
274239675Srwatson
275239675Srwatson	/*
276239675Srwatson	 * If the I/O isn't complete, remain in the IO state without further
277239675Srwatson	 * action, even if DETACHREQ is in flight.
278239675Srwatson	 */
279239675Srwatson	if (asr & ALTERA_SDCARD_ASR_CMDINPROGRESS)
280239675Srwatson		return;
281239675Srwatson
282239675Srwatson	/*
283239675Srwatson	 * Handle various forms of I/O completion, successful and otherwise.
284239675Srwatson	 * The I/O layer may restart the transaction if an error occurred, in
285239675Srwatson	 * which case remain in the IO state and reschedule.
286239675Srwatson	 */
287239675Srwatson	if (!altera_sdcard_io_complete(sc, asr))
288239675Srwatson		return;
289239675Srwatson
290239675Srwatson	/*
291239675Srwatson	 * Now that I/O is complete, process detach requests in preference to
292239675Srwatson	 * starting new I/O.
293239675Srwatson	 */
294239675Srwatson	if (sc->as_flags & ALTERA_SDCARD_FLAG_DETACHREQ) {
295239675Srwatson		sc->as_state = ALTERA_SDCARD_STATE_DETACHED;
296239675Srwatson		return;
297239675Srwatson	}
298239675Srwatson
299239675Srwatson	/*
300239675Srwatson	 * Finally, either start the next I/O or transition to the IDLE state.
301239675Srwatson	 */
302239675Srwatson	if (bioq_first(&sc->as_bioq) != NULL)
303239675Srwatson		altera_sdcard_nextio(sc);
304239675Srwatson	else
305239675Srwatson		sc->as_state = ALTERA_SDCARD_STATE_IDLE;
306239675Srwatson}
307239675Srwatson
308239675Srwatsonstatic void
309239675Srwatsonaltera_sdcard_task_rechedule(struct altera_sdcard_softc *sc)
310239675Srwatson{
311239675Srwatson	int interval;
312239675Srwatson
313239675Srwatson	/*
314239675Srwatson	 * Reschedule based on new state.  Or not, if detaching the device
315239675Srwatson	 * driver.  Treat a bad card as though it were no card at all.
316239675Srwatson	 */
317239675Srwatson	switch (sc->as_state) {
318239675Srwatson	case ALTERA_SDCARD_STATE_NOCARD:
319239675Srwatson	case ALTERA_SDCARD_STATE_BADCARD:
320239675Srwatson		interval = ALTERA_SDCARD_TIMEOUT_NOCARD;
321239675Srwatson		break;
322239675Srwatson
323239675Srwatson	case ALTERA_SDCARD_STATE_IDLE:
324239675Srwatson		interval = ALTERA_SDCARD_TIMEOUT_IDLE;
325239675Srwatson		break;
326239675Srwatson
327239675Srwatson	case ALTERA_SDCARD_STATE_IO:
328239675Srwatson		if (sc->as_flags & ALTERA_SDCARD_FLAG_IOERROR)
329239675Srwatson			interval = ALTERA_SDCARD_TIMEOUT_IOERROR;
330239675Srwatson		else
331239675Srwatson			interval = ALTERA_SDCARD_TIMEOUT_IO;
332239675Srwatson		break;
333239675Srwatson
334239675Srwatson	default:
335239675Srwatson		panic("%s: invalid exit state %d", __func__, sc->as_state);
336239675Srwatson	}
337239675Srwatson	taskqueue_enqueue_timeout(sc->as_taskqueue, &sc->as_task, interval);
338239675Srwatson}
339239675Srwatson
340239675Srwatson/*
341239675Srwatson * Because the Altera SD Card IP Core doesn't support interrupts, we do all
342239675Srwatson * asynchronous work from a timeout.  Poll at two different rates -- an
343239675Srwatson * infrequent check for card insertion status changes, and a frequent one for
344239675Srwatson * I/O completion.  The task should never start in DETACHED, as that would
345239675Srwatson * imply that a previous instance failed to cancel rather than reschedule.
346239675Srwatson */
347239675Srwatsonvoid
348239675Srwatsonaltera_sdcard_task(void *arg, int pending)
349239675Srwatson{
350239675Srwatson	struct altera_sdcard_softc *sc;
351239675Srwatson
352239675Srwatson	sc = arg;
353239675Srwatson	KASSERT(sc->as_state != ALTERA_SDCARD_STATE_DETACHED,
354239675Srwatson	    ("%s: already in detached", __func__));
355239675Srwatson
356239675Srwatson	ALTERA_SDCARD_LOCK(sc);
357239675Srwatson	switch (sc->as_state) {
358239675Srwatson	case ALTERA_SDCARD_STATE_NOCARD:
359239675Srwatson		altera_sdcard_task_nocard(sc);
360239675Srwatson		break;
361239675Srwatson
362239675Srwatson	case ALTERA_SDCARD_STATE_BADCARD:
363239675Srwatson		altera_sdcard_task_badcard(sc);
364239675Srwatson		break;
365239675Srwatson
366239675Srwatson	case ALTERA_SDCARD_STATE_IDLE:
367239675Srwatson		altera_sdcard_task_idle(sc);
368239675Srwatson		break;
369239675Srwatson
370239675Srwatson	case ALTERA_SDCARD_STATE_IO:
371239675Srwatson		altera_sdcard_task_io(sc);
372239675Srwatson		break;
373239675Srwatson
374239675Srwatson	default:
375239675Srwatson		panic("%s: invalid enter state %d", __func__, sc->as_state);
376239675Srwatson	}
377239675Srwatson
378239675Srwatson	/*
379239675Srwatson	 * If we have transitioned to DETACHED, signal the detach thread and
380239675Srwatson	 * cancel the timeout-driven task.  Otherwise reschedule on an
381239675Srwatson	 * appropriate timeout.
382239675Srwatson	 */
383239675Srwatson	if (sc->as_state == ALTERA_SDCARD_STATE_DETACHED)
384239675Srwatson		ALTERA_SDCARD_CONDVAR_SIGNAL(sc);
385239675Srwatson	else
386239675Srwatson		altera_sdcard_task_rechedule(sc);
387239675Srwatson	ALTERA_SDCARD_UNLOCK(sc);
388239675Srwatson}
389239675Srwatson
390239675Srwatsonvoid
391239675Srwatsonaltera_sdcard_start(struct altera_sdcard_softc *sc)
392239675Srwatson{
393239675Srwatson
394239675Srwatson	ALTERA_SDCARD_LOCK_ASSERT(sc);
395239675Srwatson
396239675Srwatson	KASSERT(sc->as_state == ALTERA_SDCARD_STATE_IDLE,
397239675Srwatson	    ("%s: starting when not IDLE", __func__));
398239675Srwatson
399239675Srwatson	taskqueue_cancel_timeout(sc->as_taskqueue, &sc->as_task, NULL);
400239675Srwatson	altera_sdcard_nextio(sc);
401239675Srwatson	taskqueue_enqueue_timeout(sc->as_taskqueue, &sc->as_task,
402239675Srwatson	    ALTERA_SDCARD_TIMEOUT_IO);
403239675Srwatson}
404