The diff below adds support for higher speeds as supported by UHS-I SD
cards to the generic sdmmc(4) layer.  The diff in itself does not
enable the use of those modes.  That needs separate changes to the
SD/MMC controller drivers.  I have such a diff for amlmmc(4) that
allows me to use SDR50 mode.

However, to make sure this diff doesn't break existing lower speed
modes I'd appreciate tests on a variety of hardware.  So if sdmmc(4)
shows up in your dmesg, please test this by exercising your (u)SD or
(e)MMC cards.

Thanks,

Mark


Index: dev/sdmmc/sdmmc.c
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmc.c,v
retrieving revision 1.57
diff -u -p -r1.57 sdmmc.c
--- dev/sdmmc/sdmmc.c   15 Aug 2020 13:21:02 -0000      1.57
+++ dev/sdmmc/sdmmc.c   16 Aug 2020 17:15:55 -0000
@@ -111,6 +111,10 @@ sdmmc_attach(struct device *parent, stru
                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))
Index: dev/sdmmc/sdmmc_mem.c
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmc_mem.c,v
retrieving revision 1.34
diff -u -p -r1.34 sdmmc_mem.c
--- dev/sdmmc/sdmmc_mem.c       14 Aug 2020 14:49:04 -0000      1.34
+++ dev/sdmmc/sdmmc_mem.c       16 Aug 2020 17:15:55 -0000
@@ -52,6 +52,7 @@ int   sdmmc_mem_decode_scr(struct sdmmc_so
 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_bitfiel
 }
 
 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_so
 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
 
                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
                /* 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_soft
                        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);
                }
Index: dev/sdmmc/sdmmcreg.h
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmcreg.h,v
retrieving revision 1.12
diff -u -p -r1.12 sdmmcreg.h
--- dev/sdmmc/sdmmcreg.h        14 Aug 2020 14:49:04 -0000      1.12
+++ dev/sdmmc/sdmmcreg.h        16 Aug 2020 17:15:55 -0000
@@ -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 */

Reply via email to