Module Name:    src
Committed By:   jmcneill
Date:           Mon Oct 23 13:11:17 UTC 2017

Modified Files:
        src/sys/arch/arm/sunxi: sunxi_mmc.c

Log Message:
SDIO IO_RW_EXTENDED (CMD53) commands have datalen set but no dmamap. Setup
and use a bounce buffer to service these requests.


To generate a diff of this commit:
cvs rdiff -u -r1.13 -r1.14 src/sys/arch/arm/sunxi/sunxi_mmc.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/sunxi/sunxi_mmc.c
diff -u src/sys/arch/arm/sunxi/sunxi_mmc.c:1.13 src/sys/arch/arm/sunxi/sunxi_mmc.c:1.14
--- src/sys/arch/arm/sunxi/sunxi_mmc.c:1.13	Mon Oct 23 11:06:31 2017
+++ src/sys/arch/arm/sunxi/sunxi_mmc.c	Mon Oct 23 13:11:17 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_mmc.c,v 1.13 2017/10/23 11:06:31 jmcneill Exp $ */
+/* $NetBSD: sunxi_mmc.c,v 1.14 2017/10/23 13:11:17 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2014-2017 Jared McNeill <jmcne...@invisible.ca>
@@ -29,7 +29,7 @@
 #include "opt_sunximmc.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_mmc.c,v 1.13 2017/10/23 11:06:31 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_mmc.c,v 1.14 2017/10/23 13:11:17 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -96,6 +96,7 @@ static void	sunxi_mmc_attach(device_t, d
 static void	sunxi_mmc_attach_i(device_t);
 
 static int	sunxi_mmc_intr(void *);
+static int	sunxi_mmc_dmabounce_setup(struct sunxi_mmc_softc *);
 static int	sunxi_mmc_idma_setup(struct sunxi_mmc_softc *);
 
 static int	sunxi_mmc_host_reset(sdmmc_chipset_handle_t);
@@ -165,6 +166,10 @@ struct sunxi_mmc_softc {
 	int sc_idma_ndesc;
 	void *sc_idma_desc;
 
+	bus_dmamap_t sc_dmabounce_map;
+	void *sc_dmabounce_buf;
+	size_t sc_dmabounce_buflen;
+
 	uint32_t sc_intr_rint;
 	uint32_t sc_idma_idst;
 
@@ -321,7 +326,8 @@ sunxi_mmc_attach(device_t parent, device
 	sc->sc_gpio_cd_inverted = of_hasprop(phandle, "cd-inverted") ? 0 : 1;
 	sc->sc_gpio_wp_inverted = of_hasprop(phandle, "wp-inverted") ? 0 : 1;
 
-	if (sunxi_mmc_idma_setup(sc) != 0) {
+	if (sunxi_mmc_dmabounce_setup(sc) != 0 ||
+	    sunxi_mmc_idma_setup(sc) != 0) {
 		aprint_error_dev(self, "failed to setup DMA\n");
 		return;
 	}
@@ -344,6 +350,42 @@ sunxi_mmc_attach(device_t parent, device
 }
 
 static int
+sunxi_mmc_dmabounce_setup(struct sunxi_mmc_softc *sc)
+{
+	bus_dma_segment_t ds[1];
+	int error, rseg;
+
+	sc->sc_dmabounce_buflen = sunxi_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
 sunxi_mmc_idma_setup(struct sunxi_mmc_softc *sc)
 {
 	int error;
@@ -798,14 +840,38 @@ sunxi_mmc_dma_prepare(struct sunxi_mmc_s
 {
 	struct sunxi_mmc_idma_descriptor *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 includes 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)) {
+			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);
+		} else {
+			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);
+		}
+	}
+
 	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 = min(len, cmd->c_resid);
 		off = 0;
 		while (resid > 0) {
@@ -859,7 +925,7 @@ sunxi_mmc_dma_prepare(struct sunxi_mmc_s
 	    SUNXI_MMC_DMAC_IDMA_ON|SUNXI_MMC_DMAC_FIX_BURST);
 	val = MMC_READ(sc, SUNXI_MMC_IDIE);
 	val &= ~(SUNXI_MMC_IDST_RECEIVE_INT|SUNXI_MMC_IDST_TRANSMIT_INT);
-	if (cmd->c_flags & SCF_CMD_READ)
+	if (ISSET(cmd->c_flags, SCF_CMD_READ))
 		val |= SUNXI_MMC_IDST_RECEIVE_INT;
 	else
 		val |= SUNXI_MMC_IDST_TRANSMIT_INT;
@@ -871,10 +937,22 @@ sunxi_mmc_dma_prepare(struct sunxi_mmc_s
 }
 
 static void
-sunxi_mmc_dma_complete(struct sunxi_mmc_softc *sc)
+sunxi_mmc_dma_complete(struct sunxi_mmc_softc *sc, struct sdmmc_command *cmd)
 {
 	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_POSTWRITE);
+			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_POSTREAD);
+		}
+	}
 }
 
 static void
@@ -949,7 +1027,7 @@ sunxi_mmc_exec_command(sdmmc_chipset_han
 				    &sc->sc_intr_lock, hz);
 			}
 		}
-		sunxi_mmc_dma_complete(sc);
+		sunxi_mmc_dma_complete(sc, cmd);
 		if (sc->sc_idma_idst & SUNXI_MMC_IDST_ERROR) {
 			cmd->c_error = EIO;
 		} else if (!(sc->sc_idma_idst & SUNXI_MMC_IDST_COMPLETE)) {

Reply via email to