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
bcmsdhost_write(struct bcmsdhost_softc * sc,bus_size_t offset,uint32_t value)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
bcmsdhost_read(struct bcmsdhost_softc * sc,bus_size_t offset)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
bcmsdhost_match(struct device * parent,void * match,void * aux)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
bcmsdhost_attach(struct device * parent,struct device * self,void * aux)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
bcmsdhost_host_reset(sdmmc_chipset_handle_t sch)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
bcmsdhost_host_ocr(sdmmc_chipset_handle_t sch)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
bcmsdhost_host_maxblklen(sdmmc_chipset_handle_t sch)347 bcmsdhost_host_maxblklen(sdmmc_chipset_handle_t sch)
348 {
349 return 1024;
350 }
351
352 int
bcmsdhost_card_detect(sdmmc_chipset_handle_t sch)353 bcmsdhost_card_detect(sdmmc_chipset_handle_t sch)
354 {
355 return 1;
356 }
357
358 int
bcmsdhost_bus_power(sdmmc_chipset_handle_t sch,uint32_t ocr)359 bcmsdhost_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
360 {
361 return 0;
362 }
363
364 int
bcmsdhost_bus_clock(sdmmc_chipset_handle_t sch,int freq,int ddr)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
bcmsdhost_bus_width(sdmmc_chipset_handle_t sch,int width)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
bcmsdhost_exec_command(sdmmc_chipset_handle_t sch,struct sdmmc_command * cmd)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
bcmsdhost_wait_idle(struct bcmsdhost_softc * sc,int timeout)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
bcmsdhost_dma_wait(struct bcmsdhost_softc * sc,struct sdmmc_command * cmd)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
bcmsdhost_dma_transfer(struct bcmsdhost_softc * sc,struct sdmmc_command * cmd)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
bcmsdhost_dma_done(uint32_t status,uint32_t error,void * arg)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
bcmsdhost_pio_transfer(struct bcmsdhost_softc * sc,struct sdmmc_command * cmd)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
bcmsdhost_intr(void * arg)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