diff options
author | 2020-08-24 15:06:10 +0000 | |
---|---|---|
committer | 2020-08-24 15:06:10 +0000 | |
commit | d3ec1c866e9430d7ce7695f3393de4712072e4e8 (patch) | |
tree | e99807c703c6c33d74677bac80d93be755e0a783 | |
parent | Add some USB HID devices. (diff) | |
download | wireguard-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.h | 5 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc.c | 6 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_mem.c | 156 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcreg.h | 12 |
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 */ |