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