Module Name:    src
Committed By:   kiyohara
Date:           Fri Oct  1 09:50:43 UTC 2010

Modified Files:
        src/sys/dev/sdmmc: sdmmc.c sdmmc_mem.c sdmmcvar.h

Log Message:
Use DMA bounce buffer, if DMA buffer is making by multiple segments.  A lot
of host controllers do not support to two or more segments.


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/sdmmc/sdmmc.c
cvs rdiff -u -r1.11 -r1.12 src/sys/dev/sdmmc/sdmmc_mem.c
cvs rdiff -u -r1.6 -r1.7 src/sys/dev/sdmmc/sdmmcvar.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/sdmmc/sdmmc.c
diff -u src/sys/dev/sdmmc/sdmmc.c:1.3 src/sys/dev/sdmmc/sdmmc.c:1.4
--- src/sys/dev/sdmmc/sdmmc.c:1.3	Mon Sep 20 09:06:03 2010
+++ src/sys/dev/sdmmc/sdmmc.c	Fri Oct  1 09:50:42 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdmmc.c,v 1.3 2010/09/20 09:06:03 kiyohara Exp $	*/
+/*	$NetBSD: sdmmc.c,v 1.4 2010/10/01 09:50:42 kiyohara Exp $	*/
 /*	$OpenBSD: sdmmc.c,v 1.18 2009/01/09 10:58:38 jsg Exp $	*/
 
 /*
@@ -50,7 +50,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sdmmc.c,v 1.3 2010/09/20 09:06:03 kiyohara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sdmmc.c,v 1.4 2010/10/01 09:50:42 kiyohara Exp $");
 
 #include <sys/param.h>
 #include <sys/device.h>
@@ -61,6 +61,8 @@
 #include <sys/systm.h>
 #include <sys/callout.h>
 
+#include <machine/vmparam.h>
+
 #include <dev/sdmmc/sdmmc_ioreg.h>
 #include <dev/sdmmc/sdmmcchip.h>
 #include <dev/sdmmc/sdmmcreg.h>
@@ -587,12 +589,58 @@
 	sf->cis.product = SDMMC_PRODUCT_INVALID;
 	sf->cis.function = SDMMC_FUNCTION_INVALID;
 
+	if (ISSET(sc->sc_flags, SMF_MEM_MODE) &&
+	    ISSET(sc->sc_caps, SMC_CAPS_DMA) &&
+	    !ISSET(sc->sc_caps, SMC_CAPS_MULTI_SEG_DMA)) {
+		bus_dma_segment_t ds;
+		int rseg, error;
+
+		error = bus_dmamap_create(sc->sc_dmat, SDMMC_SECTOR_SIZE, 1,
+		    SDMMC_SECTOR_SIZE, 0, BUS_DMA_WAITOK, &sf->bbuf_dmap);
+		if (error)
+			goto fail1;
+		error = bus_dmamem_alloc(sc->sc_dmat, SDMMC_SECTOR_SIZE,
+		    PAGE_SIZE, 0, &ds, 1, &rseg, BUS_DMA_WAITOK);
+		if (error)
+			goto fail2;
+		error = bus_dmamem_map(sc->sc_dmat, &ds, 1, SDMMC_SECTOR_SIZE,
+		    &sf->bbuf, BUS_DMA_WAITOK);
+		if (error)
+			goto fail3;
+		error = bus_dmamap_load(sc->sc_dmat, sf->bbuf_dmap,
+		    sf->bbuf, SDMMC_SECTOR_SIZE, NULL,
+		    BUS_DMA_WAITOK|BUS_DMA_READ|BUS_DMA_WRITE);
+		if (!error)
+			goto out;
+
+		bus_dmamem_unmap(sc->sc_dmat, sf->bbuf, SDMMC_SECTOR_SIZE);
+fail3:
+		bus_dmamem_free(sc->sc_dmat, &ds, 1);
+fail2:
+		bus_dmamap_destroy(sc->sc_dmat, sf->bbuf_dmap);
+fail1:
+		free(sf, M_DEVBUF);
+		sf = NULL;
+	}
+out:
+
 	return sf;
 }
 
 void
 sdmmc_function_free(struct sdmmc_function *sf)
 {
+	struct sdmmc_softc *sc = sf->sc;
+
+	if (ISSET(sc->sc_flags, SMF_MEM_MODE) &&
+	    ISSET(sc->sc_caps, SMC_CAPS_DMA) &&
+	    !ISSET(sc->sc_caps, SMC_CAPS_MULTI_SEG_DMA)) {
+		bus_dmamap_unload(sc->sc_dmat, sf->bbuf_dmap);
+		bus_dmamem_unmap(sc->sc_dmat, sf->bbuf, SDMMC_SECTOR_SIZE);
+		bus_dmamem_free(sc->sc_dmat,
+		    sf->bbuf_dmap->dm_segs, sf->bbuf_dmap->dm_nsegs);
+		bus_dmamap_destroy(sc->sc_dmat, sf->bbuf_dmap);
+	}
 
 	free(sf, M_DEVBUF);
 }

Index: src/sys/dev/sdmmc/sdmmc_mem.c
diff -u src/sys/dev/sdmmc/sdmmc_mem.c:1.11 src/sys/dev/sdmmc/sdmmc_mem.c:1.12
--- src/sys/dev/sdmmc/sdmmc_mem.c:1.11	Thu Sep 23 12:03:27 2010
+++ src/sys/dev/sdmmc/sdmmc_mem.c	Fri Oct  1 09:50:42 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdmmc_mem.c,v 1.11 2010/09/23 12:03:27 kiyohara Exp $	*/
+/*	$NetBSD: sdmmc_mem.c,v 1.12 2010/10/01 09:50:42 kiyohara Exp $	*/
 /*	$OpenBSD: sdmmc_mem.c,v 1.10 2009/01/09 10:55:22 jsg Exp $	*/
 
 /*
@@ -46,7 +46,7 @@
 /* Routines for SD/MMC memory cards. */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.11 2010/09/23 12:03:27 kiyohara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.12 2010/10/01 09:50:42 kiyohara Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -934,10 +934,12 @@
 sdmmc_mem_single_read_block(struct sdmmc_function *sf, uint32_t blkno,
     u_char *data, size_t datalen)
 {
+	struct sdmmc_softc *sc __unused = sf->sc;
 	int error = 0;
 	int i;
 
 	KASSERT((datalen % SDMMC_SECTOR_SIZE) == 0);
+	KASSERT(!ISSET(sc->sc_caps, SMC_CAPS_DMA));
 
 	for (i = 0; i < datalen / SDMMC_SECTOR_SIZE; i++) {
 		error = sdmmc_mem_read_block_subr(sf, blkno + i,
@@ -954,7 +956,7 @@
 {
 	struct sdmmc_softc *sc = sf->sc;
 	struct sdmmc_command cmd;
-	int error;
+	int error, bbuf, seg, off, len, num;
 
 	if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) {
 		error = sdmmc_select_card(sc, sf);
@@ -962,6 +964,10 @@
 			goto out;
 	}
 
+	bbuf = 0;
+	num = 0;
+	seg = off = len = 0;
+retry:
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.c_data = data;
 	cmd.c_datalen = datalen;
@@ -972,8 +978,30 @@
 	if (!ISSET(sf->flags, SFF_SDHC))
 		cmd.c_arg <<= SDMMC_SECTOR_SIZE_SB;
 	cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1 | SCF_RSP_SPI_R1;
-	if (ISSET(sc->sc_caps, SMC_CAPS_DMA))
+	if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
 		cmd.c_dmamap = sc->sc_dmap;
+		if (!ISSET(sc->sc_caps, SMC_CAPS_MULTI_SEG_DMA)) {
+			len = sc->sc_dmap->dm_segs[seg].ds_len - off;
+			len &= ~(SDMMC_SECTOR_SIZE - 1);
+			cmd.c_datalen = len;
+			cmd.c_dmaseg = seg;
+			cmd.c_dmaoff = off;
+			bbuf = 0;
+			if (len == 0) {
+				/* Use bounce buffer */
+				bus_dmamap_sync(sc->sc_dmat, sf->bbuf_dmap,
+				    0, SDMMC_SECTOR_SIZE, BUS_DMASYNC_PREREAD);
+				cmd.c_datalen = SDMMC_SECTOR_SIZE;
+				cmd.c_dmamap = sf->bbuf_dmap;
+				cmd.c_dmaseg = 0;
+				cmd.c_dmaoff = 0;
+				bbuf = 1;
+				len = SDMMC_SECTOR_SIZE;
+			}
+			cmd.c_opcode = (cmd.c_datalen / cmd.c_blklen) > 1 ?
+			    MMC_READ_BLOCK_MULTIPLE : MMC_READ_BLOCK_SINGLE;
+		}
+	}
 
 	error = sdmmc_mmc_command(sc, &cmd);
 	if (error)
@@ -1005,6 +1033,34 @@
 		} while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA));
 	}
 
+	if (ISSET(sc->sc_caps, SMC_CAPS_DMA) &&
+	    !ISSET(sc->sc_caps, SMC_CAPS_MULTI_SEG_DMA)) {
+		bus_dma_segment_t *dm_segs = sc->sc_dmap->dm_segs;
+
+		if (bbuf) {
+			bus_dmamap_sync(sc->sc_dmat, sf->bbuf_dmap,
+			    0, SDMMC_SECTOR_SIZE, BUS_DMASYNC_POSTREAD);
+			bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, num,
+			    SDMMC_SECTOR_SIZE, BUS_DMASYNC_POSTREAD);
+			memcpy(data, sf->bbuf, SDMMC_SECTOR_SIZE);
+			bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, num,
+			    SDMMC_SECTOR_SIZE, BUS_DMASYNC_PREREAD);
+		}
+		num += len;
+		data += len;
+		datalen -= len;
+		blkno += (len / SDMMC_SECTOR_SIZE);
+
+		while (off + len >= dm_segs[seg].ds_len) {
+			len -= dm_segs[seg++].ds_len;
+			off = 0;
+		}
+		off += len;
+
+		if (seg < sc->sc_dmap->dm_nsegs)
+			goto retry;
+	}
+
 out:
 	return error;
 }
@@ -1065,10 +1121,12 @@
 sdmmc_mem_single_write_block(struct sdmmc_function *sf, uint32_t blkno,
     u_char *data, size_t datalen)
 {
+	struct sdmmc_softc *sc __unused = sf->sc;
 	int error = 0;
 	int i;
 
 	KASSERT((datalen % SDMMC_SECTOR_SIZE) == 0);
+	KASSERT(!ISSET(sc->sc_caps, SMC_CAPS_DMA));
 
 	for (i = 0; i < datalen / SDMMC_SECTOR_SIZE; i++) {
 		error = sdmmc_mem_write_block_subr(sf, blkno + i,
@@ -1085,7 +1143,7 @@
 {
 	struct sdmmc_softc *sc = sf->sc;
 	struct sdmmc_command cmd;
-	int error;
+	int error, bbuf, seg, off, len, num;
 
 	if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) {
 		error = sdmmc_select_card(sc, sf);
@@ -1093,6 +1151,10 @@
 			goto out;
 	}
 
+	bbuf = 0;
+	num = 0;
+	seg = off = len = 0;
+retry:
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.c_data = data;
 	cmd.c_datalen = datalen;
@@ -1103,8 +1165,35 @@
 	if (!ISSET(sf->flags, SFF_SDHC))
 		cmd.c_arg <<= SDMMC_SECTOR_SIZE_SB;
 	cmd.c_flags = SCF_CMD_ADTC | SCF_RSP_R1;
-	if (ISSET(sc->sc_caps, SMC_CAPS_DMA))
+	if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
 		cmd.c_dmamap = sc->sc_dmap;
+		if (!ISSET(sc->sc_caps, SMC_CAPS_MULTI_SEG_DMA)) {
+			len = sc->sc_dmap->dm_segs[seg].ds_len - off;
+			len &= ~(SDMMC_SECTOR_SIZE - 1);
+			cmd.c_datalen = len;
+			cmd.c_dmaseg = seg;
+			cmd.c_dmaoff = off;
+			bbuf = 0;
+			if (len == 0) {
+				/* Use bounce buffer */
+				bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, num,
+				    SDMMC_SECTOR_SIZE, BUS_DMASYNC_POSTWRITE);
+				memcpy(sf->bbuf, data, SDMMC_SECTOR_SIZE);
+				bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, num,
+				    SDMMC_SECTOR_SIZE, BUS_DMASYNC_PREWRITE);
+				bus_dmamap_sync(sc->sc_dmat, sf->bbuf_dmap, 0,
+				    SDMMC_SECTOR_SIZE, BUS_DMASYNC_PREWRITE);
+				cmd.c_datalen = SDMMC_SECTOR_SIZE;
+				cmd.c_dmamap = sf->bbuf_dmap;
+				cmd.c_dmaseg = 0;
+				cmd.c_dmaoff = 0;
+				bbuf = 1;
+				len = SDMMC_SECTOR_SIZE;
+			}
+			cmd.c_opcode = (cmd.c_datalen / cmd.c_blklen) > 1 ?
+			    MMC_WRITE_BLOCK_MULTIPLE : MMC_WRITE_BLOCK_SINGLE;
+		}
+	}
 
 	error = sdmmc_mmc_command(sc, &cmd);
 	if (error)
@@ -1135,6 +1224,28 @@
 		} while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA));
 	}
 
+	if (ISSET(sc->sc_caps, SMC_CAPS_DMA) &&
+	    !ISSET(sc->sc_caps, SMC_CAPS_MULTI_SEG_DMA)) {
+		bus_dma_segment_t *dm_segs = sc->sc_dmap->dm_segs;
+
+		if (bbuf)
+			bus_dmamap_sync(sc->sc_dmat, sf->bbuf_dmap,
+			    0, SDMMC_SECTOR_SIZE, BUS_DMASYNC_POSTWRITE);
+		num += len;
+		data += len;
+		datalen -= len;
+		blkno += (len / SDMMC_SECTOR_SIZE);
+
+		while (off + len >= dm_segs[seg].ds_len) {
+			len -= dm_segs[seg++].ds_len;
+			off = 0;
+		}
+		off += len;
+
+		if (seg < sc->sc_dmap->dm_nsegs)
+			goto retry;
+	}
+
 out:
 	return error;
 }

Index: src/sys/dev/sdmmc/sdmmcvar.h
diff -u src/sys/dev/sdmmc/sdmmcvar.h:1.6 src/sys/dev/sdmmc/sdmmcvar.h:1.7
--- src/sys/dev/sdmmc/sdmmcvar.h:1.6	Thu Sep 23 12:03:27 2010
+++ src/sys/dev/sdmmc/sdmmcvar.h	Fri Oct  1 09:50:42 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdmmcvar.h,v 1.6 2010/09/23 12:03:27 kiyohara Exp $	*/
+/*	$NetBSD: sdmmcvar.h,v 1.7 2010/10/01 09:50:42 kiyohara Exp $	*/
 /*	$OpenBSD: sdmmcvar.h,v 1.13 2009/01/09 10:55:22 jsg Exp $	*/
 
 /*
@@ -85,6 +85,8 @@
 	uint32_t	 c_arg;		/* SD/MMC command argument */
 	sdmmc_response	 c_resp;	/* response buffer */
 	bus_dmamap_t	 c_dmamap;
+	int		 c_dmaseg;	/* DMA segment number */
+	int		 c_dmaoff;	/* offset in DMA segment */
 	void		*c_data;	/* buffer to send or read into */
 	int		 c_datalen;	/* length of data buffer */
 	int		 c_blklen;	/* block length */
@@ -176,6 +178,8 @@
 	sdmmc_response raw_cid;		/* temp. storage for decoding */
 	uint32_t raw_scr[2];
 	struct sdmmc_scr scr;		/* decoded CSR value */
+	void *bbuf;			/* bounce buffer */
+	bus_dmamap_t bbuf_dmap;		/* DMA map for bounce buffer */
 };
 
 /*
@@ -211,6 +215,7 @@
 #define SMC_CAPS_POLL_CARD_DET	0x0010	/* Polling card detect */
 #define SMC_CAPS_SINGLE_ONLY	0x0020	/* only single read/write */
 #define SMC_CAPS_8BIT_MODE	0x0040	/* 8-bits data bus width */
+#define SMC_CAPS_MULTI_SEG_DMA	0x0080	/* multiple segment DMA transfer */
 
 	/* function */
 	int sc_function_count;		/* number of I/O functions (SDIO) */

Reply via email to