summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkettenis <kettenis@openbsd.org>2020-08-24 15:06:10 +0000
committerkettenis <kettenis@openbsd.org>2020-08-24 15:06:10 +0000
commitd3ec1c866e9430d7ce7695f3393de4712072e4e8 (patch)
treee99807c703c6c33d74677bac80d93be755e0a783
parentAdd some USB HID devices. (diff)
downloadwireguard-openbsd-d3ec1c866e9430d7ce7695f3393de4712072e4e8.tar.xz
wireguard-openbsd-d3ec1c866e9430d7ce7695f3393de4712072e4e8.zip
Implement UHS-I support in the sdmmc(4) midlayer.
ok mglocker@
-rw-r--r--sys/dev/ic/rtsxreg.h5
-rw-r--r--sys/dev/sdmmc/sdmmc.c6
-rw-r--r--sys/dev/sdmmc/sdmmc_mem.c156
-rw-r--r--sys/dev/sdmmc/sdmmcreg.h12
4 files changed, 158 insertions, 21 deletions
diff --git a/sys/dev/ic/rtsxreg.h b/sys/dev/ic/rtsxreg.h
index 26744a3f3a8..b2f0576f87b 100644
--- a/sys/dev/ic/rtsxreg.h
+++ b/sys/dev/ic/rtsxreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: rtsxreg.h,v 1.4 2017/09/06 13:07:38 jcs Exp $ */
+/* $OpenBSD: rtsxreg.h,v 1.5 2020/08/24 15:06:10 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -387,8 +387,7 @@
#define RTSX_PPBUF_SIZE 256
#define RTSX_SUPPORT_VOLTAGE (MMC_OCR_3_3V_3_4V|MMC_OCR_3_2V_3_3V|\
- MMC_OCR_3_1V_3_2V|MMC_OCR_3_0V_3_1V|\
- SD_OCR_SDHC_CAP)
+ MMC_OCR_3_1V_3_2V|MMC_OCR_3_0V_3_1V)
#define RTSX_CFG_PCI 0x1C
#define RTSX_CFG_ASIC 0x10
diff --git a/sys/dev/sdmmc/sdmmc.c b/sys/dev/sdmmc/sdmmc.c
index ac111711396..3923f4ac4a9 100644
--- a/sys/dev/sdmmc/sdmmc.c
+++ b/sys/dev/sdmmc/sdmmc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmc.c,v 1.57 2020/08/15 13:21:02 kettenis Exp $ */
+/* $OpenBSD: sdmmc.c,v 1.58 2020/08/24 15:06:10 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -111,6 +111,10 @@ sdmmc_attach(struct device *parent, struct device *self, void *aux)
printf(": 1-bit");
if (ISSET(saa->caps, SMC_CAPS_SD_HIGHSPEED))
printf(", sd high-speed");
+ if (ISSET(saa->caps, SMC_CAPS_UHS_SDR50))
+ printf(", sdr50");
+ if (ISSET(saa->caps, SMC_CAPS_UHS_SDR104))
+ printf(", sdr104");
if (ISSET(saa->caps, SMC_CAPS_MMC_HIGHSPEED))
printf(", mmc high-speed");
if (ISSET(saa->caps, SMC_CAPS_MMC_DDR52))
diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c
index fcbc2349c51..59bcb1b4a11 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.34 2020/08/14 14:49:04 kettenis Exp $ */
+/* $OpenBSD: sdmmc_mem.c,v 1.35 2020/08/24 15:06:10 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -52,6 +52,7 @@ int sdmmc_mem_decode_scr(struct sdmmc_softc *, uint32_t *,
int sdmmc_mem_send_cxd_data(struct sdmmc_softc *, int, void *, size_t);
int sdmmc_mem_set_bus_width(struct sdmmc_function *, int);
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 *);
@@ -104,12 +105,16 @@ const int sdmmc_mmc_timings[] = {
int
sdmmc_mem_enable(struct sdmmc_softc *sc)
{
- u_int32_t host_ocr;
- u_int32_t card_ocr;
+ uint32_t host_ocr;
+ uint32_t card_ocr;
+ uint32_t new_ocr;
+ uint32_t ocr = 0;
+ int error;
rw_assert_wrlock(&sc->sc_lock);
/* Set host mode to SD "combo" card or SD memory-only. */
+ CLR(sc->sc_flags, SMF_UHS_MODE);
SET(sc->sc_flags, SMF_SD_MODE|SMF_MEM_MODE);
/* Reset memory (*must* do that before CMD55 or CMD1). */
@@ -153,14 +158,86 @@ sdmmc_mem_enable(struct sdmmc_softc *sc)
host_ocr &= card_ocr; /* only allow the common voltages */
- if (sdmmc_send_if_cond(sc, card_ocr) == 0)
- host_ocr |= SD_OCR_SDHC_CAP;
+ if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
+ if (sdmmc_send_if_cond(sc, card_ocr) == 0)
+ SET(ocr, MMC_OCR_HCS);
+
+ if (sdmmc_chip_host_ocr(sc->sct, sc->sch) & MMC_OCR_S18A)
+ SET(ocr, MMC_OCR_S18A);
+ }
+ host_ocr |= ocr;
/* 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", DEVNAME(sc)));
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error) {
+ DPRINTF(("%s: voltage switch command failed\n",
+ SDMMCDEVNAME(sc)));
+ return error;
+ }
+
+ error = sdmmc_mem_signal_voltage(sc, SDMMC_SIGNAL_VOLTAGE_180);
+ if (error)
+ return error;
+
+ 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, 0, SDMMC_TIMING_LEGACY);
+ if (error)
+ return error;
+
+ delay(1000);
+
+ /*
+ * Card switch command was successful, update host controller
+ * signal voltage setting.
+ */
+ DPRINTF(("%s: switching host to %s\n", SDMMCDEVNAME(sc),
+ signal_voltage == SDMMC_SIGNAL_VOLTAGE_180 ? "1.8V" : "3.3V"));
+ error = sdmmc_chip_signal_voltage(sc->sct, sc->sch, signal_voltage);
+ if (error)
+ return error;
+
+ delay(5000);
+
+ /*
+ * Switch to SDR12 timing
+ */
+ error = sdmmc_chip_bus_clock(sc->sct, sc->sch, SDMMC_SDCLK_25MHZ,
+ SDMMC_TIMING_LEGACY);
+ if (error)
+ return error;
+
+ delay(1000);
+
return 0;
}
@@ -609,6 +686,30 @@ sdmmc_be512_to_bitfield512(sdmmc_bitfield512_t *buf) {
}
int
+sdmmc_mem_select_transfer_mode(struct sdmmc_softc *sc, int support_func)
+{
+ if (ISSET(sc->sc_flags, SMF_UHS_MODE)) {
+ if (ISSET(sc->sc_caps, SMC_CAPS_UHS_SDR104) &&
+ ISSET(support_func, 1 << SD_ACCESS_MODE_SDR104)) {
+ return SD_ACCESS_MODE_SDR104;
+ }
+ if (ISSET(sc->sc_caps, SMC_CAPS_UHS_DDR50) &&
+ ISSET(support_func, 1 << SD_ACCESS_MODE_DDR50)) {
+ return SD_ACCESS_MODE_DDR50;
+ }
+ if (ISSET(sc->sc_caps, SMC_CAPS_UHS_SDR50) &&
+ ISSET(support_func, 1 << SD_ACCESS_MODE_SDR50)) {
+ return SD_ACCESS_MODE_SDR50;
+ }
+ }
+ if (ISSET(sc->sc_caps, SMC_CAPS_SD_HIGHSPEED) &&
+ ISSET(support_func, 1 << SD_ACCESS_MODE_SDR25)) {
+ return SD_ACCESS_MODE_SDR25;
+ }
+ return SD_ACCESS_MODE_SDR12;
+}
+
+int
sdmmc_mem_execute_tuning(struct sdmmc_softc *sc, struct sdmmc_function *sf)
{
int timing = -1;
@@ -646,7 +747,7 @@ sdmmc_mem_execute_tuning(struct sdmmc_softc *sc, struct sdmmc_function *sf)
int
sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
{
- int support_func, best_func, error;
+ int support_func, best_func, error, i;
sdmmc_bitfield512_t status; /* Switch Function Status */
uint32_t raw_scr[2];
@@ -695,8 +796,32 @@ sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
support_func = SFUNC_STATUS_GROUP(&status, 1);
- if (support_func & (1 << SD_ACCESS_MODE_SDR25))
- best_func = 1;
+ if (!ISSET(sc->sc_flags, SMF_UHS_MODE) &&
+ (ISSET(support_func, 1 << SD_ACCESS_MODE_SDR50) ||
+ ISSET(support_func, 1 << SD_ACCESS_MODE_DDR50) ||
+ ISSET(support_func, 1 << SD_ACCESS_MODE_SDR104))) {
+ /* XXX UHS-I card started in 1.8V mode, switch now */
+ error = sdmmc_mem_signal_voltage(sc,
+ SDMMC_SIGNAL_VOLTAGE_180);
+ if (error) {
+ printf("%s: failed to recover UHS card\n", DEVNAME(sc));
+ return error;
+ }
+ SET(sc->sc_flags, SMF_UHS_MODE);
+ }
+
+ for (i = 0; i < nitems(switch_group0_functions); i++) {
+ if (!(support_func & (1 << i)))
+ continue;
+ DPRINTF(("%s: card supports mode %s\n",
+ SDMMCDEVNAME(sc),
+ switch_group0_functions[i].name));
+ }
+
+ best_func = sdmmc_mem_select_transfer_mode(sc, support_func);
+
+ DPRINTF(("%s: using mode %s\n", SDMMCDEVNAME(sc),
+ switch_group0_functions[best_func].name));
}
if (best_func != 0) {
@@ -716,13 +841,20 @@ sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
/* Wait 400KHz x 8 clock (2.5us * 8 + slop) */
delay(25);
- /* High Speed mode, Frequency up to 50MHz. */
+ /* change bus clock */
error = sdmmc_chip_bus_clock(sc->sct, sc->sch,
- SDMMC_SDCLK_50MHZ, SDMMC_TIMING_HIGHSPEED);
+ sf->csd.tran_speed, SDMMC_TIMING_HIGHSPEED);
if (error) {
printf("%s: can't change bus clock\n", DEVNAME(sc));
return error;
}
+
+ /* execute tuning (UHS) */
+ error = sdmmc_mem_execute_tuning(sc, sf);
+ if (error) {
+ printf("%s: can't execute SD tuning\n", DEVNAME(sc));
+ return error;
+ }
}
return 0;
@@ -937,7 +1069,7 @@ sdmmc_mem_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr,
error = sdmmc_app_command(sc, &cmd);
} else {
cmd.c_arg &= ~MMC_OCR_ACCESS_MODE_MASK;
- cmd.c_arg |= MMC_OCR_SECTOR_MODE;
+ cmd.c_arg |= MMC_OCR_ACCESS_MODE_SECTOR;
cmd.c_opcode = MMC_SEND_OP_COND;
error = sdmmc_mmc_command(sc, &cmd);
}
diff --git a/sys/dev/sdmmc/sdmmcreg.h b/sys/dev/sdmmc/sdmmcreg.h
index a9f12fe3af6..245dbe84907 100644
--- a/sys/dev/sdmmc/sdmmcreg.h
+++ b/sys/dev/sdmmc/sdmmcreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcreg.h,v 1.12 2020/08/14 14:49:04 kettenis Exp $ */
+/* $OpenBSD: sdmmcreg.h,v 1.13 2020/08/24 15:06:10 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -44,6 +44,7 @@
#define SD_SEND_RELATIVE_ADDR 3 /* R6 */
#define SD_SEND_SWITCH_FUNC 6 /* R1 */
#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 */
@@ -52,9 +53,11 @@
/* OCR bits */
#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */
-#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_HCS (1<<30) /* SD only */
+#define MMC_OCR_ACCESS_MODE_MASK (3<<29) /* MMC only */
+#define MMC_OCR_ACCESS_MODE_BYTE (0<<29) /* MMC only */
+#define MMC_OCR_ACCESS_MODE_SECTOR (2<<29) /* MMC only */
+#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)
@@ -73,7 +76,6 @@
#define MMC_OCR_2_0V_2_1V (1<<8)
#define MMC_OCR_1_65V_1_95V (1<<7)
-#define SD_OCR_SDHC_CAP (1<<30)
#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */
/* R1 response type bits */