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