Current-Users archive

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

Re: sdmmc Multi segment DMA support



Oops,  A different function mixed with this patch.
# 'rod' needed by high speed support.  ;-)


From: KIYOHARA Takashi <kiyohara%kk.iij4u.or.jp@localhost>
Date: Thu, 23 Sep 2010 21:29:34 +0900 (JST)

> I want support for multi segment DMA on sdmmc layer.
> Some SDIO controllers cannot handle two or more DMA segments.  For
> instance, Marvell SDIO Controller and, perhaps, sdhc(4) also.  I
> supported this by securing the bounce buffer for DMA in the sdmmc
> layer.
> 
> This works on Marvell Sheevaplug.

Thanks,
--
kiyohara
Index: sdmmc.c
===================================================================
RCS file: /cvsroot/src/sys/dev/sdmmc/sdmmc.c,v
retrieving revision 1.3
diff -u -r1.3 sdmmc.c
--- sdmmc.c     20 Sep 2010 09:06:03 -0000      1.3
+++ sdmmc.c     23 Sep 2010 12:06:30 -0000
@@ -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: sdmmc_mem.c
===================================================================
RCS file: /cvsroot/src/sys/dev/sdmmc/sdmmc_mem.c,v
retrieving revision 1.11
diff -u -r1.11 sdmmc_mem.c
--- sdmmc_mem.c 23 Sep 2010 12:03:27 -0000      1.11
+++ sdmmc_mem.c 23 Sep 2010 12:06:31 -0000
@@ -934,10 +941,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 +963,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 +971,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 +985,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 +1040,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 +1128,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 +1150,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 +1158,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 +1172,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 +1231,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: sdmmcvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/sdmmc/sdmmcvar.h,v
retrieving revision 1.6
diff -u -r1.6 sdmmcvar.h
--- sdmmcvar.h  23 Sep 2010 12:03:27 -0000      1.6
+++ sdmmcvar.h  23 Sep 2010 12:06:31 -0000
@@ -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) */


Home | Main Index | Thread Index | Old Index