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 */

Reply via email to