]> granicus.if.org Git - esp-idf/commitdiff
sdmmc: implement partial DDR support
authorIvan Grokhotkov <ivan@espressif.com>
Wed, 22 Aug 2018 10:16:32 +0000 (18:16 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Thu, 30 Aug 2018 05:11:54 +0000 (13:11 +0800)
Works for 3.3V eMMC in 4 line mode.
Not implemented:
- DDR mode for SD cards (UHS-I) also need voltage to be switched to 1.8V.
- 8-line DDR mode for eMMC to be implemented later.

12 files changed:
components/driver/include/driver/sdmmc_defs.h
components/driver/include/driver/sdmmc_host.h
components/driver/include/driver/sdmmc_types.h
components/driver/include/driver/sdspi_host.h
components/driver/sdmmc_host.c
components/driver/sdmmc_private.h
components/driver/sdmmc_transaction.c
components/sdmmc/sdmmc_common.c
components/sdmmc/sdmmc_init.c
components/sdmmc/sdmmc_mmc.c
components/soc/esp32/include/soc/sdmmc_reg.h
components/soc/esp32/include/soc/sdmmc_struct.h

index a6b135d6a0387f764605848fc8000f97b0e8bff7..6ea567b8f5697d12589b561127dc79d46d066d6f 100644 (file)
 /* EXT_CSD_CARD_TYPE */
 /* The only currently valid values for this field are 0x01, 0x03, 0x07,
  * 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_F_26M         (1 << 0)        /* SDR at "rated voltages */
+#define EXT_CSD_CARD_TYPE_F_52M         (1 << 1)        /* SDR at "rated voltages */
+#define EXT_CSD_CARD_TYPE_F_52M_1_8V    (1 << 2)        /* DDR, 1.8V or 3.3V I/O */
+#define EXT_CSD_CARD_TYPE_F_52M_1_2V    (1 << 3)        /* DDR, 1.2V I/O */
 #define EXT_CSD_CARD_TYPE_26M           0x01
 #define EXT_CSD_CARD_TYPE_52M           0x03
 #define EXT_CSD_CARD_TYPE_52M_V18       0x07
index f57b959cc73a7a22de02c515a7f524025f6a4806..4d94d03c7cdb46a4c7580c9832884b4e444851f2 100644 (file)
@@ -33,13 +33,17 @@ extern "C" {
  * Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz
  */
 #define SDMMC_HOST_DEFAULT() {\
-    .flags = SDMMC_HOST_FLAG_4BIT, \
+    .flags = SDMMC_HOST_FLAG_8BIT | \
+             SDMMC_HOST_FLAG_4BIT | \
+             SDMMC_HOST_FLAG_1BIT | \
+             SDMMC_HOST_FLAG_DDR, \
     .slot = SDMMC_HOST_SLOT_1, \
     .max_freq_khz = SDMMC_FREQ_DEFAULT, \
     .io_voltage = 3.3f, \
     .init = &sdmmc_host_init, \
     .set_bus_width = &sdmmc_host_set_bus_width, \
     .get_bus_width = &sdmmc_host_get_slot_width, \
+    .set_bus_ddr_mode = &sdmmc_host_set_bus_ddr_mode, \
     .set_card_clk = &sdmmc_host_set_card_clk, \
     .do_transaction = &sdmmc_host_do_transaction, \
     .deinit = &sdmmc_host_deinit, \
@@ -150,6 +154,16 @@ size_t sdmmc_host_get_slot_width(int slot);
  */
 esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz);
 
+/**
+ * @brief Enable or disable DDR mode of SD interface
+ * @param slot  slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
+ * @param ddr_enabled  enable or disable DDR mode
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_NOT_SUPPORTED if DDR mode is not supported on this slot
+ */
+esp_err_t sdmmc_host_set_bus_ddr_mode(int slot, bool ddr_enabled);
+
 /**
  * @brief Send command to the card and get response
  *
index 1c584eff698ce9b23852595ad61205364aea7e41..369a9210f8bf0a7ec1609debc28507ed76a2bba1 100644 (file)
@@ -110,6 +110,8 @@ typedef struct {
 #define SCF_RSP_R5B      (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
 #define SCF_RSP_R6       (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
 #define SCF_RSP_R7       (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
+/* special flags */
+#define SCF_WAIT_BUSY    0x2000     /*!< Wait for completion of card busy signal before returning */
 /** @endcond */
         esp_err_t error;            /*!< error returned from transfer */
         int timeout_ms;             /*!< response timeout, in milliseconds */
@@ -127,6 +129,7 @@ typedef struct {
 #define SDMMC_HOST_FLAG_4BIT    BIT(1)      /*!< host supports 4-line SD and MMC protocol */
 #define SDMMC_HOST_FLAG_8BIT    BIT(2)      /*!< host supports 8-line MMC protocol */
 #define SDMMC_HOST_FLAG_SPI     BIT(3)      /*!< host supports SPI protocol */
+#define SDMMC_HOST_FLAG_DDR     BIT(4)      /*!< host supports DDR mode for SD/MMC */
     int slot;                   /*!< slot number, to be passed to host functions */
     int max_freq_khz;           /*!< max frequency supported by the host */
 #define SDMMC_FREQ_DEFAULT      20000       /*!< SD/MMC Default speed (limited by clock divider) */
@@ -138,6 +141,7 @@ typedef struct {
     esp_err_t (*init)(void);    /*!< Host function to initialize the driver */
     esp_err_t (*set_bus_width)(int slot, size_t width);    /*!< host function to set bus width */
     size_t (*get_bus_width)(int slot); /*!< host function to get bus width */
+    esp_err_t (*set_bus_ddr_mode)(int slot, bool ddr_enable); /*!< host function to set DDR mode */
     esp_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */
     esp_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo);    /*!< host function to do a transaction */
     esp_err_t (*deinit)(void);  /*!< host function to deinitialize the driver */
@@ -163,7 +167,8 @@ typedef struct {
     uint32_t is_mmc : 1;        /*!< Bit indicates if the card is MMC */
     uint32_t num_io_functions : 3;  /*!< If is_sdio is 1, contains the number of IO functions on the card */
     uint32_t log_bus_width : 2; /*!< log2(bus width supported by card) */
-    uint32_t reserved : 24;     /*!< Reserved for future expansion */
+    uint32_t is_ddr : 1;        /*!< Card supports DDR mode */
+    uint32_t reserved : 23;     /*!< Reserved for future expansion */
 } sdmmc_card_t;
 
 
index 9f72cb0dc4bc0c7a2d056ecd5d511357535c0520..1b3b67017e1568a64ff410ed6ef9fb51545d35fb 100644 (file)
@@ -41,6 +41,7 @@ extern "C" {
     .init = &sdspi_host_init, \
     .set_bus_width = NULL, \
     .get_bus_width = NULL, \
+    .set_bus_ddr_mode = NULL, \
     .set_card_clk = &sdspi_host_set_card_clk, \
     .do_transaction = &sdspi_host_do_transaction, \
     .deinit = &sdspi_host_deinit, \
index 58fe9713faa45a4b9e75bdadad7649e90dd0bbd5..2d276b40922be2d52c7ce560399659185a2d6590 100644 (file)
@@ -267,6 +267,9 @@ esp_err_t sdmmc_host_init()
             SDMMC_INTMASK_RESP_ERR | SDMMC_INTMASK_HLE; //sdio is enabled only when use.
     SDMMC.ctrl.int_enable = 1;
 
+    // Disable generation of Busy Clear Interrupt
+    SDMMC.cardthrctl.busy_clr_int_en = 0;
+
     // Enable DMA
     sdmmc_host_dma_init();
 
@@ -465,6 +468,28 @@ size_t sdmmc_host_get_slot_width(int slot)
     return s_slot_width[slot];
 }
 
+esp_err_t sdmmc_host_set_bus_ddr_mode(int slot, bool ddr_enabled)
+{
+    if (!(slot == 0 || slot == 1)) {
+        return ESP_ERR_INVALID_ARG;
+    }
+    if (s_slot_width[slot] == 8 && ddr_enabled) {
+        ESP_LOGW(TAG, "DDR mode with 8-bit bus width is not supported yet");
+        // requires reconfiguring controller clock for 2x card frequency
+        return ESP_ERR_NOT_SUPPORTED;
+    }
+    uint32_t mask = BIT(slot);
+    if (ddr_enabled) {
+        SDMMC.uhs.ddr |= mask;
+        SDMMC.emmc_ddr_reg |= mask;
+    } else {
+        SDMMC.uhs.ddr &= ~mask;
+        SDMMC.emmc_ddr_reg &= ~mask;
+    }
+    ESP_LOGD(TAG, "slot=%d ddr=%d", slot, ddr_enabled ? 1 : 0);
+    return ESP_OK;
+}
+
 static void sdmmc_host_dma_init()
 {
     SDMMC.ctrl.dma_enable = 1;
@@ -504,6 +529,11 @@ void sdmmc_host_dma_resume()
     SDMMC.pldmnd = 1;
 }
 
+bool sdmmc_host_card_busy()
+{
+    return SDMMC.status.data_busy == 1;
+}
+
 esp_err_t sdmmc_host_io_int_enable(int slot)
 {
     configure_pin(sdmmc_slot_info[slot].d1_gpio);
index 8f0aab78b2b056e9a8d15eda481359f3b2508b15..9223dce3192546296ef1a61c31d9ce9a3f7525d0 100644 (file)
@@ -38,6 +38,8 @@ void sdmmc_host_dma_stop();
 
 void sdmmc_host_dma_resume();
 
+bool sdmmc_host_card_busy();
+
 esp_err_t sdmmc_host_transaction_handler_init();
 
 void sdmmc_host_transaction_handler_deinit();
index 43f74c086360a23ff6aa15332d071cb3e6e5b32e..6cddd4b8e03db5d35412519491d9d93bb5d3df24 100644 (file)
@@ -19,6 +19,7 @@
 #include "freertos/FreeRTOS.h"
 #include "freertos/queue.h"
 #include "freertos/semphr.h"
+#include "freertos/task.h"
 #include "soc/sdmmc_periph.h"
 #include "soc/soc_memory_layout.h"
 #include "driver/sdmmc_types.h"
@@ -80,6 +81,7 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
 static void process_command_response(uint32_t status, sdmmc_command_t* cmd);
 static void fill_dma_descriptors(size_t num_desc);
 static size_t get_free_descriptors_count();
+static bool wait_for_busy_cleared(int timeout_ms);
 
 esp_err_t sdmmc_host_transaction_handler_init()
 {
@@ -165,6 +167,11 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
             break;
         }
     }
+    if (ret == ESP_OK && (cmdinfo->flags & SCF_WAIT_BUSY)) {
+        if (!wait_for_busy_cleared(cmdinfo->timeout_ms)) {
+            ret = ESP_ERR_TIMEOUT;
+        }
+    }
     s_is_app_cmd = (ret == ESP_OK && cmdinfo->opcode == MMC_APP_CMD);
 
 out:
@@ -461,5 +468,23 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
     return ESP_OK;
 }
 
+static bool wait_for_busy_cleared(int timeout_ms)
+{
+    if (timeout_ms == 0) {
+        return !sdmmc_host_card_busy();
+    }
 
+    /* It would have been nice to do this without polling, however the peripheral
+     * can only generate Busy Clear Interrupt for data write commands, and waiting
+     * for busy clear is mostly needed for other commands such as MMC_SWITCH.
+     */
+    int timeout_ticks = (timeout_ms + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS;
+    while (timeout_ticks-- > 0) {
+        if (!sdmmc_host_card_busy()) {
+            return true;
+        }
+        vTaskDelay(1);
+    }
+    return false;
+}
 
index 601d7a433e16cab61692c9e56adc13322b2d8fa7..564b00636197944fba0a2090b88a4ca1af9f4b1a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
- * Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -214,6 +214,18 @@ esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card)
             return err;
         }
     }
+
+    if (card->is_ddr) {
+        if (card->host.set_bus_ddr_mode == NULL) {
+            ESP_LOGE(TAG, "host doesn't support DDR mode or voltage switching");
+            return ESP_ERR_NOT_SUPPORTED;
+        }
+        esp_err_t err = (*card->host.set_bus_ddr_mode)(card->host.slot, true);
+        if (err != ESP_OK) {
+            ESP_LOGE(TAG, "failed to switch bus to DDR mode (0x%x)", err);
+            return err;
+        }
+    }
     return ESP_OK;
 }
 
@@ -246,7 +258,12 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
         type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC";
     }
     fprintf(stream, "Type: %s\n", type);
-    fprintf(stream, "Speed: %s\n", (card->max_freq_khz > SDMMC_FREQ_26M) ? "high speed" : "default speed");
+    if (card->max_freq_khz < 1000) {
+        fprintf(stream, "Speed: %d kHz\n", card->max_freq_khz);
+    } else {
+        fprintf(stream, "Speed: %d MHz%s\n", card->max_freq_khz / 1000,
+                card->is_ddr ? ", DDR" : "");
+    }
     fprintf(stream, "Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
 
     if (print_csd) {
@@ -269,13 +286,17 @@ esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card)
     int slot_bit_width = card->host.get_bus_width(card->host.slot);
     if (slot_bit_width == 1 &&
             (card->host.flags & (width_4bit | width_8bit))) {
-        ESP_LOGW(TAG, "host slot is configured in 1-bit mode");
-        card->host.flags &= ~width_mask;
-        card->host.flags |= ~(width_1bit);
-    } else if (slot_bit_width == 4 && (card->host.flags & width_8bit)){
-        ESP_LOGW(TAG, "host slot is configured in 4-bit mode");
         card->host.flags &= ~width_mask;
-        card->host.flags |= width_4bit;
+        card->host.flags |= width_1bit;
+    } else if (slot_bit_width == 4 && (card->host.flags & width_8bit)) {
+        if ((card->host.flags & width_4bit) == 0) {
+            ESP_LOGW(TAG, "slot width set to 4, but host flags don't have 4 line mode enabled; using 1 line mode");
+            card->host.flags &= ~width_mask;
+            card->host.flags |= width_1bit;
+        } else {
+            card->host.flags &= ~width_mask;
+            card->host.flags |= width_4bit;
+        }
     }
     return ESP_OK;
 }
index 312ae88f46edae7577b37113d10d1f50f03d5654..6bd5a88533ccc39081ff6a10e441a7294ea3f368 100644 (file)
@@ -93,6 +93,11 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
     /* MMC cards: read CXD */
     SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_read_ext_csd);
 
+    /* Try to switch card to HS mode if the card supports it.
+     * Set card->max_freq_khz value accordingly.
+     */
+    SDMMC_INIT_STEP(always, sdmmc_init_card_hs_mode);
+
     /* Set bus width. One call for every kind of card, then one for the host */
     if (!is_spi) {
         SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_bus_width);
@@ -101,19 +106,10 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
         SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width);
     }
 
-    SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
-
-    /* Try to switch card to HS mode if the card supports it.
-     * Set card->max_freq_khz value accordingly.
-     */
-    SDMMC_INIT_STEP(always, sdmmc_init_card_hs_mode);
-
-    /* So far initialization has been done at probing frequency.
-     * Switch to the host to use card->max_freq_khz frequency.
-     */
+    /* Switch to the host to use card->max_freq_khz frequency. */
     SDMMC_INIT_STEP(always, sdmmc_init_host_frequency);
 
-    /* Sanity check after switching the frequency */
+    /* Sanity check after switching the bus mode and frequency */
     SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
     /* TODO: add similar checks for eMMC and SDIO */
 
index d54a463e5379510b32dd1d4892f5f45c1e9d710a..e123763f0de86beec541acef411e9bca3df5f8cd 100644 (file)
@@ -48,9 +48,15 @@ esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card)
     }
     card_type = ext_csd[EXT_CSD_CARD_TYPE];
 
-    /* TODO: add DDR support */
+    card->is_ddr = 0;
     if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V) {
         card->max_freq_khz = SDMMC_FREQ_52M;
+        if ((card->host.flags & SDMMC_HOST_FLAG_DDR) &&
+                card->host.max_freq_khz >= SDMMC_FREQ_26M &&
+                card->host.get_bus_width(card->host.slot) == 4) {
+            ESP_LOGD(TAG, "card and host support DDR mode");
+            card->is_ddr = 1;
+        }
     } else if (card_type & EXT_CSD_CARD_TYPE_F_52M) {
         card->max_freq_khz = SDMMC_FREQ_52M;
     } else if (card_type & EXT_CSD_CARD_TYPE_F_26M) {
@@ -60,7 +66,7 @@ esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card)
     }
     /* For MMC cards, use speed value from EXT_CSD */
     card->csd.tr_speed = card->max_freq_khz * 1000;
-    ESP_LOGD(TAG, "MMC card supports %d khz bus frequency", card->max_freq_khz);
+    ESP_LOGD(TAG, "MMC card type %d, max_freq_khz=%d, is_ddr=%d", card_type, card->max_freq_khz, card->is_ddr);
     card->max_freq_khz = MIN(card->max_freq_khz, card->host.max_freq_khz);
 
     if (card->host.flags & SDMMC_HOST_FLAG_8BIT) {
@@ -104,13 +110,21 @@ esp_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card)
     }
 
     if (card->log_bus_width > 0) {
-        int csd_bus_width_value = 0;
+        int csd_bus_width_value = EXT_CSD_BUS_WIDTH_1;
         int bus_width = 1;
         if (card->log_bus_width == 2) {
-            csd_bus_width_value = EXT_CSD_BUS_WIDTH_4;
+            if (card->is_ddr) {
+                csd_bus_width_value = EXT_CSD_BUS_WIDTH_4_DDR;
+            } else {
+                csd_bus_width_value = EXT_CSD_BUS_WIDTH_4;
+            }
             bus_width = 4;
         } else if (card->log_bus_width == 3) {
-            csd_bus_width_value = EXT_CSD_BUS_WIDTH_8;
+            if (card->is_ddr) {
+                csd_bus_width_value = EXT_CSD_BUS_WIDTH_8_DDR;
+            } else {
+                csd_bus_width_value = EXT_CSD_BUS_WIDTH_8;
+            }
             bus_width = 8;
         }
         err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
@@ -205,7 +219,7 @@ esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8
     sdmmc_command_t cmd = {
             .opcode = MMC_SWITCH,
             .arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set,
-            .flags = SCF_RSP_R1B | SCF_CMD_AC,
+            .flags = SCF_RSP_R1B | SCF_CMD_AC | SCF_WAIT_BUSY,
     };
     esp_err_t err = sdmmc_send_cmd(card, &cmd);
     if (err == ESP_OK) {
index 2f9c68f80bbba2438adf1e1ed7312aa98c5c5c86..0e92f682244985206b163528738fb0b2e2a8b8c6 100644 (file)
@@ -71,6 +71,7 @@
 #define SDMMC_INTMASK_EBE       BIT(15)
 #define SDMMC_INTMASK_ACD       BIT(14)
 #define SDMMC_INTMASK_SBE       BIT(13)
+#define SDMMC_INTMASK_BCI       BIT(13)
 #define SDMMC_INTMASK_HLE       BIT(12)
 #define SDMMC_INTMASK_FRUN      BIT(11)
 #define SDMMC_INTMASK_HTO       BIT(10)
index 7e3c6912eb0568de0f72410af7e903dd64f08b8e..6aa64b43cc3d0632e7f7b10af1c341103b5220bc 100644 (file)
@@ -283,7 +283,12 @@ typedef volatile struct {
     uint32_t usrid;     ///< user ID
     uint32_t verid;     ///< IP block version
     uint32_t hcon;      ///< compile-time IP configuration
-    uint32_t uhs;       ///< TBD
+    union {
+        struct {
+            uint32_t voltage: 16;           ///< voltage control for slots; no-op on ESP32.
+            uint32_t ddr: 16;                ///< bit N enables DDR mode for card N
+        };
+    } uhs;              ///< UHS related settings
 
     union {
         struct {
@@ -348,7 +353,16 @@ typedef volatile struct {
     uint32_t bufaddrl;      ///< unused
     uint32_t bufaddru;      ///< unused
     uint32_t reserved_a8[22];
-    uint32_t cardthrctl;
+    union {
+        struct {
+            uint32_t read_thr_en : 1;       ///< initiate transfer only if FIFO has more space than the read threshold
+            uint32_t busy_clr_int_en : 1;   ///< enable generation of busy clear interrupts
+            uint32_t write_thr_en : 1;      ///< equivalent of read_thr_en for writes
+            uint32_t reserved1 : 13;
+            uint32_t card_threshold : 12;   ///< threshold value for reads/writes, in bytes
+        };
+        uint32_t val;
+    } cardthrctl;
     uint32_t back_end_power;
     uint32_t uhs_reg_ext;
     uint32_t emmc_ddr_reg;