summaryrefslogtreecommitdiff
path: root/sys/dev/sdmmc
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2016-05-04 09:30:07 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2016-05-04 09:30:07 +0000
commit1146b7d4f08ca95d9ead578690554f1dd9397cc4 (patch)
tree953f14916930bfba111f4499bacc2be208363dd4 /sys/dev/sdmmc
parent7799261cca90c31177d000061abd733783efe345 (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.c7
-rw-r--r--sys/dev/sdmmc/sdmmc_io.c8
-rw-r--r--sys/dev/sdmmc/sdmmc_mem.c116
-rw-r--r--sys/dev/sdmmc/sdmmcreg.h23
-rw-r--r--sys/dev/sdmmc/sdmmcvar.h3
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 */
/* ... */
};