This diff lays the groundwork for HS200 mode for eMMC. This mode supports data transfer modes of up to 200 MB/s. The crucial bit here is that using this mode requires tuning which is done by calling a chip-specific execute_tuning function. I have an implementation for amlmmc(4) that seems to work which I'll send in a separate diff.
The diff also adds some bits to support higher speed modes for SD cards, but those aren't hooked up yet. I'll leave that for a future diff. I'll probably look into adding support for sdhc(4) in the near future as well. Don't expect enormous speed increases from this; most eMMC chips I've seen are not actually limited by the tramsfer speed. But for amlmmc(4) this makes a difference since the DDR52 mode isn't reliable. Tested on the Odroid-N2, Odroid-C4 and an amd64 netbook that uses eMMC. ok? Index: dev/sdmmc/sdhc.c =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdhc.c,v retrieving revision 1.68 diff -u -p -r1.68 sdhc.c --- dev/sdmmc/sdhc.c 14 Jun 2020 18:37:16 -0000 1.68 +++ dev/sdmmc/sdhc.c 12 Aug 2020 18:52:05 -0000 @@ -121,26 +121,18 @@ void sdhc_dump_regs(struct sdhc_host *); #endif struct sdmmc_chip_functions sdhc_functions = { - /* host controller reset */ - sdhc_host_reset, - /* host controller capabilities */ - sdhc_host_ocr, - sdhc_host_maxblklen, - /* card detection */ - sdhc_card_detect, - /* 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, - /* UHS functions */ - sdhc_signal_voltage, - /* hibernate */ - sdhc_hibernate_init, + .host_reset = sdhc_host_reset, + .host_ocr = sdhc_host_ocr, + .host_maxblklen = sdhc_host_maxblklen, + .card_detect = sdhc_card_detect, + .bus_power = sdhc_bus_power, + .bus_clock = sdhc_bus_clock, + .bus_width = sdhc_bus_width, + .exec_command = sdhc_exec_command, + .card_intr_mask = sdhc_card_intr_mask, + .card_intr_ack = sdhc_card_intr_ack, + .signal_voltage = sdhc_signal_voltage, + .hibernate_init = sdhc_hibernate_init, }; struct cfdriver sdhc_cd = { Index: dev/sdmmc/sdmmc_mem.c =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdmmc_mem.c,v retrieving revision 1.33 diff -u -p -r1.33 sdmmc_mem.c --- dev/sdmmc/sdmmc_mem.c 4 Jun 2018 13:33:10 -0000 1.33 +++ dev/sdmmc/sdmmc_mem.c 12 Aug 2020 18:52:05 -0000 @@ -70,6 +70,34 @@ int sdmmc_mem_write_block_subr(struct sd #define DPRINTF(s) /**/ #endif +const struct { + const char *name; + int v; + int freq; +} switch_group0_functions[] = { + /* Default/SDR12 */ + { "Default/SDR12", 0, 25000 }, + + /* High-Speed/SDR25 */ + { "High-Speed/SDR25", SMC_CAPS_SD_HIGHSPEED, 50000 }, + + /* SDR50 */ + { "SDR50", SMC_CAPS_UHS_SDR50, 100000 }, + + /* SDR104 */ + { "SDR104", SMC_CAPS_UHS_SDR104, 208000 }, + + /* DDR50 */ + { "DDR50", SMC_CAPS_UHS_DDR50, 50000 }, +}; + +const int sdmmc_mmc_timings[] = { + [SDMMC_TIMING_LEGACY] = 26000, + [SDMMC_TIMING_HIGHSPEED] = 52000, + [SDMMC_TIMING_MMC_DDR52] = 52000, + [SDMMC_TIMING_MMC_HS200] = 200000 +}; + /* * Initialize SD/MMC memory cards and memory in SDIO "combo" cards. */ @@ -581,6 +609,41 @@ sdmmc_be512_to_bitfield512(sdmmc_bitfiel } int +sdmmc_mem_execute_tuning(struct sdmmc_softc *sc, struct sdmmc_function *sf) +{ + int timing = -1; + + if (ISSET(sc->sc_flags, SMF_SD_MODE)) { + if (!ISSET(sc->sc_flags, SMF_UHS_MODE)) + return 0; + + switch (sf->csd.tran_speed) { + case 100000: + timing = SDMMC_TIMING_UHS_SDR50; + break; + case 208000: + timing = SDMMC_TIMING_UHS_SDR104; + break; + default: + return 0; + } + } else { + switch (sf->csd.tran_speed) { + case 200000: + timing = SDMMC_TIMING_MMC_HS200; + break; + default: + return 0; + } + } + + DPRINTF(("%s: execute tuning for timing %d\n", SDMMCDEVNAME(sc), + timing)); + + return sdmmc_chip_execute_tuning(sc->sct, sc->sch, timing); +} + +int sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) { int support_func, best_func, error; @@ -647,6 +710,8 @@ sdmmc_mem_sd_init(struct sdmmc_softc *sc DEVNAME(sc), best_func, support_func); return error; } + sf->csd.tran_speed = + switch_group0_functions[best_func].freq; /* Wait 400KHz x 8 clock (2.5us * 8 + slop) */ delay(25); @@ -692,7 +757,11 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s card_type = ext_csd[EXT_CSD_CARD_TYPE]; - if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V && + if (card_type & EXT_CSD_CARD_TYPE_F_HS200_1_8V && + ISSET(sc->sc_caps, SMC_CAPS_MMC_HS200)) { + speed = 200000; + timing = SDMMC_TIMING_MMC_HS200; + } else if (card_type & EXT_CSD_CARD_TYPE_F_DDR52_1_8V && ISSET(sc->sc_caps, SMC_CAPS_MMC_DDR52)) { speed = 52000; timing = SDMMC_TIMING_MMC_DDR52; @@ -707,39 +776,6 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s ext_csd[EXT_CSD_CARD_TYPE]); } - if (timing != SDMMC_TIMING_LEGACY) { - /* switch to high speed timing */ - error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, EXT_CSD_HS_TIMING_HS); - if (error != 0) { - printf("%s: can't change high speed\n", - DEVNAME(sc)); - return error; - } - - sdmmc_delay(10000); - } - - error = sdmmc_chip_bus_clock(sc->sct, sc->sch, speed, SDMMC_TIMING_HIGHSPEED); - if (error != 0) { - printf("%s: can't change bus clock\n", DEVNAME(sc)); - return error; - } - - if (timing != SDMMC_TIMING_LEGACY) { - /* read EXT_CSD again */ - error = sdmmc_mem_send_cxd_data(sc, - MMC_SEND_EXT_CSD, ext_csd, sizeof(ext_csd)); - if (error != 0) { - printf("%s: can't re-read EXT_CSD\n", DEVNAME(sc)); - return error; - } - if (ext_csd[EXT_CSD_HS_TIMING] != EXT_CSD_HS_TIMING_HS) { - printf("%s, HS_TIMING set failed\n", DEVNAME(sc)); - return EINVAL; - } - } - if (ISSET(sc->sc_caps, SMC_CAPS_8BIT_MODE)) { width = 8; value = EXT_CSD_BUS_WIDTH_8; @@ -767,6 +803,52 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s sdmmc_delay(10000); } + if (timing != SDMMC_TIMING_LEGACY) { + switch (timing) { + case SDMMC_TIMING_MMC_HS200: + value = EXT_CSD_HS_TIMING_HS200; + break; + case SDMMC_TIMING_MMC_DDR52: + case SDMMC_TIMING_HIGHSPEED: + value = EXT_CSD_HS_TIMING_HS; + break; + } + + /* switch to high speed timing */ + error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, value); + if (error != 0) { + printf("%s: can't change timing\n", + DEVNAME(sc)); + return error; + } + + sdmmc_delay(10000); + } + + KASSERT(timing < nitems(sdmmc_mmc_timings)); + sf->csd.tran_speed = sdmmc_mmc_timings[timing]; + + if (timing != SDMMC_TIMING_LEGACY) { + /* read EXT_CSD again */ + error = sdmmc_mem_send_cxd_data(sc, + MMC_SEND_EXT_CSD, ext_csd, sizeof(ext_csd)); + if (error != 0) { + printf("%s: can't re-read EXT_CSD\n", DEVNAME(sc)); + return error; + } + if (ext_csd[EXT_CSD_HS_TIMING] != value) { + printf("%s, HS_TIMING set failed\n", DEVNAME(sc)); + return EINVAL; + } + } + + error = sdmmc_chip_bus_clock(sc->sct, sc->sch, speed, SDMMC_TIMING_HIGHSPEED); + if (error != 0) { + printf("%s: can't change bus clock\n", DEVNAME(sc)); + return error; + } + if (timing == SDMMC_TIMING_MMC_DDR52) { switch (width) { case 4: @@ -812,6 +894,15 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s if (sectors > (2u * 1024 * 1024 * 1024) / 512) { sf->flags |= SFF_SDHC; sf->csd.capacity = sectors; + } + + if (timing == SDMMC_TIMING_MMC_HS200) { + /* execute tuning (HS200) */ + error = sdmmc_mem_execute_tuning(sc, sf); + if (error) { + printf("%s: can't execute MMC tuning\n", DEVNAME(sc)); + return error; + } } } Index: dev/sdmmc/sdmmcchip.h =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdmmcchip.h,v retrieving revision 1.13 diff -u -p -r1.13 sdmmcchip.h --- dev/sdmmc/sdmmcchip.h 29 Dec 2018 11:37:30 -0000 1.13 +++ dev/sdmmc/sdmmcchip.h 12 Aug 2020 18:52:05 -0000 @@ -45,7 +45,8 @@ struct sdmmc_chip_functions { 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 (*signal_voltage)(sdmmc_chipset_handle_t, int); + int (*execute_tuning)(sdmmc_chipset_handle_t, int); /* hibernate */ int (*hibernate_init)(sdmmc_chipset_handle_t, void *); }; @@ -79,6 +80,8 @@ struct sdmmc_chip_functions { /* 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((handle), (timing))) /* clock frequencies for sdmmc_chip_bus_clock() */ #define SDMMC_SDCLK_OFF 0 @@ -92,7 +95,10 @@ struct sdmmc_chip_functions { #define SDMMC_TIMING_LEGACY 0 #define SDMMC_TIMING_HIGHSPEED 1 -#define SDMMC_TIMING_MMC_DDR52 2 +#define SDMMC_TIMING_UHS_SDR50 2 +#define SDMMC_TIMING_UHS_SDR104 3 +#define SDMMC_TIMING_MMC_DDR52 4 +#define SDMMC_TIMING_MMC_HS200 5 #define SDMMC_MAX_FUNCTIONS 8 Index: dev/sdmmc/sdmmcreg.h =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdmmcreg.h,v retrieving revision 1.11 diff -u -p -r1.11 sdmmcreg.h --- dev/sdmmc/sdmmcreg.h 5 May 2016 11:01:08 -0000 1.11 +++ dev/sdmmc/sdmmcreg.h 12 Aug 2020 18:52:05 -0000 @@ -33,6 +33,8 @@ #define MMC_SET_BLOCKLEN 16 /* R1 */ #define MMC_READ_BLOCK_SINGLE 17 /* R1 */ #define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */ +#define MMC_SEND_TUNING_BLOCK 19 /* R1 */ +#define MMC_SEND_TUNING_BLOCK_HS200 21 /* R1 */ #define MMC_SET_BLOCK_COUNT 23 /* R1 */ #define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */ #define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */ @@ -122,13 +124,12 @@ * 0x0B and 0x0F. */ #define EXT_CSD_CARD_TYPE_F_26M (1 << 0) #define EXT_CSD_CARD_TYPE_F_52M (1 << 1) -#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2) -#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3) -#define EXT_CSD_CARD_TYPE_26M 0x01 -#define EXT_CSD_CARD_TYPE_52M 0x03 -#define EXT_CSD_CARD_TYPE_52M_V18 0x07 -#define EXT_CSD_CARD_TYPE_52M_V12 0x0b -#define EXT_CSD_CARD_TYPE_52M_V12_18 0x0f +#define EXT_CSD_CARD_TYPE_F_DDR52_1_8V (1 << 2) +#define EXT_CSD_CARD_TYPE_F_DDR52_1_2V (1 << 3) +#define EXT_CSD_CARD_TYPE_F_HS200_1_8V (1 << 4) +#define EXT_CSD_CARD_TYPE_F_HS200_1_2V (1 << 5) +#define EXT_CSD_CARD_TYPE_F_HS400_1_8V (1 << 6) +#define EXT_CSD_CARD_TYPE_F_HS400_1_2V (1 << 7) /* MMC_SWITCH access mode */ #define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ Index: dev/sdmmc/sdmmcvar.h =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdmmcvar.h,v retrieving revision 1.33 diff -u -p -r1.33 sdmmcvar.h --- dev/sdmmc/sdmmcvar.h 3 Jul 2020 13:31:47 -0000 1.33 +++ dev/sdmmc/sdmmcvar.h 12 Aug 2020 18:52:05 -0000 @@ -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 tran_speed; /* transfer speed (kbit/s) */ int ccc; /* Card Command Class for SD */ /* ... */ }; @@ -177,10 +178,11 @@ struct sdmmc_softc { #define SMF_SD_MODE 0x0001 /* host in SD mode (MMC otherwise) */ #define SMF_IO_MODE 0x0002 /* host in I/O mode (SD mode only) */ #define SMF_MEM_MODE 0x0004 /* host in memory mode (SD or MMC) */ -#define SMF_CARD_PRESENT 0x0010 /* card presence noticed */ -#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 0x0010 /* host in UHS mode */ +#define SMF_CARD_PRESENT 0x0020 /* card presence noticed */ +#define SMF_CARD_ATTACHED 0x0040 /* card driver(s) attached */ +#define SMF_STOP_AFTER_MULTIPLE 0x0080 /* send a stop after a multiple cmd */ +#define SMF_CONFIG_PENDING 0x0100 /* config_pending_incr() called */ uint32_t sc_caps; /* host capability */ #define SMC_CAPS_AUTO_STOP 0x0001 /* send CMD12 automagically by host */