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 *);

Reply via email to