xref: /netbsd-src/sys/dev/sdmmc/sdmmc.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
1*c7fb772bSthorpej /*	$NetBSD: sdmmc.c,v 1.43 2021/08/07 16:19:16 thorpej Exp $	*/
2e0297d1eSnonaka /*	$OpenBSD: sdmmc.c,v 1.18 2009/01/09 10:58:38 jsg Exp $	*/
3e0297d1eSnonaka 
4e0297d1eSnonaka /*
5e0297d1eSnonaka  * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
6e0297d1eSnonaka  *
7e0297d1eSnonaka  * Permission to use, copy, modify, and distribute this software for any
8e0297d1eSnonaka  * purpose with or without fee is hereby granted, provided that the above
9e0297d1eSnonaka  * copyright notice and this permission notice appear in all copies.
10e0297d1eSnonaka  *
11e0297d1eSnonaka  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12e0297d1eSnonaka  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13e0297d1eSnonaka  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14e0297d1eSnonaka  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15e0297d1eSnonaka  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16e0297d1eSnonaka  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17e0297d1eSnonaka  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18e0297d1eSnonaka  */
19e0297d1eSnonaka 
20e0297d1eSnonaka /*-
212388feefSnonaka  * Copyright (C) 2007, 2008, 2009 NONAKA Kimihiro <nonaka@netbsd.org>
22e0297d1eSnonaka  * All rights reserved.
23e0297d1eSnonaka  *
24e0297d1eSnonaka  * Redistribution and use in source and binary forms, with or without
25e0297d1eSnonaka  * modification, are permitted provided that the following conditions
26e0297d1eSnonaka  * are met:
27e0297d1eSnonaka  * 1. Redistributions of source code must retain the above copyright
28e0297d1eSnonaka  *    notice, this list of conditions and the following disclaimer.
29e0297d1eSnonaka  * 2. Redistributions in binary form must reproduce the above copyright
30e0297d1eSnonaka  *    notice, this list of conditions and the following disclaimer in the
31e0297d1eSnonaka  *    documentation and/or other materials provided with the distribution.
32e0297d1eSnonaka  *
332388feefSnonaka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
342388feefSnonaka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
352388feefSnonaka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
362388feefSnonaka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
372388feefSnonaka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
382388feefSnonaka  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
392388feefSnonaka  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
402388feefSnonaka  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
412388feefSnonaka  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
422388feefSnonaka  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43e0297d1eSnonaka  */
44e0297d1eSnonaka 
45e0297d1eSnonaka /*
46e0297d1eSnonaka  * Host controller independent SD/MMC bus driver based on information
47e0297d1eSnonaka  * from SanDisk SD Card Product Manual Revision 2.2 (SanDisk), SDIO
48e0297d1eSnonaka  * Simple Specification Version 1.0 (SDIO) and the Linux "mmc" driver.
49e0297d1eSnonaka  */
50e0297d1eSnonaka 
51e0297d1eSnonaka #include <sys/cdefs.h>
52*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: sdmmc.c,v 1.43 2021/08/07 16:19:16 thorpej Exp $");
535e2f4552Smatt 
545e2f4552Smatt #ifdef _KERNEL_OPT
555e2f4552Smatt #include "opt_sdmmc.h"
565e2f4552Smatt #endif
57e0297d1eSnonaka 
58e0297d1eSnonaka #include <sys/param.h>
59e0297d1eSnonaka #include <sys/device.h>
60e0297d1eSnonaka #include <sys/kernel.h>
61e0297d1eSnonaka #include <sys/kthread.h>
62e0297d1eSnonaka #include <sys/malloc.h>
63e0297d1eSnonaka #include <sys/proc.h>
64e0297d1eSnonaka #include <sys/systm.h>
657e6a2d09Snonaka #include <sys/callout.h>
66e0297d1eSnonaka 
67542a9f73Skiyohara #include <machine/vmparam.h>
68542a9f73Skiyohara 
69e0297d1eSnonaka #include <dev/sdmmc/sdmmc_ioreg.h>
70e0297d1eSnonaka #include <dev/sdmmc/sdmmcchip.h>
71e0297d1eSnonaka #include <dev/sdmmc/sdmmcreg.h>
72e0297d1eSnonaka #include <dev/sdmmc/sdmmcvar.h>
73e0297d1eSnonaka 
74e0297d1eSnonaka #ifdef SDMMC_DEBUG
75c3077021Snonaka int sdmmcdebug = 0;
76e0297d1eSnonaka static void sdmmc_dump_command(struct sdmmc_softc *, struct sdmmc_command *);
77e0297d1eSnonaka #define DPRINTF(n,s)	do { if ((n) <= sdmmcdebug) printf s; } while (0)
78e0297d1eSnonaka #else
79e0297d1eSnonaka #define	DPRINTF(n,s)	do {} while (0)
80e0297d1eSnonaka #endif
81e0297d1eSnonaka 
82e0297d1eSnonaka #define	DEVNAME(sc)	SDMMCDEVNAME(sc)
83e0297d1eSnonaka 
84e0297d1eSnonaka static int sdmmc_match(device_t, cfdata_t, void *);
85e0297d1eSnonaka static void sdmmc_attach(device_t, device_t, void *);
86e0297d1eSnonaka static int sdmmc_detach(device_t, int);
87e0297d1eSnonaka 
88e0297d1eSnonaka CFATTACH_DECL_NEW(sdmmc, sizeof(struct sdmmc_softc),
89e0297d1eSnonaka     sdmmc_match, sdmmc_attach, sdmmc_detach, NULL);
90e0297d1eSnonaka 
91e0297d1eSnonaka static void sdmmc_doattach(device_t);
92e0297d1eSnonaka static void sdmmc_task_thread(void *);
93e0297d1eSnonaka static void sdmmc_discover_task(void *);
947e6a2d09Snonaka static void sdmmc_polling_card(void *);
95e0297d1eSnonaka static void sdmmc_card_attach(struct sdmmc_softc *);
96e0297d1eSnonaka static void sdmmc_card_detach(struct sdmmc_softc *, int);
97e0297d1eSnonaka static int sdmmc_print(void *, const char *);
98e0297d1eSnonaka static int sdmmc_enable(struct sdmmc_softc *);
99e0297d1eSnonaka static void sdmmc_disable(struct sdmmc_softc *);
100e0297d1eSnonaka static int sdmmc_scan(struct sdmmc_softc *);
101e0297d1eSnonaka static int sdmmc_init(struct sdmmc_softc *);
102e0297d1eSnonaka 
103e0297d1eSnonaka static int
sdmmc_match(device_t parent,cfdata_t cf,void * aux)104e0297d1eSnonaka sdmmc_match(device_t parent, cfdata_t cf, void *aux)
105e0297d1eSnonaka {
106e0297d1eSnonaka 	struct sdmmcbus_attach_args *saa = (struct sdmmcbus_attach_args *)aux;
107e0297d1eSnonaka 
108e0297d1eSnonaka 	if (strcmp(saa->saa_busname, cf->cf_name) == 0)
109e0297d1eSnonaka 		return 1;
110e0297d1eSnonaka 	return 0;
111e0297d1eSnonaka }
112e0297d1eSnonaka 
113e0297d1eSnonaka static void
sdmmc_attach(device_t parent,device_t self,void * aux)114e0297d1eSnonaka sdmmc_attach(device_t parent, device_t self, void *aux)
115e0297d1eSnonaka {
116e0297d1eSnonaka 	struct sdmmc_softc *sc = device_private(self);
117e0297d1eSnonaka 	struct sdmmcbus_attach_args *saa = (struct sdmmcbus_attach_args *)aux;
118e0297d1eSnonaka 	int error;
119e0297d1eSnonaka 
120e0297d1eSnonaka 	aprint_normal("\n");
121e0297d1eSnonaka 	aprint_naive("\n");
122e0297d1eSnonaka 
123e0297d1eSnonaka 	sc->sc_dev = self;
124e0297d1eSnonaka 	sc->sc_sct = saa->saa_sct;
1257e6a2d09Snonaka 	sc->sc_spi_sct = saa->saa_spi_sct;
126e0297d1eSnonaka 	sc->sc_sch = saa->saa_sch;
127e0297d1eSnonaka 	sc->sc_dmat = saa->saa_dmat;
128e0297d1eSnonaka 	sc->sc_clkmin = saa->saa_clkmin;
129e0297d1eSnonaka 	sc->sc_clkmax = saa->saa_clkmax;
130e0297d1eSnonaka 	sc->sc_busclk = sc->sc_clkmax;
131e0297d1eSnonaka 	sc->sc_buswidth = 1;
132e0297d1eSnonaka 	sc->sc_caps = saa->saa_caps;
133af57fdaaShkenken 	sc->sc_max_seg = saa->saa_max_seg ? saa->saa_max_seg : MAXPHYS;
134e0297d1eSnonaka 
135e0297d1eSnonaka 	if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
136e0297d1eSnonaka 		error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, SDMMC_MAXNSEGS,
137af57fdaaShkenken 		    sc->sc_max_seg, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmap);
138e0297d1eSnonaka 		if (error) {
139e0297d1eSnonaka 			aprint_error_dev(sc->sc_dev,
140e0297d1eSnonaka 			    "couldn't create dma map. (error=%d)\n", error);
141e0297d1eSnonaka 			return;
142e0297d1eSnonaka 		}
143e0297d1eSnonaka 	}
144e0297d1eSnonaka 
145e0297d1eSnonaka 	SIMPLEQ_INIT(&sc->sf_head);
146e0297d1eSnonaka 	TAILQ_INIT(&sc->sc_tskq);
147e0297d1eSnonaka 	TAILQ_INIT(&sc->sc_intrq);
148e0297d1eSnonaka 
149e0297d1eSnonaka 	sdmmc_init_task(&sc->sc_discover_task, sdmmc_discover_task, sc);
150e0297d1eSnonaka 	sdmmc_init_task(&sc->sc_intr_task, sdmmc_intr_task, sc);
151e0297d1eSnonaka 
152ab5c3234Smlelstv 	mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE);
153e0297d1eSnonaka 	mutex_init(&sc->sc_tskq_mtx, MUTEX_DEFAULT, IPL_SDMMC);
154e0297d1eSnonaka 	mutex_init(&sc->sc_discover_task_mtx, MUTEX_DEFAULT, IPL_SDMMC);
155e0297d1eSnonaka 	cv_init(&sc->sc_tskq_cv, "mmctaskq");
156e0297d1eSnonaka 
1577f43a6e6Sjmcneill 	evcnt_attach_dynamic(&sc->sc_ev_xfer, EVCNT_TYPE_MISC, NULL,
1587f43a6e6Sjmcneill 	    device_xname(self), "xfer");
1597f43a6e6Sjmcneill 	evcnt_attach_dynamic(&sc->sc_ev_xfer_aligned[0], EVCNT_TYPE_MISC,
1607f43a6e6Sjmcneill 	    &sc->sc_ev_xfer, device_xname(self), "xfer 512");
1617f43a6e6Sjmcneill 	evcnt_attach_dynamic(&sc->sc_ev_xfer_aligned[1], EVCNT_TYPE_MISC,
1627f43a6e6Sjmcneill 	    &sc->sc_ev_xfer, device_xname(self), "xfer 1024");
1637f43a6e6Sjmcneill 	evcnt_attach_dynamic(&sc->sc_ev_xfer_aligned[2], EVCNT_TYPE_MISC,
1647f43a6e6Sjmcneill 	    &sc->sc_ev_xfer, device_xname(self), "xfer 2048");
1657f43a6e6Sjmcneill 	evcnt_attach_dynamic(&sc->sc_ev_xfer_aligned[3], EVCNT_TYPE_MISC,
1667f43a6e6Sjmcneill 	    &sc->sc_ev_xfer, device_xname(self), "xfer 4096");
1677f43a6e6Sjmcneill 	evcnt_attach_dynamic(&sc->sc_ev_xfer_aligned[4], EVCNT_TYPE_MISC,
1687f43a6e6Sjmcneill 	    &sc->sc_ev_xfer, device_xname(self), "xfer 8192");
1697f43a6e6Sjmcneill 	evcnt_attach_dynamic(&sc->sc_ev_xfer_aligned[5], EVCNT_TYPE_MISC,
1707f43a6e6Sjmcneill 	    &sc->sc_ev_xfer, device_xname(self), "xfer 16384");
1717f43a6e6Sjmcneill 	evcnt_attach_dynamic(&sc->sc_ev_xfer_aligned[6], EVCNT_TYPE_MISC,
1727f43a6e6Sjmcneill 	    &sc->sc_ev_xfer, device_xname(self), "xfer 32768");
1737f43a6e6Sjmcneill 	evcnt_attach_dynamic(&sc->sc_ev_xfer_aligned[7], EVCNT_TYPE_MISC,
1747f43a6e6Sjmcneill 	    &sc->sc_ev_xfer, device_xname(self), "xfer 65536");
1757f43a6e6Sjmcneill 	evcnt_attach_dynamic(&sc->sc_ev_xfer_unaligned, EVCNT_TYPE_MISC,
1767f43a6e6Sjmcneill 	    &sc->sc_ev_xfer, device_xname(self), "xfer unaligned");
1777f43a6e6Sjmcneill 	evcnt_attach_dynamic(&sc->sc_ev_xfer_error, EVCNT_TYPE_MISC,
1787f43a6e6Sjmcneill 	    &sc->sc_ev_xfer, device_xname(self), "xfer error");
1797f43a6e6Sjmcneill 
180c3077021Snonaka 	if (ISSET(sc->sc_caps, SMC_CAPS_POLL_CARD_DET)) {
181c3077021Snonaka 		callout_init(&sc->sc_card_detect_ch, 0);
182c3077021Snonaka 		callout_reset(&sc->sc_card_detect_ch, hz,
183c3077021Snonaka 		    sdmmc_polling_card, sc);
184c3077021Snonaka 	}
185c3077021Snonaka 
186e0297d1eSnonaka 	if (!pmf_device_register(self, NULL, NULL)) {
187e0297d1eSnonaka 		aprint_error_dev(self, "couldn't establish power handler\n");
188e0297d1eSnonaka 	}
189e0297d1eSnonaka 
190e0297d1eSnonaka 	SET(sc->sc_flags, SMF_INITED);
191e0297d1eSnonaka 
192e0297d1eSnonaka 	/*
193e0297d1eSnonaka 	 * Create the event thread that will attach and detach cards
194e0297d1eSnonaka 	 * and perform other lengthy operations.
195e0297d1eSnonaka 	 */
19611beb626Schristos 	config_pending_incr(self);
197e0297d1eSnonaka 	config_interrupts(self, sdmmc_doattach);
198e0297d1eSnonaka }
199e0297d1eSnonaka 
200e0297d1eSnonaka static int
sdmmc_detach(device_t self,int flags)201e0297d1eSnonaka sdmmc_detach(device_t self, int flags)
202e0297d1eSnonaka {
203e0297d1eSnonaka 	struct sdmmc_softc *sc = device_private(self);
2047f43a6e6Sjmcneill 	int error, i;
205e0297d1eSnonaka 
206e0297d1eSnonaka 	mutex_enter(&sc->sc_tskq_mtx);
207e0297d1eSnonaka 	sc->sc_dying = 1;
208e0297d1eSnonaka 	cv_signal(&sc->sc_tskq_cv);
209e0297d1eSnonaka 	while (sc->sc_tskq_lwp != NULL)
210e0297d1eSnonaka 		cv_wait(&sc->sc_tskq_cv, &sc->sc_tskq_mtx);
211e0297d1eSnonaka 	mutex_exit(&sc->sc_tskq_mtx);
212e0297d1eSnonaka 
213e0297d1eSnonaka 	pmf_device_deregister(self);
214e0297d1eSnonaka 
215e0297d1eSnonaka 	error = config_detach_children(self, flags);
216e0297d1eSnonaka 	if (error)
217e0297d1eSnonaka 		return error;
21855591e4aSjakllsch 
21955591e4aSjakllsch 	if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
22055591e4aSjakllsch 		bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap);
22155591e4aSjakllsch 		bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmap);
22255591e4aSjakllsch 	}
22355591e4aSjakllsch 
224c3077021Snonaka 	if (ISSET(sc->sc_caps, SMC_CAPS_POLL_CARD_DET)) {
225433c9fcdSozaki-r 		callout_halt(&sc->sc_card_detect_ch, NULL);
226c3077021Snonaka 		callout_destroy(&sc->sc_card_detect_ch);
227c3077021Snonaka 	}
228c3077021Snonaka 
2290723c0bfSriastradh 	sdmmc_del_task(sc, &sc->sc_intr_task, NULL);
2300723c0bfSriastradh 	sdmmc_del_task(sc, &sc->sc_discover_task, NULL);
2310723c0bfSriastradh 
232c3077021Snonaka 	cv_destroy(&sc->sc_tskq_cv);
233c3077021Snonaka 	mutex_destroy(&sc->sc_discover_task_mtx);
234c3077021Snonaka 	mutex_destroy(&sc->sc_tskq_mtx);
235c3077021Snonaka 	mutex_destroy(&sc->sc_mtx);
236c3077021Snonaka 
2377f43a6e6Sjmcneill 	evcnt_detach(&sc->sc_ev_xfer_error);
2387f43a6e6Sjmcneill 	evcnt_detach(&sc->sc_ev_xfer_unaligned);
2397f43a6e6Sjmcneill 	for (i = 0; i < __arraycount(sc->sc_ev_xfer_aligned); i++)
2407f43a6e6Sjmcneill 		evcnt_detach(&sc->sc_ev_xfer_aligned[i]);
2417f43a6e6Sjmcneill 	evcnt_detach(&sc->sc_ev_xfer);
2427f43a6e6Sjmcneill 
243e0297d1eSnonaka 	return 0;
244e0297d1eSnonaka }
245e0297d1eSnonaka 
246e0297d1eSnonaka static void
sdmmc_doattach(device_t dev)247e0297d1eSnonaka sdmmc_doattach(device_t dev)
248e0297d1eSnonaka {
249e0297d1eSnonaka 	struct sdmmc_softc *sc = device_private(dev);
250e0297d1eSnonaka 
2511d9b6487Sjmcneill 	if (kthread_create(PRI_SOFTBIO, 0, NULL,
252e0297d1eSnonaka 	    sdmmc_task_thread, sc, &sc->sc_tskq_lwp, "%s", device_xname(dev))) {
253e0297d1eSnonaka 		aprint_error_dev(dev, "couldn't create task thread\n");
254e0297d1eSnonaka 	}
255e0297d1eSnonaka }
256e0297d1eSnonaka 
257e0297d1eSnonaka void
sdmmc_add_task(struct sdmmc_softc * sc,struct sdmmc_task * task)258e0297d1eSnonaka sdmmc_add_task(struct sdmmc_softc *sc, struct sdmmc_task *task)
259e0297d1eSnonaka {
260e0297d1eSnonaka 
261e0297d1eSnonaka 	mutex_enter(&sc->sc_tskq_mtx);
2620723c0bfSriastradh 	if (task->sc == sc) {
2630723c0bfSriastradh 		KASSERT(task->onqueue);
2640723c0bfSriastradh 		goto out;
2650723c0bfSriastradh 	}
2660723c0bfSriastradh 	KASSERT(task->sc == NULL);
2670723c0bfSriastradh 	KASSERT(!task->onqueue);
268e0297d1eSnonaka 	task->onqueue = 1;
269e0297d1eSnonaka 	task->sc = sc;
270e0297d1eSnonaka 	TAILQ_INSERT_TAIL(&sc->sc_tskq, task, next);
271e0297d1eSnonaka 	cv_broadcast(&sc->sc_tskq_cv);
2720723c0bfSriastradh out:	mutex_exit(&sc->sc_tskq_mtx);
273e0297d1eSnonaka }
274e0297d1eSnonaka 
275e0297d1eSnonaka static inline void
sdmmc_del_task1(struct sdmmc_softc * sc,struct sdmmc_task * task)276e0297d1eSnonaka sdmmc_del_task1(struct sdmmc_softc *sc, struct sdmmc_task *task)
277e0297d1eSnonaka {
278e0297d1eSnonaka 
2790723c0bfSriastradh 	KASSERT(mutex_owned(&sc->sc_tskq_mtx));
2800723c0bfSriastradh 
281e0297d1eSnonaka 	TAILQ_REMOVE(&sc->sc_tskq, task, next);
282e0297d1eSnonaka 	task->sc = NULL;
283e0297d1eSnonaka 	task->onqueue = 0;
284e0297d1eSnonaka }
285e0297d1eSnonaka 
2860723c0bfSriastradh bool
sdmmc_del_task(struct sdmmc_softc * sc,struct sdmmc_task * task,kmutex_t * interlock)2870723c0bfSriastradh sdmmc_del_task(struct sdmmc_softc *sc, struct sdmmc_task *task,
2880723c0bfSriastradh     kmutex_t *interlock)
289e0297d1eSnonaka {
2900723c0bfSriastradh 	bool cancelled;
291e0297d1eSnonaka 
2920723c0bfSriastradh 	KASSERT(interlock == NULL || mutex_owned(interlock));
2930723c0bfSriastradh 
294e0297d1eSnonaka 	mutex_enter(&sc->sc_tskq_mtx);
2950723c0bfSriastradh 	if (task->sc == sc) {
2960723c0bfSriastradh 		KASSERT(task->onqueue);
2970723c0bfSriastradh 		KASSERT(sc->sc_curtask != task);
298e0297d1eSnonaka 		sdmmc_del_task1(sc, task);
2990723c0bfSriastradh 		cancelled = true;
3000723c0bfSriastradh 	} else {
3010723c0bfSriastradh 		KASSERT(task->sc == NULL);
3020723c0bfSriastradh 		KASSERT(!task->onqueue);
303fec89f5fSmlelstv 		if (interlock != NULL)
3040723c0bfSriastradh 			mutex_exit(interlock);
3050723c0bfSriastradh 		while (sc->sc_curtask == task) {
3060723c0bfSriastradh 			KASSERT(curlwp != sc->sc_tskq_lwp);
3070723c0bfSriastradh 			cv_wait(&sc->sc_tskq_cv, &sc->sc_tskq_mtx);
308e0297d1eSnonaka 		}
309fec89f5fSmlelstv 		if (interlock == NULL || !mutex_tryenter(interlock)) {
3100723c0bfSriastradh 			mutex_exit(&sc->sc_tskq_mtx);
311fec89f5fSmlelstv 			if (interlock != NULL)
3120723c0bfSriastradh 				mutex_enter(interlock);
3130723c0bfSriastradh 			mutex_enter(&sc->sc_tskq_mtx);
3140723c0bfSriastradh 		}
3150723c0bfSriastradh 		cancelled = false;
3160723c0bfSriastradh 	}
3170723c0bfSriastradh 	mutex_exit(&sc->sc_tskq_mtx);
3180723c0bfSriastradh 
3190723c0bfSriastradh 	KASSERT(interlock == NULL || mutex_owned(interlock));
3200723c0bfSriastradh 
3210723c0bfSriastradh 	return cancelled;
322e0297d1eSnonaka }
323e0297d1eSnonaka 
324e0297d1eSnonaka static void
sdmmc_task_thread(void * arg)325e0297d1eSnonaka sdmmc_task_thread(void *arg)
326e0297d1eSnonaka {
327e0297d1eSnonaka 	struct sdmmc_softc *sc = (struct sdmmc_softc *)arg;
328e0297d1eSnonaka 	struct sdmmc_task *task;
329e0297d1eSnonaka 
330e0297d1eSnonaka 	sdmmc_discover_task(sc);
33111beb626Schristos 	config_pending_decr(sc->sc_dev);
332e0297d1eSnonaka 
333e0297d1eSnonaka 	mutex_enter(&sc->sc_tskq_mtx);
334e0297d1eSnonaka 	for (;;) {
335e0297d1eSnonaka 		task = TAILQ_FIRST(&sc->sc_tskq);
336e0297d1eSnonaka 		if (task != NULL) {
337e0297d1eSnonaka 			sdmmc_del_task1(sc, task);
3380723c0bfSriastradh 			sc->sc_curtask = task;
339e0297d1eSnonaka 			mutex_exit(&sc->sc_tskq_mtx);
340e0297d1eSnonaka 			(*task->func)(task->arg);
341e0297d1eSnonaka 			mutex_enter(&sc->sc_tskq_mtx);
3420723c0bfSriastradh 			sc->sc_curtask = NULL;
3430723c0bfSriastradh 			cv_broadcast(&sc->sc_tskq_cv);
344e0297d1eSnonaka 		} else {
345e0297d1eSnonaka 			/* Check for the exit condition. */
346e0297d1eSnonaka 			if (sc->sc_dying)
347e0297d1eSnonaka 				break;
348e0297d1eSnonaka 			cv_wait(&sc->sc_tskq_cv, &sc->sc_tskq_mtx);
349e0297d1eSnonaka 		}
350e0297d1eSnonaka 	}
351e0297d1eSnonaka 	/* time to die. */
352e0297d1eSnonaka 	sc->sc_dying = 0;
35357df4d0bSjakllsch 	if (ISSET(sc->sc_flags, SMF_CARD_PRESENT)) {
35457df4d0bSjakllsch 		/*
35557df4d0bSjakllsch 		 * sdmmc_card_detach() may issue commands,
35657df4d0bSjakllsch 		 * so temporarily drop the interrupt-blocking lock.
35757df4d0bSjakllsch 		 */
35857df4d0bSjakllsch 		mutex_exit(&sc->sc_tskq_mtx);
359e0297d1eSnonaka 		sdmmc_card_detach(sc, DETACH_FORCE);
36057df4d0bSjakllsch 		mutex_enter(&sc->sc_tskq_mtx);
36157df4d0bSjakllsch 	}
362e0297d1eSnonaka 	sc->sc_tskq_lwp = NULL;
363e0297d1eSnonaka 	cv_broadcast(&sc->sc_tskq_cv);
364e0297d1eSnonaka 	mutex_exit(&sc->sc_tskq_mtx);
365e0297d1eSnonaka 	kthread_exit(0);
366e0297d1eSnonaka }
367e0297d1eSnonaka 
368e0297d1eSnonaka void
sdmmc_needs_discover(device_t dev)369e0297d1eSnonaka sdmmc_needs_discover(device_t dev)
370e0297d1eSnonaka {
371e0297d1eSnonaka 	struct sdmmc_softc *sc = device_private(dev);
372e0297d1eSnonaka 
373e0297d1eSnonaka 	if (!ISSET(sc->sc_flags, SMF_INITED))
374e0297d1eSnonaka 		return;
375e0297d1eSnonaka 
376e0297d1eSnonaka 	sdmmc_add_task(sc, &sc->sc_discover_task);
377e0297d1eSnonaka }
378e0297d1eSnonaka 
379e0297d1eSnonaka static void
sdmmc_discover_task(void * arg)380e0297d1eSnonaka sdmmc_discover_task(void *arg)
381e0297d1eSnonaka {
382e0297d1eSnonaka 	struct sdmmc_softc *sc = (struct sdmmc_softc *)arg;
3834fbe659aSmlelstv 	int card_detect, card_present;
384e0297d1eSnonaka 
3854fbe659aSmlelstv 	mutex_enter(&sc->sc_discover_task_mtx);
3864fbe659aSmlelstv 	card_detect = sdmmc_chip_card_detect(sc->sc_sct, sc->sc_sch);
3874fbe659aSmlelstv 	card_present = ISSET(sc->sc_flags, SMF_CARD_PRESENT);
3884fbe659aSmlelstv 	if (card_detect)
389e0297d1eSnonaka 		SET(sc->sc_flags, SMF_CARD_PRESENT);
3904fbe659aSmlelstv 	else
3914fbe659aSmlelstv 		CLR(sc->sc_flags, SMF_CARD_PRESENT);
3924fbe659aSmlelstv 	mutex_exit(&sc->sc_discover_task_mtx);
3934fbe659aSmlelstv 
3944fbe659aSmlelstv 	if (card_detect) {
3954fbe659aSmlelstv 		if (!card_present) {
396e0297d1eSnonaka 			sdmmc_card_attach(sc);
3974fbe659aSmlelstv 			mutex_enter(&sc->sc_discover_task_mtx);
398ea26f433Skiyohara 			if (!ISSET(sc->sc_flags, SMF_CARD_ATTACHED))
399ea26f433Skiyohara 				CLR(sc->sc_flags, SMF_CARD_PRESENT);
4004fbe659aSmlelstv 			mutex_exit(&sc->sc_discover_task_mtx);
401e0297d1eSnonaka 		}
402e0297d1eSnonaka 	} else {
4034fbe659aSmlelstv 		if (card_present)
404cd889bc3Sjakllsch 			sdmmc_card_detach(sc, DETACH_FORCE);
405e0297d1eSnonaka 	}
406e0297d1eSnonaka }
407e0297d1eSnonaka 
4087e6a2d09Snonaka static void
sdmmc_polling_card(void * arg)4097e6a2d09Snonaka sdmmc_polling_card(void *arg)
4107e6a2d09Snonaka {
4117e6a2d09Snonaka 	struct sdmmc_softc *sc = (struct sdmmc_softc *)arg;
4124fbe659aSmlelstv 	int card_detect, card_present;
4137e6a2d09Snonaka 
4144fbe659aSmlelstv 	mutex_enter(&sc->sc_discover_task_mtx);
4157e6a2d09Snonaka 	card_detect = sdmmc_chip_card_detect(sc->sc_sct, sc->sc_sch);
4164fbe659aSmlelstv 	card_present = ISSET(sc->sc_flags, SMF_CARD_PRESENT);
4174fbe659aSmlelstv 	mutex_exit(&sc->sc_discover_task_mtx);
4184fbe659aSmlelstv 
4194fbe659aSmlelstv 	if (card_detect != card_present)
4207e6a2d09Snonaka 		sdmmc_needs_discover(sc->sc_dev);
4217e6a2d09Snonaka 
4227e6a2d09Snonaka 	callout_schedule(&sc->sc_card_detect_ch, hz);
4237e6a2d09Snonaka }
4247e6a2d09Snonaka 
425e0297d1eSnonaka /*
426e0297d1eSnonaka  * Called from process context when a card is present.
427e0297d1eSnonaka  */
428e0297d1eSnonaka static void
sdmmc_card_attach(struct sdmmc_softc * sc)429e0297d1eSnonaka sdmmc_card_attach(struct sdmmc_softc *sc)
430e0297d1eSnonaka {
431e0297d1eSnonaka 	struct sdmmc_function *sf;
432e0297d1eSnonaka 	struct sdmmc_attach_args saa;
433e0297d1eSnonaka 	int error;
434e0297d1eSnonaka 
435e0297d1eSnonaka 	DPRINTF(1,("%s: attach card\n", DEVNAME(sc)));
436e0297d1eSnonaka 
437e0297d1eSnonaka 	CLR(sc->sc_flags, SMF_CARD_ATTACHED);
438e0297d1eSnonaka 
4397003a765Snonaka 	sdmmc_chip_hw_reset(sc->sc_sct, sc->sc_sch);
4407003a765Snonaka 
441e0297d1eSnonaka 	/*
442e0297d1eSnonaka 	 * Power up the card (or card stack).
443e0297d1eSnonaka 	 */
444e0297d1eSnonaka 	error = sdmmc_enable(sc);
445e0297d1eSnonaka 	if (error) {
446cf8c0613Snonaka 		if (!ISSET(sc->sc_caps, SMC_CAPS_POLL_CARD_DET)) {
44784efaa5dSmatt 			aprint_error_dev(sc->sc_dev, "couldn't enable card: %d\n", error);
448cf8c0613Snonaka 		}
449e0297d1eSnonaka 		goto err;
450e0297d1eSnonaka 	}
451e0297d1eSnonaka 
452e0297d1eSnonaka 	/*
453e0297d1eSnonaka 	 * Scan for I/O functions and memory cards on the bus,
454e0297d1eSnonaka 	 * allocating a sdmmc_function structure for each.
455e0297d1eSnonaka 	 */
456e0297d1eSnonaka 	error = sdmmc_scan(sc);
457e0297d1eSnonaka 	if (error) {
458e0297d1eSnonaka 		aprint_error_dev(sc->sc_dev, "no functions\n");
459e0297d1eSnonaka 		goto err;
460e0297d1eSnonaka 	}
461e0297d1eSnonaka 
462e0297d1eSnonaka 	/*
4637e6a2d09Snonaka 	 * Initialize the I/O functions and memory cards.
4647e6a2d09Snonaka 	 */
4657e6a2d09Snonaka 	error = sdmmc_init(sc);
4667e6a2d09Snonaka 	if (error) {
4677e6a2d09Snonaka 		aprint_error_dev(sc->sc_dev, "init failed\n");
4687e6a2d09Snonaka 		goto err;
4697e6a2d09Snonaka 	}
4707e6a2d09Snonaka 
471e0297d1eSnonaka 	SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
472e0297d1eSnonaka 		if (ISSET(sc->sc_flags, SMF_IO_MODE) && sf->number < 1)
473e0297d1eSnonaka 			continue;
474e0297d1eSnonaka 
475e0297d1eSnonaka 		memset(&saa, 0, sizeof saa);
476e0297d1eSnonaka 		saa.manufacturer = sf->cis.manufacturer;
477e0297d1eSnonaka 		saa.product = sf->cis.product;
478b5a696dfSkiyohara 		saa.interface = sf->interface;
479e0297d1eSnonaka 		saa.sf = sf;
480e0297d1eSnonaka 
481e0297d1eSnonaka 		sf->child =
482*c7fb772bSthorpej 		    config_found(sc->sc_dev, &saa, sdmmc_print, CFARGS_NONE);
483e0297d1eSnonaka 	}
484e0297d1eSnonaka 
485e0297d1eSnonaka 	SET(sc->sc_flags, SMF_CARD_ATTACHED);
486e0297d1eSnonaka 	return;
487e0297d1eSnonaka 
488e0297d1eSnonaka err:
489cd889bc3Sjakllsch 	sdmmc_card_detach(sc, DETACH_FORCE);
490e0297d1eSnonaka }
491e0297d1eSnonaka 
492e0297d1eSnonaka /*
493e0297d1eSnonaka  * Called from process context with DETACH_* flags from <sys/device.h>
494e0297d1eSnonaka  * when cards are gone.
495e0297d1eSnonaka  */
496e0297d1eSnonaka static void
sdmmc_card_detach(struct sdmmc_softc * sc,int flags)497e0297d1eSnonaka sdmmc_card_detach(struct sdmmc_softc *sc, int flags)
498e0297d1eSnonaka {
499e0297d1eSnonaka 	struct sdmmc_function *sf, *sfnext;
500e0297d1eSnonaka 
501e0297d1eSnonaka 	DPRINTF(1,("%s: detach card\n", DEVNAME(sc)));
502e0297d1eSnonaka 
503e0297d1eSnonaka 	if (ISSET(sc->sc_flags, SMF_CARD_ATTACHED)) {
504e0297d1eSnonaka 		SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
505e0297d1eSnonaka 			if (sf->child != NULL) {
506cd889bc3Sjakllsch 				config_detach(sf->child, DETACH_FORCE);
507e0297d1eSnonaka 				sf->child = NULL;
508e0297d1eSnonaka 			}
509e0297d1eSnonaka 		}
510e0297d1eSnonaka 
511e0297d1eSnonaka 		KASSERT(TAILQ_EMPTY(&sc->sc_intrq));
512e0297d1eSnonaka 
513e0297d1eSnonaka 		CLR(sc->sc_flags, SMF_CARD_ATTACHED);
514e0297d1eSnonaka 	}
515e0297d1eSnonaka 
516e0297d1eSnonaka 	/* Power down. */
517e0297d1eSnonaka 	sdmmc_disable(sc);
518e0297d1eSnonaka 
519e0297d1eSnonaka 	/* Free all sdmmc_function structures. */
520e0297d1eSnonaka 	for (sf = SIMPLEQ_FIRST(&sc->sf_head); sf != NULL; sf = sfnext) {
521e0297d1eSnonaka 		sfnext = SIMPLEQ_NEXT(sf, sf_list);
522e0297d1eSnonaka 		sdmmc_function_free(sf);
523e0297d1eSnonaka 	}
524e0297d1eSnonaka 	SIMPLEQ_INIT(&sc->sf_head);
525e0297d1eSnonaka 	sc->sc_function_count = 0;
526e0297d1eSnonaka 	sc->sc_fn0 = NULL;
527e0297d1eSnonaka }
528e0297d1eSnonaka 
529e0297d1eSnonaka static int
sdmmc_print(void * aux,const char * pnp)530e0297d1eSnonaka sdmmc_print(void *aux, const char *pnp)
531e0297d1eSnonaka {
532e0297d1eSnonaka 	struct sdmmc_attach_args *sa = aux;
533e0297d1eSnonaka 	struct sdmmc_function *sf = sa->sf;
534e0297d1eSnonaka 	struct sdmmc_cis *cis = &sf->sc->sc_fn0->cis;
535c3316e27Skiyohara 	int i, x;
536e0297d1eSnonaka 
537e0297d1eSnonaka 	if (pnp) {
538e0297d1eSnonaka 		if (sf->number == 0)
539e0297d1eSnonaka 			return QUIET;
540e0297d1eSnonaka 
541e0297d1eSnonaka 		for (i = 0; i < 4 && cis->cis1_info[i]; i++)
542e0297d1eSnonaka 			printf("%s%s", i ? ", " : "\"", cis->cis1_info[i]);
543e0297d1eSnonaka 		if (i != 0)
544e0297d1eSnonaka 			printf("\"");
545e0297d1eSnonaka 
546c3316e27Skiyohara 		if ((cis->manufacturer != SDMMC_VENDOR_INVALID &&
547c3316e27Skiyohara 		    cis->product != SDMMC_PRODUCT_INVALID) ||
548c3316e27Skiyohara 		    sa->interface != SD_IO_SFIC_NO_STANDARD) {
549c3316e27Skiyohara 			x = !!(cis->manufacturer != SDMMC_VENDOR_INVALID);
550c3316e27Skiyohara 			x += !!(cis->product != SDMMC_PRODUCT_INVALID);
551c3316e27Skiyohara 			x += !!(sa->interface != SD_IO_SFIC_NO_STANDARD);
552e0297d1eSnonaka 			printf("%s(", i ? " " : "");
553e0297d1eSnonaka 			if (cis->manufacturer != SDMMC_VENDOR_INVALID)
554e0297d1eSnonaka 				printf("manufacturer 0x%x%s",
555c3316e27Skiyohara 				    cis->manufacturer, (--x == 0) ?  "" : ", ");
556e0297d1eSnonaka 			if (cis->product != SDMMC_PRODUCT_INVALID)
557c3316e27Skiyohara 				printf("product 0x%x%s",
558c3316e27Skiyohara 				    cis->product, (--x == 0) ?  "" : ", ");
559c3316e27Skiyohara 			if (sa->interface != SD_IO_SFIC_NO_STANDARD)
560c3316e27Skiyohara 				printf("standard function interface code 0x%x",
561c3316e27Skiyohara 				    sf->interface);
562e0297d1eSnonaka 			printf(")");
56352f4944bSmlelstv 			i = 1;
564e0297d1eSnonaka 		}
565e0297d1eSnonaka 		printf("%sat %s", i ? " " : "", pnp);
566e0297d1eSnonaka 	}
567e0297d1eSnonaka 	if (sf->number > 0)
568e0297d1eSnonaka 		printf(" function %d", sf->number);
569e0297d1eSnonaka 
570e0297d1eSnonaka 	if (!pnp) {
571e0297d1eSnonaka 		for (i = 0; i < 3 && cis->cis1_info[i]; i++)
572e0297d1eSnonaka 			printf("%s%s", i ? ", " : " \"", cis->cis1_info[i]);
573e0297d1eSnonaka 		if (i != 0)
574e0297d1eSnonaka 			printf("\"");
575e0297d1eSnonaka 	}
576e0297d1eSnonaka 	return UNCONF;
577e0297d1eSnonaka }
578e0297d1eSnonaka 
579e0297d1eSnonaka static int
sdmmc_enable(struct sdmmc_softc * sc)580e0297d1eSnonaka sdmmc_enable(struct sdmmc_softc *sc)
581e0297d1eSnonaka {
582e0297d1eSnonaka 	int error;
583e0297d1eSnonaka 
584e0297d1eSnonaka 	/*
585e0297d1eSnonaka 	 * Calculate the equivalent of the card OCR from the host
586e0297d1eSnonaka 	 * capabilities and select the maximum supported bus voltage.
587e0297d1eSnonaka 	 */
588e0297d1eSnonaka 	error = sdmmc_chip_bus_power(sc->sc_sct, sc->sc_sch,
589e0297d1eSnonaka 	    sdmmc_chip_host_ocr(sc->sc_sct, sc->sc_sch));
590e0297d1eSnonaka 	if (error) {
591e0297d1eSnonaka 		aprint_error_dev(sc->sc_dev, "couldn't supply bus power\n");
592e0297d1eSnonaka 		goto out;
593e0297d1eSnonaka 	}
594e0297d1eSnonaka 
595e0297d1eSnonaka 	/*
596e0297d1eSnonaka 	 * Select the minimum clock frequency.
597e0297d1eSnonaka 	 */
5989350eba5Sjmcneill 	error = sdmmc_chip_bus_clock(sc->sc_sct, sc->sc_sch, SDMMC_SDCLK_400K,
5999350eba5Sjmcneill 	    false);
600e0297d1eSnonaka 	if (error) {
601e0297d1eSnonaka 		aprint_error_dev(sc->sc_dev, "couldn't supply clock\n");
602e0297d1eSnonaka 		goto out;
603e0297d1eSnonaka 	}
604e0297d1eSnonaka 
605e0297d1eSnonaka 	/* XXX wait for card to power up */
606c301b5e4Smlelstv 	sdmmc_pause(100000, NULL);
607e0297d1eSnonaka 
6087e6a2d09Snonaka 	if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) {
609e0297d1eSnonaka 		/* Initialize SD I/O card function(s). */
610e0297d1eSnonaka 		error = sdmmc_io_enable(sc);
611b6b4315aSjakllsch 		if (error) {
612e4c8fcd8Sjakllsch 			DPRINTF(1, ("%s: sdmmc_io_enable failed %d\n", DEVNAME(sc), error));
613e0297d1eSnonaka 			goto out;
6147e6a2d09Snonaka 		}
615b6b4315aSjakllsch 	}
616e0297d1eSnonaka 
617e0297d1eSnonaka 	/* Initialize SD/MMC memory card(s). */
6187e6a2d09Snonaka 	if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE) ||
619b6b4315aSjakllsch 	    ISSET(sc->sc_flags, SMF_MEM_MODE)) {
620e0297d1eSnonaka 		error = sdmmc_mem_enable(sc);
621b6b4315aSjakllsch 		if (error) {
622e4c8fcd8Sjakllsch 			DPRINTF(1, ("%s: sdmmc_mem_enable failed %d\n", DEVNAME(sc), error));
623b6b4315aSjakllsch 			goto out;
624b6b4315aSjakllsch 		}
625b6b4315aSjakllsch 	}
626e0297d1eSnonaka 
627e0297d1eSnonaka out:
628e0297d1eSnonaka 	if (error)
629e0297d1eSnonaka 		sdmmc_disable(sc);
630e0297d1eSnonaka 	return error;
631e0297d1eSnonaka }
632e0297d1eSnonaka 
633e0297d1eSnonaka static void
sdmmc_disable(struct sdmmc_softc * sc)634e0297d1eSnonaka sdmmc_disable(struct sdmmc_softc *sc)
635e0297d1eSnonaka {
636e0297d1eSnonaka 	/* XXX complete commands if card is still present. */
637e0297d1eSnonaka 
6387e6a2d09Snonaka 	if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) {
639e0297d1eSnonaka 		/* Make sure no card is still selected. */
640e0297d1eSnonaka 		(void)sdmmc_select_card(sc, NULL);
6417e6a2d09Snonaka 	}
642e0297d1eSnonaka 
643e0297d1eSnonaka 	/* Turn off bus power and clock. */
644e0297d1eSnonaka 	(void)sdmmc_chip_bus_width(sc->sc_sct, sc->sc_sch, 1);
6459350eba5Sjmcneill 	(void)sdmmc_chip_bus_clock(sc->sc_sct, sc->sc_sch, SDMMC_SDCLK_OFF,
6469350eba5Sjmcneill 	    false);
647e0297d1eSnonaka 	(void)sdmmc_chip_bus_power(sc->sc_sct, sc->sc_sch, 0);
6482431d55eSnonaka 	sc->sc_busclk = sc->sc_clkmax;
649e0297d1eSnonaka }
650e0297d1eSnonaka 
651e0297d1eSnonaka /*
652e0297d1eSnonaka  * Set the lowest bus voltage supported by the card and the host.
653e0297d1eSnonaka  */
654e0297d1eSnonaka int
sdmmc_set_bus_power(struct sdmmc_softc * sc,uint32_t host_ocr,uint32_t card_ocr)655e0297d1eSnonaka sdmmc_set_bus_power(struct sdmmc_softc *sc, uint32_t host_ocr,
656e0297d1eSnonaka     uint32_t card_ocr)
657e0297d1eSnonaka {
658e0297d1eSnonaka 	uint32_t bit;
659e0297d1eSnonaka 
660e0297d1eSnonaka 	/* Mask off unsupported voltage levels and select the lowest. */
661e0297d1eSnonaka 	DPRINTF(1,("%s: host_ocr=%x ", DEVNAME(sc), host_ocr));
662e0297d1eSnonaka 	host_ocr &= card_ocr;
663e0297d1eSnonaka 	for (bit = 4; bit < 23; bit++) {
664e0297d1eSnonaka 		if (ISSET(host_ocr, (1 << bit))) {
665e0297d1eSnonaka 			host_ocr &= (3 << bit);
666e0297d1eSnonaka 			break;
667e0297d1eSnonaka 		}
668e0297d1eSnonaka 	}
669e0297d1eSnonaka 	DPRINTF(1,("card_ocr=%x new_ocr=%x\n", card_ocr, host_ocr));
670e0297d1eSnonaka 
671e0297d1eSnonaka 	if (host_ocr == 0 ||
672e0297d1eSnonaka 	    sdmmc_chip_bus_power(sc->sc_sct, sc->sc_sch, host_ocr) != 0)
673e0297d1eSnonaka 		return 1;
674e0297d1eSnonaka 	return 0;
675e0297d1eSnonaka }
676e0297d1eSnonaka 
677e0297d1eSnonaka struct sdmmc_function *
sdmmc_function_alloc(struct sdmmc_softc * sc)678e0297d1eSnonaka sdmmc_function_alloc(struct sdmmc_softc *sc)
679e0297d1eSnonaka {
680e0297d1eSnonaka 	struct sdmmc_function *sf;
681e0297d1eSnonaka 
682e0297d1eSnonaka 	sf = malloc(sizeof *sf, M_DEVBUF, M_WAITOK|M_ZERO);
683e0297d1eSnonaka 	if (sf == NULL) {
684e0297d1eSnonaka 		aprint_error_dev(sc->sc_dev,
685e0297d1eSnonaka 		    "couldn't alloc memory (sdmmc function)\n");
686e0297d1eSnonaka 		return NULL;
687e0297d1eSnonaka 	}
688e0297d1eSnonaka 
689e0297d1eSnonaka 	sf->sc = sc;
690e0297d1eSnonaka 	sf->number = -1;
691e0297d1eSnonaka 	sf->cis.manufacturer = SDMMC_VENDOR_INVALID;
692e0297d1eSnonaka 	sf->cis.product = SDMMC_PRODUCT_INVALID;
693e0297d1eSnonaka 	sf->cis.function = SDMMC_FUNCTION_INVALID;
694833f4534Snonaka 	sf->width = 1;
6955ab3b2b2Smlelstv 	sf->blklen = sdmmc_chip_host_maxblklen(sc->sc_sct, sc->sc_sch);
696e0297d1eSnonaka 
697542a9f73Skiyohara 	if (ISSET(sc->sc_flags, SMF_MEM_MODE) &&
698542a9f73Skiyohara 	    ISSET(sc->sc_caps, SMC_CAPS_DMA) &&
699542a9f73Skiyohara 	    !ISSET(sc->sc_caps, SMC_CAPS_MULTI_SEG_DMA)) {
700542a9f73Skiyohara 		bus_dma_segment_t ds;
701542a9f73Skiyohara 		int rseg, error;
702542a9f73Skiyohara 
703ce4ff06eSnonaka 		error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, 1,
704ce4ff06eSnonaka 		    MAXPHYS, 0, BUS_DMA_WAITOK, &sf->bbuf_dmap);
705542a9f73Skiyohara 		if (error)
706542a9f73Skiyohara 			goto fail1;
707ce4ff06eSnonaka 		error = bus_dmamem_alloc(sc->sc_dmat, MAXPHYS,
708542a9f73Skiyohara 		    PAGE_SIZE, 0, &ds, 1, &rseg, BUS_DMA_WAITOK);
709542a9f73Skiyohara 		if (error)
710542a9f73Skiyohara 			goto fail2;
711ce4ff06eSnonaka 		error = bus_dmamem_map(sc->sc_dmat, &ds, 1, MAXPHYS,
712542a9f73Skiyohara 		    &sf->bbuf, BUS_DMA_WAITOK);
713542a9f73Skiyohara 		if (error)
714542a9f73Skiyohara 			goto fail3;
715542a9f73Skiyohara 		error = bus_dmamap_load(sc->sc_dmat, sf->bbuf_dmap,
716ce4ff06eSnonaka 		    sf->bbuf, MAXPHYS, NULL,
717542a9f73Skiyohara 		    BUS_DMA_WAITOK|BUS_DMA_READ|BUS_DMA_WRITE);
718ce4ff06eSnonaka 		if (error)
719ce4ff06eSnonaka 			goto fail4;
720ce4ff06eSnonaka 		error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, 1,
721ce4ff06eSnonaka 		    MAXPHYS, 0, BUS_DMA_WAITOK, &sf->sseg_dmap);
722542a9f73Skiyohara 		if (!error)
723542a9f73Skiyohara 			goto out;
724542a9f73Skiyohara 
725ce4ff06eSnonaka 		bus_dmamap_unload(sc->sc_dmat, sf->bbuf_dmap);
726ce4ff06eSnonaka fail4:
727ce4ff06eSnonaka 		bus_dmamem_unmap(sc->sc_dmat, sf->bbuf, MAXPHYS);
728542a9f73Skiyohara fail3:
729542a9f73Skiyohara 		bus_dmamem_free(sc->sc_dmat, &ds, 1);
730542a9f73Skiyohara fail2:
731542a9f73Skiyohara 		bus_dmamap_destroy(sc->sc_dmat, sf->bbuf_dmap);
732542a9f73Skiyohara fail1:
733542a9f73Skiyohara 		free(sf, M_DEVBUF);
734542a9f73Skiyohara 		sf = NULL;
735542a9f73Skiyohara 	}
736542a9f73Skiyohara out:
737542a9f73Skiyohara 
738e0297d1eSnonaka 	return sf;
739e0297d1eSnonaka }
740e0297d1eSnonaka 
741e0297d1eSnonaka void
sdmmc_function_free(struct sdmmc_function * sf)742e0297d1eSnonaka sdmmc_function_free(struct sdmmc_function *sf)
743e0297d1eSnonaka {
744542a9f73Skiyohara 	struct sdmmc_softc *sc = sf->sc;
745542a9f73Skiyohara 
746542a9f73Skiyohara 	if (ISSET(sc->sc_flags, SMF_MEM_MODE) &&
747542a9f73Skiyohara 	    ISSET(sc->sc_caps, SMC_CAPS_DMA) &&
748542a9f73Skiyohara 	    !ISSET(sc->sc_caps, SMC_CAPS_MULTI_SEG_DMA)) {
749ce4ff06eSnonaka 		bus_dmamap_destroy(sc->sc_dmat, sf->sseg_dmap);
750542a9f73Skiyohara 		bus_dmamap_unload(sc->sc_dmat, sf->bbuf_dmap);
751ce4ff06eSnonaka 		bus_dmamem_unmap(sc->sc_dmat, sf->bbuf, MAXPHYS);
752542a9f73Skiyohara 		bus_dmamem_free(sc->sc_dmat,
753542a9f73Skiyohara 		    sf->bbuf_dmap->dm_segs, sf->bbuf_dmap->dm_nsegs);
754542a9f73Skiyohara 		bus_dmamap_destroy(sc->sc_dmat, sf->bbuf_dmap);
755542a9f73Skiyohara 	}
756e0297d1eSnonaka 
757e0297d1eSnonaka 	free(sf, M_DEVBUF);
758e0297d1eSnonaka }
759e0297d1eSnonaka 
760e0297d1eSnonaka /*
761e0297d1eSnonaka  * Scan for I/O functions and memory cards on the bus, allocating a
762e0297d1eSnonaka  * sdmmc_function structure for each.
763e0297d1eSnonaka  */
764e0297d1eSnonaka static int
sdmmc_scan(struct sdmmc_softc * sc)765e0297d1eSnonaka sdmmc_scan(struct sdmmc_softc *sc)
766e0297d1eSnonaka {
767e0297d1eSnonaka 
7687e6a2d09Snonaka 	if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) {
769e0297d1eSnonaka 		/* Scan for I/O functions. */
770e0297d1eSnonaka 		if (ISSET(sc->sc_flags, SMF_IO_MODE))
771e0297d1eSnonaka 			sdmmc_io_scan(sc);
7727e6a2d09Snonaka 	}
773e0297d1eSnonaka 
774e0297d1eSnonaka 	/* Scan for memory cards on the bus. */
775e0297d1eSnonaka 	if (ISSET(sc->sc_flags, SMF_MEM_MODE))
776e0297d1eSnonaka 		sdmmc_mem_scan(sc);
777e0297d1eSnonaka 
778e0297d1eSnonaka 	/* There should be at least one function now. */
779e0297d1eSnonaka 	if (SIMPLEQ_EMPTY(&sc->sf_head)) {
780e0297d1eSnonaka 		aprint_error_dev(sc->sc_dev, "couldn't identify card\n");
781e0297d1eSnonaka 		return 1;
782e0297d1eSnonaka 	}
783e0297d1eSnonaka 	return 0;
784e0297d1eSnonaka }
785e0297d1eSnonaka 
786e0297d1eSnonaka /*
787e0297d1eSnonaka  * Initialize all the distinguished functions of the card, be it I/O
788e0297d1eSnonaka  * or memory functions.
789e0297d1eSnonaka  */
790e0297d1eSnonaka static int
sdmmc_init(struct sdmmc_softc * sc)791e0297d1eSnonaka sdmmc_init(struct sdmmc_softc *sc)
792e0297d1eSnonaka {
793e0297d1eSnonaka 	struct sdmmc_function *sf;
794e0297d1eSnonaka 
795e0297d1eSnonaka 	/* Initialize all identified card functions. */
796e0297d1eSnonaka 	SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
7977e6a2d09Snonaka 		if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) {
798e0297d1eSnonaka 			if (ISSET(sc->sc_flags, SMF_IO_MODE) &&
799e0297d1eSnonaka 			    sdmmc_io_init(sc, sf) != 0) {
8007e6a2d09Snonaka 				aprint_error_dev(sc->sc_dev,
8017e6a2d09Snonaka 				    "i/o init failed\n");
8027e6a2d09Snonaka 			}
803e0297d1eSnonaka 		}
804e0297d1eSnonaka 
805e0297d1eSnonaka 		if (ISSET(sc->sc_flags, SMF_MEM_MODE) &&
806e0297d1eSnonaka 		    sdmmc_mem_init(sc, sf) != 0) {
807e0297d1eSnonaka 			aprint_error_dev(sc->sc_dev, "mem init failed\n");
808e0297d1eSnonaka 		}
809e0297d1eSnonaka 	}
810e0297d1eSnonaka 
811e0297d1eSnonaka 	/* Any good functions left after initialization? */
812e0297d1eSnonaka 	SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
813e0297d1eSnonaka 		if (!ISSET(sf->flags, SFF_ERROR))
814e0297d1eSnonaka 			return 0;
815e0297d1eSnonaka 	}
816e0297d1eSnonaka 
817e0297d1eSnonaka 	/* No, we should probably power down the card. */
818e0297d1eSnonaka 	return 1;
819e0297d1eSnonaka }
820e0297d1eSnonaka 
821e0297d1eSnonaka void
sdmmc_delay(u_int usecs)822e0297d1eSnonaka sdmmc_delay(u_int usecs)
823e0297d1eSnonaka {
824e0297d1eSnonaka 
825e0297d1eSnonaka 	delay(usecs);
826e0297d1eSnonaka }
827e0297d1eSnonaka 
828c301b5e4Smlelstv void
sdmmc_pause(u_int usecs,kmutex_t * lock)829c301b5e4Smlelstv sdmmc_pause(u_int usecs, kmutex_t *lock)
830c301b5e4Smlelstv {
831c301b5e4Smlelstv 	unsigned ticks = mstohz(usecs/1000);
832c301b5e4Smlelstv 
833c301b5e4Smlelstv 	if (cold || ticks < 1)
834c301b5e4Smlelstv 		delay(usecs);
835c301b5e4Smlelstv 	else
836c301b5e4Smlelstv 		kpause("sdmmcdelay", false, ticks, lock);
837c301b5e4Smlelstv }
838c301b5e4Smlelstv 
839e0297d1eSnonaka int
sdmmc_app_command(struct sdmmc_softc * sc,struct sdmmc_function * sf,struct sdmmc_command * cmd)8407e6a2d09Snonaka sdmmc_app_command(struct sdmmc_softc *sc, struct sdmmc_function *sf, struct sdmmc_command *cmd)
841e0297d1eSnonaka {
842e0297d1eSnonaka 	struct sdmmc_command acmd;
843e0297d1eSnonaka 	int error;
844e0297d1eSnonaka 
845e0297d1eSnonaka 	DPRINTF(1,("sdmmc_app_command: start\n"));
846e0297d1eSnonaka 
847e0297d1eSnonaka 	/* Don't lock */
848e0297d1eSnonaka 
849e0297d1eSnonaka 	memset(&acmd, 0, sizeof(acmd));
850e0297d1eSnonaka 	acmd.c_opcode = MMC_APP_CMD;
851c3077021Snonaka 	acmd.c_arg = (sf != NULL) ? (sf->rca << 16) : 0;
852898693b4Smlelstv 	acmd.c_flags = SCF_CMD_AC | SCF_RSP_R1 | SCF_RSP_SPI_R1 | (cmd->c_flags & SCF_TOUT_OK);
853e0297d1eSnonaka 
854e0297d1eSnonaka 	error = sdmmc_mmc_command(sc, &acmd);
855e0297d1eSnonaka 	if (error == 0) {
8567e6a2d09Snonaka 		if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE) &&
8577e6a2d09Snonaka 		    !ISSET(MMC_R1(acmd.c_resp), MMC_R1_APP_CMD)) {
858e0297d1eSnonaka 			/* Card does not support application commands. */
859e0297d1eSnonaka 			error = ENODEV;
860e0297d1eSnonaka 		} else {
861e0297d1eSnonaka 			error = sdmmc_mmc_command(sc, cmd);
862e0297d1eSnonaka 		}
863e0297d1eSnonaka 	}
864e0297d1eSnonaka 	DPRINTF(1,("sdmmc_app_command: done (error=%d)\n", error));
865e0297d1eSnonaka 	return error;
866e0297d1eSnonaka }
867e0297d1eSnonaka 
868e0297d1eSnonaka /*
869e0297d1eSnonaka  * Execute MMC command and data transfers.  All interactions with the
870e0297d1eSnonaka  * host controller to complete the command happen in the context of
871e0297d1eSnonaka  * the current process.
872e0297d1eSnonaka  */
873e0297d1eSnonaka int
sdmmc_mmc_command(struct sdmmc_softc * sc,struct sdmmc_command * cmd)874e0297d1eSnonaka sdmmc_mmc_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd)
875e0297d1eSnonaka {
876e0297d1eSnonaka 	int error;
877e0297d1eSnonaka 
8787e6a2d09Snonaka 	DPRINTF(1,("sdmmc_mmc_command: cmd=%d, arg=%#x, flags=%#x\n",
879e0297d1eSnonaka 	    cmd->c_opcode, cmd->c_arg, cmd->c_flags));
880e0297d1eSnonaka 
881e0297d1eSnonaka 	/* Don't lock */
882e0297d1eSnonaka 
883e0297d1eSnonaka #if defined(DIAGNOSTIC) || defined(SDMMC_DEBUG)
8847e6a2d09Snonaka 	if (cmd->c_data && !ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) {
885e0297d1eSnonaka 		if (sc->sc_card == NULL)
886e0297d1eSnonaka 			panic("%s: deselected card\n", DEVNAME(sc));
887e0297d1eSnonaka 	}
888e0297d1eSnonaka #endif
889e0297d1eSnonaka 
890e0297d1eSnonaka 	sdmmc_chip_exec_command(sc->sc_sct, sc->sc_sch, cmd);
891e0297d1eSnonaka 
892e0297d1eSnonaka #ifdef SDMMC_DEBUG
893e0297d1eSnonaka 	sdmmc_dump_command(sc, cmd);
894e0297d1eSnonaka #endif
895e0297d1eSnonaka 
896e0297d1eSnonaka 	error = cmd->c_error;
897e0297d1eSnonaka 
898e0297d1eSnonaka 	DPRINTF(1,("sdmmc_mmc_command: error=%d\n", error));
899e0297d1eSnonaka 
9000051183dSmlelstv 	if (error &&
9010051183dSmlelstv 	   (cmd->c_opcode == MMC_READ_BLOCK_MULTIPLE ||
9020051183dSmlelstv 	    cmd->c_opcode == MMC_WRITE_BLOCK_MULTIPLE)) {
9030051183dSmlelstv 		sdmmc_stop_transmission(sc);
9040051183dSmlelstv 	}
9050051183dSmlelstv 
906e0297d1eSnonaka 	return error;
907e0297d1eSnonaka }
908e0297d1eSnonaka 
909e0297d1eSnonaka /*
9100051183dSmlelstv  * Send the "STOP TRANSMISSION" command
9110051183dSmlelstv  */
9120051183dSmlelstv void
sdmmc_stop_transmission(struct sdmmc_softc * sc)9130051183dSmlelstv sdmmc_stop_transmission(struct sdmmc_softc *sc)
9140051183dSmlelstv {
9150051183dSmlelstv 	struct sdmmc_command cmd;
9160051183dSmlelstv 
9170051183dSmlelstv 	DPRINTF(1,("sdmmc_stop_transmission\n"));
9180051183dSmlelstv 
9190051183dSmlelstv 	/* Don't lock */
9200051183dSmlelstv 
9210051183dSmlelstv 	memset(&cmd, 0, sizeof(cmd));
9220051183dSmlelstv 	cmd.c_opcode = MMC_STOP_TRANSMISSION;
9230051183dSmlelstv 	cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1B | SCF_RSP_SPI_R1B;
9240051183dSmlelstv 
9250051183dSmlelstv 	(void)sdmmc_mmc_command(sc, &cmd);
9260051183dSmlelstv }
9270051183dSmlelstv 
9280051183dSmlelstv /*
929e0297d1eSnonaka  * Send the "GO IDLE STATE" command.
930e0297d1eSnonaka  */
931e0297d1eSnonaka void
sdmmc_go_idle_state(struct sdmmc_softc * sc)932e0297d1eSnonaka sdmmc_go_idle_state(struct sdmmc_softc *sc)
933e0297d1eSnonaka {
934e0297d1eSnonaka 	struct sdmmc_command cmd;
935e0297d1eSnonaka 
936e0297d1eSnonaka 	DPRINTF(1,("sdmmc_go_idle_state\n"));
937e0297d1eSnonaka 
938e0297d1eSnonaka 	/* Don't lock */
939e0297d1eSnonaka 
940e0297d1eSnonaka 	memset(&cmd, 0, sizeof(cmd));
941e0297d1eSnonaka 	cmd.c_opcode = MMC_GO_IDLE_STATE;
9427e6a2d09Snonaka 	cmd.c_flags = SCF_CMD_BC | SCF_RSP_R0 | SCF_RSP_SPI_R1;
943e0297d1eSnonaka 
944e0297d1eSnonaka 	(void)sdmmc_mmc_command(sc, &cmd);
945e0297d1eSnonaka }
946e0297d1eSnonaka 
947e0297d1eSnonaka /*
948e0297d1eSnonaka  * Retrieve (SD) or set (MMC) the relative card address (RCA).
949e0297d1eSnonaka  */
950e0297d1eSnonaka int
sdmmc_set_relative_addr(struct sdmmc_softc * sc,struct sdmmc_function * sf)951e0297d1eSnonaka sdmmc_set_relative_addr(struct sdmmc_softc *sc, struct sdmmc_function *sf)
952e0297d1eSnonaka {
953e0297d1eSnonaka 	struct sdmmc_command cmd;
954e0297d1eSnonaka 	int error;
955e0297d1eSnonaka 
956e0297d1eSnonaka 	/* Don't lock */
957e0297d1eSnonaka 
9581c4e8673Smlelstv 	if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) {
959c301b5e4Smlelstv 		device_printf(sc->sc_dev,
9601c4e8673Smlelstv 			"sdmmc_set_relative_addr: SMC_CAPS_SPI_MODE set");
9617e6a2d09Snonaka 		return EIO;
9621c4e8673Smlelstv 	}
9637e6a2d09Snonaka 
964e0297d1eSnonaka 	memset(&cmd, 0, sizeof(cmd));
965e0297d1eSnonaka 	if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
966e0297d1eSnonaka 		cmd.c_opcode = SD_SEND_RELATIVE_ADDR;
967e0297d1eSnonaka 		cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R6;
968e0297d1eSnonaka 	} else {
969e0297d1eSnonaka 		cmd.c_opcode = MMC_SET_RELATIVE_ADDR;
970e0297d1eSnonaka 		cmd.c_arg = MMC_ARG_RCA(sf->rca);
971e0297d1eSnonaka 		cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
972e0297d1eSnonaka 	}
973e0297d1eSnonaka 	error = sdmmc_mmc_command(sc, &cmd);
974e0297d1eSnonaka 	if (error)
975e0297d1eSnonaka 		return error;
976e0297d1eSnonaka 
977e0297d1eSnonaka 	if (ISSET(sc->sc_flags, SMF_SD_MODE))
978e0297d1eSnonaka 		sf->rca = SD_R6_RCA(cmd.c_resp);
979e0297d1eSnonaka 
980e0297d1eSnonaka 	return 0;
981e0297d1eSnonaka }
982e0297d1eSnonaka 
983e0297d1eSnonaka int
sdmmc_select_card(struct sdmmc_softc * sc,struct sdmmc_function * sf)984e0297d1eSnonaka sdmmc_select_card(struct sdmmc_softc *sc, struct sdmmc_function *sf)
985e0297d1eSnonaka {
986e0297d1eSnonaka 	struct sdmmc_command cmd;
987e0297d1eSnonaka 	int error;
988e0297d1eSnonaka 
989e0297d1eSnonaka 	/* Don't lock */
990e0297d1eSnonaka 
9911c4e8673Smlelstv 	if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) {
992c301b5e4Smlelstv 		device_printf(sc->sc_dev,
9931c4e8673Smlelstv 			"sdmmc_select_card: SMC_CAPS_SPI_MODE set");
9947e6a2d09Snonaka 		return EIO;
9951c4e8673Smlelstv 	}
9967e6a2d09Snonaka 
997e0297d1eSnonaka 	if (sc->sc_card == sf
998e0297d1eSnonaka 	 || (sf && sc->sc_card && sc->sc_card->rca == sf->rca)) {
999e0297d1eSnonaka 		sc->sc_card = sf;
1000e0297d1eSnonaka 		return 0;
1001e0297d1eSnonaka 	}
1002e0297d1eSnonaka 
1003e0297d1eSnonaka 	memset(&cmd, 0, sizeof(cmd));
1004e0297d1eSnonaka 	cmd.c_opcode = MMC_SELECT_CARD;
1005e0297d1eSnonaka 	cmd.c_arg = (sf == NULL) ? 0 : MMC_ARG_RCA(sf->rca);
1006e0297d1eSnonaka 	cmd.c_flags = SCF_CMD_AC | ((sf == NULL) ? SCF_RSP_R0 : SCF_RSP_R1);
1007e0297d1eSnonaka 	error = sdmmc_mmc_command(sc, &cmd);
1008e0297d1eSnonaka 	if (error == 0 || sf == NULL)
1009e0297d1eSnonaka 		sc->sc_card = sf;
1010e0297d1eSnonaka 
1011c301b5e4Smlelstv 	if (error) {
1012c301b5e4Smlelstv 		device_printf(sc->sc_dev,
1013c301b5e4Smlelstv 			"sdmmc_select_card: error %d", error);
1014c301b5e4Smlelstv 	}
1015c301b5e4Smlelstv 
1016e0297d1eSnonaka 	return error;
1017e0297d1eSnonaka }
1018e0297d1eSnonaka 
1019e0297d1eSnonaka #ifdef SDMMC_DEBUG
1020e0297d1eSnonaka static void
sdmmc_dump_command(struct sdmmc_softc * sc,struct sdmmc_command * cmd)1021e0297d1eSnonaka sdmmc_dump_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd)
1022e0297d1eSnonaka {
1023e0297d1eSnonaka 	int i;
1024e0297d1eSnonaka 
10257e6a2d09Snonaka 	DPRINTF(1,("%s: cmd %u arg=%#x data=%p dlen=%d flags=%#x (error %d)\n",
1026e0297d1eSnonaka 	    DEVNAME(sc), cmd->c_opcode, cmd->c_arg, cmd->c_data,
10277e6a2d09Snonaka 	    cmd->c_datalen, cmd->c_flags, cmd->c_error));
1028e0297d1eSnonaka 
1029e0297d1eSnonaka 	if (cmd->c_error || sdmmcdebug < 1)
1030e0297d1eSnonaka 		return;
1031e0297d1eSnonaka 
1032e0297d1eSnonaka 	aprint_normal_dev(sc->sc_dev, "resp=");
1033e0297d1eSnonaka 	if (ISSET(cmd->c_flags, SCF_RSP_136))
1034e0297d1eSnonaka 		for (i = 0; i < sizeof cmd->c_resp; i++)
1035e0297d1eSnonaka 			aprint_normal("%02x ", ((uint8_t *)cmd->c_resp)[i]);
1036e0297d1eSnonaka 	else if (ISSET(cmd->c_flags, SCF_RSP_PRESENT))
1037e0297d1eSnonaka 		for (i = 0; i < 4; i++)
1038e0297d1eSnonaka 			aprint_normal("%02x ", ((uint8_t *)cmd->c_resp)[i]);
10397e6a2d09Snonaka 	else
10407e6a2d09Snonaka 		aprint_normal("none");
1041e0297d1eSnonaka 	aprint_normal("\n");
1042e0297d1eSnonaka }
10437e6a2d09Snonaka 
10447e6a2d09Snonaka void
sdmmc_dump_data(const char * title,void * ptr,size_t size)10457e6a2d09Snonaka sdmmc_dump_data(const char *title, void *ptr, size_t size)
10467e6a2d09Snonaka {
10477e6a2d09Snonaka 	char buf[16];
10487e6a2d09Snonaka 	uint8_t *p = ptr;
10497e6a2d09Snonaka 	int i, j;
10507e6a2d09Snonaka 
10517e6a2d09Snonaka 	printf("sdmmc_dump_data: %s\n", title ? title : "");
10527e6a2d09Snonaka 	printf("--------+--------------------------------------------------+------------------+\n");
10537e6a2d09Snonaka 	printf("offset  | +0 +1 +2 +3 +4 +5 +6 +7  +8 +9 +a +b +c +d +e +f | data             |\n");
10547e6a2d09Snonaka 	printf("--------+--------------------------------------------------+------------------+\n");
10557e6a2d09Snonaka 	for (i = 0; i < (int)size; i++) {
10567e6a2d09Snonaka 		if ((i % 16) == 0) {
10577e6a2d09Snonaka 			printf("%08x| ", i);
10587e6a2d09Snonaka 		} else if ((i % 16) == 8) {
10597e6a2d09Snonaka 			printf(" ");
10607e6a2d09Snonaka 		}
10617e6a2d09Snonaka 
10627e6a2d09Snonaka 		printf("%02x ", p[i]);
10637e6a2d09Snonaka 		buf[i % 16] = p[i];
10647e6a2d09Snonaka 
10657e6a2d09Snonaka 		if ((i % 16) == 15) {
10667e6a2d09Snonaka 			printf("| ");
10677e6a2d09Snonaka 			for (j = 0; j < 16; j++) {
10687e6a2d09Snonaka 				if (buf[j] >= 0x20 && buf[j] <= 0x7e) {
10697e6a2d09Snonaka 					printf("%c", buf[j]);
10707e6a2d09Snonaka 				} else {
10717e6a2d09Snonaka 					printf(".");
10727e6a2d09Snonaka 				}
10737e6a2d09Snonaka 			}
10747e6a2d09Snonaka 			printf(" |\n");
10757e6a2d09Snonaka 		}
10767e6a2d09Snonaka 	}
10777e6a2d09Snonaka 	if ((i % 16) != 0) {
10787e6a2d09Snonaka 		j = (i % 16);
10797e6a2d09Snonaka 		for (; j < 16; j++) {
10807e6a2d09Snonaka 			printf("   ");
10817e6a2d09Snonaka 			if ((j % 16) == 8) {
10827e6a2d09Snonaka 				printf(" ");
10837e6a2d09Snonaka 			}
10847e6a2d09Snonaka 		}
10857e6a2d09Snonaka 
10867e6a2d09Snonaka 		printf("| ");
10877e6a2d09Snonaka 		for (j = 0; j < (i % 16); j++) {
10887e6a2d09Snonaka 			if (buf[j] >= 0x20 && buf[j] <= 0x7e) {
10897e6a2d09Snonaka 				printf("%c", buf[j]);
10907e6a2d09Snonaka 			} else {
10917e6a2d09Snonaka 				printf(".");
10927e6a2d09Snonaka 			}
10937e6a2d09Snonaka 		}
10947e6a2d09Snonaka 		for (; j < 16; j++) {
10957e6a2d09Snonaka 			printf(" ");
10967e6a2d09Snonaka 		}
10977e6a2d09Snonaka 		printf(" |\n");
10987e6a2d09Snonaka 	}
10997e6a2d09Snonaka 	printf("--------+--------------------------------------------------+------------------+\n");
11007e6a2d09Snonaka }
1101e0297d1eSnonaka #endif
1102