1*62b14db6Sskrll /* $NetBSD: dwc_mmc.c,v 1.31 2024/02/09 17:16:42 skrll Exp $ */
221d168c0Sjmcneill
321d168c0Sjmcneill /*-
45912d346Sjmcneill * Copyright (c) 2014-2017 Jared McNeill <jmcneill@invisible.ca>
521d168c0Sjmcneill * All rights reserved.
621d168c0Sjmcneill *
721d168c0Sjmcneill * Redistribution and use in source and binary forms, with or without
821d168c0Sjmcneill * modification, are permitted provided that the following conditions
921d168c0Sjmcneill * are met:
1021d168c0Sjmcneill * 1. Redistributions of source code must retain the above copyright
1121d168c0Sjmcneill * notice, this list of conditions and the following disclaimer.
1221d168c0Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
1321d168c0Sjmcneill * notice, this list of conditions and the following disclaimer in the
1421d168c0Sjmcneill * documentation and/or other materials provided with the distribution.
1521d168c0Sjmcneill *
1621d168c0Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1721d168c0Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1821d168c0Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1921d168c0Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2021d168c0Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2121d168c0Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2221d168c0Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2321d168c0Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2421d168c0Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2521d168c0Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2621d168c0Sjmcneill * SUCH DAMAGE.
2721d168c0Sjmcneill */
2821d168c0Sjmcneill
2921d168c0Sjmcneill #include <sys/cdefs.h>
30*62b14db6Sskrll __KERNEL_RCSID(0, "$NetBSD: dwc_mmc.c,v 1.31 2024/02/09 17:16:42 skrll Exp $");
3121d168c0Sjmcneill
3221d168c0Sjmcneill #include <sys/param.h>
3321d168c0Sjmcneill #include <sys/bus.h>
3421d168c0Sjmcneill #include <sys/device.h>
3521d168c0Sjmcneill #include <sys/intr.h>
3621d168c0Sjmcneill #include <sys/systm.h>
3721d168c0Sjmcneill #include <sys/kernel.h>
38c1484821Sjmcneill #include <sys/proc.h>
3921d168c0Sjmcneill
4021d168c0Sjmcneill #include <dev/sdmmc/sdmmcvar.h>
4121d168c0Sjmcneill #include <dev/sdmmc/sdmmcchip.h>
4221d168c0Sjmcneill #include <dev/sdmmc/sdmmc_ioreg.h>
4321d168c0Sjmcneill
4421d168c0Sjmcneill #include <dev/ic/dwc_mmc_reg.h>
4521d168c0Sjmcneill #include <dev/ic/dwc_mmc_var.h>
4621d168c0Sjmcneill
475912d346Sjmcneill #define DWC_MMC_NDESC 64
485912d346Sjmcneill
4921d168c0Sjmcneill static int dwc_mmc_host_reset(sdmmc_chipset_handle_t);
5021d168c0Sjmcneill static uint32_t dwc_mmc_host_ocr(sdmmc_chipset_handle_t);
5121d168c0Sjmcneill static int dwc_mmc_host_maxblklen(sdmmc_chipset_handle_t);
5221d168c0Sjmcneill static int dwc_mmc_card_detect(sdmmc_chipset_handle_t);
5321d168c0Sjmcneill static int dwc_mmc_write_protect(sdmmc_chipset_handle_t);
5421d168c0Sjmcneill static int dwc_mmc_bus_power(sdmmc_chipset_handle_t, uint32_t);
5521d168c0Sjmcneill static int dwc_mmc_bus_clock(sdmmc_chipset_handle_t, int);
5621d168c0Sjmcneill static int dwc_mmc_bus_width(sdmmc_chipset_handle_t, int);
5721d168c0Sjmcneill static int dwc_mmc_bus_rod(sdmmc_chipset_handle_t, int);
5840fd064dSjmcneill static int dwc_mmc_signal_voltage(sdmmc_chipset_handle_t, int);
5921d168c0Sjmcneill static void dwc_mmc_exec_command(sdmmc_chipset_handle_t,
6021d168c0Sjmcneill struct sdmmc_command *);
6121d168c0Sjmcneill static void dwc_mmc_card_enable_intr(sdmmc_chipset_handle_t, int);
6221d168c0Sjmcneill static void dwc_mmc_card_intr_ack(sdmmc_chipset_handle_t);
6321d168c0Sjmcneill
6421d168c0Sjmcneill static struct sdmmc_chip_functions dwc_mmc_chip_functions = {
6521d168c0Sjmcneill .host_reset = dwc_mmc_host_reset,
6621d168c0Sjmcneill .host_ocr = dwc_mmc_host_ocr,
6721d168c0Sjmcneill .host_maxblklen = dwc_mmc_host_maxblklen,
6821d168c0Sjmcneill .card_detect = dwc_mmc_card_detect,
6921d168c0Sjmcneill .write_protect = dwc_mmc_write_protect,
7021d168c0Sjmcneill .bus_power = dwc_mmc_bus_power,
7121d168c0Sjmcneill .bus_clock = dwc_mmc_bus_clock,
7221d168c0Sjmcneill .bus_width = dwc_mmc_bus_width,
7321d168c0Sjmcneill .bus_rod = dwc_mmc_bus_rod,
7440fd064dSjmcneill .signal_voltage = dwc_mmc_signal_voltage,
7521d168c0Sjmcneill .exec_command = dwc_mmc_exec_command,
7621d168c0Sjmcneill .card_enable_intr = dwc_mmc_card_enable_intr,
7721d168c0Sjmcneill .card_intr_ack = dwc_mmc_card_intr_ack,
7821d168c0Sjmcneill };
7921d168c0Sjmcneill
8021d168c0Sjmcneill #define MMC_WRITE(sc, reg, val) \
8121d168c0Sjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
8221d168c0Sjmcneill #define MMC_READ(sc, reg) \
8321d168c0Sjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
8421d168c0Sjmcneill
8526e0c5cbSjmcneill static int
dwc_mmc_dmabounce_setup(struct dwc_mmc_softc * sc)8626e0c5cbSjmcneill dwc_mmc_dmabounce_setup(struct dwc_mmc_softc *sc)
8721d168c0Sjmcneill {
8826e0c5cbSjmcneill bus_dma_segment_t ds[1];
8926e0c5cbSjmcneill int error, rseg;
9026e0c5cbSjmcneill
9126e0c5cbSjmcneill sc->sc_dmabounce_buflen = dwc_mmc_host_maxblklen(sc);
9226e0c5cbSjmcneill error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmabounce_buflen, 0,
9326e0c5cbSjmcneill sc->sc_dmabounce_buflen, ds, 1, &rseg, BUS_DMA_WAITOK);
9426e0c5cbSjmcneill if (error)
9526e0c5cbSjmcneill return error;
9626e0c5cbSjmcneill error = bus_dmamem_map(sc->sc_dmat, ds, 1, sc->sc_dmabounce_buflen,
9726e0c5cbSjmcneill &sc->sc_dmabounce_buf, BUS_DMA_WAITOK);
9826e0c5cbSjmcneill if (error)
9926e0c5cbSjmcneill goto free;
10026e0c5cbSjmcneill error = bus_dmamap_create(sc->sc_dmat, sc->sc_dmabounce_buflen, 1,
10126e0c5cbSjmcneill sc->sc_dmabounce_buflen, 0, BUS_DMA_WAITOK, &sc->sc_dmabounce_map);
10226e0c5cbSjmcneill if (error)
10326e0c5cbSjmcneill goto unmap;
10426e0c5cbSjmcneill error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmabounce_map,
10526e0c5cbSjmcneill sc->sc_dmabounce_buf, sc->sc_dmabounce_buflen, NULL,
10626e0c5cbSjmcneill BUS_DMA_WAITOK);
10726e0c5cbSjmcneill if (error)
10826e0c5cbSjmcneill goto destroy;
10926e0c5cbSjmcneill return 0;
11026e0c5cbSjmcneill
11126e0c5cbSjmcneill destroy:
11226e0c5cbSjmcneill bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmabounce_map);
11326e0c5cbSjmcneill unmap:
11426e0c5cbSjmcneill bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmabounce_buf,
11526e0c5cbSjmcneill sc->sc_dmabounce_buflen);
11626e0c5cbSjmcneill free:
11726e0c5cbSjmcneill bus_dmamem_free(sc->sc_dmat, ds, rseg);
11826e0c5cbSjmcneill return error;
1195912d346Sjmcneill }
1205912d346Sjmcneill
1215912d346Sjmcneill static int
dwc_mmc_idma_setup(struct dwc_mmc_softc * sc)1225912d346Sjmcneill dwc_mmc_idma_setup(struct dwc_mmc_softc *sc)
1235912d346Sjmcneill {
1245912d346Sjmcneill int error;
1255912d346Sjmcneill
1265912d346Sjmcneill sc->sc_idma_xferlen = 0x1000;
1275912d346Sjmcneill
1285912d346Sjmcneill sc->sc_idma_ndesc = DWC_MMC_NDESC;
1295912d346Sjmcneill sc->sc_idma_size = sizeof(struct dwc_mmc_idma_desc) *
1305912d346Sjmcneill sc->sc_idma_ndesc;
1315912d346Sjmcneill error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_idma_size, 8,
1325912d346Sjmcneill sc->sc_idma_size, sc->sc_idma_segs, 1,
1335912d346Sjmcneill &sc->sc_idma_nsegs, BUS_DMA_WAITOK);
1345912d346Sjmcneill if (error)
1355912d346Sjmcneill return error;
1365912d346Sjmcneill error = bus_dmamem_map(sc->sc_dmat, sc->sc_idma_segs,
1375912d346Sjmcneill sc->sc_idma_nsegs, sc->sc_idma_size,
1385912d346Sjmcneill &sc->sc_idma_desc, BUS_DMA_WAITOK);
1395912d346Sjmcneill if (error)
1405912d346Sjmcneill goto free;
1415912d346Sjmcneill error = bus_dmamap_create(sc->sc_dmat, sc->sc_idma_size, 1,
1425912d346Sjmcneill sc->sc_idma_size, 0, BUS_DMA_WAITOK, &sc->sc_idma_map);
1435912d346Sjmcneill if (error)
1445912d346Sjmcneill goto unmap;
1455912d346Sjmcneill error = bus_dmamap_load(sc->sc_dmat, sc->sc_idma_map,
1465912d346Sjmcneill sc->sc_idma_desc, sc->sc_idma_size, NULL, BUS_DMA_WAITOK);
1475912d346Sjmcneill if (error)
1485912d346Sjmcneill goto destroy;
1495912d346Sjmcneill return 0;
1505912d346Sjmcneill
1515912d346Sjmcneill destroy:
1525912d346Sjmcneill bus_dmamap_destroy(sc->sc_dmat, sc->sc_idma_map);
1535912d346Sjmcneill unmap:
1545912d346Sjmcneill bus_dmamem_unmap(sc->sc_dmat, sc->sc_idma_desc, sc->sc_idma_size);
1555912d346Sjmcneill free:
1565912d346Sjmcneill bus_dmamem_free(sc->sc_dmat, sc->sc_idma_segs, sc->sc_idma_nsegs);
1575912d346Sjmcneill return error;
1585912d346Sjmcneill }
1595912d346Sjmcneill
1605912d346Sjmcneill static void
dwc_mmc_attach_i(device_t self)1615912d346Sjmcneill dwc_mmc_attach_i(device_t self)
1625912d346Sjmcneill {
1635912d346Sjmcneill struct dwc_mmc_softc *sc = device_private(self);
16421d168c0Sjmcneill struct sdmmcbus_attach_args saa;
165fe36728dSjmcneill bool poll_detect;
166fe36728dSjmcneill
167fe36728dSjmcneill poll_detect = sc->sc_card_detect != NULL ||
168fe36728dSjmcneill (sc->sc_flags & (DWC_MMC_F_BROKEN_CD|DWC_MMC_F_NON_REMOVABLE)) == 0;
16921d168c0Sjmcneill
17040fd064dSjmcneill if (sc->sc_pre_power_on)
17140fd064dSjmcneill sc->sc_pre_power_on(sc);
17240fd064dSjmcneill
17340fd064dSjmcneill dwc_mmc_signal_voltage(sc, SDMMC_SIGNAL_VOLTAGE_330);
17421d168c0Sjmcneill dwc_mmc_host_reset(sc);
17521d168c0Sjmcneill dwc_mmc_bus_width(sc, 1);
17621d168c0Sjmcneill
17740fd064dSjmcneill if (sc->sc_post_power_on)
17840fd064dSjmcneill sc->sc_post_power_on(sc);
17940fd064dSjmcneill
18021d168c0Sjmcneill memset(&saa, 0, sizeof(saa));
18121d168c0Sjmcneill saa.saa_busname = "sdmmc";
18221d168c0Sjmcneill saa.saa_sct = &dwc_mmc_chip_functions;
18321d168c0Sjmcneill saa.saa_sch = sc;
18421d168c0Sjmcneill saa.saa_clkmin = 400;
1852e2c694bSjmcneill saa.saa_clkmax = sc->sc_clock_freq / 1000;
18626e0c5cbSjmcneill saa.saa_dmat = sc->sc_dmat;
18740fd064dSjmcneill saa.saa_caps = SMC_CAPS_SD_HIGHSPEED |
18821d168c0Sjmcneill SMC_CAPS_MMC_HIGHSPEED |
18926e0c5cbSjmcneill SMC_CAPS_AUTO_STOP |
19026e0c5cbSjmcneill SMC_CAPS_DMA |
19121d168c0Sjmcneill SMC_CAPS_MULTI_SEG_DMA;
192fe36728dSjmcneill if (sc->sc_bus_width == 8) {
19340fd064dSjmcneill saa.saa_caps |= SMC_CAPS_8BIT_MODE;
194fe36728dSjmcneill } else {
19540fd064dSjmcneill saa.saa_caps |= SMC_CAPS_4BIT_MODE;
196fe36728dSjmcneill }
197fe36728dSjmcneill
198fe36728dSjmcneill if (poll_detect) {
1995912d346Sjmcneill saa.saa_caps |= SMC_CAPS_POLL_CARD_DET;
200fe36728dSjmcneill }
20121d168c0Sjmcneill
202c7fb772bSthorpej sc->sc_sdmmc_dev = config_found(self, &saa, NULL, CFARGS_NONE);
20321d168c0Sjmcneill }
20421d168c0Sjmcneill
2055912d346Sjmcneill static void
dwc_mmc_led(struct dwc_mmc_softc * sc,int on)2065912d346Sjmcneill dwc_mmc_led(struct dwc_mmc_softc *sc, int on)
2075912d346Sjmcneill {
2085912d346Sjmcneill if (sc->sc_set_led)
2095912d346Sjmcneill sc->sc_set_led(sc, on);
2105912d346Sjmcneill }
2115912d346Sjmcneill
2125912d346Sjmcneill static int
dwc_mmc_host_reset(sdmmc_chipset_handle_t sch)2135912d346Sjmcneill dwc_mmc_host_reset(sdmmc_chipset_handle_t sch)
2145912d346Sjmcneill {
2155912d346Sjmcneill struct dwc_mmc_softc *sc = sch;
2165912d346Sjmcneill uint32_t fifoth, ctrl;
2175912d346Sjmcneill int retry = 1000;
2185912d346Sjmcneill
2195912d346Sjmcneill #ifdef DWC_MMC_DEBUG
2205912d346Sjmcneill aprint_normal_dev(sc->sc_dev, "host reset\n");
2215912d346Sjmcneill #endif
2225912d346Sjmcneill
22340fd064dSjmcneill if (ISSET(sc->sc_flags, DWC_MMC_F_PWREN_INV))
22440fd064dSjmcneill MMC_WRITE(sc, DWC_MMC_PWREN, 0);
22540fd064dSjmcneill else
2265912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_PWREN, 1);
2275912d346Sjmcneill
22840fd064dSjmcneill ctrl = MMC_READ(sc, DWC_MMC_GCTRL);
22940fd064dSjmcneill ctrl &= ~DWC_MMC_GCTRL_USE_INTERNAL_DMAC;
23040fd064dSjmcneill MMC_WRITE(sc, DWC_MMC_GCTRL, ctrl);
23140fd064dSjmcneill
23240fd064dSjmcneill MMC_WRITE(sc, DWC_MMC_DMAC, DWC_MMC_DMAC_SOFTRESET);
23340fd064dSjmcneill
2345912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_GCTRL,
2355912d346Sjmcneill MMC_READ(sc, DWC_MMC_GCTRL) | DWC_MMC_GCTRL_RESET);
2365912d346Sjmcneill while (--retry > 0) {
2375912d346Sjmcneill if (!(MMC_READ(sc, DWC_MMC_GCTRL) & DWC_MMC_GCTRL_RESET))
2385912d346Sjmcneill break;
2395912d346Sjmcneill delay(100);
2405912d346Sjmcneill }
2415912d346Sjmcneill
2425912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_CLKSRC, 0);
2435912d346Sjmcneill
2445912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_TIMEOUT, 0xffffffff);
2455912d346Sjmcneill
24626e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IMASK, 0);
24726e0c5cbSjmcneill
24826e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_RINT, 0xffffffff);
2495912d346Sjmcneill
2505912d346Sjmcneill const uint32_t rx_wmark = (sc->sc_fifo_depth / 2) - 1;
2515912d346Sjmcneill const uint32_t tx_wmark = sc->sc_fifo_depth / 2;
2525912d346Sjmcneill fifoth = __SHIFTIN(DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE_16,
2535912d346Sjmcneill DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE);
2545912d346Sjmcneill fifoth |= __SHIFTIN(rx_wmark, DWC_MMC_FIFOTH_RX_WMARK);
2555912d346Sjmcneill fifoth |= __SHIFTIN(tx_wmark, DWC_MMC_FIFOTH_TX_WMARK);
2565912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_FIFOTH, fifoth);
2575912d346Sjmcneill
2585912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_UHS, 0);
2595912d346Sjmcneill
2605912d346Sjmcneill ctrl = MMC_READ(sc, DWC_MMC_GCTRL);
2615912d346Sjmcneill ctrl |= DWC_MMC_GCTRL_INTEN;
2625912d346Sjmcneill ctrl |= DWC_MMC_GCTRL_DMAEN;
2635912d346Sjmcneill ctrl |= DWC_MMC_GCTRL_SEND_AUTO_STOP_CCSD;
2645912d346Sjmcneill ctrl |= DWC_MMC_GCTRL_USE_INTERNAL_DMAC;
2655912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_GCTRL, ctrl);
2665912d346Sjmcneill
2675912d346Sjmcneill return 0;
2685912d346Sjmcneill }
2695912d346Sjmcneill
2705912d346Sjmcneill static uint32_t
dwc_mmc_host_ocr(sdmmc_chipset_handle_t sch)2715912d346Sjmcneill dwc_mmc_host_ocr(sdmmc_chipset_handle_t sch)
2725912d346Sjmcneill {
2735912d346Sjmcneill return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V | MMC_OCR_HCS;
2745912d346Sjmcneill }
2755912d346Sjmcneill
2765912d346Sjmcneill static int
dwc_mmc_host_maxblklen(sdmmc_chipset_handle_t sch)2775912d346Sjmcneill dwc_mmc_host_maxblklen(sdmmc_chipset_handle_t sch)
2785912d346Sjmcneill {
2795912d346Sjmcneill return 32768;
2805912d346Sjmcneill }
2815912d346Sjmcneill
2825912d346Sjmcneill static int
dwc_mmc_card_detect(sdmmc_chipset_handle_t sch)2835912d346Sjmcneill dwc_mmc_card_detect(sdmmc_chipset_handle_t sch)
2845912d346Sjmcneill {
2855912d346Sjmcneill struct dwc_mmc_softc *sc = sch;
286fe36728dSjmcneill int det_n;
2875912d346Sjmcneill
288fe36728dSjmcneill if (sc->sc_card_detect != NULL) {
28953eae96bSjmcneill return sc->sc_card_detect(sc);
2905912d346Sjmcneill }
291fe36728dSjmcneill if ((sc->sc_flags & DWC_MMC_F_BROKEN_CD) != 0) {
292fe36728dSjmcneill return 1; /* broken card detect, assume present */
293fe36728dSjmcneill }
294fe36728dSjmcneill if ((sc->sc_flags & DWC_MMC_F_NON_REMOVABLE) != 0) {
295fe36728dSjmcneill return 1; /* non-removable device is always present */
296fe36728dSjmcneill }
297fe36728dSjmcneill
298fe36728dSjmcneill det_n = MMC_READ(sc, DWC_MMC_CDETECT) & DWC_MMC_CDETECT_CARD_DETECT_N;
299fe36728dSjmcneill
300fe36728dSjmcneill return !det_n;
301fe36728dSjmcneill }
3025912d346Sjmcneill
3035912d346Sjmcneill static int
dwc_mmc_write_protect(sdmmc_chipset_handle_t sch)3045912d346Sjmcneill dwc_mmc_write_protect(sdmmc_chipset_handle_t sch)
3055912d346Sjmcneill {
3065912d346Sjmcneill struct dwc_mmc_softc *sc = sch;
3075912d346Sjmcneill
3085912d346Sjmcneill if (!sc->sc_write_protect)
3095912d346Sjmcneill return 0; /* no write protect pin, assume rw */
3105912d346Sjmcneill
3115912d346Sjmcneill return sc->sc_write_protect(sc);
3125912d346Sjmcneill }
3135912d346Sjmcneill
3145912d346Sjmcneill static int
dwc_mmc_bus_power(sdmmc_chipset_handle_t sch,uint32_t ocr)3155912d346Sjmcneill dwc_mmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
3165912d346Sjmcneill {
317bf24108eSjmcneill struct dwc_mmc_softc *sc = sch;
318bf24108eSjmcneill
319bf24108eSjmcneill if (ocr == 0)
320bf24108eSjmcneill sc->sc_card_inited = false;
321bf24108eSjmcneill
3225912d346Sjmcneill return 0;
3235912d346Sjmcneill }
3245912d346Sjmcneill
3255912d346Sjmcneill static int
dwc_mmc_signal_voltage(sdmmc_chipset_handle_t sch,int signal_voltage)32640fd064dSjmcneill dwc_mmc_signal_voltage(sdmmc_chipset_handle_t sch, int signal_voltage)
32740fd064dSjmcneill {
32840fd064dSjmcneill struct dwc_mmc_softc *sc = sch;
32940fd064dSjmcneill
33040fd064dSjmcneill if (sc->sc_signal_voltage == NULL)
33140fd064dSjmcneill return 0;
33240fd064dSjmcneill
33340fd064dSjmcneill return sc->sc_signal_voltage(sc, signal_voltage);
33440fd064dSjmcneill }
33540fd064dSjmcneill
33640fd064dSjmcneill static int
dwc_mmc_update_clock(struct dwc_mmc_softc * sc)3375912d346Sjmcneill dwc_mmc_update_clock(struct dwc_mmc_softc *sc)
3385912d346Sjmcneill {
3395912d346Sjmcneill uint32_t cmd;
3405912d346Sjmcneill int retry;
3415912d346Sjmcneill
3425912d346Sjmcneill #ifdef DWC_MMC_DEBUG
3435912d346Sjmcneill aprint_normal_dev(sc->sc_dev, "update clock\n");
3445912d346Sjmcneill #endif
3455912d346Sjmcneill
3465912d346Sjmcneill cmd = DWC_MMC_CMD_START |
3475912d346Sjmcneill DWC_MMC_CMD_UPCLK_ONLY |
3485912d346Sjmcneill DWC_MMC_CMD_WAIT_PRE_OVER;
3495912d346Sjmcneill if (ISSET(sc->sc_flags, DWC_MMC_F_USE_HOLD_REG))
3505912d346Sjmcneill cmd |= DWC_MMC_CMD_USE_HOLD_REG;
3515912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_ARG, 0);
3525912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_CMD, cmd);
3537cc6919eSjmcneill retry = 200000;
3545912d346Sjmcneill while (--retry > 0) {
3555912d346Sjmcneill if (!(MMC_READ(sc, DWC_MMC_CMD) & DWC_MMC_CMD_START))
3565912d346Sjmcneill break;
3575912d346Sjmcneill delay(10);
3585912d346Sjmcneill }
3595912d346Sjmcneill
3605912d346Sjmcneill if (retry == 0) {
3615912d346Sjmcneill aprint_error_dev(sc->sc_dev, "timeout updating clock\n");
3625912d346Sjmcneill #ifdef DWC_MMC_DEBUG
3635912d346Sjmcneill device_printf(sc->sc_dev, "GCTRL: 0x%08x\n",
3645912d346Sjmcneill MMC_READ(sc, DWC_MMC_GCTRL));
3655912d346Sjmcneill device_printf(sc->sc_dev, "CLKENA: 0x%08x\n",
3665912d346Sjmcneill MMC_READ(sc, DWC_MMC_CLKENA));
3675912d346Sjmcneill device_printf(sc->sc_dev, "CLKDIV: 0x%08x\n",
3685912d346Sjmcneill MMC_READ(sc, DWC_MMC_CLKDIV));
3695912d346Sjmcneill device_printf(sc->sc_dev, "TIMEOUT: 0x%08x\n",
3705912d346Sjmcneill MMC_READ(sc, DWC_MMC_TIMEOUT));
3715912d346Sjmcneill device_printf(sc->sc_dev, "WIDTH: 0x%08x\n",
3725912d346Sjmcneill MMC_READ(sc, DWC_MMC_WIDTH));
3735912d346Sjmcneill device_printf(sc->sc_dev, "CMD: 0x%08x\n",
3745912d346Sjmcneill MMC_READ(sc, DWC_MMC_CMD));
3755912d346Sjmcneill device_printf(sc->sc_dev, "MINT: 0x%08x\n",
3765912d346Sjmcneill MMC_READ(sc, DWC_MMC_MINT));
3775912d346Sjmcneill device_printf(sc->sc_dev, "RINT: 0x%08x\n",
3785912d346Sjmcneill MMC_READ(sc, DWC_MMC_RINT));
3795912d346Sjmcneill device_printf(sc->sc_dev, "STATUS: 0x%08x\n",
3805912d346Sjmcneill MMC_READ(sc, DWC_MMC_STATUS));
3815912d346Sjmcneill #endif
3825912d346Sjmcneill return ETIMEDOUT;
3835912d346Sjmcneill }
3845912d346Sjmcneill
3855912d346Sjmcneill return 0;
3865912d346Sjmcneill }
3875912d346Sjmcneill
3885912d346Sjmcneill static int
dwc_mmc_set_clock(struct dwc_mmc_softc * sc,u_int freq)3895912d346Sjmcneill dwc_mmc_set_clock(struct dwc_mmc_softc *sc, u_int freq)
3905912d346Sjmcneill {
3915912d346Sjmcneill const u_int pll_freq = sc->sc_clock_freq / 1000;
392e734e7f1Sjmcneill u_int clk_div, ciu_div;
393e734e7f1Sjmcneill
394e734e7f1Sjmcneill ciu_div = sc->sc_ciu_div > 0 ? sc->sc_ciu_div : 1;
395a4c07ed0Sjmcneill
396a4c07ed0Sjmcneill if (freq != pll_freq)
397e734e7f1Sjmcneill clk_div = howmany(pll_freq, freq * ciu_div);
398a4c07ed0Sjmcneill else
399a4c07ed0Sjmcneill clk_div = 0;
4005912d346Sjmcneill
4015912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_CLKDIV, clk_div);
4025912d346Sjmcneill
4035912d346Sjmcneill return dwc_mmc_update_clock(sc);
4045912d346Sjmcneill }
4055912d346Sjmcneill
4065912d346Sjmcneill static int
dwc_mmc_bus_clock(sdmmc_chipset_handle_t sch,int freq)4075912d346Sjmcneill dwc_mmc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
4085912d346Sjmcneill {
4095912d346Sjmcneill struct dwc_mmc_softc *sc = sch;
4105912d346Sjmcneill uint32_t clkena;
4115912d346Sjmcneill
4125912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_CLKSRC, 0);
4135912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_CLKENA, 0);
4145912d346Sjmcneill if (dwc_mmc_update_clock(sc) != 0)
4155912d346Sjmcneill return 1;
4165912d346Sjmcneill
4175912d346Sjmcneill if (freq) {
418a4c07ed0Sjmcneill if (sc->sc_bus_clock && sc->sc_bus_clock(sc, freq) != 0)
419a4c07ed0Sjmcneill return 1;
420a4c07ed0Sjmcneill
4215912d346Sjmcneill if (dwc_mmc_set_clock(sc, freq) != 0)
4225912d346Sjmcneill return 1;
4235912d346Sjmcneill
4245912d346Sjmcneill clkena = DWC_MMC_CLKENA_CARDCLKON;
4255912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_CLKENA, clkena);
4265912d346Sjmcneill if (dwc_mmc_update_clock(sc) != 0)
4275912d346Sjmcneill return 1;
4285912d346Sjmcneill }
4295912d346Sjmcneill
4305912d346Sjmcneill delay(1000);
4315912d346Sjmcneill
4325912d346Sjmcneill return 0;
4335912d346Sjmcneill }
4345912d346Sjmcneill
4355912d346Sjmcneill static int
dwc_mmc_bus_width(sdmmc_chipset_handle_t sch,int width)4365912d346Sjmcneill dwc_mmc_bus_width(sdmmc_chipset_handle_t sch, int width)
4375912d346Sjmcneill {
4385912d346Sjmcneill struct dwc_mmc_softc *sc = sch;
4395912d346Sjmcneill
4405912d346Sjmcneill #ifdef DWC_MMC_DEBUG
4415912d346Sjmcneill aprint_normal_dev(sc->sc_dev, "width = %d\n", width);
4425912d346Sjmcneill #endif
4435912d346Sjmcneill
4445912d346Sjmcneill switch (width) {
4455912d346Sjmcneill case 1:
4465912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_WIDTH, DWC_MMC_WIDTH_1);
4475912d346Sjmcneill break;
4485912d346Sjmcneill case 4:
4495912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_WIDTH, DWC_MMC_WIDTH_4);
4505912d346Sjmcneill break;
4515912d346Sjmcneill case 8:
4525912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_WIDTH, DWC_MMC_WIDTH_8);
4535912d346Sjmcneill break;
4545912d346Sjmcneill default:
4555912d346Sjmcneill return 1;
4565912d346Sjmcneill }
4575912d346Sjmcneill
4585912d346Sjmcneill sc->sc_mmc_width = width;
4595912d346Sjmcneill
4605912d346Sjmcneill return 0;
4615912d346Sjmcneill }
4625912d346Sjmcneill
4635912d346Sjmcneill static int
dwc_mmc_bus_rod(sdmmc_chipset_handle_t sch,int on)4645912d346Sjmcneill dwc_mmc_bus_rod(sdmmc_chipset_handle_t sch, int on)
4655912d346Sjmcneill {
4665912d346Sjmcneill return -1;
4675912d346Sjmcneill }
4685912d346Sjmcneill
46921d168c0Sjmcneill static int
dwc_mmc_dma_prepare(struct dwc_mmc_softc * sc,struct sdmmc_command * cmd)4705912d346Sjmcneill dwc_mmc_dma_prepare(struct dwc_mmc_softc *sc, struct sdmmc_command *cmd)
47121d168c0Sjmcneill {
4725912d346Sjmcneill struct dwc_mmc_idma_desc *dma = sc->sc_idma_desc;
4735912d346Sjmcneill bus_addr_t desc_paddr = sc->sc_idma_map->dm_segs[0].ds_addr;
47426e0c5cbSjmcneill bus_dmamap_t map;
4755912d346Sjmcneill bus_size_t off;
4765912d346Sjmcneill int desc, resid, seg;
4775912d346Sjmcneill uint32_t val;
47821d168c0Sjmcneill
47926e0c5cbSjmcneill /*
480ab013bb3Sskrll * If the command includes a dma map use it, otherwise we need to
48126e0c5cbSjmcneill * bounce. This can happen for SDIO IO_RW_EXTENDED (CMD53) commands.
48226e0c5cbSjmcneill */
48326e0c5cbSjmcneill if (cmd->c_dmamap) {
48426e0c5cbSjmcneill map = cmd->c_dmamap;
48526e0c5cbSjmcneill } else {
48626e0c5cbSjmcneill if (cmd->c_datalen > sc->sc_dmabounce_buflen)
48726e0c5cbSjmcneill return E2BIG;
48826e0c5cbSjmcneill map = sc->sc_dmabounce_map;
48926e0c5cbSjmcneill
49026e0c5cbSjmcneill if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
49126e0c5cbSjmcneill memset(sc->sc_dmabounce_buf, 0, cmd->c_datalen);
49226e0c5cbSjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dmabounce_map,
49326e0c5cbSjmcneill 0, cmd->c_datalen, BUS_DMASYNC_PREREAD);
49426e0c5cbSjmcneill } else {
49526e0c5cbSjmcneill memcpy(sc->sc_dmabounce_buf, cmd->c_data,
49626e0c5cbSjmcneill cmd->c_datalen);
49726e0c5cbSjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dmabounce_map,
49826e0c5cbSjmcneill 0, cmd->c_datalen, BUS_DMASYNC_PREWRITE);
49926e0c5cbSjmcneill }
50026e0c5cbSjmcneill }
50126e0c5cbSjmcneill
5025912d346Sjmcneill desc = 0;
50326e0c5cbSjmcneill for (seg = 0; seg < map->dm_nsegs; seg++) {
50426e0c5cbSjmcneill bus_addr_t paddr = map->dm_segs[seg].ds_addr;
50526e0c5cbSjmcneill bus_size_t len = map->dm_segs[seg].ds_len;
506d1579b2dSriastradh resid = uimin(len, cmd->c_resid);
5075912d346Sjmcneill off = 0;
5085912d346Sjmcneill while (resid > 0) {
5095912d346Sjmcneill if (desc == sc->sc_idma_ndesc)
51021d168c0Sjmcneill break;
511d1579b2dSriastradh len = uimin(sc->sc_idma_xferlen, resid);
5125912d346Sjmcneill dma[desc].dma_buf_size = htole32(len);
5135912d346Sjmcneill dma[desc].dma_buf_addr = htole32(paddr + off);
5145912d346Sjmcneill dma[desc].dma_config = htole32(
51526e0c5cbSjmcneill DWC_MMC_IDMA_CONFIG_CH |
5165912d346Sjmcneill DWC_MMC_IDMA_CONFIG_OWN);
5175912d346Sjmcneill cmd->c_resid -= len;
5185912d346Sjmcneill resid -= len;
5195912d346Sjmcneill off += len;
5205912d346Sjmcneill if (desc == 0) {
5215912d346Sjmcneill dma[desc].dma_config |= htole32(
5225912d346Sjmcneill DWC_MMC_IDMA_CONFIG_FD);
52321d168c0Sjmcneill }
5245912d346Sjmcneill if (cmd->c_resid == 0) {
5255912d346Sjmcneill dma[desc].dma_config |= htole32(
5265912d346Sjmcneill DWC_MMC_IDMA_CONFIG_LD);
52726e0c5cbSjmcneill dma[desc].dma_config |= htole32(
52826e0c5cbSjmcneill DWC_MMC_IDMA_CONFIG_ER);
52926e0c5cbSjmcneill dma[desc].dma_next = 0;
530b934ffb3Sjmcneill } else {
5315912d346Sjmcneill dma[desc].dma_config |=
53226e0c5cbSjmcneill htole32(DWC_MMC_IDMA_CONFIG_DIC);
53326e0c5cbSjmcneill dma[desc].dma_next = htole32(
53426e0c5cbSjmcneill desc_paddr + ((desc+1) *
53526e0c5cbSjmcneill sizeof(struct dwc_mmc_idma_desc)));
5365912d346Sjmcneill }
5375912d346Sjmcneill ++desc;
53821d168c0Sjmcneill }
539b934ffb3Sjmcneill }
5405912d346Sjmcneill if (desc == sc->sc_idma_ndesc) {
5415912d346Sjmcneill aprint_error_dev(sc->sc_dev,
5425912d346Sjmcneill "not enough descriptors for %d byte transfer!\n",
5435912d346Sjmcneill cmd->c_datalen);
54421d168c0Sjmcneill return EIO;
54521d168c0Sjmcneill }
54621d168c0Sjmcneill
5475912d346Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_idma_map, 0,
5485912d346Sjmcneill sc->sc_idma_size, BUS_DMASYNC_PREWRITE);
5492e2c694bSjmcneill
55026e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_DLBA, desc_paddr);
5515912d346Sjmcneill
5525912d346Sjmcneill val = MMC_READ(sc, DWC_MMC_GCTRL);
5535912d346Sjmcneill val |= DWC_MMC_GCTRL_DMAEN;
5545912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_GCTRL, val);
5555912d346Sjmcneill val |= DWC_MMC_GCTRL_DMARESET;
5565912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_GCTRL, val);
55726e0c5cbSjmcneill
55826e0c5cbSjmcneill if (cmd->c_flags & SCF_CMD_READ)
55926e0c5cbSjmcneill val = DWC_MMC_IDST_RECEIVE_INT;
56026e0c5cbSjmcneill else
56126e0c5cbSjmcneill val = 0;
56226e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IDIE, val);
5635912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_DMAC,
5645912d346Sjmcneill DWC_MMC_DMAC_IDMA_ON|DWC_MMC_DMAC_FIX_BURST);
5657ffab44eSjmcneill
56621d168c0Sjmcneill return 0;
56721d168c0Sjmcneill }
56821d168c0Sjmcneill
5695912d346Sjmcneill static void
dwc_mmc_dma_complete(struct dwc_mmc_softc * sc,struct sdmmc_command * cmd)57026e0c5cbSjmcneill dwc_mmc_dma_complete(struct dwc_mmc_softc *sc, struct sdmmc_command *cmd)
57121d168c0Sjmcneill {
57226e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_DMAC, 0);
57326e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IDIE, 0);
57426e0c5cbSjmcneill
5755912d346Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_idma_map, 0,
5765912d346Sjmcneill sc->sc_idma_size, BUS_DMASYNC_POSTWRITE);
57726e0c5cbSjmcneill
57826e0c5cbSjmcneill if (cmd->c_dmamap == NULL) {
57926e0c5cbSjmcneill if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
58026e0c5cbSjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dmabounce_map,
58126e0c5cbSjmcneill 0, cmd->c_datalen, BUS_DMASYNC_POSTREAD);
58226e0c5cbSjmcneill memcpy(cmd->c_data, sc->sc_dmabounce_buf,
58326e0c5cbSjmcneill cmd->c_datalen);
58426e0c5cbSjmcneill } else {
58526e0c5cbSjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dmabounce_map,
58626e0c5cbSjmcneill 0, cmd->c_datalen, BUS_DMASYNC_POSTWRITE);
58726e0c5cbSjmcneill }
58826e0c5cbSjmcneill }
58921d168c0Sjmcneill }
59021d168c0Sjmcneill
59121d168c0Sjmcneill static void
dwc_mmc_exec_command(sdmmc_chipset_handle_t sch,struct sdmmc_command * cmd)59221d168c0Sjmcneill dwc_mmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
59321d168c0Sjmcneill {
59421d168c0Sjmcneill struct dwc_mmc_softc *sc = sch;
5955912d346Sjmcneill uint32_t cmdval = DWC_MMC_CMD_START;
59626e0c5cbSjmcneill int retry, error;
59726e0c5cbSjmcneill uint32_t imask;
598bf24108eSjmcneill u_int reg;
59921d168c0Sjmcneill
6002e2c694bSjmcneill #ifdef DWC_MMC_DEBUG
6015912d346Sjmcneill aprint_normal_dev(sc->sc_dev,
6025912d346Sjmcneill "opcode %d flags 0x%x data %p datalen %d blklen %d\n",
6035912d346Sjmcneill cmd->c_opcode, cmd->c_flags, cmd->c_data, cmd->c_datalen,
6045912d346Sjmcneill cmd->c_blklen);
6052e2c694bSjmcneill #endif
6062e2c694bSjmcneill
607bf24108eSjmcneill mutex_enter(&sc->sc_lock);
60826e0c5cbSjmcneill if (sc->sc_curcmd != NULL) {
60926e0c5cbSjmcneill device_printf(sc->sc_dev,
61026e0c5cbSjmcneill "WARNING: driver submitted a command while the controller was busy\n");
61126e0c5cbSjmcneill cmd->c_error = EBUSY;
61226e0c5cbSjmcneill SET(cmd->c_flags, SCF_ITSDONE);
613bf24108eSjmcneill mutex_exit(&sc->sc_lock);
61426e0c5cbSjmcneill return;
6155912d346Sjmcneill }
61626e0c5cbSjmcneill sc->sc_curcmd = cmd;
6175912d346Sjmcneill
6185912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_IDST, 0xffffffff);
6195912d346Sjmcneill
620bf24108eSjmcneill if (!sc->sc_card_inited) {
621bf24108eSjmcneill cmdval |= DWC_MMC_CMD_SEND_INIT_SEQ;
622bf24108eSjmcneill sc->sc_card_inited = true;
623bf24108eSjmcneill }
624bf24108eSjmcneill
6255912d346Sjmcneill if (ISSET(sc->sc_flags, DWC_MMC_F_USE_HOLD_REG))
6265912d346Sjmcneill cmdval |= DWC_MMC_CMD_USE_HOLD_REG;
627c7dcf944Sjmcneill
628bf24108eSjmcneill switch (cmd->c_opcode) {
629bf24108eSjmcneill case SD_IO_RW_DIRECT:
630bf24108eSjmcneill reg = (cmd->c_arg >> SD_ARG_CMD52_REG_SHIFT) &
631bf24108eSjmcneill SD_ARG_CMD52_REG_MASK;
632bf24108eSjmcneill if (reg != 0x6) /* func abort / card reset */
633bf24108eSjmcneill break;
634bf24108eSjmcneill /* FALLTHROUGH */
635bf24108eSjmcneill case MMC_GO_IDLE_STATE:
636bf24108eSjmcneill case MMC_STOP_TRANSMISSION:
637bf24108eSjmcneill case MMC_INACTIVE_STATE:
638bf24108eSjmcneill cmdval |= DWC_MMC_CMD_STOP_ABORT_CMD;
639bf24108eSjmcneill break;
640bf24108eSjmcneill }
641bf24108eSjmcneill
64221d168c0Sjmcneill if (cmd->c_flags & SCF_RSP_PRESENT)
6435912d346Sjmcneill cmdval |= DWC_MMC_CMD_RSP_EXP;
64421d168c0Sjmcneill if (cmd->c_flags & SCF_RSP_136)
6455912d346Sjmcneill cmdval |= DWC_MMC_CMD_LONG_RSP;
64621d168c0Sjmcneill if (cmd->c_flags & SCF_RSP_CRC)
6475912d346Sjmcneill cmdval |= DWC_MMC_CMD_CHECK_RSP_CRC;
64821d168c0Sjmcneill
64926e0c5cbSjmcneill imask = DWC_MMC_INT_ERROR | DWC_MMC_INT_CMD_DONE;
65026e0c5cbSjmcneill
65121d168c0Sjmcneill if (cmd->c_datalen > 0) {
65221d168c0Sjmcneill unsigned int nblks;
65321d168c0Sjmcneill
654c1484821Sjmcneill MMC_WRITE(sc, DWC_MMC_GCTRL,
655c1484821Sjmcneill MMC_READ(sc, DWC_MMC_GCTRL) | DWC_MMC_GCTRL_FIFORESET);
656bf24108eSjmcneill for (retry = 0; retry < 100000; retry++) {
657c1484821Sjmcneill if (!(MMC_READ(sc, DWC_MMC_DMAC) & DWC_MMC_DMAC_SOFTRESET))
658c1484821Sjmcneill break;
659bf24108eSjmcneill delay(1);
660c1484821Sjmcneill }
661c1484821Sjmcneill
6625912d346Sjmcneill cmdval |= DWC_MMC_CMD_DATA_EXP | DWC_MMC_CMD_WAIT_PRE_OVER;
66321d168c0Sjmcneill if (!ISSET(cmd->c_flags, SCF_CMD_READ)) {
6645912d346Sjmcneill cmdval |= DWC_MMC_CMD_WRITE;
66521d168c0Sjmcneill }
66621d168c0Sjmcneill
66721d168c0Sjmcneill nblks = cmd->c_datalen / cmd->c_blklen;
66821d168c0Sjmcneill if (nblks == 0 || (cmd->c_datalen % cmd->c_blklen) != 0)
66921d168c0Sjmcneill ++nblks;
67021d168c0Sjmcneill
671c1484821Sjmcneill if (nblks > 1 && cmd->c_opcode != SD_IO_RW_EXTENDED) {
67221d168c0Sjmcneill cmdval |= DWC_MMC_CMD_SEND_AUTO_STOP;
67326e0c5cbSjmcneill imask |= DWC_MMC_INT_AUTO_CMD_DONE;
67426e0c5cbSjmcneill } else {
67526e0c5cbSjmcneill imask |= DWC_MMC_INT_DATA_OVER;
67621d168c0Sjmcneill }
67721d168c0Sjmcneill
678c1484821Sjmcneill MMC_WRITE(sc, DWC_MMC_TIMEOUT, 0xffffffff);
6795912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_BLKSZ, cmd->c_blklen);
680c1484821Sjmcneill MMC_WRITE(sc, DWC_MMC_BYTECNT,
681c1484821Sjmcneill nblks > 1 ? nblks * cmd->c_blklen : cmd->c_datalen);
682c5feb15cSskrll
683c5feb15cSskrll #if 0
684c5feb15cSskrll /*
685c5feb15cSskrll * The following doesn't work on the 250a verid IP in Odroid-XU4.
686c5feb15cSskrll *
687c5feb15cSskrll * thrctl should only be used for UHS/HS200 and faster timings on
688c5feb15cSskrll * >=240a
689c5feb15cSskrll */
690c5feb15cSskrll
691c1484821Sjmcneill if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
692c1484821Sjmcneill MMC_WRITE(sc, DWC_MMC_CARDTHRCTL,
693c1484821Sjmcneill __SHIFTIN(cmd->c_blklen, DWC_MMC_CARDTHRCTL_RDTHR) |
694c1484821Sjmcneill DWC_MMC_CARDTHRCTL_RDTHREN);
695c1484821Sjmcneill }
696c5feb15cSskrll #endif
69721d168c0Sjmcneill }
69821d168c0Sjmcneill
69926e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IMASK, imask | sc->sc_intr_card);
70026e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_RINT, 0x7fff);
70121d168c0Sjmcneill
7025912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_ARG, cmd->c_arg);
70321d168c0Sjmcneill
7045912d346Sjmcneill #ifdef DWC_MMC_DEBUG
7055912d346Sjmcneill aprint_normal_dev(sc->sc_dev, "cmdval = %08x\n", cmdval);
7065912d346Sjmcneill #endif
7075912d346Sjmcneill
70821d168c0Sjmcneill cmd->c_resid = cmd->c_datalen;
70921d168c0Sjmcneill if (cmd->c_datalen > 0) {
71026e0c5cbSjmcneill dwc_mmc_led(sc, 0);
71126e0c5cbSjmcneill cmd->c_error = dwc_mmc_dma_prepare(sc, cmd);
71226e0c5cbSjmcneill if (cmd->c_error != 0) {
71326e0c5cbSjmcneill SET(cmd->c_flags, SCF_ITSDONE);
71426e0c5cbSjmcneill goto done;
71521d168c0Sjmcneill }
71626e0c5cbSjmcneill sc->sc_wait_dma = ISSET(cmd->c_flags, SCF_CMD_READ);
71726e0c5cbSjmcneill sc->sc_wait_data = true;
71826e0c5cbSjmcneill } else {
71926e0c5cbSjmcneill sc->sc_wait_dma = false;
72026e0c5cbSjmcneill sc->sc_wait_data = false;
72126e0c5cbSjmcneill }
72226e0c5cbSjmcneill sc->sc_wait_cmd = true;
72326e0c5cbSjmcneill
724bf24108eSjmcneill if ((cmdval & DWC_MMC_CMD_WAIT_PRE_OVER) != 0) {
725bf24108eSjmcneill for (retry = 0; retry < 10000; retry++) {
726bf24108eSjmcneill if (!(MMC_READ(sc, DWC_MMC_STATUS) & DWC_MMC_STATUS_CARD_DATA_BUSY))
727bf24108eSjmcneill break;
728bf24108eSjmcneill delay(1);
729bf24108eSjmcneill }
730bf24108eSjmcneill }
731bf24108eSjmcneill
732bf24108eSjmcneill mutex_enter(&sc->sc_intr_lock);
733bf24108eSjmcneill
73426e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_CMD, cmdval | cmd->c_opcode);
73526e0c5cbSjmcneill
73626e0c5cbSjmcneill if (sc->sc_wait_dma)
73726e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_PLDMND, 1);
73826e0c5cbSjmcneill
73926e0c5cbSjmcneill struct bintime timeout = { .sec = 15, .frac = 0 };
74026e0c5cbSjmcneill const struct bintime epsilon = { .sec = 1, .frac = 0 };
74126e0c5cbSjmcneill while (!ISSET(cmd->c_flags, SCF_ITSDONE)) {
74226e0c5cbSjmcneill error = cv_timedwaitbt(&sc->sc_intr_cv,
74326e0c5cbSjmcneill &sc->sc_intr_lock, &timeout, &epsilon);
74426e0c5cbSjmcneill if (error != 0) {
74526e0c5cbSjmcneill cmd->c_error = error;
74626e0c5cbSjmcneill SET(cmd->c_flags, SCF_ITSDONE);
7470f6b3638Sskrll mutex_exit(&sc->sc_intr_lock);
74821d168c0Sjmcneill goto done;
74921d168c0Sjmcneill }
75021d168c0Sjmcneill }
75121d168c0Sjmcneill
752bf24108eSjmcneill mutex_exit(&sc->sc_intr_lock);
753bf24108eSjmcneill
75426e0c5cbSjmcneill if (cmd->c_error == 0 && cmd->c_datalen > 0)
75526e0c5cbSjmcneill dwc_mmc_dma_complete(sc, cmd);
75626e0c5cbSjmcneill
75726e0c5cbSjmcneill if (cmd->c_datalen > 0)
75826e0c5cbSjmcneill dwc_mmc_led(sc, 1);
75926e0c5cbSjmcneill
76021d168c0Sjmcneill if (cmd->c_flags & SCF_RSP_PRESENT) {
76121d168c0Sjmcneill if (cmd->c_flags & SCF_RSP_136) {
7625912d346Sjmcneill cmd->c_resp[0] = MMC_READ(sc, DWC_MMC_RESP0);
7635912d346Sjmcneill cmd->c_resp[1] = MMC_READ(sc, DWC_MMC_RESP1);
7645912d346Sjmcneill cmd->c_resp[2] = MMC_READ(sc, DWC_MMC_RESP2);
7655912d346Sjmcneill cmd->c_resp[3] = MMC_READ(sc, DWC_MMC_RESP3);
76621d168c0Sjmcneill if (cmd->c_flags & SCF_RSP_CRC) {
76721d168c0Sjmcneill cmd->c_resp[0] = (cmd->c_resp[0] >> 8) |
76821d168c0Sjmcneill (cmd->c_resp[1] << 24);
76921d168c0Sjmcneill cmd->c_resp[1] = (cmd->c_resp[1] >> 8) |
77021d168c0Sjmcneill (cmd->c_resp[2] << 24);
77121d168c0Sjmcneill cmd->c_resp[2] = (cmd->c_resp[2] >> 8) |
77221d168c0Sjmcneill (cmd->c_resp[3] << 24);
77321d168c0Sjmcneill cmd->c_resp[3] = (cmd->c_resp[3] >> 8);
77421d168c0Sjmcneill }
77521d168c0Sjmcneill } else {
7765912d346Sjmcneill cmd->c_resp[0] = MMC_READ(sc, DWC_MMC_RESP0);
77721d168c0Sjmcneill }
77821d168c0Sjmcneill }
77921d168c0Sjmcneill
78021d168c0Sjmcneill done:
78126e0c5cbSjmcneill KASSERT(ISSET(cmd->c_flags, SCF_ITSDONE));
78226e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IMASK, sc->sc_intr_card);
78326e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IDIE, 0);
78426e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_RINT, 0x7fff);
78526e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IDST, 0xffffffff);
78621d168c0Sjmcneill
7875912d346Sjmcneill if (cmd->c_error) {
7885912d346Sjmcneill #ifdef DWC_MMC_DEBUG
7895912d346Sjmcneill aprint_error_dev(sc->sc_dev, "i/o error %d\n", cmd->c_error);
7905912d346Sjmcneill #endif
791c1484821Sjmcneill MMC_WRITE(sc, DWC_MMC_DMAC, DWC_MMC_DMAC_SOFTRESET);
792c1484821Sjmcneill for (retry = 0; retry < 100; retry++) {
793c1484821Sjmcneill if (!(MMC_READ(sc, DWC_MMC_DMAC) & DWC_MMC_DMAC_SOFTRESET))
7945912d346Sjmcneill break;
795bf24108eSjmcneill kpause("dwcmmcrst", false, uimax(mstohz(1), 1), &sc->sc_lock);
7965912d346Sjmcneill }
797b934ffb3Sjmcneill }
798bf24108eSjmcneill
799bf24108eSjmcneill sc->sc_curcmd = NULL;
800bf24108eSjmcneill mutex_exit(&sc->sc_lock);
8015912d346Sjmcneill }
80221d168c0Sjmcneill
80321d168c0Sjmcneill static void
dwc_mmc_card_enable_intr(sdmmc_chipset_handle_t sch,int enable)80421d168c0Sjmcneill dwc_mmc_card_enable_intr(sdmmc_chipset_handle_t sch, int enable)
80521d168c0Sjmcneill {
80626e0c5cbSjmcneill struct dwc_mmc_softc *sc = sch;
80726e0c5cbSjmcneill uint32_t imask;
80826e0c5cbSjmcneill
80926e0c5cbSjmcneill mutex_enter(&sc->sc_intr_lock);
81026e0c5cbSjmcneill imask = MMC_READ(sc, DWC_MMC_IMASK);
81126e0c5cbSjmcneill if (enable)
812c1484821Sjmcneill imask |= sc->sc_intr_cardmask;
81326e0c5cbSjmcneill else
814c1484821Sjmcneill imask &= ~sc->sc_intr_cardmask;
815c1484821Sjmcneill sc->sc_intr_card = imask & sc->sc_intr_cardmask;
81626e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IMASK, imask);
81726e0c5cbSjmcneill mutex_exit(&sc->sc_intr_lock);
81821d168c0Sjmcneill }
81921d168c0Sjmcneill
82021d168c0Sjmcneill static void
dwc_mmc_card_intr_ack(sdmmc_chipset_handle_t sch)82121d168c0Sjmcneill dwc_mmc_card_intr_ack(sdmmc_chipset_handle_t sch)
82221d168c0Sjmcneill {
82326e0c5cbSjmcneill struct dwc_mmc_softc *sc = sch;
82426e0c5cbSjmcneill uint32_t imask;
82526e0c5cbSjmcneill
82626e0c5cbSjmcneill mutex_enter(&sc->sc_intr_lock);
82726e0c5cbSjmcneill imask = MMC_READ(sc, DWC_MMC_IMASK);
82826e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IMASK, imask | sc->sc_intr_card);
82926e0c5cbSjmcneill mutex_exit(&sc->sc_intr_lock);
83021d168c0Sjmcneill }
83121d168c0Sjmcneill
8325912d346Sjmcneill int
dwc_mmc_init(struct dwc_mmc_softc * sc)8335912d346Sjmcneill dwc_mmc_init(struct dwc_mmc_softc *sc)
8348e097910Sjmcneill {
835b8cc3839Sjmcneill uint32_t val;
836b8cc3839Sjmcneill
837b8cc3839Sjmcneill val = MMC_READ(sc, DWC_MMC_VERID);
838bc8d1afbSskrll sc->sc_verid = __SHIFTOUT(val, DWC_MMC_VERID_ID);
839b8cc3839Sjmcneill
840*62b14db6Sskrll if (sizeof(bus_addr_t) > 4) {
841*62b14db6Sskrll int error = bus_dmatag_subregion(sc->sc_dmat, 0, __MASK(32),
842*62b14db6Sskrll &sc->sc_dmat, BUS_DMA_WAITOK);
843*62b14db6Sskrll if (error != 0) {
844*62b14db6Sskrll aprint_error_dev(sc->sc_dev,
845*62b14db6Sskrll "failed to create DMA subregion\n");
846*62b14db6Sskrll return ENOMEM;
847*62b14db6Sskrll }
848*62b14db6Sskrll }
849*62b14db6Sskrll
850bc8d1afbSskrll if (sc->sc_fifo_reg == 0) {
851bc8d1afbSskrll if (sc->sc_verid < DWC_MMC_VERID_240A)
852b8cc3839Sjmcneill sc->sc_fifo_reg = 0x100;
853b8cc3839Sjmcneill else
854b8cc3839Sjmcneill sc->sc_fifo_reg = 0x200;
855b8cc3839Sjmcneill }
856b8cc3839Sjmcneill
857b8cc3839Sjmcneill if (sc->sc_fifo_depth == 0) {
858b8cc3839Sjmcneill val = MMC_READ(sc, DWC_MMC_FIFOTH);
859b8cc3839Sjmcneill sc->sc_fifo_depth = __SHIFTOUT(val, DWC_MMC_FIFOTH_RX_WMARK) + 1;
860b8cc3839Sjmcneill }
861b8cc3839Sjmcneill
862c1484821Sjmcneill if (sc->sc_intr_cardmask == 0)
863c1484821Sjmcneill sc->sc_intr_cardmask = DWC_MMC_INT_SDIO_INT(0);
864c1484821Sjmcneill
865bf24108eSjmcneill mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
8665912d346Sjmcneill mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_BIO);
8675912d346Sjmcneill cv_init(&sc->sc_intr_cv, "dwcmmcirq");
8685912d346Sjmcneill
86926e0c5cbSjmcneill if (dwc_mmc_dmabounce_setup(sc) != 0 ||
87026e0c5cbSjmcneill dwc_mmc_idma_setup(sc) != 0) {
8715912d346Sjmcneill aprint_error_dev(sc->sc_dev, "failed to setup DMA\n");
8725912d346Sjmcneill return ENOMEM;
8738e097910Sjmcneill }
8745912d346Sjmcneill
8755912d346Sjmcneill config_interrupts(sc->sc_dev, dwc_mmc_attach_i);
8765912d346Sjmcneill
8775912d346Sjmcneill return 0;
8785912d346Sjmcneill }
8795912d346Sjmcneill
8805912d346Sjmcneill int
dwc_mmc_intr(void * priv)8815912d346Sjmcneill dwc_mmc_intr(void *priv)
8825912d346Sjmcneill {
8835912d346Sjmcneill struct dwc_mmc_softc *sc = priv;
88426e0c5cbSjmcneill struct sdmmc_command *cmd;
88526e0c5cbSjmcneill uint32_t idst, mint, imask;
8865912d346Sjmcneill
8875912d346Sjmcneill mutex_enter(&sc->sc_intr_lock);
8885912d346Sjmcneill idst = MMC_READ(sc, DWC_MMC_IDST);
8895912d346Sjmcneill mint = MMC_READ(sc, DWC_MMC_MINT);
89026e0c5cbSjmcneill if (!idst && !mint) {
8915912d346Sjmcneill mutex_exit(&sc->sc_intr_lock);
8925912d346Sjmcneill return 0;
8935912d346Sjmcneill }
8945912d346Sjmcneill MMC_WRITE(sc, DWC_MMC_IDST, idst);
89526e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_RINT, mint);
89626e0c5cbSjmcneill
89726e0c5cbSjmcneill cmd = sc->sc_curcmd;
8985912d346Sjmcneill
8995912d346Sjmcneill #ifdef DWC_MMC_DEBUG
90026e0c5cbSjmcneill device_printf(sc->sc_dev, "mmc intr idst=%08X mint=%08X\n",
90126e0c5cbSjmcneill idst, mint);
9028e097910Sjmcneill #endif
9038e097910Sjmcneill
90426e0c5cbSjmcneill /* Handle SDIO card interrupt */
905c1484821Sjmcneill if ((mint & sc->sc_intr_cardmask) != 0) {
90626e0c5cbSjmcneill imask = MMC_READ(sc, DWC_MMC_IMASK);
907c1484821Sjmcneill MMC_WRITE(sc, DWC_MMC_IMASK, imask & ~sc->sc_intr_cardmask);
90826e0c5cbSjmcneill sdmmc_card_intr(sc->sc_sdmmc_dev);
90921d168c0Sjmcneill }
9105912d346Sjmcneill
91126e0c5cbSjmcneill /* Error interrupts take priority over command and transfer interrupts */
91226e0c5cbSjmcneill if (cmd != NULL && (mint & DWC_MMC_INT_ERROR) != 0) {
91326e0c5cbSjmcneill imask = MMC_READ(sc, DWC_MMC_IMASK);
91426e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IMASK, imask & ~DWC_MMC_INT_ERROR);
91526e0c5cbSjmcneill if ((mint & DWC_MMC_INT_RESP_TIMEOUT) != 0) {
91626e0c5cbSjmcneill cmd->c_error = ETIMEDOUT;
91726e0c5cbSjmcneill /* Wait for command to complete */
91826e0c5cbSjmcneill sc->sc_wait_data = sc->sc_wait_dma = false;
91926e0c5cbSjmcneill if (cmd->c_opcode != SD_IO_SEND_OP_COND &&
92026e0c5cbSjmcneill cmd->c_opcode != SD_IO_RW_DIRECT &&
92126e0c5cbSjmcneill !ISSET(cmd->c_flags, SCF_TOUT_OK))
92226e0c5cbSjmcneill device_printf(sc->sc_dev, "host controller timeout, mint=0x%08x\n", mint);
92326e0c5cbSjmcneill } else {
92426e0c5cbSjmcneill device_printf(sc->sc_dev, "host controller error, mint=0x%08x\n", mint);
92526e0c5cbSjmcneill cmd->c_error = EIO;
92626e0c5cbSjmcneill SET(cmd->c_flags, SCF_ITSDONE);
92726e0c5cbSjmcneill goto done;
92826e0c5cbSjmcneill }
92926e0c5cbSjmcneill }
93026e0c5cbSjmcneill
93126e0c5cbSjmcneill if (cmd != NULL && (idst & DWC_MMC_IDST_RECEIVE_INT) != 0) {
93226e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IDIE, 0);
93326e0c5cbSjmcneill if (sc->sc_wait_dma == false)
93426e0c5cbSjmcneill device_printf(sc->sc_dev, "unexpected DMA receive interrupt\n");
93526e0c5cbSjmcneill sc->sc_wait_dma = false;
93626e0c5cbSjmcneill }
93726e0c5cbSjmcneill
93826e0c5cbSjmcneill if (cmd != NULL && (mint & DWC_MMC_INT_CMD_DONE) != 0) {
93926e0c5cbSjmcneill imask = MMC_READ(sc, DWC_MMC_IMASK);
94026e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IMASK, imask & ~DWC_MMC_INT_CMD_DONE);
94126e0c5cbSjmcneill if (sc->sc_wait_cmd == false)
94226e0c5cbSjmcneill device_printf(sc->sc_dev, "unexpected command complete interrupt\n");
94326e0c5cbSjmcneill sc->sc_wait_cmd = false;
94426e0c5cbSjmcneill }
94526e0c5cbSjmcneill
94626e0c5cbSjmcneill const uint32_t dmadone_mask = DWC_MMC_INT_AUTO_CMD_DONE|DWC_MMC_INT_DATA_OVER;
94726e0c5cbSjmcneill if (cmd != NULL && (mint & dmadone_mask) != 0) {
94826e0c5cbSjmcneill imask = MMC_READ(sc, DWC_MMC_IMASK);
94926e0c5cbSjmcneill MMC_WRITE(sc, DWC_MMC_IMASK, imask & ~dmadone_mask);
95026e0c5cbSjmcneill if (sc->sc_wait_data == false)
95126e0c5cbSjmcneill device_printf(sc->sc_dev, "unexpected data complete interrupt\n");
95226e0c5cbSjmcneill sc->sc_wait_data = false;
95326e0c5cbSjmcneill }
95426e0c5cbSjmcneill
95526e0c5cbSjmcneill if (cmd != NULL &&
95626e0c5cbSjmcneill sc->sc_wait_dma == false &&
95726e0c5cbSjmcneill sc->sc_wait_cmd == false &&
95826e0c5cbSjmcneill sc->sc_wait_data == false) {
95926e0c5cbSjmcneill SET(cmd->c_flags, SCF_ITSDONE);
96026e0c5cbSjmcneill }
96126e0c5cbSjmcneill
96226e0c5cbSjmcneill done:
96326e0c5cbSjmcneill if (cmd != NULL && ISSET(cmd->c_flags, SCF_ITSDONE)) {
9645912d346Sjmcneill cv_broadcast(&sc->sc_intr_cv);
9655912d346Sjmcneill }
9665912d346Sjmcneill
9675912d346Sjmcneill mutex_exit(&sc->sc_intr_lock);
9685912d346Sjmcneill
9695912d346Sjmcneill return 1;
97021d168c0Sjmcneill }
971