diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2016-05-04 09:30:07 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2016-05-04 09:30:07 +0000 |
commit | 1146b7d4f08ca95d9ead578690554f1dd9397cc4 (patch) | |
tree | 953f14916930bfba111f4499bacc2be208363dd4 /sys/dev/sdmmc | |
parent | 7799261cca90c31177d000061abd733783efe345 (diff) |
Add high-speed support for SD cards. In theory this should double the
transfer rates to and from the card. In practice the improvement will be
smaller, but I am seeing serious improvement in the read speeds.
Diffstat (limited to 'sys/dev/sdmmc')
-rw-r--r-- | sys/dev/sdmmc/sdmmc.c | 7 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_io.c | 8 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_mem.c | 116 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcreg.h | 23 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcvar.h | 3 |
5 files changed, 142 insertions, 15 deletions
diff --git a/sys/dev/sdmmc/sdmmc.c b/sys/dev/sdmmc/sdmmc.c index c97669117e7..e67338aebaa 100644 --- a/sys/dev/sdmmc/sdmmc.c +++ b/sys/dev/sdmmc/sdmmc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc.c,v 1.42 2016/05/01 22:07:42 kettenis Exp $ */ +/* $OpenBSD: sdmmc.c,v 1.43 2016/05/04 09:30:06 kettenis Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -438,11 +438,6 @@ sdmmc_enable(struct sdmmc_softc *sc) (error = sdmmc_mem_enable(sc)) != 0) goto err; - /* XXX respect host and card capabilities */ - if (ISSET(sc->sc_flags, SMF_SD_MODE)) - (void)sdmmc_chip_bus_clock(sc->sct, sc->sch, - SDMMC_SDCLK_25MHZ); - err: if (error != 0) sdmmc_disable(sc); diff --git a/sys/dev/sdmmc/sdmmc_io.c b/sys/dev/sdmmc/sdmmc_io.c index 922d6fa7537..3c9372aa965 100644 --- a/sys/dev/sdmmc/sdmmc_io.c +++ b/sys/dev/sdmmc/sdmmc_io.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc_io.c,v 1.23 2016/04/23 14:15:59 kettenis Exp $ */ +/* $OpenBSD: sdmmc_io.c,v 1.24 2016/05/04 09:30:06 kettenis Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -182,6 +182,12 @@ sdmmc_io_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) if (sdmmc_verbose) sdmmc_print_cis(sf); + if (sf->number == 0) { + /* XXX respect host and card capabilities */ + (void)sdmmc_chip_bus_clock(sc->sct, sc->sch, + SDMMC_SDCLK_25MHZ); + } + return 0; } diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c index bed588b6fd5..8b5bb829fa1 100644 --- a/sys/dev/sdmmc/sdmmc_mem.c +++ b/sys/dev/sdmmc/sdmmc_mem.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc_mem.c,v 1.26 2016/05/01 18:29:44 kettenis Exp $ */ +/* $OpenBSD: sdmmc_mem.c,v 1.27 2016/05/04 09:30:06 kettenis Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -28,6 +28,10 @@ #include <dev/sdmmc/sdmmcreg.h> #include <dev/sdmmc/sdmmcvar.h> +typedef struct { uint32_t _bits[512/32]; } __packed __aligned(4) sdmmc_bitfield512_t; + +void sdmmc_be512_to_bitfield512(sdmmc_bitfield512_t *); + int sdmmc_decode_csd(struct sdmmc_softc *, sdmmc_response, struct sdmmc_function *); int sdmmc_decode_cid(struct sdmmc_softc *, sdmmc_response, @@ -269,7 +273,7 @@ sdmmc_decode_csd(struct sdmmc_softc *sc, sdmmc_response resp, return 1; break; } - + csd->ccc = SD_CSD_CCC(resp); } else { csd->csdver = MMC_CSD_CSDVER(resp); if (csd->csdver == MMC_CSD_CSDVER_1_0 || @@ -364,9 +368,8 @@ sdmmc_mem_send_scr(struct sdmmc_softc *sc, uint32_t *scr) cmd.c_opcode = SD_APP_SEND_SCR; error = sdmmc_app_command(sc, &cmd); - if (error == 0) { + if (error == 0) memcpy(scr, ptr, datalen); - } out: if (ptr != NULL) @@ -475,6 +478,53 @@ sdmmc_set_bus_width(struct sdmmc_function *sf, int width) } int +sdmmc_mem_sd_switch(struct sdmmc_function *sf, int mode, int group, + int function, sdmmc_bitfield512_t *status) +{ + struct sdmmc_softc *sc = sf->sc; + struct sdmmc_command cmd; + void *ptr = NULL; + int gsft, error = 0; + const int statlen = 64; + + if (sf->scr.sd_spec >= SCR_SD_SPEC_VER_1_10 && + !ISSET(sf->csd.ccc, SD_CSD_CCC_SWITCH)) + return EINVAL; + + if (group <= 0 || group > 6 || + function < 0 || function > 15) + return EINVAL; + + gsft = (group - 1) << 2; + + ptr = malloc(statlen, M_DEVBUF, M_NOWAIT | M_ZERO); + if (ptr == NULL) + goto out; + + memset(&cmd, 0, sizeof(cmd)); + cmd.c_data = ptr; + cmd.c_datalen = statlen; + cmd.c_blklen = statlen; + cmd.c_opcode = SD_SEND_SWITCH_FUNC; + cmd.c_arg = + (!!mode << 31) | (function << gsft) | (0x00ffffff & ~(0xf << gsft)); + cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1; + + error = sdmmc_mmc_command(sc, &cmd); + if (error == 0) + memcpy(status, ptr, statlen); + +out: + if (ptr != NULL) + free(ptr, M_DEVBUF, statlen); + + if (error == 0) + sdmmc_be512_to_bitfield512(status); + + return error; +} + +int sdmmc_mem_mmc_switch(struct sdmmc_function *sf, uint8_t set, uint8_t index, uint8_t value) { @@ -512,11 +562,26 @@ sdmmc_mem_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) return error; } +/* make 512-bit BE quantity __bitfield()-compatible */ +void +sdmmc_be512_to_bitfield512(sdmmc_bitfield512_t *buf) { + size_t i; + uint32_t tmp0, tmp1; + const size_t bitswords = nitems(buf->_bits); + for (i = 0; i < bitswords/2; i++) { + tmp0 = buf->_bits[i]; + tmp1 = buf->_bits[bitswords - 1 - i]; + buf->_bits[i] = be32toh(tmp1); + buf->_bits[bitswords - 1 - i] = be32toh(tmp0); + } +} + int sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) { + int support_func, best_func, bus_clock, error; + sdmmc_bitfield512_t status; /* Switch Function Status */ uint32_t raw_scr[2]; - int error; error = sdmmc_mem_send_scr(sc, raw_scr); if (error) { @@ -537,6 +602,47 @@ sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) } } + best_func = 0; + bus_clock = 25000; + if (sf->scr.sd_spec >= SCR_SD_SPEC_VER_1_10 && + ISSET(sf->csd.ccc, SD_CSD_CCC_SWITCH)) { + DPRINTF(("%s: switch func mode 0\n", DEVNAME(sc))); + error = sdmmc_mem_sd_switch(sf, 0, 1, 0, &status); + if (error) { + printf("%s: switch func mode 0 failed\n", DEVNAME(sc)); + return error; + } + + support_func = SFUNC_STATUS_GROUP(&status, 1); + + if (support_func & (1 << SD_ACCESS_MODE_SDR25)) + best_func = 1; + + if (best_func != 0) { + DPRINTF(("%s: switch func mode 1(func=%d)\n", + DEVNAME(sc), best_func)); + error = + sdmmc_mem_sd_switch(sf, 1, 1, best_func, &status); + if (error) { + printf("%s: switch func mode 1 failed:" + " group 1 function %d(0x%2x)\n", + DEVNAME(sc), best_func, support_func); + return error; + } + bus_clock = 50000; + + /* Wait 400KHz x 8 clock (2.5us * 8 + slop) */ + delay(25); + } + } + + /* change bus clock */ + error = sdmmc_chip_bus_clock(sc->sct, sc->sch, bus_clock); + if (error) { + printf("%s: can't change bus clock\n", DEVNAME(sc)); + return error; + } + return 0; } diff --git a/sys/dev/sdmmc/sdmmcreg.h b/sys/dev/sdmmc/sdmmcreg.h index 7036217cc69..c2751488b9e 100644 --- a/sys/dev/sdmmc/sdmmcreg.h +++ b/sys/dev/sdmmc/sdmmcreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmcreg.h,v 1.9 2016/05/01 16:04:39 kettenis Exp $ */ +/* $OpenBSD: sdmmcreg.h,v 1.10 2016/05/04 09:30:06 kettenis Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -40,6 +40,7 @@ /* SD commands */ /* response type */ #define SD_SEND_RELATIVE_ADDR 3 /* R6 */ +#define SD_SEND_SWITCH_FUNC 6 /* R1 */ #define SD_SEND_IF_COND 8 /* R7 */ /* SD application commands */ /* response type */ @@ -187,7 +188,15 @@ #define SD_CSD_SPEED_25_MHZ 0x32 #define SD_CSD_SPEED_50_MHZ 0x5a #define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12) -#define SD_CSD_CCC_ALL 0x5f5 +#define SD_CSD_CCC_BASIC (1 << 0) /* basic */ +#define SD_CSD_CCC_BR (1 << 2) /* block read */ +#define SD_CSD_CCC_BW (1 << 4) /* block write */ +#define SD_CSD_CCC_ERACE (1 << 5) /* erase */ +#define SD_CSD_CCC_WP (1 << 6) /* write protection */ +#define SD_CSD_CCC_LC (1 << 7) /* lock card */ +#define SD_CSD_CCC_AS (1 << 8) /*application specific*/ +#define SD_CSD_CCC_IOM (1 << 9) /* I/O mode */ +#define SD_CSD_CCC_SWITCH (1 << 10) /* switch */ #define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) #define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1) #define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1) @@ -260,6 +269,16 @@ #define SCR_CMD_SUPPORT_CMD20(scr) MMC_RSP_BITS((scr), 32, 1) #define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32) +/* Status of Switch Function */ +#define SFUNC_STATUS_GROUP(status, group) \ + (__bitfield((uint32_t *)(status), 400 + (group - 1) * 16, 16)) + +#define SD_ACCESS_MODE_SDR12 0 +#define SD_ACCESS_MODE_SDR25 1 +#define SD_ACCESS_MODE_SDR50 2 +#define SD_ACCESS_MODE_SDR104 3 +#define SD_ACCESS_MODE_DDR50 4 + /* Might be slow, but it should work on big and little endian systems. */ #define MMC_RSP_BITS(resp, start, len) __bitfield((resp), (start)-8, (len)) static __inline int diff --git a/sys/dev/sdmmc/sdmmcvar.h b/sys/dev/sdmmc/sdmmcvar.h index e2867aa23f2..7a66dd097c5 100644 --- a/sys/dev/sdmmc/sdmmcvar.h +++ b/sys/dev/sdmmc/sdmmcvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmcvar.h,v 1.24 2016/05/01 16:04:39 kettenis Exp $ */ +/* $OpenBSD: sdmmcvar.h,v 1.25 2016/05/04 09:30:06 kettenis Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -36,6 +36,7 @@ struct sdmmc_csd { int capacity; /* total number of sectors */ int sector_size; /* sector size in bytes */ int read_bl_len; /* block length for reads */ + int ccc; /* Card Command Class for SD */ /* ... */ }; |