The diff below implements some speedups for sdhc(4). In particular:
* Implement high speed mode
* Implement support for 4-bit and 8-bit busses
* Use DMA for block transfers
High speed mode and wider bus support are only used for (e)MMC for
now, but DMA support should benefit SD cards as well.
Most of this code was derived from the imrpovements made over at
NetBSD. However, I chose to only implement for ADMA2 and not for the
older SDMA and ADMA1 mechanisms. These older DMA mechanism have
severe limitations and it seems that most of the available hardware
supports ADAM2. And of course PIO (programmed IO) will continue to
work just fine for hardware that doesn't support ADMA2.
Emulates SCSI transfers continue to be bounced through the sdmmc task
thread. That probably limits the total throughput a bit.
Nevertheless, with this diff I can read from eMMC in the Lenovo Bay
Trail stick at 40MB/s, which is quite an improvement from the 5MB/s I
reached before.
This is probably several commit's worth of changes, but this provides
them all in an easily testable form.
Please test and/or review.
Index: dev/sdmmc/sdhc.c
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdhc.c,v
retrieving revision 1.43
diff -u -p -r1.43 sdhc.c
--- dev/sdmmc/sdhc.c 30 Mar 2016 09:58:01 -0000 1.43
+++ dev/sdmmc/sdhc.c 28 Apr 2016 10:27:28 -0000
@@ -36,6 +36,7 @@
#define SDHC_COMMAND_TIMEOUT hz
#define SDHC_BUFFER_TIMEOUT hz
#define SDHC_TRANSFER_TIMEOUT hz
+#define SDHC_DMA_TIMEOUT (hz*3)
struct sdhc_host {
struct sdhc_softc *sc; /* host controller device */
@@ -50,6 +51,10 @@ struct sdhc_host {
u_int8_t regs[14]; /* host controller state */
u_int16_t intr_status; /* soft interrupt status */
u_int16_t intr_error_status; /* soft error status */
+
+ bus_dmamap_t adma_map;
+ bus_dma_segment_t adma_segs[1];
+ caddr_t adma2;
};
/* flag values */
@@ -82,8 +87,10 @@ int sdhc_host_maxblklen(sdmmc_chipset_ha
int sdhc_card_detect(sdmmc_chipset_handle_t);
int sdhc_bus_power(sdmmc_chipset_handle_t, u_int32_t);
int sdhc_bus_clock(sdmmc_chipset_handle_t, int);
+int sdhc_bus_width(sdmmc_chipset_handle_t, int);
void sdhc_card_intr_mask(sdmmc_chipset_handle_t, int);
void sdhc_card_intr_ack(sdmmc_chipset_handle_t);
+int sdhc_signal_voltage(sdmmc_chipset_handle_t, int);
void sdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
int sdhc_start_command(struct sdhc_host *, struct sdmmc_command *);
int sdhc_wait_state(struct sdhc_host *, u_int32_t, u_int32_t);
@@ -112,11 +119,13 @@ struct sdmmc_chip_functions sdhc_functio
/* bus power and clock frequency */
sdhc_bus_power,
sdhc_bus_clock,
+ sdhc_bus_width,
/* command execution */
sdhc_exec_command,
/* card interrupt */
sdhc_card_intr_mask,
- sdhc_card_intr_ack
+ sdhc_card_intr_ack,
+ sdhc_signal_voltage
};
struct cfdriver sdhc_cd = {
@@ -135,6 +144,7 @@ sdhc_host_found(struct sdhc_softc *sc, b
struct sdhc_host *hp;
int error = 1;
int max_clock;
+ uint32_t caps2 = 0;
#ifdef SDHC_DEBUG
u_int16_t version;
@@ -174,7 +184,7 @@ sdhc_host_found(struct sdhc_softc *sc, b
caps = HREAD4(hp, SDHC_CAPABILITIES);
/* Use DMA if the host system and the controller support it. */
- if (usedma && ISSET(caps, SDHC_DMA_SUPPORT))
+ if (usedma && ISSET(caps, SDHC_ADMA2_SUPP))
SET(hp->flags, SHF_USE_DMA);
/*
@@ -210,6 +220,14 @@ sdhc_host_found(struct sdhc_softc *sc, b
/*
* Determine SD bus voltage levels supported by the controller.
*/
+ if (ISSET(caps, SDHC_HIGH_SPEED_SUPP))
+ SET(hp->ocr, MMC_OCR_HCS);
+ if (ISSET(caps2, SDHC_SDR50_SUPP))
+ SET(hp->ocr, MMC_OCR_S18A);
+ if (ISSET(caps2, SDHC_DDR50_SUPP))
+ SET(hp->ocr, MMC_OCR_S18A);
+ if (ISSET(caps2, SDHC_SDR104_SUPP))
+ SET(hp->ocr, MMC_OCR_S18A);
if (ISSET(caps, SDHC_VOLTAGE_SUPP_1_8V))
SET(hp->ocr, MMC_OCR_1_65V_1_95V);
if (ISSET(caps, SDHC_VOLTAGE_SUPP_3_0V))
@@ -236,6 +254,46 @@ sdhc_host_found(struct sdhc_softc *sc, b
break;
}
+ if (ISSET(hp->flags, SHF_USE_DMA)) {
+ 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 | BUS_DMA_ZERO);
+ if (error)
+ goto adma_done;
+ error = bus_dmamem_map(sc->sc_dmat, hp->adma_segs, rseg,
+ PAGE_SIZE, &hp->adma2, BUS_DMA_WAITOK);
+ if (error) {
+ bus_dmamem_free(sc->sc_dmat, hp->adma_segs, rseg);
+ goto adma_done;
+ }
+ error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE,
+ 0, BUS_DMA_WAITOK, &hp->adma_map);
+ if (error) {
+ bus_dmamem_unmap(sc->sc_dmat, hp->adma2, PAGE_SIZE);
+ bus_dmamem_free(sc->sc_dmat, hp->adma_segs, rseg);
+ 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) {
+ 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, rseg);
+ goto adma_done;
+ }
+
+ adma_done:
+ if (error) {
+ printf("%s: can't allocate DMA descriptor table\n",
+ DEVNAME(hp->sc));
+ CLR(hp->flags, SHF_USE_DMA);
+ }
+ }
+
/*
* Attach the generic SD/MMC bus driver. (The bus driver must
* not invoke any chipset functions before it is attached.)
@@ -244,6 +302,16 @@ sdhc_host_found(struct sdhc_softc *sc, b
saa.saa_busname = "sdmmc";
saa.sct = &sdhc_functions;
saa.sch = hp;
+ saa.dmat = sc->sc_dmat;
+ saa.caps = SMC_CAPS_4BIT_MODE;
+
+ if (ISSET(caps, SDHC_HIGH_SPEED_SUPP))
+ saa.caps |= SMC_CAPS_MMC_HIGHSPEED;
+ if (ISSET(caps, SDHC_8BIT_MODE_SUPP))
+ saa.caps |= SMC_CAPS_8BIT_MODE;
+
+ if (ISSET(hp->flags, SHF_USE_DMA))
+ saa.caps |= SMC_CAPS_DMA;
hp->sdmmc = config_found(&sc->sc_dev, &saa, NULL);
if (hp->sdmmc == NULL) {
@@ -486,6 +554,7 @@ sdhc_bus_clock(sdmmc_chipset_handle_t sc
int sdclk;
int timo;
int error = 0;
+// int ddr = 0;
s = splsdmmc();
@@ -503,6 +572,27 @@ sdhc_bus_clock(sdmmc_chipset_handle_t sc
if (freq == SDMMC_SDCLK_OFF)
goto ret;
+#if 0
+ if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3) {
+ HCLR2(hp, SDHC_HOST_CTL2, SDHC_UHS_MODE_SELECT_MASK);
+ if (freq > 100000) {
+ HSET2(hp, SDHC_HOST_CTL2, SDHC_UHS_MODE_SELECT_SDR104);
+ } else if (freq > 50000) {
+ HSET2(hp, SDHC_HOST_CTL2, SDHC_UHS_MODE_SELECT_SDR50);
+ } else if (freq > 25000) {
+ if (ddr) {
+ HSET2(hp, SDHC_HOST_CTL2,
+ SDHC_UHS_MODE_SELECT_DDR50);
+ } else {
+ HSET2(hp, SDHC_HOST_CTL2,
+ SDHC_UHS_MODE_SELECT_SDR25);
+ }
+ } else if (freq > 400) {
+ HSET2(hp, SDHC_HOST_CTL2, SDHC_UHS_MODE_SELECT_SDR12);
+ }
+ }
+#endif
+
/*
* Set the minimum base clock frequency divisor.
*/
@@ -536,11 +626,53 @@ sdhc_bus_clock(sdmmc_chipset_handle_t sc
*/
HSET2(hp, SDHC_CLOCK_CTL, SDHC_SDCLK_ENABLE);
+ if (freq > 25000)
+ HSET1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED);
+ else
+ HCLR1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED);
+
ret:
splx(s);
return error;
}
+int
+sdhc_bus_width(sdmmc_chipset_handle_t sch, int width)
+{
+ struct sdhc_host *hp = (struct sdhc_host *)sch;
+ int reg;
+ int s;
+
+ switch (width) {
+ case 1:
+ case 4:
+ case 8:
+ break;
+ default:
+ DPRINTF(0,("%s: unsupported bus width (%d)\n",
+ DEVNAME(hp->sc), width));
+ return 1;
+ }
+
+ s = splsdmmc();
+
+ reg = HREAD1(hp, SDHC_HOST_CTL);
+ reg &= ~SDHC_4BIT_MODE;
+ if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3) {
+ reg &= ~SDHC_8BIT_MODE;
+ }
+ if (width == 4) {
+ reg |= SDHC_4BIT_MODE;
+ } else if (width == 8 && SDHC_SPEC_VERSION(hp->version) >=
SDHC_SPEC_V3) {
+ reg |= SDHC_8BIT_MODE;
+ }
+ HWRITE1(hp, SDHC_HOST_CTL, reg);
+
+ splx(s);
+
+ return 0;
+}
+
void
sdhc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable)
{
@@ -564,6 +696,32 @@ sdhc_card_intr_ack(sdmmc_chipset_handle_
}
int
+sdhc_signal_voltage(sdmmc_chipset_handle_t sch, int signal_voltage)
+{
+ struct sdhc_host *hp = (struct sdhc_host *)sch;
+ int error = 0;
+ int s;
+
+ s = splsdmmc();
+
+ switch (signal_voltage) {
+ case SDMMC_SIGNAL_VOLTAGE_180:
+ HSET2(hp, SDHC_HOST_CTL2, SDHC_1_8V_SIGNAL_EN);
+ break;
+ case SDMMC_SIGNAL_VOLTAGE_330:
+ HCLR2(hp, SDHC_HOST_CTL2, SDHC_1_8V_SIGNAL_EN);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ splx(s);
+
+ return error;
+}
+
+int
sdhc_wait_state(struct sdhc_host *hp, u_int32_t mask, u_int32_t value)
{
u_int32_t state;
@@ -641,11 +799,14 @@ sdhc_exec_command(sdmmc_chipset_handle_t
int
sdhc_start_command(struct sdhc_host *hp, struct sdmmc_command *cmd)
{
+ struct sdhc_adma2_descriptor32 *desc = (void *)hp->adma2;
+ struct sdhc_softc *sc = hp->sc;
u_int16_t blksize = 0;
u_int16_t blkcount = 0;
u_int16_t mode;
u_int16_t command;
int error;
+ int seg;
int s;
DPRINTF(1,("%s: start cmd %u arg=%#x data=%#x dlen=%d flags=%#x "
@@ -688,10 +849,9 @@ sdhc_start_command(struct sdhc_host *hp,
mode |= SDHC_AUTO_CMD12_ENABLE;
}
}
-#ifdef notyet
- if (ISSET(hp->flags, SHF_USE_DMA))
+ if (cmd->c_dmamap && cmd->c_datalen > 0 &&
+ ISSET(hp->flags, SHF_USE_DMA))
mode |= SDHC_DMA_ENABLE;
-#endif
/*
* Prepare command register value. (2.2.6)
@@ -725,6 +885,34 @@ sdhc_start_command(struct sdhc_host *hp,
HSET1(hp, SDHC_HOST_CTL, SDHC_LED_ON);
/* XXX: Set DMA start address if SHF_USE_DMA is set. */
+ if (cmd->c_dmamap && ISSET(hp->flags, SHF_USE_DMA)) {
+ for (seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) {
+ bus_addr_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;
+
+ attr = SDHC_ADMA2_VALID | SDHC_ADMA2_ACT_TRANS;
+ if (seg == cmd->c_dmamap->dm_nsegs - 1)
+ attr |= SDHC_ADMA2_END;
+
+ desc[seg].attribute = htole16(attr);
+ desc[seg].length = htole16(len);
+ desc[seg].address = htole32(paddr);
+ }
+
+ 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);
+
+ HWRITE4(hp, SDHC_ADMA_SYSTEM_ADDR,
hp->adma_map->dm_segs[0].ds_addr);
+ }
DPRINTF(1,("%s: cmd=%#x mode=%#x blksize=%d blkcount=%d\n",
DEVNAME(hp->sc), command, mode, blksize, blkcount));
@@ -752,6 +940,25 @@ sdhc_transfer_data(struct sdhc_host *hp,
int mask;
int error;
+ if (cmd->c_dmamap) {
+ int status;
+
+ error = 0;
+ for (;;) {
+ status = sdhc_wait_intr(hp,
+ SDHC_DMA_INTERRUPT|SDHC_TRANSFER_COMPLETE,
+ SDHC_DMA_TIMEOUT);
+ if (status & SDHC_TRANSFER_COMPLETE)
+ break;
+ if (!status) {
+ error = ETIMEDOUT;
+ break;
+ }
+ }
+
+ goto done;
+ }
+
mask = ISSET(cmd->c_flags, SCF_CMD_READ) ?
SDHC_BUFFER_READ_ENABLE : SDHC_BUFFER_WRITE_ENABLE;
error = 0;
@@ -792,6 +999,7 @@ sdhc_transfer_data(struct sdhc_host *hp,
SDHC_TRANSFER_TIMEOUT))
error = ETIMEDOUT;
+done:
if (error != 0)
cmd->c_error = error;
SET(cmd->c_flags, SCF_ITSDONE);
Index: dev/sdmmc/sdhcreg.h
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdhcreg.h,v
retrieving revision 1.5
diff -u -p -r1.5 sdhcreg.h
--- dev/sdmmc/sdhcreg.h 11 Jan 2016 06:54:53 -0000 1.5
+++ dev/sdmmc/sdhcreg.h 28 Apr 2016 10:27:29 -0000
@@ -80,6 +80,10 @@
#define SDHC_CMD_INHIBIT_CMD (1<<0)
#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_4BIT_MODE (1<<1)
#define SDHC_LED_ON (1<<0)
@@ -137,12 +141,25 @@
#define SDHC_EINTR_SIGNAL_EN 0x3a
#define SDHC_EINTR_SIGNAL_MASK 0x01ff /* excluding vendor
signals */
#define SDHC_CMD12_ERROR_STATUS 0x3c
+#define SDHC_HOST_CTL2 0x3e
+#define SDHC_SAMPLING_CLOCK_SEL (1<<7)
+#define SDHC_EXECUTE_TUNING (1<<6)
+#define SDHC_1_8V_SIGNAL_EN (1<<3)
+#define SDHC_UHS_MODE_SELECT_SHIFT 0
+#define SDHC_UHS_MODE_SELECT_MASK 0x7
+#define SDHC_UHS_MODE_SELECT_SDR12 0
+#define SDHC_UHS_MODE_SELECT_SDR25 1
+#define SDHC_UHS_MODE_SELECT_SDR50 2
+#define SDHC_UHS_MODE_SELECT_SDR104 3
+#define SDHC_UHS_MODE_SELECT_DDR50 4
#define SDHC_CAPABILITIES 0x40
#define SDHC_VOLTAGE_SUPP_1_8V (1<<26)
#define SDHC_VOLTAGE_SUPP_3_0V (1<<25)
#define SDHC_VOLTAGE_SUPP_3_3V (1<<24)
-#define SDHC_DMA_SUPPORT (1<<22)
+#define SDHC_SDMA_SUPP (1<<22)
#define SDHC_HIGH_SPEED_SUPP (1<<21)
+#define SDHC_ADMA2_SUPP (1<<19)
+#define SDHC_8BIT_MODE_SUPP (1<<18)
#define SDHC_MAX_BLK_LEN_512 0
#define SDHC_MAX_BLK_LEN_1024 1
#define SDHC_MAX_BLK_LEN_2048 2
@@ -154,7 +171,27 @@
#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_CAPABILITIES_1 0x44
+#define SDHC_CAPABILITIES2 0x44
+#define SDHC_SDR50_SUPP (1<<0)
+#define SDHC_SDR104_SUPP (1<<1)
+#define SDHC_DDR50_SUPP (1<<2)
+#define SDHC_DRIVER_TYPE_A (1<<4)
+#define SDHC_DRIVER_TYPE_C (1<<5)
+#define SDHC_DRIVER_TYPE_D (1<<6)
+#define SDHC_TIMER_COUNT_SHIFT 8
+#define SDHC_TIMER_COUNT_MASK 0xf
+#define SDHC_TUNING_SDR50 (1<<13)
+#define SDHC_RETUNING_MODES_SHIFT 14
+#define SDHC_RETUNING_MODES_MASK 0x3
+#define SDHC_RETUNING_MODE_1 (0 << SDHC_RETUNING_MODES_SHIFT)
+#define SDHC_RETUNING_MODE_2 (1 << SDHC_RETUNING_MODES_SHIFT)
+#define SDHC_RETUNING_MODE_3 (2 << SDHC_RETUNING_MODES_SHIFT)
+#define SDHC_CLOCK_MULTIPLIER_SHIFT 16
+#define SDHC_CLOCK_MULTIPLIER_MASK 0xff
+#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_MAX_CAPABILITIES 0x48
#define SDHC_SLOT_INTR_STATUS 0xfc
#define SDHC_HOST_CTL_VERSION 0xfe
@@ -201,5 +238,19 @@
"\20\11ACMD12\10CL\7DEB\6DCRC\5DT\4CI\3CEB\2CCRC\1CT"
#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;
#endif
Index: dev/sdmmc/sdhcvar.h
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdhcvar.h,v
retrieving revision 1.8
diff -u -p -r1.8 sdhcvar.h
--- dev/sdmmc/sdhcvar.h 30 Mar 2016 09:58:01 -0000 1.8
+++ dev/sdmmc/sdhcvar.h 28 Apr 2016 10:27:29 -0000
@@ -29,6 +29,8 @@ struct sdhc_softc {
int sc_nhosts;
u_int sc_flags;
+ bus_dma_tag_t sc_dmat;
+
int (*sc_card_detect)(struct sdhc_softc *);
};
Index: dev/sdmmc/sdmmc.c
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmc.c,v
retrieving revision 1.39
diff -u -p -r1.39 sdmmc.c
--- dev/sdmmc/sdmmc.c 19 Mar 2016 11:41:56 -0000 1.39
+++ dev/sdmmc/sdmmc.c 28 Apr 2016 10:27:29 -0000
@@ -97,14 +97,25 @@ sdmmc_attach(struct device *parent, stru
{
struct sdmmc_softc *sc = (struct sdmmc_softc *)self;
struct sdmmcbus_attach_args *saa = aux;
+ int error;
printf("\n");
sc->sct = saa->sct;
sc->sch = saa->sch;
+ sc->sc_dmat = saa->dmat;
sc->sc_flags = saa->flags;
sc->sc_caps = saa->caps;
sc->sc_max_xfer = saa->max_xfer;
+
+ if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
+ error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, SDMMC_MAXNSEGS,
+ MAXPHYS, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmap);
+ if (error) {
+ printf("QQQ\n");
+ return;
+ }
+ }
SIMPLEQ_INIT(&sc->sf_head);
TAILQ_INIT(&sc->sc_tskq);
Index: dev/sdmmc/sdmmc_mem.c
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmc_mem.c,v
retrieving revision 1.22
diff -u -p -r1.22 sdmmc_mem.c
--- dev/sdmmc/sdmmc_mem.c 8 Nov 2015 12:10:27 -0000 1.22
+++ dev/sdmmc/sdmmc_mem.c 28 Apr 2016 10:27:29 -0000
@@ -40,16 +40,18 @@ int sdmmc_mem_set_blocklen(struct sdmmc_
int sdmmc_mem_send_cxd_data(struct sdmmc_softc *, int, void *, size_t);
int sdmmc_mem_mmc_switch(struct sdmmc_function *, uint8_t, uint8_t,
uint8_t);
+int sdmmc_mem_signal_voltage(struct sdmmc_softc *, int);
+
int sdmmc_mem_sd_init(struct sdmmc_softc *, struct sdmmc_function *);
int sdmmc_mem_mmc_init(struct sdmmc_softc *, struct sdmmc_function *);
int sdmmc_mem_single_read_block(struct sdmmc_function *, int, u_char *,
size_t);
-int sdmmc_mem_read_block_subr(struct sdmmc_function *, int, u_char *,
- size_t);
+int sdmmc_mem_read_block_subr(struct sdmmc_function *, bus_dmamap_t,
+ int, u_char *, size_t);
int sdmmc_mem_single_write_block(struct sdmmc_function *, int, u_char *,
size_t);
-int sdmmc_mem_write_block_subr(struct sdmmc_function *, int, u_char *,
- size_t);
+int sdmmc_mem_write_block_subr(struct sdmmc_function *, bus_dmamap_t,
+ int, u_char *, size_t);
#ifdef SDMMC_DEBUG
#define DPRINTF(s) printf s
@@ -65,6 +67,8 @@ sdmmc_mem_enable(struct sdmmc_softc *sc)
{
u_int32_t host_ocr;
u_int32_t card_ocr;
+ u_int32_t new_ocr;
+ int error;
rw_assert_wrlock(&sc->sc_lock);
@@ -116,13 +120,79 @@ sdmmc_mem_enable(struct sdmmc_softc *sc)
host_ocr |= SD_OCR_SDHC_CAP;
/* Send the new OCR value until all cards are ready. */
- if (sdmmc_mem_send_op_cond(sc, host_ocr, NULL) != 0) {
+ if (sdmmc_mem_send_op_cond(sc, host_ocr, &new_ocr) != 0) {
DPRINTF(("%s: can't send memory OCR\n", DEVNAME(sc)));
return 1;
}
+
+ if (ISSET(sc->sc_flags, SMF_SD_MODE) && ISSET(new_ocr, MMC_OCR_S18A)) {
+ /*
+ * Card and host support low voltage mode, begin switch
+ * sequence.
+ */
+ struct sdmmc_command cmd;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.c_arg = 0;
+ cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
+ cmd.c_opcode = SD_VOLTAGE_SWITCH;
+ DPRINTF(("%s: switching card to 1.8V\n", SDMMCDEVNAME(sc)));
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error) {
+ DPRINTF(("%s: voltage switch command failed\n",
+ DEVNAME(sc)));
+ return 1;
+ }
+
+ error = sdmmc_mem_signal_voltage(sc, SDMMC_SIGNAL_VOLTAGE_180);
+ if (error)
+ return 1;
+
+ SET(sc->sc_flags, SMF_UHS_MODE);
+ }
+
return 0;
}
+int
+sdmmc_mem_signal_voltage(struct sdmmc_softc *sc, int signal_voltage)
+{
+ int error;
+
+ /*
+ * Stop the clock
+ */
+ error = sdmmc_chip_bus_clock(sc->sct, sc->sch, SDMMC_SDCLK_OFF);
+ if (error)
+ goto out;
+
+ delay(1000);
+
+ /*
+ * Card switch command was successful, update host controller
+ * signal voltage setting.
+ */
+ DPRINTF(("%s: switching host to %s\n", DEVNAME(sc),
+ signal_voltage == SDMMC_SIGNAL_VOLTAGE_180 ? "1.8V" : "3.3V"));
+ error = sdmmc_chip_signal_voltage(sc->sct,
+ sc->sch, signal_voltage);
+ if (error)
+ goto out;
+
+ delay(5000);
+
+ /*
+ * Switch to SDR12 timing
+ */
+ error = sdmmc_chip_bus_clock(sc->sct, sc->sch, 25000);
+ if (error)
+ goto out;
+
+ delay(1000);
+
+out:
+ return error;
+}
+
/*
* Read the CSD and CID from all cards and assign each card a unique
* relative card address (RCA). CMD2 is ignored by SDIO-only cards.
@@ -424,6 +494,7 @@ sdmmc_mem_sd_init(struct sdmmc_softc *sc
int
sdmmc_mem_mmc_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
{
+ int width, value;
int error = 0;
u_int8_t ext_csd[512];
int speed = 0;
@@ -449,6 +520,34 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s
printf("%s: unknown CARD_TYPE 0x%x\n", DEVNAME(sc),
ext_csd[EXT_CSD_CARD_TYPE]);
}
+
+ if (ISSET(sc->sc_caps, SMC_CAPS_8BIT_MODE)) {
+ width = 8;
+ value = EXT_CSD_BUS_WIDTH_8;
+ } else if (ISSET(sc->sc_caps, SMC_CAPS_4BIT_MODE)) {
+ width = 4;
+ value = EXT_CSD_BUS_WIDTH_4;
+ } else {
+ width = 1;
+ value = EXT_CSD_BUS_WIDTH_1;
+ }
+
+ if (width != 1) {
+ error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH, value);
+ if (error == 0)
+ error = sdmmc_chip_bus_width(sc->sct,
+ sc->sch, width);
+ else {
+ DPRINTF(("%s: can't change bus width"
+ " (%d bit)\n", SDMMCDEVNAME(sc), width));
+ return error;
+ }
+
+ /* XXXX: need bus test? (using by CMD14 & CMD19) */
+ delay(10000);
+ }
+
if (!ISSET(sc->sc_caps, SMC_CAPS_MMC_HIGHSPEED))
hs_timing = 0;
@@ -461,10 +560,11 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s
DEVNAME(sc));
return error;
}
+
+ delay(10000);
}
- error =
- sdmmc_chip_bus_clock(sc->sct, sc->sch, speed);
+ error = sdmmc_chip_bus_clock(sc->sct, sc->sch, speed);
if (error != 0) {
printf("%s: can't change bus clock\n", DEVNAME(sc));
return error;
@@ -478,7 +578,7 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s
printf("%s: can't re-read EXT_CSD\n",
DEVNAME(sc));
return error;
}
- if (ext_csd[EXT_CSD_HS_TIMING] != 1) {
+ if (ext_csd[EXT_CSD_HS_TIMING] != hs_timing) {
printf("%s, HS_TIMING set failed\n",
DEVNAME(sc));
return EINVAL;
}
@@ -566,8 +666,8 @@ sdmmc_mem_set_blocklen(struct sdmmc_soft
}
int
-sdmmc_mem_read_block_subr(struct sdmmc_function *sf, int blkno, u_char *data,
- size_t datalen)
+sdmmc_mem_read_block_subr(struct sdmmc_function *sf, bus_dmamap_t dmap,
+ int blkno, u_char *data, size_t datalen)
{
struct sdmmc_softc *sc = sf->sc;
struct sdmmc_command cmd;
@@ -588,6 +688,7 @@ sdmmc_mem_read_block_subr(struct sdmmc_f
else
cmd.c_arg = blkno << 9;
cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1;
+ cmd.c_dmamap = dmap;
error = sdmmc_mmc_command(sc, &cmd);
if (error != 0)
@@ -627,8 +728,8 @@ sdmmc_mem_single_read_block(struct sdmmc
int i;
for (i = 0; i < datalen / sf->csd.sector_size; i++) {
- error = sdmmc_mem_read_block_subr(sf, blkno + i, data + i *
- sf->csd.sector_size, sf->csd.sector_size);
+ error = sdmmc_mem_read_block_subr(sf,NULL, blkno + i,
+ data + i * sf->csd.sector_size, sf->csd.sector_size);
if (error)
break;
}
@@ -647,17 +748,42 @@ sdmmc_mem_read_block(struct sdmmc_functi
if (ISSET(sc->sc_caps, SMC_CAPS_SINGLE_ONLY)) {
error = sdmmc_mem_single_read_block(sf, blkno, data, datalen);
- } else {
- error = sdmmc_mem_read_block_subr(sf, blkno, data, datalen);
+ goto out;
}
+ if (!ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
+ error = sdmmc_mem_read_block_subr(sf, NULL, blkno,
+ data, datalen);
+ goto out;
+ }
+
+ /* DMA transfer */
+ error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, data, datalen,
+ NULL, BUS_DMA_NOWAIT|BUS_DMA_READ);
+ if (error)
+ goto out;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen,
+ BUS_DMASYNC_PREREAD);
+
+ error = sdmmc_mem_read_block_subr(sf, sc->sc_dmap, blkno, data,
+ datalen);
+ if (error)
+ goto unload;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen,
+ BUS_DMASYNC_POSTREAD);
+unload:
+ bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap);
+
+out:
rw_exit(&sc->sc_lock);
return (error);
}
int
-sdmmc_mem_write_block_subr(struct sdmmc_function *sf, int blkno, u_char *data,
- size_t datalen)
+sdmmc_mem_write_block_subr(struct sdmmc_function *sf, bus_dmamap_t dmap,
+ int blkno, u_char *data, size_t datalen)
{
struct sdmmc_softc *sc = sf->sc;
struct sdmmc_command cmd;
@@ -677,6 +803,7 @@ sdmmc_mem_write_block_subr(struct sdmmc_
else
cmd.c_arg = blkno << 9;
cmd.c_flags = SCF_CMD_ADTC | SCF_RSP_R1;
+ cmd.c_dmamap = dmap;
error = sdmmc_mmc_command(sc, &cmd);
if (error != 0)
@@ -715,8 +842,8 @@ sdmmc_mem_single_write_block(struct sdmm
int i;
for (i = 0; i < datalen / sf->csd.sector_size; i++) {
- error = sdmmc_mem_write_block_subr(sf, blkno + i, data + i *
- sf->csd.sector_size, sf->csd.sector_size);
+ error = sdmmc_mem_write_block_subr(sf, NULL, blkno + i,
+ data + i * sf->csd.sector_size, sf->csd.sector_size);
if (error)
break;
}
@@ -735,10 +862,35 @@ sdmmc_mem_write_block(struct sdmmc_funct
if (ISSET(sc->sc_caps, SMC_CAPS_SINGLE_ONLY)) {
error = sdmmc_mem_single_write_block(sf, blkno, data, datalen);
- } else {
- error = sdmmc_mem_write_block_subr(sf, blkno, data, datalen);
+ goto out;
}
+ if (!ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
+ error = sdmmc_mem_write_block_subr(sf, NULL, blkno,
+ data, datalen);
+ goto out;
+ }
+
+ /* DMA transfer */
+ error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, data, datalen,
+ NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE);
+ if (error)
+ goto out;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen,
+ BUS_DMASYNC_PREWRITE);
+
+ error = sdmmc_mem_write_block_subr(sf, sc->sc_dmap, blkno, data,
+ datalen);
+ if (error)
+ goto unload;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen,
+ BUS_DMASYNC_POSTWRITE);
+unload:
+ bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap);
+
+out:
rw_exit(&sc->sc_lock);
return (error);
}
Index: dev/sdmmc/sdmmcchip.h
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmcchip.h,v
retrieving revision 1.5
diff -u -p -r1.5 sdmmcchip.h
--- dev/sdmmc/sdmmcchip.h 12 Sep 2013 11:54:04 -0000 1.5
+++ dev/sdmmc/sdmmcchip.h 28 Apr 2016 10:27:29 -0000
@@ -19,6 +19,8 @@
#ifndef _SDMMC_CHIP_H_
#define _SDMMC_CHIP_H_
+#include <machine/bus.h>
+
struct sdmmc_command;
typedef struct sdmmc_chip_functions *sdmmc_chipset_tag_t;
@@ -32,15 +34,21 @@ struct sdmmc_chip_functions {
int (*host_maxblklen)(sdmmc_chipset_handle_t);
/* card detection */
int (*card_detect)(sdmmc_chipset_handle_t);
- /* bus power and clock frequency */
+ /* bus power, clock frequency and width */
int (*bus_power)(sdmmc_chipset_handle_t, u_int32_t);
int (*bus_clock)(sdmmc_chipset_handle_t, int);
+ int (*bus_width)(sdmmc_chipset_handle_t, int);
/* command execution */
void (*exec_command)(sdmmc_chipset_handle_t,
struct sdmmc_command *);
/* card interrupt */
void (*card_intr_mask)(sdmmc_chipset_handle_t, int);
void (*card_intr_ack)(sdmmc_chipset_handle_t);
+
+ /* UHS functions */
+ int (*signal_voltage)(sdmmc_chipset_handle_t, int);
+ int (*bus_clock_ddr)(sdmmc_chipset_handle_t, int, bool);
+ int (*execute_tuning)(sdmmc_chipset_handle_t, int);
};
/* host controller reset */
@@ -59,6 +67,8 @@ struct sdmmc_chip_functions {
((tag)->bus_power((handle), (ocr)))
#define sdmmc_chip_bus_clock(tag, handle, freq)
\
((tag)->bus_clock((handle), (freq)))
+#define sdmmc_chip_bus_width(tag, handle, width) \
+ ((tag)->bus_width((handle), (width)))
/* command execution */
#define sdmmc_chip_exec_command(tag, handle, cmdp) \
((tag)->exec_command((handle), (cmdp)))
@@ -67,16 +77,26 @@ struct sdmmc_chip_functions {
((tag)->card_intr_mask((handle), (enable)))
#define sdmmc_chip_card_intr_ack(tag, handle) \
((tag)->card_intr_ack((handle)))
+/* UHS functions */
+#define sdmmc_chip_signal_voltage(tag, handle, voltage)
\
+ ((tag)->signal_voltage((handle), (voltage)))
+#define sdmmc_chip_execute_tuning(tag, handle, timing) \
+ ((tag)->execute_tuning ? (tag)->execute_tuning((handle), (timing)) :
EINVAL)
/* clock frequencies for sdmmc_chip_bus_clock() */
#define SDMMC_SDCLK_OFF 0
#define SDMMC_SDCLK_400KHZ 400
#define SDMMC_SDCLK_25MHZ 25000
+/* voltage levels for sdmmc_chip_signal_voltage() */
+#define SDMMC_SIGNAL_VOLTAGE_330 0
+#define SDMMC_SIGNAL_VOLTAGE_180 1
+
struct sdmmcbus_attach_args {
const char *saa_busname;
sdmmc_chipset_tag_t sct;
sdmmc_chipset_handle_t sch;
+ bus_dma_tag_t dmat;
int flags;
int caps;
long max_xfer;
Index: dev/sdmmc/sdmmcreg.h
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmcreg.h,v
retrieving revision 1.8
diff -u -p -r1.8 sdmmcreg.h
--- dev/sdmmc/sdmmcreg.h 10 Jan 2016 14:11:43 -0000 1.8
+++ dev/sdmmc/sdmmcreg.h 28 Apr 2016 10:27:29 -0000
@@ -41,6 +41,7 @@
/* SD commands */ /* response type */
#define SD_SEND_RELATIVE_ADDR 3 /* R6 */
#define SD_SEND_IF_COND 8 /* R7 */
+#define SD_VOLTAGE_SWITCH 11 /* R1 */
/* SD application commands */ /* response type */
#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
@@ -48,9 +49,11 @@
/* OCR bits */
#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */
+#define MMC_OCR_HCS (1<<30) /* SD only */
#define MMC_OCR_ACCESS_MODE_MASK 0x60000000 /* bits 30:29 */
#define MMC_OCR_SECTOR_MODE (1<<30)
#define MMC_OCR_BYTE_MODE (1<<29)
+#define MMC_OCR_S18A (1<<24)
#define MMC_OCR_3_5V_3_6V (1<<23)
#define MMC_OCR_3_4V_3_5V (1<<22)
#define MMC_OCR_3_3V_3_4V (1<<21)
Index: dev/sdmmc/sdmmcvar.h
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmcvar.h,v
retrieving revision 1.22
diff -u -p -r1.22 sdmmcvar.h
--- dev/sdmmc/sdmmcvar.h 12 Sep 2013 11:54:04 -0000 1.22
+++ dev/sdmmc/sdmmcvar.h 28 Apr 2016 10:27:29 -0000
@@ -22,6 +22,8 @@
#include <sys/queue.h>
#include <sys/rwlock.h>
+#include <machine/bus.h>
+
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
@@ -72,6 +74,7 @@ struct sdmmc_command {
u_int16_t c_opcode; /* SD or MMC command index */
u_int32_t c_arg; /* SD/MMC command argument */
sdmmc_response c_resp; /* response buffer */
+ bus_dmamap_t c_dmamap;
void *c_data; /* buffer to send or read into */
int c_datalen; /* length of data buffer */
int c_blklen; /* block length */
@@ -156,6 +159,11 @@ struct sdmmc_softc {
#define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
sdmmc_chipset_tag_t sct; /* host controller chipset tag */
sdmmc_chipset_handle_t sch; /* host controller chipset handle */
+
+ bus_dma_tag_t sc_dmat;
+ bus_dmamap_t sc_dmap;
+#define SDMMC_MAXNSEGS ((MAXPHYS / PAGE_SIZE) + 1)
+
int sc_flags;
#define SMF_SD_MODE 0x0001 /* host in SD mode (MMC otherwise) */
#define SMF_IO_MODE 0x0002 /* host in I/O mode (SD mode only) */
@@ -164,6 +172,7 @@ struct sdmmc_softc {
#define SMF_CARD_ATTACHED 0x0020 /* card driver(s) attached */
#define SMF_STOP_AFTER_MULTIPLE 0x0040 /* send a stop after a multiple
cmd */
#define SMF_CONFIG_PENDING 0x0080 /* config_pending_incr() called */
+#define SMF_UHS_MODE 0x1000 /* host in UHS mode */
uint32_t sc_caps; /* host capability */
#define SMC_CAPS_AUTO_STOP 0x0001 /* send CMD12 automagically by host */
Index: dev/acpi/sdhc_acpi.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/sdhc_acpi.c,v
retrieving revision 1.7
diff -u -p -r1.7 sdhc_acpi.c
--- dev/acpi/sdhc_acpi.c 26 Apr 2016 19:10:10 -0000 1.7
+++ dev/acpi/sdhc_acpi.c 28 Apr 2016 10:27:29 -0000
@@ -29,6 +29,8 @@
#include <dev/sdmmc/sdhcvar.h>
#include <dev/sdmmc/sdmmcvar.h>
+extern struct bus_dma_tag pci_bus_dma_tag;
+
struct sdhc_acpi_softc {
struct sdhc_softc sc;
struct acpi_softc *sc_acpi;
@@ -134,7 +136,8 @@ sdhc_acpi_attach(struct device *parent,
printf("\n");
sc->sc.sc_host = &sc->sc_host;
- sdhc_host_found(&sc->sc, sc->sc_memt, sc->sc_memh, sc->sc_size, 0, 0);
+ sc->sc.sc_dmat = &pci_bus_dma_tag;
+ sdhc_host_found(&sc->sc, sc->sc_memt, sc->sc_memh, sc->sc_size, 1, 0);
}
int
Index: dev/ic/rtsx.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/rtsx.c,v
retrieving revision 1.12
diff -u -p -r1.12 rtsx.c
--- dev/ic/rtsx.c 28 Apr 2015 07:55:13 -0000 1.12
+++ dev/ic/rtsx.c 28 Apr 2016 10:27:29 -0000
@@ -146,6 +146,7 @@ struct sdmmc_chip_functions rtsx_functio
/* bus power and clock frequency */
rtsx_bus_power,
rtsx_bus_clock,
+ NULL,
/* command execution */
rtsx_exec_command,
/* card interrupt */
Index: dev/pci/sdhc_pci.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/sdhc_pci.c,v
retrieving revision 1.19
diff -u -p -r1.19 sdhc_pci.c
--- dev/pci/sdhc_pci.c 24 Nov 2015 19:38:01 -0000 1.19
+++ dev/pci/sdhc_pci.c 28 Apr 2016 10:27:30 -0000
@@ -149,6 +149,7 @@ sdhc_pci_attach(struct device *parent, s
/* Enable use of DMA if supported by the interface. */
usedma = PCI_INTERFACE(pa->pa_class) == SDHC_PCI_INTERFACE_DMA;
+ sc->sc.sc_dmat = pa->pa_dmat;
/*
* Map and attach all hosts supported by the host controller.
Index: arch/armv7/exynos/exesdhc.c
===================================================================
RCS file: /cvs/src/sys/arch/armv7/exynos/exesdhc.c,v
retrieving revision 1.5
diff -u -p -r1.5 exesdhc.c
--- arch/armv7/exynos/exesdhc.c 24 Apr 2016 00:57:23 -0000 1.5
+++ arch/armv7/exynos/exesdhc.c 28 Apr 2016 10:27:30 -0000
@@ -228,6 +228,7 @@ struct sdmmc_chip_functions exesdhc_func
/* bus power and clock frequency */
exesdhc_bus_power,
exesdhc_bus_clock,
+ NULL,
/* command execution */
exesdhc_exec_command,
/* card interrupt */
Index: arch/armv7/imx/imxesdhc.c
===================================================================
RCS file: /cvs/src/sys/arch/armv7/imx/imxesdhc.c,v
retrieving revision 1.13
diff -u -p -r1.13 imxesdhc.c
--- arch/armv7/imx/imxesdhc.c 10 Jan 2016 14:11:43 -0000 1.13
+++ arch/armv7/imx/imxesdhc.c 28 Apr 2016 10:27:30 -0000
@@ -226,6 +226,7 @@ struct sdmmc_chip_functions imxesdhc_fun
/* bus power and clock frequency */
imxesdhc_bus_power,
imxesdhc_bus_clock,
+ NULL,
/* command execution */
imxesdhc_exec_command,
/* card interrupt */