Module Name: src Committed By: jmcneill Date: Sat Oct 5 12:27:14 UTC 2019
Modified Files: src/sys/dev/ic: dwc_mmc.c dwc_mmc_var.h Log Message: Add support for SDIO interrupts. To generate a diff of this commit: cvs rdiff -u -r1.17 -r1.18 src/sys/dev/ic/dwc_mmc.c cvs rdiff -u -r1.8 -r1.9 src/sys/dev/ic/dwc_mmc_var.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/ic/dwc_mmc.c diff -u src/sys/dev/ic/dwc_mmc.c:1.17 src/sys/dev/ic/dwc_mmc.c:1.18 --- src/sys/dev/ic/dwc_mmc.c:1.17 Sun Jul 28 10:30:44 2019 +++ src/sys/dev/ic/dwc_mmc.c Sat Oct 5 12:27:14 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: dwc_mmc.c,v 1.17 2019/07/28 10:30:44 jmcneill Exp $ */ +/* $NetBSD: dwc_mmc.c,v 1.18 2019/10/05 12:27:14 jmcneill Exp $ */ /*- * Copyright (c) 2014-2017 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: dwc_mmc.c,v 1.17 2019/07/28 10:30:44 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: dwc_mmc.c,v 1.18 2019/10/05 12:27:14 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -79,16 +79,40 @@ static struct sdmmc_chip_functions dwc_m #define MMC_READ(sc, reg) \ bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) -static void -dwc_mmc_dump_regs(struct dwc_mmc_softc *sc) +static int +dwc_mmc_dmabounce_setup(struct dwc_mmc_softc *sc) { - device_printf(sc->sc_dev, "device registers:\n"); - for (u_int off = 0x00; off < 0x100; off += 16) { - device_printf(sc->sc_dev, "xxxxxx%02x: %08x %08x %08x %08x\n", - off, - MMC_READ(sc, off + 0), MMC_READ(sc, off + 4), - MMC_READ(sc, off + 8), MMC_READ(sc, off + 12)); - } + bus_dma_segment_t ds[1]; + int error, rseg; + + sc->sc_dmabounce_buflen = dwc_mmc_host_maxblklen(sc); + error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmabounce_buflen, 0, + sc->sc_dmabounce_buflen, ds, 1, &rseg, BUS_DMA_WAITOK); + if (error) + return error; + error = bus_dmamem_map(sc->sc_dmat, ds, 1, sc->sc_dmabounce_buflen, + &sc->sc_dmabounce_buf, BUS_DMA_WAITOK); + if (error) + goto free; + error = bus_dmamap_create(sc->sc_dmat, sc->sc_dmabounce_buflen, 1, + sc->sc_dmabounce_buflen, 0, BUS_DMA_WAITOK, &sc->sc_dmabounce_map); + if (error) + goto unmap; + error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmabounce_map, + sc->sc_dmabounce_buf, sc->sc_dmabounce_buflen, NULL, + BUS_DMA_WAITOK); + if (error) + goto destroy; + return 0; + +destroy: + bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmabounce_map); +unmap: + bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmabounce_buf, + sc->sc_dmabounce_buflen); +free: + bus_dmamem_free(sc->sc_dmat, ds, rseg); + return error; } static int @@ -145,55 +169,20 @@ dwc_mmc_attach_i(device_t self) saa.saa_sch = sc; saa.saa_clkmin = 400; saa.saa_clkmax = sc->sc_clock_freq / 1000; + saa.saa_dmat = sc->sc_dmat; saa.saa_caps = SMC_CAPS_4BIT_MODE| SMC_CAPS_8BIT_MODE| SMC_CAPS_SD_HIGHSPEED| SMC_CAPS_MMC_HIGHSPEED| - SMC_CAPS_AUTO_STOP; - if (ISSET(sc->sc_flags, DWC_MMC_F_DMA)) { - saa.saa_dmat = sc->sc_dmat; - saa.saa_caps |= SMC_CAPS_DMA | - SMC_CAPS_MULTI_SEG_DMA; - } + SMC_CAPS_AUTO_STOP | + SMC_CAPS_DMA | + SMC_CAPS_MULTI_SEG_DMA; if (sc->sc_card_detect) saa.saa_caps |= SMC_CAPS_POLL_CARD_DET; sc->sc_sdmmc_dev = config_found(self, &saa, NULL); } -static int -dwc_mmc_wait_rint(struct dwc_mmc_softc *sc, uint32_t mask, int timeout) -{ - const bool use_dma = ISSET(sc->sc_flags, DWC_MMC_F_DMA); - int retry, error; - - KASSERT(mutex_owned(&sc->sc_intr_lock)); - - if (sc->sc_intr_rint & mask) - return 0; - - retry = timeout / hz; - - while (retry > 0) { - if (use_dma) { - error = cv_timedwait(&sc->sc_intr_cv, - &sc->sc_intr_lock, hz); - if (error && error != EWOULDBLOCK) - return error; - if (sc->sc_intr_rint & mask) - return 0; - } else { - sc->sc_intr_rint |= MMC_READ(sc, DWC_MMC_RINT); - if (sc->sc_intr_rint & mask) - return 0; - delay(1000); - } - --retry; - } - - return ETIMEDOUT; -} - static void dwc_mmc_led(struct dwc_mmc_softc *sc, int on) { @@ -226,9 +215,9 @@ dwc_mmc_host_reset(sdmmc_chipset_handle_ MMC_WRITE(sc, DWC_MMC_TIMEOUT, 0xffffffff); - MMC_WRITE(sc, DWC_MMC_IMASK, - DWC_MMC_INT_CMD_DONE | DWC_MMC_INT_ERROR | - DWC_MMC_INT_DATA_OVER | DWC_MMC_INT_AUTO_CMD_DONE); + MMC_WRITE(sc, DWC_MMC_IMASK, 0); + + MMC_WRITE(sc, DWC_MMC_RINT, 0xffffffff); const uint32_t rx_wmark = (sc->sc_fifo_depth / 2) - 1; const uint32_t tx_wmark = sc->sc_fifo_depth / 2; @@ -421,56 +410,43 @@ dwc_mmc_bus_rod(sdmmc_chipset_handle_t s return -1; } - -static int -dwc_mmc_pio_wait(struct dwc_mmc_softc *sc, struct sdmmc_command *cmd) -{ - int retry = 0xfffff; - uint32_t bit = (cmd->c_flags & SCF_CMD_READ) ? - DWC_MMC_STATUS_FIFO_EMPTY : DWC_MMC_STATUS_FIFO_FULL; - - while (--retry > 0) { - uint32_t status = MMC_READ(sc, DWC_MMC_STATUS); - if (!(status & bit)) - return 0; - delay(10); - } - - return ETIMEDOUT; -} - -static int -dwc_mmc_pio_transfer(struct dwc_mmc_softc *sc, struct sdmmc_command *cmd) -{ - uint32_t *datap = (uint32_t *)cmd->c_data; - int i; - - for (i = 0; i < (cmd->c_resid >> 2); i++) { - if (dwc_mmc_pio_wait(sc, cmd)) - return ETIMEDOUT; - if (cmd->c_flags & SCF_CMD_READ) { - datap[i] = MMC_READ(sc, sc->sc_fifo_reg); - } else { - MMC_WRITE(sc, sc->sc_fifo_reg, datap[i]); - } - } - - return 0; -} - static int dwc_mmc_dma_prepare(struct dwc_mmc_softc *sc, struct sdmmc_command *cmd) { struct dwc_mmc_idma_desc *dma = sc->sc_idma_desc; bus_addr_t desc_paddr = sc->sc_idma_map->dm_segs[0].ds_addr; + bus_dmamap_t map; bus_size_t off; int desc, resid, seg; uint32_t val; + /* + * If the command includs a dma map use it, otherwise we need to + * bounce. This can happen for SDIO IO_RW_EXTENDED (CMD53) commands. + */ + if (cmd->c_dmamap) { + map = cmd->c_dmamap; + } else { + if (cmd->c_datalen > sc->sc_dmabounce_buflen) + return E2BIG; + map = sc->sc_dmabounce_map; + + if (ISSET(cmd->c_flags, SCF_CMD_READ)) { + memset(sc->sc_dmabounce_buf, 0, cmd->c_datalen); + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmabounce_map, + 0, cmd->c_datalen, BUS_DMASYNC_PREREAD); + } else { + memcpy(sc->sc_dmabounce_buf, cmd->c_data, + cmd->c_datalen); + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmabounce_map, + 0, cmd->c_datalen, BUS_DMASYNC_PREWRITE); + } + } + desc = 0; - for (seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) { - bus_addr_t paddr = cmd->c_dmamap->dm_segs[seg].ds_addr; - bus_size_t len = cmd->c_dmamap->dm_segs[seg].ds_len; + for (seg = 0; seg < map->dm_nsegs; seg++) { + bus_addr_t paddr = map->dm_segs[seg].ds_addr; + bus_size_t len = map->dm_segs[seg].ds_len; resid = uimin(len, cmd->c_resid); off = 0; while (resid > 0) { @@ -480,13 +456,11 @@ dwc_mmc_dma_prepare(struct dwc_mmc_softc dma[desc].dma_buf_size = htole32(len); dma[desc].dma_buf_addr = htole32(paddr + off); dma[desc].dma_config = htole32( + DWC_MMC_IDMA_CONFIG_CH | DWC_MMC_IDMA_CONFIG_OWN); cmd->c_resid -= len; resid -= len; off += len; - dma[desc].dma_next = htole32( - desc_paddr + ((desc+1) * - sizeof(struct dwc_mmc_idma_desc))); if (desc == 0) { dma[desc].dma_config |= htole32( DWC_MMC_IDMA_CONFIG_FD); @@ -494,10 +468,15 @@ dwc_mmc_dma_prepare(struct dwc_mmc_softc if (cmd->c_resid == 0) { dma[desc].dma_config |= htole32( DWC_MMC_IDMA_CONFIG_LD); + dma[desc].dma_config |= htole32( + DWC_MMC_IDMA_CONFIG_ER); + dma[desc].dma_next = 0; } else { dma[desc].dma_config |= - htole32(DWC_MMC_IDMA_CONFIG_CH| - DWC_MMC_IDMA_CONFIG_DIC); + htole32(DWC_MMC_IDMA_CONFIG_DIC); + dma[desc].dma_next = htole32( + desc_paddr + ((desc+1) * + sizeof(struct dwc_mmc_idma_desc))); } ++desc; } @@ -512,34 +491,46 @@ dwc_mmc_dma_prepare(struct dwc_mmc_softc bus_dmamap_sync(sc->sc_dmat, sc->sc_idma_map, 0, sc->sc_idma_size, BUS_DMASYNC_PREWRITE); - sc->sc_idma_idst = 0; + MMC_WRITE(sc, DWC_MMC_DLBA, desc_paddr); val = MMC_READ(sc, DWC_MMC_GCTRL); val |= DWC_MMC_GCTRL_DMAEN; - val |= DWC_MMC_GCTRL_INTEN; MMC_WRITE(sc, DWC_MMC_GCTRL, val); val |= DWC_MMC_GCTRL_DMARESET; MMC_WRITE(sc, DWC_MMC_GCTRL, val); + MMC_WRITE(sc, DWC_MMC_DMAC, DWC_MMC_DMAC_SOFTRESET); - MMC_WRITE(sc, DWC_MMC_DMAC, - DWC_MMC_DMAC_IDMA_ON|DWC_MMC_DMAC_FIX_BURST); - val = MMC_READ(sc, DWC_MMC_IDIE); - val &= ~(DWC_MMC_IDST_RECEIVE_INT|DWC_MMC_IDST_TRANSMIT_INT); if (cmd->c_flags & SCF_CMD_READ) - val |= DWC_MMC_IDST_RECEIVE_INT; + val = DWC_MMC_IDST_RECEIVE_INT; else - val |= DWC_MMC_IDST_TRANSMIT_INT; + val = 0; MMC_WRITE(sc, DWC_MMC_IDIE, val); - MMC_WRITE(sc, DWC_MMC_DLBA, desc_paddr); + MMC_WRITE(sc, DWC_MMC_DMAC, + DWC_MMC_DMAC_IDMA_ON|DWC_MMC_DMAC_FIX_BURST); return 0; } static void -dwc_mmc_dma_complete(struct dwc_mmc_softc *sc) +dwc_mmc_dma_complete(struct dwc_mmc_softc *sc, struct sdmmc_command *cmd) { + MMC_WRITE(sc, DWC_MMC_DMAC, 0); + MMC_WRITE(sc, DWC_MMC_IDIE, 0); + bus_dmamap_sync(sc->sc_dmat, sc->sc_idma_map, 0, sc->sc_idma_size, BUS_DMASYNC_POSTWRITE); + + if (cmd->c_dmamap == NULL) { + if (ISSET(cmd->c_flags, SCF_CMD_READ)) { + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmabounce_map, + 0, cmd->c_datalen, BUS_DMASYNC_POSTREAD); + memcpy(cmd->c_data, sc->sc_dmabounce_buf, + cmd->c_datalen); + } else { + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmabounce_map, + 0, cmd->c_datalen, BUS_DMASYNC_POSTWRITE); + } + } } static void @@ -547,7 +538,8 @@ dwc_mmc_exec_command(sdmmc_chipset_handl { struct dwc_mmc_softc *sc = sch; uint32_t cmdval = DWC_MMC_CMD_START; - int retry = 200000; + int retry, error; + uint32_t imask; #ifdef DWC_MMC_DEBUG aprint_normal_dev(sc->sc_dev, @@ -557,21 +549,17 @@ dwc_mmc_exec_command(sdmmc_chipset_handl #endif mutex_enter(&sc->sc_intr_lock); - - do { - const uint32_t status = MMC_READ(sc, DWC_MMC_STATUS); - if ((status & DWC_MMC_STATUS_CARD_DATA_BUSY) == 0) - break; - delay(10); - } while (--retry > 0); - if (retry == 0) { - aprint_error_dev(sc->sc_dev, "timeout waiting for data busy\n"); - cmd->c_error = ETIMEDOUT; - goto done; + if (sc->sc_curcmd != NULL) { + device_printf(sc->sc_dev, + "WARNING: driver submitted a command while the controller was busy\n"); + cmd->c_error = EBUSY; + SET(cmd->c_flags, SCF_ITSDONE); + mutex_exit(&sc->sc_intr_lock); + return; } + sc->sc_curcmd = cmd; MMC_WRITE(sc, DWC_MMC_IDST, 0xffffffff); - MMC_WRITE(sc, DWC_MMC_RINT, 0xffffffff); if (ISSET(sc->sc_flags, DWC_MMC_F_USE_HOLD_REG)) cmdval |= DWC_MMC_CMD_USE_HOLD_REG; @@ -585,6 +573,8 @@ dwc_mmc_exec_command(sdmmc_chipset_handl if (cmd->c_flags & SCF_RSP_CRC) cmdval |= DWC_MMC_CMD_CHECK_RSP_CRC; + imask = DWC_MMC_INT_ERROR | DWC_MMC_INT_CMD_DONE; + if (cmd->c_datalen > 0) { unsigned int nblks; @@ -599,13 +589,17 @@ dwc_mmc_exec_command(sdmmc_chipset_handl if (nblks > 1) { cmdval |= DWC_MMC_CMD_SEND_AUTO_STOP; + imask |= DWC_MMC_INT_AUTO_CMD_DONE; + } else { + imask |= DWC_MMC_INT_DATA_OVER; } MMC_WRITE(sc, DWC_MMC_BLKSZ, cmd->c_blklen); MMC_WRITE(sc, DWC_MMC_BYTECNT, nblks * cmd->c_blklen); } - sc->sc_intr_rint = 0; + MMC_WRITE(sc, DWC_MMC_IMASK, imask | sc->sc_intr_card); + MMC_WRITE(sc, DWC_MMC_RINT, 0x7fff); MMC_WRITE(sc, DWC_MMC_ARG, cmd->c_arg); @@ -613,89 +607,45 @@ dwc_mmc_exec_command(sdmmc_chipset_handl aprint_normal_dev(sc->sc_dev, "cmdval = %08x\n", cmdval); #endif + cmd->c_resid = cmd->c_datalen; if (cmd->c_datalen > 0) { - cmd->c_resid = cmd->c_datalen; dwc_mmc_led(sc, 0); - if (ISSET(sc->sc_flags, DWC_MMC_F_DMA)) { - cmd->c_error = dwc_mmc_dma_prepare(sc, cmd); - MMC_WRITE(sc, DWC_MMC_CMD, cmdval | cmd->c_opcode); - MMC_WRITE(sc, DWC_MMC_PLDMND, 1); - if (cmd->c_error == 0) { - const uint32_t idst_mask = - DWC_MMC_IDST_ERROR | DWC_MMC_IDST_COMPLETE; - retry = 10; - while ((sc->sc_idma_idst & idst_mask) == 0) { - if (--retry == 0) { - cmd->c_error = ETIMEDOUT; - break; - } - cv_timedwait(&sc->sc_idst_cv, - &sc->sc_intr_lock, hz); - } - } - dwc_mmc_dma_complete(sc); - if (sc->sc_idma_idst & DWC_MMC_IDST_ERROR) { - cmd->c_error = EIO; - } else if (!(sc->sc_idma_idst & DWC_MMC_IDST_COMPLETE)) { - cmd->c_error = ETIMEDOUT; - } - } else { - mutex_exit(&sc->sc_intr_lock); - MMC_WRITE(sc, DWC_MMC_CMD, cmdval | cmd->c_opcode); - cmd->c_error = dwc_mmc_pio_transfer(sc, cmd); - mutex_enter(&sc->sc_intr_lock); - } - dwc_mmc_led(sc, 1); - if (cmd->c_error) { -#ifdef DWC_MMC_DEBUG - aprint_error_dev(sc->sc_dev, - "xfer failed, error %d\n", cmd->c_error); -#endif + cmd->c_error = dwc_mmc_dma_prepare(sc, cmd); + if (cmd->c_error != 0) { + SET(cmd->c_flags, SCF_ITSDONE); goto done; } + sc->sc_wait_dma = ISSET(cmd->c_flags, SCF_CMD_READ); + sc->sc_wait_data = true; } else { - MMC_WRITE(sc, DWC_MMC_CMD, cmdval | cmd->c_opcode); + sc->sc_wait_dma = false; + sc->sc_wait_data = false; } + sc->sc_wait_cmd = true; - cmd->c_error = dwc_mmc_wait_rint(sc, - DWC_MMC_INT_ERROR|DWC_MMC_INT_CMD_DONE, hz * 10); - if (cmd->c_error == 0 && (sc->sc_intr_rint & DWC_MMC_INT_ERROR)) { - if (sc->sc_intr_rint & DWC_MMC_INT_RESP_TIMEOUT) { - cmd->c_error = ETIMEDOUT; - } else { - cmd->c_error = EIO; - } - } - if (cmd->c_error) { -#ifdef DWC_MMC_DEBUG - aprint_error_dev(sc->sc_dev, - "cmd failed, error %d\n", cmd->c_error); -#endif - goto done; - } + MMC_WRITE(sc, DWC_MMC_CMD, cmdval | cmd->c_opcode); - if (cmd->c_datalen > 0) { - cmd->c_error = dwc_mmc_wait_rint(sc, - DWC_MMC_INT_ERROR| - DWC_MMC_INT_AUTO_CMD_DONE| - DWC_MMC_INT_DATA_OVER, - hz*10); - if (cmd->c_error == 0 && - (sc->sc_intr_rint & DWC_MMC_INT_ERROR)) { - cmd->c_error = ETIMEDOUT; - } - if (cmd->c_error) { -#ifdef DWC_MMC_DEBUG - aprint_error_dev(sc->sc_dev, - "data timeout, rint = %08x\n", - sc->sc_intr_rint); -#endif - dwc_mmc_dump_regs(sc); - cmd->c_error = ETIMEDOUT; + if (sc->sc_wait_dma) + MMC_WRITE(sc, DWC_MMC_PLDMND, 1); + + struct bintime timeout = { .sec = 15, .frac = 0 }; + const struct bintime epsilon = { .sec = 1, .frac = 0 }; + while (!ISSET(cmd->c_flags, SCF_ITSDONE)) { + error = cv_timedwaitbt(&sc->sc_intr_cv, + &sc->sc_intr_lock, &timeout, &epsilon); + if (error != 0) { + cmd->c_error = error; + SET(cmd->c_flags, SCF_ITSDONE); goto done; } } + if (cmd->c_error == 0 && cmd->c_datalen > 0) + dwc_mmc_dma_complete(sc, cmd); + + if (cmd->c_datalen > 0) + dwc_mmc_led(sc, 1); + if (cmd->c_flags & SCF_RSP_PRESENT) { if (cmd->c_flags & SCF_RSP_136) { cmd->c_resp[0] = MMC_READ(sc, DWC_MMC_RESP0); @@ -717,7 +667,12 @@ dwc_mmc_exec_command(sdmmc_chipset_handl } done: - cmd->c_flags |= SCF_ITSDONE; + KASSERT(ISSET(cmd->c_flags, SCF_ITSDONE)); + MMC_WRITE(sc, DWC_MMC_IMASK, sc->sc_intr_card); + MMC_WRITE(sc, DWC_MMC_IDIE, 0); + MMC_WRITE(sc, DWC_MMC_RINT, 0x7fff); + MMC_WRITE(sc, DWC_MMC_IDST, 0xffffffff); + sc->sc_curcmd = NULL; mutex_exit(&sc->sc_intr_lock); if (cmd->c_error) { @@ -735,20 +690,37 @@ done: dwc_mmc_update_clock(sc); } - if (!ISSET(sc->sc_flags, DWC_MMC_F_DMA)) { - MMC_WRITE(sc, DWC_MMC_GCTRL, - MMC_READ(sc, DWC_MMC_GCTRL) | DWC_MMC_GCTRL_FIFORESET); - } + MMC_WRITE(sc, DWC_MMC_GCTRL, + MMC_READ(sc, DWC_MMC_GCTRL) | DWC_MMC_GCTRL_FIFORESET); } static void dwc_mmc_card_enable_intr(sdmmc_chipset_handle_t sch, int enable) { + struct dwc_mmc_softc *sc = sch; + uint32_t imask; + + mutex_enter(&sc->sc_intr_lock); + imask = MMC_READ(sc, DWC_MMC_IMASK); + if (enable) + imask |= DWC_MMC_INT_SDIO_INT; + else + imask &= ~DWC_MMC_INT_SDIO_INT; + sc->sc_intr_card = imask & DWC_MMC_INT_SDIO_INT; + MMC_WRITE(sc, DWC_MMC_IMASK, imask); + mutex_exit(&sc->sc_intr_lock); } static void dwc_mmc_card_intr_ack(sdmmc_chipset_handle_t sch) { + struct dwc_mmc_softc *sc = sch; + uint32_t imask; + + mutex_enter(&sc->sc_intr_lock); + imask = MMC_READ(sc, DWC_MMC_IMASK); + MMC_WRITE(sc, DWC_MMC_IMASK, imask | sc->sc_intr_card); + mutex_exit(&sc->sc_intr_lock); } int @@ -773,14 +745,9 @@ dwc_mmc_init(struct dwc_mmc_softc *sc) mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_BIO); cv_init(&sc->sc_intr_cv, "dwcmmcirq"); - cv_init(&sc->sc_idst_cv, "dwcmmcdma"); - - const bool use_dma = ISSET(sc->sc_flags, DWC_MMC_F_DMA); - aprint_debug_dev(sc->sc_dev, "using %s for transfers\n", - use_dma ? "DMA" : "PIO"); - - if (use_dma && dwc_mmc_idma_setup(sc) != 0) { + if (dwc_mmc_dmabounce_setup(sc) != 0 || + dwc_mmc_idma_setup(sc) != 0) { aprint_error_dev(sc->sc_dev, "failed to setup DMA\n"); return ENOMEM; } @@ -794,32 +761,86 @@ int dwc_mmc_intr(void *priv) { struct dwc_mmc_softc *sc = priv; - uint32_t idst, rint, mint; + struct sdmmc_command *cmd; + uint32_t idst, mint, imask; mutex_enter(&sc->sc_intr_lock); idst = MMC_READ(sc, DWC_MMC_IDST); - rint = MMC_READ(sc, DWC_MMC_RINT); mint = MMC_READ(sc, DWC_MMC_MINT); - if (!idst && !rint && !mint) { + if (!idst && !mint) { mutex_exit(&sc->sc_intr_lock); return 0; } MMC_WRITE(sc, DWC_MMC_IDST, idst); - MMC_WRITE(sc, DWC_MMC_RINT, rint); - MMC_WRITE(sc, DWC_MMC_MINT, mint); + MMC_WRITE(sc, DWC_MMC_RINT, mint); + + cmd = sc->sc_curcmd; #ifdef DWC_MMC_DEBUG - device_printf(sc->sc_dev, "mmc intr idst=%08X rint=%08X mint=%08X\n", - idst, rint, mint); + device_printf(sc->sc_dev, "mmc intr idst=%08X mint=%08X\n", + idst, mint); #endif - if (idst) { - sc->sc_idma_idst |= idst; - cv_broadcast(&sc->sc_idst_cv); + /* Handle SDIO card interrupt */ + if ((mint & DWC_MMC_INT_SDIO_INT) != 0) { + imask = MMC_READ(sc, DWC_MMC_IMASK); + MMC_WRITE(sc, DWC_MMC_IMASK, imask & ~DWC_MMC_INT_SDIO_INT); + sdmmc_card_intr(sc->sc_sdmmc_dev); + } + + /* Error interrupts take priority over command and transfer interrupts */ + if (cmd != NULL && (mint & DWC_MMC_INT_ERROR) != 0) { + imask = MMC_READ(sc, DWC_MMC_IMASK); + MMC_WRITE(sc, DWC_MMC_IMASK, imask & ~DWC_MMC_INT_ERROR); + if ((mint & DWC_MMC_INT_RESP_TIMEOUT) != 0) { + cmd->c_error = ETIMEDOUT; + /* Wait for command to complete */ + sc->sc_wait_data = sc->sc_wait_dma = false; + if (cmd->c_opcode != SD_IO_SEND_OP_COND && + cmd->c_opcode != SD_IO_RW_DIRECT && + !ISSET(cmd->c_flags, SCF_TOUT_OK)) + device_printf(sc->sc_dev, "host controller timeout, mint=0x%08x\n", mint); + } else { + device_printf(sc->sc_dev, "host controller error, mint=0x%08x\n", mint); + cmd->c_error = EIO; + SET(cmd->c_flags, SCF_ITSDONE); + goto done; + } + } + + if (cmd != NULL && (idst & DWC_MMC_IDST_RECEIVE_INT) != 0) { + MMC_WRITE(sc, DWC_MMC_IDIE, 0); + if (sc->sc_wait_dma == false) + device_printf(sc->sc_dev, "unexpected DMA receive interrupt\n"); + sc->sc_wait_dma = false; + } + + if (cmd != NULL && (mint & DWC_MMC_INT_CMD_DONE) != 0) { + imask = MMC_READ(sc, DWC_MMC_IMASK); + MMC_WRITE(sc, DWC_MMC_IMASK, imask & ~DWC_MMC_INT_CMD_DONE); + if (sc->sc_wait_cmd == false) + device_printf(sc->sc_dev, "unexpected command complete interrupt\n"); + sc->sc_wait_cmd = false; + } + + const uint32_t dmadone_mask = DWC_MMC_INT_AUTO_CMD_DONE|DWC_MMC_INT_DATA_OVER; + if (cmd != NULL && (mint & dmadone_mask) != 0) { + imask = MMC_READ(sc, DWC_MMC_IMASK); + MMC_WRITE(sc, DWC_MMC_IMASK, imask & ~dmadone_mask); + if (sc->sc_wait_data == false) + device_printf(sc->sc_dev, "unexpected data complete interrupt\n"); + sc->sc_wait_data = false; + } + + if (cmd != NULL && + sc->sc_wait_dma == false && + sc->sc_wait_cmd == false && + sc->sc_wait_data == false) { + SET(cmd->c_flags, SCF_ITSDONE); } - if (rint) { - sc->sc_intr_rint |= rint; +done: + if (cmd != NULL && ISSET(cmd->c_flags, SCF_ITSDONE)) { cv_broadcast(&sc->sc_intr_cv); } Index: src/sys/dev/ic/dwc_mmc_var.h diff -u src/sys/dev/ic/dwc_mmc_var.h:1.8 src/sys/dev/ic/dwc_mmc_var.h:1.9 --- src/sys/dev/ic/dwc_mmc_var.h:1.8 Tue Apr 30 23:19:55 2019 +++ src/sys/dev/ic/dwc_mmc_var.h Sat Oct 5 12:27:14 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: dwc_mmc_var.h,v 1.8 2019/04/30 23:19:55 jmcneill Exp $ */ +/* $NetBSD: dwc_mmc_var.h,v 1.9 2019/10/05 12:27:14 jmcneill Exp $ */ /*- * Copyright (c) 2014-2017 Jared McNeill <jmcne...@invisible.ca> @@ -46,7 +46,6 @@ struct dwc_mmc_softc { void *sc_ih; kmutex_t sc_intr_lock; kcondvar_t sc_intr_cv; - kcondvar_t sc_idst_cv; int sc_mmc_width; int sc_mmc_port; @@ -61,9 +60,15 @@ struct dwc_mmc_softc { int sc_idma_ndesc; void *sc_idma_desc; - uint32_t sc_intr_rint; - uint32_t sc_intr_mint; - uint32_t sc_idma_idst; + bus_dmamap_t sc_dmabounce_map; + void *sc_dmabounce_buf; + size_t sc_dmabounce_buflen; + + uint32_t sc_intr_card; + struct sdmmc_command *sc_curcmd; + bool sc_wait_dma; + bool sc_wait_cmd; + bool sc_wait_data; int (*sc_card_detect)(struct dwc_mmc_softc *); int (*sc_write_protect)(struct dwc_mmc_softc *);