Module Name: src Committed By: jmcneill Date: Wed Jul 29 12:11:14 UTC 2015
Modified Files: src/sys/dev/sdmmc: sdhc.c sdhcreg.h sdhcvar.h Log Message: Add ADMA2 support, which enables scatter gather DMA for data transfers on controllers that support it. To generate a diff of this commit: cvs rdiff -u -r1.62 -r1.63 src/sys/dev/sdmmc/sdhc.c cvs rdiff -u -r1.13 -r1.14 src/sys/dev/sdmmc/sdhcreg.h cvs rdiff -u -r1.19 -r1.20 src/sys/dev/sdmmc/sdhcvar.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/sdhc.c diff -u src/sys/dev/sdmmc/sdhc.c:1.62 src/sys/dev/sdmmc/sdhc.c:1.63 --- src/sys/dev/sdmmc/sdhc.c:1.62 Tue Jul 28 07:14:48 2015 +++ src/sys/dev/sdmmc/sdhc.c Wed Jul 29 12:11:13 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: sdhc.c,v 1.62 2015/07/28 07:14:48 skrll Exp $ */ +/* $NetBSD: sdhc.c,v 1.63 2015/07/29 12:11:13 jmcneill Exp $ */ /* $OpenBSD: sdhc.c,v 1.25 2009/01/13 19:44:20 grange Exp $ */ /* @@ -23,7 +23,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.62 2015/07/28 07:14:48 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.63 2015/07/29 12:11:13 jmcneill Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -86,6 +86,13 @@ struct sdhc_host { #define SHF_USE_4BIT_MODE 0x0002 #define SHF_USE_8BIT_MODE 0x0004 #define SHF_MODE_DMAEN 0x0008 /* needs SDHC_DMA_ENABLE in mode */ +#define SHF_USE_ADMA2_32 0x0010 +#define SHF_USE_ADMA2_64 0x0020 +#define SHF_USE_ADMA2_MASK 0x0030 + + bus_dmamap_t adma_map; + bus_dma_segment_t adma_segs[1]; + void *adma2; }; #define HDEVNAME(hp) (device_xname((hp)->sc->sc_dev)) @@ -246,6 +253,7 @@ sdhc_host_found(struct sdhc_softc *sc, b struct sdhc_host *hp; uint32_t caps; uint16_t sdhcver; + int error; /* Allocate one more host structure. */ hp = malloc(sizeof(struct sdhc_host), M_DEVBUF, M_WAITOK|M_ZERO); @@ -321,11 +329,30 @@ sdhc_host_found(struct sdhc_softc *sc, b (ISSET(sc->sc_flags, SDHC_FLAG_USE_DMA && ISSET(caps, SDHC_DMA_SUPPORT)))) { SET(hp->flags, SHF_USE_DMA); - if (!ISSET(sc->sc_flags, SDHC_FLAG_EXTERNAL_DMA) || - ISSET(sc->sc_flags, SDHC_FLAG_EXTDMA_DMAEN)) - SET(hp->flags, SHF_MODE_DMAEN); - aprint_normal(", DMA"); + if (ISSET(sc->sc_flags, SDHC_FLAG_USE_ADMA2) && + ISSET(caps, SDHC_ADMA2_SUPP)) { + SET(hp->flags, SHF_MODE_DMAEN); + /* + * 64-bit mode was present in the 2.00 spec, removed + * from 3.00, and re-added in 4.00 with a different + * descriptor layout. We only support 2.00 and 3.00 + * descriptors for now. + */ + if (hp->specver == SDHC_SPEC_VERS_200 && + ISSET(caps, SDHC_64BIT_SYS_BUS)) { + SET(hp->flags, SHF_USE_ADMA2_64); + aprint_normal(", 64-bit ADMA2"); + } else { + SET(hp->flags, SHF_USE_ADMA2_32); + aprint_normal(", 32-bit ADMA2"); + } + } else { + if (!ISSET(sc->sc_flags, SDHC_FLAG_EXTERNAL_DMA) || + ISSET(sc->sc_flags, SDHC_FLAG_EXTDMA_DMAEN)) + SET(hp->flags, SHF_MODE_DMAEN); + aprint_normal(", SDMA"); + } } else { aprint_normal(", PIO"); } @@ -417,6 +444,47 @@ sdhc_host_found(struct sdhc_softc *sc, b aprint_normal(", %u byte blocks", hp->maxblklen); aprint_normal("\n"); + if (ISSET(hp->flags, SHF_USE_ADMA2_MASK)) { + int rseg; + + /* Allocate ADMA2 descriptor memory */ + error = bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, PAGE_SIZE, + PAGE_SIZE, hp->adma_segs, 1, &rseg, BUS_DMA_WAITOK); + if (error) { + aprint_error_dev(sc->sc_dev, + "ADMA2 dmamem_alloc failed (%d)\n", error); + goto adma_done; + } + error = bus_dmamem_map(sc->sc_dmat, hp->adma_segs, rseg, + PAGE_SIZE, (void **)&hp->adma2, BUS_DMA_WAITOK); + if (error) { + aprint_error_dev(sc->sc_dev, + "ADMA2 dmamem_map failed (%d)\n", error); + goto adma_done; + } + error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, + 0, BUS_DMA_WAITOK, &hp->adma_map); + if (error) { + aprint_error_dev(sc->sc_dev, + "ADMA2 dmamap_create failed (%d)\n", error); + goto adma_done; + } + error = bus_dmamap_load(sc->sc_dmat, hp->adma_map, + hp->adma2, PAGE_SIZE, NULL, + BUS_DMA_WAITOK|BUS_DMA_WRITE); + if (error) { + aprint_error_dev(sc->sc_dev, + "ADMA2 dmamap_load failed (%d)\n", error); + goto adma_done; + } + + memset(hp->adma2, 0, PAGE_SIZE); + +adma_done: + if (error) + CLR(hp->flags, SHF_USE_ADMA2_MASK); + } + /* * Attach the generic SD/MMC bus driver. (The bus driver must * not invoke any chipset functions before it is attached.) @@ -496,6 +564,12 @@ sdhc_detach(struct sdhc_softc *sc, int f bus_space_unmap(hp->iot, hp->ioh, hp->ios); hp->ios = 0; } + if (ISSET(hp->flags, SHF_USE_ADMA2_MASK)) { + bus_dmamap_unload(sc->sc_dmat, hp->adma_map); + bus_dmamap_destroy(sc->sc_dmat, hp->adma_map); + bus_dmamem_unmap(sc->sc_dmat, hp->adma2, PAGE_SIZE); + bus_dmamem_free(sc->sc_dmat, hp->adma_segs, 1); + } free(hp, M_DEVBUF); sc->sc_host[n] = NULL; } @@ -1294,9 +1368,57 @@ sdhc_start_command(struct sdhc_host *hp, } /* Set DMA start address. */ - if (ISSET(mode, SDHC_DMA_ENABLE) && - !ISSET(sc->sc_flags, SDHC_FLAG_EXTERNAL_DMA)) + if (ISSET(hp->flags, SHF_USE_ADMA2_MASK) && cmd->c_datalen > 0) { + for (int seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) { + paddr_t paddr = + cmd->c_dmamap->dm_segs[seg].ds_addr; + uint16_t len = + cmd->c_dmamap->dm_segs[seg].ds_len == 65536 ? + 0 : cmd->c_dmamap->dm_segs[seg].ds_len; + uint16_t attr = + SDHC_ADMA2_VALID | SDHC_ADMA2_ACT_TRANS; + if (seg == cmd->c_dmamap->dm_nsegs - 1) { + attr |= SDHC_ADMA2_END; + } + if (ISSET(hp->flags, SHF_USE_ADMA2_32)) { + struct sdhc_adma2_descriptor32 *desc = + hp->adma2; + desc[seg].attribute = htole16(attr); + desc[seg].length = htole16(len); + desc[seg].address = htole32(paddr); + } else { + struct sdhc_adma2_descriptor64 *desc = + hp->adma2; + desc[seg].attribute = htole16(attr); + desc[seg].length = htole16(len); + desc[seg].address = htole32(paddr & 0xffffffff); + desc[seg].address_hi = htole32( + (uint64_t)paddr >> 32); + } + } + if (ISSET(hp->flags, SHF_USE_ADMA2_32)) { + struct sdhc_adma2_descriptor32 *desc = hp->adma2; + desc[cmd->c_dmamap->dm_nsegs].attribute = htole16(0); + } else { + struct sdhc_adma2_descriptor64 *desc = hp->adma2; + desc[cmd->c_dmamap->dm_nsegs].attribute = htole16(0); + } + bus_dmamap_sync(sc->sc_dmat, hp->adma_map, 0, PAGE_SIZE, + BUS_DMASYNC_PREWRITE); + HCLR1(hp, SDHC_HOST_CTL, SDHC_DMA_SELECT); + HSET1(hp, SDHC_HOST_CTL, SDHC_DMA_SELECT_ADMA2); + + const paddr_t desc_addr = hp->adma_map->dm_segs[0].ds_addr; + + HWRITE4(hp, SDHC_ADMA_SYSTEM_ADDR, desc_addr & 0xffffffff); + if (ISSET(hp->flags, SHF_USE_ADMA2_64)) { + HWRITE4(hp, SDHC_ADMA_SYSTEM_ADDR + 4, + (uint64_t)desc_addr >> 32); + } + } else if (ISSET(mode, SDHC_DMA_ENABLE) && + !ISSET(sc->sc_flags, SDHC_FLAG_EXTERNAL_DMA)) { HWRITE4(hp, SDHC_DMA_ADDR, cmd->c_dmamap->dm_segs[0].ds_addr); + } /* * Start a CPU data transfer. Writing to the high order byte @@ -1386,6 +1508,11 @@ sdhc_transfer_data_dma(struct sdhc_host error = ETIMEDOUT; break; } + + if (ISSET(hp->flags, SHF_USE_ADMA2_MASK)) { + continue; + } + if ((status & SDHC_DMA_INTERRUPT) == 0) { continue; } @@ -1410,6 +1537,11 @@ sdhc_transfer_data_dma(struct sdhc_host KASSERT(seg < cmd->c_dmamap->dm_nsegs); } + if (ISSET(hp->flags, SHF_USE_ADMA2_MASK)) { + bus_dmamap_sync(hp->sc->sc_dmat, hp->adma_map, 0, + PAGE_SIZE, BUS_DMASYNC_POSTWRITE); + } + return error; } @@ -1769,6 +1901,12 @@ sdhc_intr(void *arg) /* Claim this interrupt. */ done = 1; + if (ISSET(error, SDHC_ADMA_ERROR)) { + uint8_t adma_err = HREAD1(hp, SDHC_ADMA_ERROR_STATUS); + printf("%s: ADMA error, status %02x\n", HDEVNAME(hp), + adma_err); + } + /* * Service error interrupts. */ Index: src/sys/dev/sdmmc/sdhcreg.h diff -u src/sys/dev/sdmmc/sdhcreg.h:1.13 src/sys/dev/sdmmc/sdhcreg.h:1.14 --- src/sys/dev/sdmmc/sdhcreg.h:1.13 Sat May 2 12:10:24 2015 +++ src/sys/dev/sdmmc/sdhcreg.h Wed Jul 29 12:11:14 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: sdhcreg.h,v 1.13 2015/05/02 12:10:24 jmcneill Exp $ */ +/* $NetBSD: sdhcreg.h,v 1.14 2015/07/29 12:11:14 jmcneill Exp $ */ /* $OpenBSD: sdhcreg.h,v 1.4 2006/07/30 17:20:40 fgsch Exp $ */ /* @@ -75,6 +75,9 @@ #define SDHC_CMD_INHIBIT_MASK 0x0003 #define SDHC_HOST_CTL 0x28 #define SDHC_8BIT_MODE (1<<5) +#define SDHC_DMA_SELECT (3<<3) +#define SDHC_DMA_SELECT_SDMA (0<<3) +#define SDHC_DMA_SELECT_ADMA2 (2<<3) #define SDHC_HIGH_SPEED (1<<2) #define SDHC_ESDHC_8BIT_MODE (1<<2) /* eSDHC */ #define SDHC_4BIT_MODE (1<<1) @@ -121,6 +124,7 @@ #define SDHC_NINTR_STATUS_MASK 0x81ff #define SDHC_EINTR_STATUS 0x32 #define SDHC_DMA_ERROR (1<<12) +#define SDHC_ADMA_ERROR (1<<9) #define SDHC_AUTO_CMD12_ERROR (1<<8) #define SDHC_CURRENT_LIMIT_ERROR (1<<7) #define SDHC_DATA_END_BIT_ERROR (1<<6) @@ -130,13 +134,13 @@ #define SDHC_CMD_END_BIT_ERROR (1<<2) #define SDHC_CMD_CRC_ERROR (1<<1) #define SDHC_CMD_TIMEOUT_ERROR (1<<0) -#define SDHC_EINTR_STATUS_MASK 0x01ff /* excluding vendor signals */ +#define SDHC_EINTR_STATUS_MASK 0x03ff /* excluding vendor signals */ #define SDHC_NINTR_STATUS_EN 0x34 #define SDHC_EINTR_STATUS_EN 0x36 #define SDHC_NINTR_SIGNAL_EN 0x38 #define SDHC_NINTR_SIGNAL_MASK 0x01ff #define SDHC_EINTR_SIGNAL_EN 0x3a -#define SDHC_EINTR_SIGNAL_MASK 0x01ff /* excluding vendor signals */ +#define SDHC_EINTR_SIGNAL_MASK 0x03ff /* excluding vendor signals */ #define SDHC_CMD12_ERROR_STATUS 0x3c #define SDHC_CAPABILITIES 0x40 #define SDHC_SHARED_BUS_SLOT (1<<31) @@ -163,6 +167,10 @@ #define SDHC_TIMEOUT_FREQ_UNIT (1<<7) /* 0=KHz, 1=MHz */ #define SDHC_TIMEOUT_FREQ_SHIFT 0 #define SDHC_TIMEOUT_FREQ_MASK 0x1f +#define SDHC_ADMA_ERROR_STATUS 0x54 +#define SDHC_ADMA_LENGTH_MISMATCH (1<<2) +#define SDHC_ADMA_ERROR_STATE (3<<0) +#define SDHC_ADMA_SYSTEM_ADDR 0x58 #define SDHC_WATERMARK_LEVEL 0x44 /* ESDHC */ #define SDHC_WATERMARK_WRITE_SHIFT 16 #define SDHC_WATERMARK_WRITE_MASK 0xff @@ -214,4 +222,25 @@ #define SDHC_CAPABILITIES_BITS \ "\20\33Vdd1.8V\32Vdd3.0V\31Vdd3.3V\30SUSPEND\27DMA\26HIGHSPEED" +#define SDHC_ADMA2_VALID (1<<0) +#define SDHC_ADMA2_END (1<<1) +#define SDHC_ADMA2_INT (1<<2) +#define SDHC_ADMA2_ACT (3<<4) +#define SDHC_ADMA2_ACT_NOP (0<<4) +#define SDHC_ADMA2_ACT_TRANS (2<<4) +#define SDHC_ADMA2_ACT_LINK (3<<4) + +struct sdhc_adma2_descriptor32 { + uint16_t attribute; + uint16_t length; + uint32_t address; +} __packed; + +struct sdhc_adma2_descriptor64 { + uint16_t attribute; + uint16_t length; + uint32_t address; + uint32_t address_hi; +} __packed; + #endif /* _SDHCREG_H_ */ Index: src/sys/dev/sdmmc/sdhcvar.h diff -u src/sys/dev/sdmmc/sdhcvar.h:1.19 src/sys/dev/sdmmc/sdhcvar.h:1.20 --- src/sys/dev/sdmmc/sdhcvar.h:1.19 Thu Jul 23 23:52:54 2015 +++ src/sys/dev/sdmmc/sdhcvar.h Wed Jul 29 12:11:14 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: sdhcvar.h,v 1.19 2015/07/23 23:52:54 jmcneill Exp $ */ +/* $NetBSD: sdhcvar.h,v 1.20 2015/07/29 12:11:14 jmcneill Exp $ */ /* $OpenBSD: sdhcvar.h,v 1.3 2007/09/06 08:01:01 jsg Exp $ */ /* @@ -55,6 +55,7 @@ struct sdhc_softc { #define SDHC_FLAG_NO_CLKBASE 0x00020000 /* ignore clkbase register */ #define SDHC_FLAG_SINGLE_POWER_WRITE 0x00040000 #define SDHC_FLAG_NO_TIMEOUT 0x00080000 /* ignore timeout interrupts */ +#define SDHC_FLAG_USE_ADMA2 0x00100000 uint32_t sc_clkbase; int sc_clkmsk; /* Mask for SDCLK */