xref: /openbsd/sys/dev/fdt/bcm2835_sdhost.c (revision 3bef86f7)
1 /*     $OpenBSD: bcm2835_sdhost.c,v 1.2 2022/04/06 18:59:28 naddy Exp $ */
2 
3 /*
4  * Copyright (c) 2020 Tobias Heider <tobhe@openbsd.org>
5  * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
6  * Copyright (c) 2019 Neil Ashford <ashfordneil0@gmail.com>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 /*-
22  * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
23  * All rights reserved.
24  *
25  * Redistribution and use in source and binary forms, with or without
26  * modification, are permitted provided that the following conditions
27  * are met:
28  * 1. Redistributions of source code must retain the above copyright
29  *    notice, this list of conditions and the following disclaimer.
30  * 2. Redistributions in binary form must reproduce the above copyright
31  *    notice, this list of conditions and the following disclaimer in the
32  *    documentation and/or other materials provided with the distribution.
33  *
34  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
35  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
37  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
38  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
39  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
40  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
41  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  */
46 
47 #include <sys/types.h>
48 #include <sys/device.h>
49 #include <sys/mutex.h>
50 #include <sys/systm.h>
51 #include <sys/task.h>
52 
53 #include <machine/bus.h>
54 #include <machine/fdt.h>
55 #include <machine/intr.h>
56 
57 #include <dev/ofw/fdt.h>
58 #include <dev/ofw/ofw_clock.h>
59 #include <dev/ofw/openfirm.h>
60 
61 #include <dev/sdmmc/sdmmcreg.h>
62 #include <dev/sdmmc/sdmmcvar.h>
63 
64 #include <dev/ic/bcm2835_dmac.h>
65 
66 #define SDCMD		0x00
67 #define  SDCMD_NEW	(1 << 15)
68 #define  SDCMD_FAIL	(1 << 14)
69 #define  SDCMD_BUSY	(1 << 11)
70 #define  SDCMD_NORESP	(1 << 10)
71 #define  SDCMD_LONGRESP	(1 << 9)
72 #define  SDCMD_WRITE	(1 << 7)
73 #define  SDCMD_READ	(1 << 6)
74 #define SDARG		0x04
75 #define SDTOUT		0x08
76 #define  SDTOUT_DEFAULT 0xf00000
77 #define SDCDIV		0x0c
78 #define  SDCDIV_MASK	((1 << 11) - 1)
79 #define SDRSP0		0x10
80 #define SDRSP1		0x14
81 #define SDRSP2		0x18
82 #define SDRSP3		0x1c
83 #define SDHSTS		0x20
84 #define  SDHSTS_BUSY	(1 << 10)
85 #define  SDHSTS_BLOCK	(1 << 9)
86 #define  SDHSTS_SDIO	(1 << 8)
87 #define  SDHSTS_REW_TO	(1 << 7)
88 #define  SDHSTS_CMD_TO	(1 << 6)
89 #define  SDHSTS_CRC16_E	(1 << 5)
90 #define  SDHSTS_CRC7_E	(1 << 4)
91 #define  SDHSTS_FIFO_E	(1 << 3)
92 #define  SDHSTS_DATA	(1 << 0)
93 #define  SDHSTS_TO_MASK	(SDHSTS_REW_TO | SDHSTS_CMD_TO)
94 #define  SDHSTS_E_MASK	(SDHSTS_CRC16_E | SDHSTS_CRC7_E | SDHSTS_FIFO_E)
95 #define SDVDD		0x30
96 #define  SDVDD_POWER	(1 << 0)
97 #define SDEDM		0x34
98 #define  SDEDM_RD_FIFO_MASK	(0x1f << 14)
99 #define  SDEDM_RD_FIFO_SHIFT	14
100 #define  SDEDM_WR_FIFO_MASK	(0x1f << 9)
101 #define  SDEDM_WR_FIFO_SHIFT	9
102 #define  SDEDM_FIFO_LEVEL(x)	(((x) >> 4) & 0x1f)
103 #define SDHCFG		0x38
104 #define  SDHCFG_BUSY_EN	(1 << 10)
105 #define  SDHCFG_BLOCK_EN (1 << 8)
106 #define  SDHCFG_SDIO_EN	(1 << 5)
107 #define  SDHCFG_DATA_EN	(1 << 4)
108 #define  SDHCFG_SLOW	(1 << 3)
109 #define  SDHCFG_WIDE_EXT (1 << 2)
110 #define  SDHCFG_WIDE_INT (1 << 1)
111 #define  SDHCFG_REL_CMD	(1 << 0)
112 #define SDHBCT		0x3c
113 #define SDDATA		0x40
114 #define SDHBLC		0x50
115 
116 #define SDHOST_FIFO_SIZE 16
117 
118 struct bcmsdhost_softc {
119 	struct device sc_dev;
120 	bus_space_tag_t sc_iot;
121 	bus_space_handle_t sc_ioh;
122 	bus_addr_t sc_addr;
123 	bus_size_t sc_size;
124 
125 	void *sc_ih;
126 
127 	bus_dma_tag_t sc_dmat;
128 	bus_dmamap_t sc_dmamap;
129 	bus_dma_segment_t sc_segs[1];
130 	struct bcmdmac_conblk *sc_cblk;
131 	struct bcmdmac_channel *sc_dmac;
132 
133 	struct mutex sc_intr_lock;
134 	uint32_t sc_intr_hsts;
135 	uint32_t sc_intr_cv;
136 	uint32_t sc_dma_cv;
137 
138 	unsigned int sc_rate;
139 
140 	uint32_t sc_dma_status;
141 	uint32_t sc_dma_error;
142 
143 	struct device *sc_sdmmc;
144 };
145 
146 int bcmsdhost_match(struct device *, void *, void *);
147 void bcmsdhost_attach(struct device *, struct device *, void *);
148 
149 const struct cfattach bcmsdhost_ca = {
150 	sizeof(struct bcmsdhost_softc),
151 	bcmsdhost_match,
152 	bcmsdhost_attach
153 };
154 
155 int bcmsdhost_host_reset(sdmmc_chipset_handle_t);
156 uint32_t bcmsdhost_host_ocr(sdmmc_chipset_handle_t);
157 int bcmsdhost_host_maxblklen(sdmmc_chipset_handle_t);
158 int bcmsdhost_card_detect(sdmmc_chipset_handle_t);
159 int bcmsdhost_bus_power(sdmmc_chipset_handle_t, uint32_t);
160 int bcmsdhost_bus_clock(sdmmc_chipset_handle_t, int, int);
161 int bcmsdhost_bus_width(sdmmc_chipset_handle_t, int);
162 void bcmsdhost_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
163 
164 struct sdmmc_chip_functions bcmsdhost_chip_functions = {
165 	.host_reset = bcmsdhost_host_reset,
166 	.host_ocr = bcmsdhost_host_ocr,
167 	.host_maxblklen = bcmsdhost_host_maxblklen,
168 	.card_detect = bcmsdhost_card_detect,
169 	.bus_power = bcmsdhost_bus_power,
170 	.bus_clock = bcmsdhost_bus_clock,
171 	.bus_width = bcmsdhost_bus_width,
172 	.exec_command = bcmsdhost_exec_command,
173 };
174 
175 int bcmsdhost_wait_idle(struct bcmsdhost_softc *sc, int timeout);
176 int bcmsdhost_dma_wait(struct bcmsdhost_softc *, struct sdmmc_command *);
177 int bcmsdhost_dma_transfer(struct bcmsdhost_softc *, struct sdmmc_command *);
178 void bcmsdhost_dma_done(uint32_t, uint32_t, void *);
179 int bcmsdhost_pio_transfer(struct bcmsdhost_softc *, struct sdmmc_command *);
180 int bcmsdhost_intr(void *);
181 
182 struct cfdriver bcmsdhost_cd = { NULL, "bcmsdhost", DV_DISK };
183 
184 static inline void
185 bcmsdhost_write(struct bcmsdhost_softc *sc, bus_size_t offset, uint32_t value)
186 {
187 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, value);
188 }
189 
190 static inline uint32_t
191 bcmsdhost_read(struct bcmsdhost_softc *sc, bus_size_t offset)
192 {
193 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset);
194 }
195 
196 int
197 bcmsdhost_match(struct device *parent, void *match, void *aux)
198 {
199 	struct fdt_attach_args *faa = aux;
200 
201 	return OF_is_compatible(faa->fa_node, "brcm,bcm2835-sdhost");
202 }
203 
204 void
205 bcmsdhost_attach(struct device *parent, struct device *self, void *aux)
206 {
207 	struct bcmsdhost_softc *sc = (struct bcmsdhost_softc *)self;
208 	struct fdt_attach_args *faa = aux;
209 	struct sdmmcbus_attach_args saa;
210 	int rseg;
211 
212 	if (faa->fa_nreg < 1) {
213 		printf(": no registers\n");
214 		return;
215 	}
216 
217 	mtx_init(&sc->sc_intr_lock, IPL_BIO);
218 
219 	sc->sc_iot = faa->fa_iot;
220 	sc->sc_dmat = faa->fa_dmat;
221 	sc->sc_size = faa->fa_reg[0].size;
222 	sc->sc_addr = faa->fa_reg[0].addr;
223 
224 	if (bus_space_map(sc->sc_iot, sc->sc_addr, sc->sc_size, 0,
225 	    &sc->sc_ioh)) {
226 		printf(": can't map registers\n");
227 		return;
228 	}
229 
230 	clock_enable_all(faa->fa_node);
231 	sc->sc_rate = clock_get_frequency_idx(faa->fa_node, 0);
232 
233 	sc->sc_dmac = bcmdmac_alloc(BCMDMAC_TYPE_NORMAL, IPL_SDMMC,
234 	    bcmsdhost_dma_done, sc);
235 	if (sc->sc_dmac == NULL) {
236 		printf(": can't allocate DMA channel\n");
237 		goto clean_clocks;
238 	}
239 
240 	sc->sc_dmat = faa->fa_dmat;
241 	if (bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, PAGE_SIZE, PAGE_SIZE,
242 	    sc->sc_segs, 1, &rseg, BUS_DMA_WAITOK)) {
243 		printf(": can't allocate DMA memory\n");
244 		goto clean_dmac_channel;
245 	}
246 
247 	if (bus_dmamem_map(sc->sc_dmat, sc->sc_segs, rseg, PAGE_SIZE,
248 			   (char **)&sc->sc_cblk, BUS_DMA_WAITOK)) {
249 		printf(": can't map DMA memory\n");
250 		goto clean_dmamap_free;
251 	}
252 
253 	memset(sc->sc_cblk, 0, PAGE_SIZE);
254 
255 	if (bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, 0,
256 			      BUS_DMA_WAITOK, &sc->sc_dmamap)) {
257 		printf(": can't create DMA map\n");
258 		goto clean_dmamap_unmap;
259 	}
260 
261 	if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_cblk, PAGE_SIZE,
262 			    NULL, BUS_DMA_WAITOK | BUS_DMA_WRITE)) {
263 		printf(": can't load DMA map\n");
264 		goto clean_dmamap_destroy;
265 	}
266 
267 	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_SDMMC, bcmsdhost_intr,
268 				       sc, sc->sc_dev.dv_xname);
269 	if (sc->sc_ih == NULL) {
270 		printf(": can't establish interrupt\n");
271 		goto clean_dmamap;
272 	}
273 
274 	printf(": %u MHz base clock\n", sc->sc_rate / 1000000);
275 
276 	bcmsdhost_write(sc, SDHCFG, SDHCFG_BUSY_EN);
277 	bcmsdhost_bus_clock(sc, 400, false);
278 	bcmsdhost_host_reset(sc);
279 	bcmsdhost_bus_width(sc, 1);
280 
281 	memset(&saa, 0, sizeof(saa));
282 	saa.saa_busname = "sdmmc";
283 	saa.sct = &bcmsdhost_chip_functions;
284 	saa.sch = sc;
285 	saa.dmat = sc->sc_dmat;
286 	saa.flags = SMF_SD_MODE | SMF_STOP_AFTER_MULTIPLE;
287 	saa.caps = SMC_CAPS_DMA | SMC_CAPS_4BIT_MODE |
288 	    SMC_CAPS_SD_HIGHSPEED | SMC_CAPS_MMC_HIGHSPEED;
289 
290 	sc->sc_sdmmc = config_found(&sc->sc_dev, &saa, NULL);
291 	return;
292 
293 clean_dmamap:
294 	bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap);
295 clean_dmamap_destroy:
296 	bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
297 clean_dmamap_unmap:
298 	bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_cblk, PAGE_SIZE);
299 clean_dmamap_free:
300 	bus_dmamem_free(sc->sc_dmat, sc->sc_segs, 1);
301 clean_dmac_channel:
302 	bcmdmac_free(sc->sc_dmac);
303 clean_clocks:
304 	clock_disable_all(faa->fa_node);
305 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
306 }
307 
308 int
309 bcmsdhost_host_reset(sdmmc_chipset_handle_t sch)
310 {
311 	struct bcmsdhost_softc *sc = sch;
312 	uint32_t edm;
313 
314 	bcmsdhost_write(sc, SDVDD, 0);
315 	bcmsdhost_write(sc, SDCMD, 0);
316 	bcmsdhost_write(sc, SDARG, 0);
317 	bcmsdhost_write(sc, SDTOUT, SDTOUT_DEFAULT);
318 	bcmsdhost_write(sc, SDCDIV, 0);
319 	bcmsdhost_write(sc, SDHSTS, bcmsdhost_read(sc, SDHSTS));
320 	bcmsdhost_write(sc, SDHCFG, 0);
321 	bcmsdhost_write(sc, SDHBCT, 0);
322 	bcmsdhost_write(sc, SDHBLC, 0);
323 
324 	edm = bcmsdhost_read(sc, SDEDM);
325 	edm &= ~(SDEDM_RD_FIFO_MASK | SDEDM_WR_FIFO_MASK);
326 	edm |= (4 << SDEDM_RD_FIFO_SHIFT);
327 	edm |= (4 << SDEDM_WR_FIFO_SHIFT);
328 	bcmsdhost_write(sc, SDEDM, edm);
329 
330 	delay(20000);
331 	bcmsdhost_write(sc, SDVDD, SDVDD_POWER);
332 	delay(20000);
333 
334 	bcmsdhost_write(sc, SDHCFG, bcmsdhost_read(sc, SDHCFG));
335 	bcmsdhost_write(sc, SDCDIV, bcmsdhost_read(sc, SDCDIV));
336 
337 	return 0;
338 }
339 
340 uint32_t
341 bcmsdhost_host_ocr(sdmmc_chipset_handle_t sch)
342 {
343 	return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V;
344 }
345 
346 int
347 bcmsdhost_host_maxblklen(sdmmc_chipset_handle_t sch)
348 {
349 	return 1024;
350 }
351 
352 int
353 bcmsdhost_card_detect(sdmmc_chipset_handle_t sch)
354 {
355 	return 1;
356 }
357 
358 int
359 bcmsdhost_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
360 {
361 	return 0;
362 }
363 
364 int
365 bcmsdhost_bus_clock(sdmmc_chipset_handle_t sch, int freq, int ddr)
366 {
367 	struct bcmsdhost_softc *sc = sch;
368 	unsigned int target_rate = freq * 1000;
369 	int div;
370 
371 	if (freq == 0)
372 		div = SDCDIV_MASK;
373 	else {
374 		div = sc->sc_rate / target_rate;
375 		if (div < 2)
376 			div = 2;
377 		if ((sc->sc_rate / div) > target_rate)
378 			div++;
379 		div -= 2;
380 		if (div > SDCDIV_MASK)
381 			div = SDCDIV_MASK;
382 	}
383 
384 	bcmsdhost_write(sc, SDCDIV, div);
385 
386 	return 0;
387 }
388 
389 int
390 bcmsdhost_bus_width(sdmmc_chipset_handle_t sch, int width)
391 {
392 	struct bcmsdhost_softc *sc = sch;
393 	uint32_t hcfg;
394 
395 	hcfg = bcmsdhost_read(sc, SDHCFG);
396 	if (width == 4)
397 		hcfg |= SDHCFG_WIDE_EXT;
398 	else
399 		hcfg &= ~SDHCFG_WIDE_EXT;
400 	hcfg |= (SDHCFG_WIDE_INT | SDHCFG_SLOW);
401 	bcmsdhost_write(sc, SDHCFG, hcfg);
402 
403 	return 0;
404 }
405 
406 void
407 bcmsdhost_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
408 {
409 	struct bcmsdhost_softc *sc = sch;
410 	uint32_t cmdval, hcfg;
411 	unsigned int nblks;
412 
413 	mtx_enter(&sc->sc_intr_lock);
414 
415 	hcfg = bcmsdhost_read(sc, SDHCFG);
416 	bcmsdhost_write(sc, SDHCFG, hcfg | SDHCFG_BUSY_EN);
417 
418 	sc->sc_intr_hsts = 0;
419 
420 	cmd->c_error = bcmsdhost_wait_idle(sc, 5000);
421 	if (cmd->c_error != 0)
422 		goto done;
423 
424 	cmdval = SDCMD_NEW;
425 	if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT))
426 		cmdval |= SDCMD_NORESP;
427 	if (ISSET(cmd->c_flags, SCF_RSP_136))
428 		cmdval |= SDCMD_LONGRESP;
429 	if (ISSET(cmd->c_flags, SCF_RSP_BSY))
430 		cmdval |= SDCMD_BUSY;
431 
432 	if (cmd->c_datalen > 0) {
433 		if (ISSET(cmd->c_flags, SCF_CMD_READ))
434 			cmdval |= SDCMD_READ;
435 		else
436 			cmdval |= SDCMD_WRITE;
437 
438 		nblks = cmd->c_datalen / cmd->c_blklen;
439 		if (nblks == 0 || (cmd->c_datalen % cmd->c_blklen) != 0)
440 			++nblks;
441 
442 		bcmsdhost_write(sc, SDHBCT, cmd->c_blklen);
443 		bcmsdhost_write(sc, SDHBLC, nblks);
444 	}
445 
446 	if (cmd->c_datalen > 0 && cmd->c_dmamap) {
447 		cmd->c_resid = cmd->c_datalen;
448 		cmd->c_error = bcmsdhost_dma_transfer(sc, cmd);
449 		if (cmd->c_error != 0)
450 			goto done;
451 	}
452 
453 	bcmsdhost_write(sc, SDARG, cmd->c_arg);
454 	bcmsdhost_write(sc, SDCMD, cmdval | cmd->c_opcode);
455 
456 	if (cmd->c_datalen > 0 && cmd->c_dmamap) {
457 		cmd->c_error = bcmsdhost_dma_wait(sc, cmd);
458 		if (cmd->c_error != 0)
459 			goto done;
460 	} else if (cmd->c_datalen > 0) {
461 		cmd->c_resid = cmd->c_datalen;
462 		cmd->c_error = bcmsdhost_pio_transfer(sc, cmd);
463 		if (cmd->c_error != 0)
464 			goto done;
465 	}
466 
467 	cmd->c_error = bcmsdhost_wait_idle(sc, 5000);
468 	if (cmd->c_error != 0)
469 		goto done;
470 
471 	if (ISSET(bcmsdhost_read(sc, SDCMD), SDCMD_FAIL)) {
472 		cmd->c_error = EIO;
473 		goto done;
474 	}
475 
476 	if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
477 		if (ISSET(cmd->c_flags, SCF_RSP_136)) {
478 			cmd->c_resp[0] = bcmsdhost_read(sc, SDRSP0);
479 			cmd->c_resp[1] = bcmsdhost_read(sc, SDRSP1);
480 			cmd->c_resp[2] = bcmsdhost_read(sc, SDRSP2);
481 			cmd->c_resp[3] = bcmsdhost_read(sc, SDRSP3);
482 			if (ISSET(cmd->c_flags, SCF_RSP_CRC)) {
483 				cmd->c_resp[0] = (cmd->c_resp[0] >> 8) |
484 						 (cmd->c_resp[1] << 24);
485 				cmd->c_resp[1] = (cmd->c_resp[1] >> 8) |
486 						 (cmd->c_resp[2] << 24);
487 				cmd->c_resp[2] = (cmd->c_resp[2] >> 8) |
488 						 (cmd->c_resp[3] << 24);
489 				cmd->c_resp[3] = (cmd->c_resp[3] >> 8);
490 			}
491 		} else {
492 			cmd->c_resp[0] = bcmsdhost_read(sc, SDRSP0);
493 		}
494 	}
495 
496 done:
497 	cmd->c_flags |= SCF_ITSDONE;
498 	bcmsdhost_write(sc, SDHCFG, hcfg);
499 	bcmsdhost_write(sc, SDHSTS, bcmsdhost_read(sc, SDHSTS));
500 	mtx_leave(&sc->sc_intr_lock);
501 }
502 
503 int
504 bcmsdhost_wait_idle(struct bcmsdhost_softc *sc, int timeout)
505 {
506 	int retry = timeout * 1000;
507 
508 	while (--retry > 0) {
509 		const uint32_t cmd = bcmsdhost_read(sc, SDCMD);
510 		if (!ISSET(cmd, SDCMD_NEW))
511 			return 0;
512 		delay(1);
513 	}
514 
515 	return ETIMEDOUT;
516 }
517 
518 int
519 bcmsdhost_dma_wait(struct bcmsdhost_softc *sc, struct sdmmc_command *cmd)
520 {
521 	int error = 0;
522 
523 	while (sc->sc_dma_status == 0 && sc->sc_dma_error == 0) {
524 		error = msleep_nsec(&sc->sc_dma_cv, &sc->sc_intr_lock,
525 		    PPAUSE, "pause", SEC_TO_NSEC(5));
526 		if (error == EWOULDBLOCK) {
527 			printf("%s: transfer timeout!\n", DEVNAME(sc));
528 			bcmdmac_halt(sc->sc_dmac);
529 			error = ETIMEDOUT;
530 			goto error;
531 		}
532 	}
533 
534 	if (ISSET(sc->sc_dma_status, DMAC_CS_END)) {
535 		cmd->c_resid = 0;
536 		error = 0;
537 	} else {
538 		error = EIO;
539 	}
540 
541 error:
542 	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0,
543 			sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE);
544 
545 	return error;
546 }
547 
548 int
549 bcmsdhost_dma_transfer(struct bcmsdhost_softc *sc, struct sdmmc_command *cmd)
550 {
551 	const bus_addr_t ad_sddata = sc->sc_addr + SDDATA;
552 	size_t seg;
553 	int error;
554 
555 	for (seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) {
556 		if (sizeof(cmd->c_dmamap->dm_segs[seg].ds_addr) >
557 		    sizeof(sc->sc_cblk[seg].cb_source_ad)) {
558 			if (cmd->c_dmamap->dm_segs[seg].ds_addr > 0xffffffffU)
559 				return EFBIG;
560 		}
561 		sc->sc_cblk[seg].cb_ti = 13 * DMAC_TI_PERMAP_BASE;
562 		sc->sc_cblk[seg].cb_txfr_len =
563 		    cmd->c_dmamap->dm_segs[seg].ds_len;
564 
565 		/*
566 		 * All transfers are assumed to be multiples of 32 bits
567 		 */
568 		KASSERTMSG((sc->sc_cblk[seg].cb_txfr_len & 0x3) == 0,
569 			   "seg %zu len %d", seg, sc->sc_cblk[seg].cb_txfr_len);
570 		/* Use 128-bit mode if transfer is a multiple of 16 bytes.  */
571 		if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
572 			sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_INC;
573 			if ((sc->sc_cblk[seg].cb_txfr_len & 0xf) == 0)
574 				sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_WIDTH;
575 			sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_DREQ;
576 			sc->sc_cblk[seg].cb_source_ad = ad_sddata;
577 			sc->sc_cblk[seg].cb_dest_ad =
578 			    cmd->c_dmamap->dm_segs[seg].ds_addr;
579 		} else {
580 			sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_INC;
581 			if ((sc->sc_cblk[seg].cb_txfr_len & 0xf) == 0)
582 				sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_WIDTH;
583 			sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_DREQ;
584 			sc->sc_cblk[seg].cb_ti |= DMAC_TI_WAIT_RESP;
585 			sc->sc_cblk[seg].cb_source_ad =
586 			    cmd->c_dmamap->dm_segs[seg].ds_addr;
587 			sc->sc_cblk[seg].cb_dest_ad = ad_sddata;
588 		}
589 		sc->sc_cblk[seg].cb_stride = 0;
590 		if (seg == cmd->c_dmamap->dm_nsegs - 1) {
591 			sc->sc_cblk[seg].cb_ti |= DMAC_TI_INTEN;
592 			sc->sc_cblk[seg].cb_nextconbk = 0;
593 		} else {
594 			sc->sc_cblk[seg].cb_nextconbk =
595 			    sc->sc_dmamap->dm_segs[0].ds_addr +
596 			    sizeof(struct bcmdmac_conblk) * (seg + 1);
597 		}
598 		sc->sc_cblk[seg].cb_padding[0] = 0;
599 		sc->sc_cblk[seg].cb_padding[1] = 0;
600 	}
601 
602 	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0,
603 			sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
604 
605 	sc->sc_dma_status = 0;
606 	sc->sc_dma_error = 0;
607 
608 	bcmdmac_set_conblk_addr(sc->sc_dmac, sc->sc_dmamap->dm_segs[0].ds_addr);
609 	error = bcmdmac_transfer(sc->sc_dmac);
610 
611 	if (error)
612 		return error;
613 
614 	return 0;
615 }
616 
617 void
618 bcmsdhost_dma_done(uint32_t status, uint32_t error, void *arg)
619 {
620 	struct bcmsdhost_softc *sc = arg;
621 
622 	mtx_enter(&sc->sc_intr_lock);
623 
624 	sc->sc_dma_status = status;
625 	sc->sc_dma_error = error;
626 	wakeup(&sc->sc_dma_cv);
627 
628 	mtx_leave(&sc->sc_intr_lock);
629 }
630 
631 int
632 bcmsdhost_pio_transfer(struct bcmsdhost_softc *sc, struct sdmmc_command *cmd)
633 {
634 	uint32_t *datap = cmd->c_data;
635 	uint32_t edm, status;
636 	int count;
637 
638 	if ((cmd->c_datalen % 4) != 0)
639 		return EINVAL;
640 
641 	while (cmd->c_resid > 0) {
642 		edm = bcmsdhost_read(sc, SDEDM);
643 		count = SDEDM_FIFO_LEVEL(edm);
644 		if (!ISSET(cmd->c_flags, SCF_CMD_READ))
645 			count = SDHOST_FIFO_SIZE - count;
646 
647 		if (count == 0) {
648 			status = bcmsdhost_read(sc, SDHSTS);
649 			if (status & SDHSTS_E_MASK)
650 				return EIO;
651 			if (status & SDHSTS_TO_MASK)
652 				return ETIMEDOUT;
653 		}
654 
655 		while (count-- > 0 && cmd->c_resid > 0) {
656 			if (ISSET(cmd->c_flags, SCF_CMD_READ))
657 				*(datap++) = bcmsdhost_read(sc, SDDATA);
658 			else
659 				bcmsdhost_write(sc, SDDATA, *(datap++));
660 
661 			cmd->c_resid -= 4;
662 			count--;
663 		}
664 	}
665 
666 	status = bcmsdhost_read(sc, SDHSTS);
667 	if (status & SDHSTS_E_MASK)
668 		return EIO;
669 	if (status & SDHSTS_TO_MASK)
670 		return ETIMEDOUT;
671 	return 0;
672 }
673 
674 int
675 bcmsdhost_intr(void *arg)
676 {
677 	struct bcmsdhost_softc *sc = arg;
678 	uint32_t hsts;
679 
680 	mtx_enter(&sc->sc_intr_lock);
681 
682 	hsts = bcmsdhost_read(sc, SDHSTS);
683 	if (hsts) {
684 		bcmsdhost_write(sc, SDHSTS, hsts);
685 		sc->sc_intr_hsts |= hsts;
686 		wakeup(&sc->sc_intr_cv);
687 	}
688 
689 	mtx_leave(&sc->sc_intr_lock);
690 
691 	return hsts ? 1 : 0;
692 }
693