Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/sdmmc Use DMA bounce buffer, if DMA buffer is making...



details:   https://anonhg.NetBSD.org/src/rev/f85c24c4a2c0
branches:  trunk
changeset: 757875:f85c24c4a2c0
user:      kiyohara <kiyohara%NetBSD.org@localhost>
date:      Fri Oct 01 09:50:42 2010 +0000

description:
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.

diffstat:

 sys/dev/sdmmc/sdmmc.c     |   52 ++++++++++++++++++-
 sys/dev/sdmmc/sdmmc_mem.c |  123 +++++++++++++++++++++++++++++++++++++++++++--
 sys/dev/sdmmc/sdmmcvar.h  |    7 ++-
 3 files changed, 173 insertions(+), 9 deletions(-)

diffs (truncated from 338 to 300 lines):

diff -r 9b9cb4bb5a8c -r f85c24c4a2c0 sys/dev/sdmmc/sdmmc.c
--- a/sys/dev/sdmmc/sdmmc.c     Thu Sep 30 19:45:24 2010 +0000
+++ b/sys/dev/sdmmc/sdmmc.c     Fri Oct 01 09:50:42 2010 +0000
@@ -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);
 }
diff -r 9b9cb4bb5a8c -r f85c24c4a2c0 sys/dev/sdmmc/sdmmc_mem.c
--- a/sys/dev/sdmmc/sdmmc_mem.c Thu Sep 30 19:45:24 2010 +0000
+++ b/sys/dev/sdmmc/sdmmc_mem.c Fri Oct 01 09:50:42 2010 +0000
@@ -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;
+       }
+



Home | Main Index | Thread Index | Old Index