Jelajahi Sumber

Merge branch 'feature/sdmmc_host_timing_tuning' into 'master'

sdmmc: supported host timing tuning on esp32s3

Closes IDF-7240 and IDF-7250

See merge request espressif/esp-idf!23257
Armando (Dou Yiwen) 2 tahun lalu
induk
melakukan
ca2624e0eb

+ 22 - 1
components/driver/sdmmc/include/driver/sdmmc_host.h

@@ -46,7 +46,9 @@ extern "C" {
     .io_int_enable = sdmmc_host_io_int_enable, \
     .io_int_wait = sdmmc_host_io_int_wait, \
     .command_timeout_ms = 0, \
-    .get_real_freq = &sdmmc_host_get_real_freq \
+    .get_real_freq = &sdmmc_host_get_real_freq, \
+    .input_delay_phase = SDMMC_DELAY_PHASE_0, \
+    .set_input_delay = &sdmmc_host_set_input_delay \
 }
 
 /**
@@ -291,6 +293,25 @@ esp_err_t sdmmc_host_deinit(void);
  */
 esp_err_t sdmmc_host_get_real_freq(int slot, int* real_freq_khz);
 
+/**
+ * @brief set input delay
+ *
+ * @note ESP32 doesn't support this feature, you will get an `ESP_ERR_NOT_SUPPORTED`
+ *
+ * - This API sets delay when the SDMMC Host samples the signal from the SD Slave.
+ * - This API will check if the given `delay_phase` is valid or not.
+ * - This API will print out the delay time, in picosecond (ps)
+ *
+ * @param slot         slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
+ * @param delay_phase  delay phase, this API will convert the phase into picoseconds and print it out
+ *
+ * @return
+ *        - ESP_OK:                ON success.
+ *        - ESP_ERR_INVALID_ARG:   Invalid argument.
+ *        - ESP_ERR_NOT_SUPPORTED: ESP32 doesn't support this feature.
+ */
+esp_err_t sdmmc_host_set_input_delay(int slot, sdmmc_delay_phase_t delay_phase);
+
 #ifdef __cplusplus
 }
 #endif

+ 15 - 0
components/driver/sdmmc/include/driver/sdmmc_types.h

@@ -148,6 +148,19 @@ typedef struct {
         uint32_t timeout_ms;        /*!< response timeout, in milliseconds */
 } sdmmc_command_t;
 
+/**
+ * SD/MMC Host clock timing delay phases
+ *
+ * This will only take effect when the host works in SDMMC_FREQ_HIGHSPEED or SDMMC_FREQ_52M.
+ * Driver will print out how long the delay is, in picosecond (ps).
+ */
+typedef enum {
+    SDMMC_DELAY_PHASE_0,            /*!< Delay phase 0 */
+    SDMMC_DELAY_PHASE_1,            /*!< Delay phase 1 */
+    SDMMC_DELAY_PHASE_2,            /*!< Delay phase 2 */
+    SDMMC_DELAY_PHASE_3,            /*!< Delay phase 3 */
+} sdmmc_delay_phase_t;
+
 /**
  * SD/MMC Host description
  *
@@ -185,6 +198,8 @@ typedef struct {
     esp_err_t (*io_int_wait)(int slot, TickType_t timeout_ticks); /*!< Host function to wait for SDIO interrupt line to be active */
     int command_timeout_ms;     /*!< timeout, in milliseconds, of a single command. Set to 0 to use the default value. */
     esp_err_t (*get_real_freq)(int slot, int* real_freq); /*!< Host function to provide real working freq, based on SDMMC controller setup */
+    sdmmc_delay_phase_t input_delay_phase; /*!< input delay phase, this will only take into effect when the host works in SDMMC_FREQ_HIGHSPEED or SDMMC_FREQ_52M. Driver will print out how long the delay is*/
+    esp_err_t (*set_input_delay)(int slot, sdmmc_delay_phase_t delay_phase); /*!< set input delay phase */
 } sdmmc_host_t;
 
 /**

+ 57 - 6
components/driver/sdmmc/sdmmc_host.c

@@ -22,7 +22,9 @@
 #include "sdmmc_private.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/semphr.h"
+#include "esp_clk_tree.h"
 #include "soc/sdmmc_periph.h"
+#include "soc/soc_caps.h"
 #include "hal/gpio_hal.h"
 
 #define SDMMC_EVENT_QUEUE_LENGTH 32
@@ -148,7 +150,7 @@ static void sdmmc_host_set_clk_div(int div)
     SDMMC.clock.div_factor_l = l;
     SDMMC.clock.div_factor_n = l;
 
-    // Make sure 160 MHz source clock is used
+    // Make sure SOC_MOD_CLK_PLL_F160M (160 MHz) source clock is used
 #if SOC_SDMMC_SUPPORT_XTAL_CLOCK
     SDMMC.clock.clk_sel = 1;
 #endif
@@ -223,6 +225,10 @@ static esp_err_t sdmmc_host_clock_update_command(int slot)
 
 void sdmmc_host_get_clk_dividers(const uint32_t freq_khz, int *host_div, int *card_div)
 {
+    uint32_t clk_src_freq_hz = 0;
+    esp_clk_tree_src_get_freq_hz(SDMMC_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
+    assert(clk_src_freq_hz == (160 * 1000 * 1000));
+
     // Calculate new dividers
     if (freq_khz >= SDMMC_FREQ_HIGHSPEED) {
         *host_div = 4;       // 160 MHz / 4 = 40 MHz
@@ -239,14 +245,14 @@ void sdmmc_host_get_clk_dividers(const uint32_t freq_khz, int *host_div, int *ca
          * if exceeded, combine with the card divider to keep reasonable precision (applies mainly to low frequencies)
          * effective frequency range: 400 kHz - 32 MHz (32.1 - 39.9 MHz cannot be covered with given divider scheme)
          */
-        *host_div = (2 * APB_CLK_FREQ) / (freq_khz * 1000);
+        *host_div = (clk_src_freq_hz) / (freq_khz * 1000);
         if (*host_div > 15 ) {
             *host_div = 2;
-            *card_div = APB_CLK_FREQ / (2 * freq_khz * 1000);
-            if ( (APB_CLK_FREQ % (2 * freq_khz * 1000)) > 0 ) {
+            *card_div = (clk_src_freq_hz / 2) / (2 * freq_khz * 1000);
+            if ( ((clk_src_freq_hz / 2) % (2 * freq_khz * 1000)) > 0 ) {
                 (*card_div)++;
             }
-        } else if ( ((2 * APB_CLK_FREQ) % (freq_khz * 1000)) > 0 ) {
+        } else if ((clk_src_freq_hz % (freq_khz * 1000)) > 0) {
             (*host_div)++;
         }
     }
@@ -254,7 +260,10 @@ void sdmmc_host_get_clk_dividers(const uint32_t freq_khz, int *host_div, int *ca
 
 static int sdmmc_host_calc_freq(const int host_div, const int card_div)
 {
-    return 2 * APB_CLK_FREQ / host_div / ((card_div == 0) ? 1 : card_div * 2) / 1000;
+    uint32_t clk_src_freq_hz = 0;
+    esp_clk_tree_src_get_freq_hz(SDMMC_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
+    assert(clk_src_freq_hz == (160 * 1000 * 1000));
+    return clk_src_freq_hz / host_div / ((card_div == 0) ? 1 : card_div * 2) / 1000;
 }
 
 esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
@@ -337,6 +346,48 @@ esp_err_t sdmmc_host_get_real_freq(int slot, int* real_freq_khz)
     return ESP_OK;
 }
 
+esp_err_t sdmmc_host_set_input_delay(int slot, sdmmc_delay_phase_t delay_phase)
+{
+#if CONFIG_IDF_TARGET_ESP32
+    //DIG-217
+    ESP_LOGW(TAG, "esp32 doesn't support input phase delay, fallback to 0 delay");
+    return ESP_ERR_NOT_SUPPORTED;
+#else
+    ESP_RETURN_ON_FALSE((slot == 0 || slot == 1), ESP_ERR_INVALID_ARG, TAG, "invalid slot");
+    ESP_RETURN_ON_FALSE(delay_phase < SOC_SDMMC_DELAY_PHASE_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid delay phase");
+
+    uint32_t clk_src_freq_hz = 0;
+    esp_clk_tree_src_get_freq_hz(SDMMC_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
+
+    //Now we're in high speed. Note ESP SDMMC Host HW only supports integer divider.
+    int delay_phase_num = 0;
+    switch (delay_phase) {
+        case SDMMC_DELAY_PHASE_1:
+            SDMMC.clock.phase_din = 0x1;
+            delay_phase_num = 1;
+            break;
+        case SDMMC_DELAY_PHASE_2:
+            SDMMC.clock.phase_din = 0x4;
+            delay_phase_num = 2;
+            break;
+        case SDMMC_DELAY_PHASE_3:
+            SDMMC.clock.phase_din = 0x6;
+            delay_phase_num = 3;
+            break;
+        default:
+            SDMMC.clock.phase_din = 0x0;
+            break;
+    }
+
+    int src_clk_period_ps = (1 * 1000 * 1000) / (clk_src_freq_hz / (1 * 1000 * 1000));
+    int phase_diff_ps = src_clk_period_ps * (SDMMC.clock.div_factor_n + 1) / SOC_SDMMC_DELAY_PHASE_NUM;
+    ESP_LOGD(TAG, "difference between input delay phases is %d ps", phase_diff_ps);
+    ESP_LOGI(TAG, "host sampling edge is delayed by %d ps", phase_diff_ps * delay_phase_num);
+#endif
+
+    return ESP_OK;
+}
+
 esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg) {
     if (!(slot == 0 || slot == 1)) {
         return ESP_ERR_INVALID_ARG;

+ 3 - 1
components/driver/spi/include/driver/sdspi_host.h

@@ -51,7 +51,9 @@ typedef int sdspi_dev_handle_t;
     .io_int_enable = &sdspi_host_io_int_enable, \
     .io_int_wait = &sdspi_host_io_int_wait, \
     .command_timeout_ms = 0, \
-    .get_real_freq = &sdspi_host_get_real_freq \
+    .get_real_freq = &sdspi_host_get_real_freq, \
+    .input_delay_phase = SDMMC_DELAY_PHASE_0, \
+    .set_input_delay = NULL \
 }
 
 /**

+ 13 - 0
components/sdmmc/sdmmc_common.c

@@ -206,6 +206,19 @@ esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card)
         }
     }
 
+    if (card->host.input_delay_phase != SDMMC_DELAY_PHASE_0) {
+        if (card->host.set_input_delay) {
+            err = (*card->host.set_input_delay)(card->host.slot, card->host.input_delay_phase);
+            if (err != ESP_OK) {
+                ESP_LOGE(TAG, "host.set_input_delay failed (0x%x)", err);
+                return err;
+            }
+        } else {
+            ESP_LOGE(TAG, "input phase delay feature isn't supported");
+            return ESP_ERR_NOT_SUPPORTED;
+        }
+    }
+
     err = (*card->host.get_real_freq)(card->host.slot, &(card->real_freq_khz));
     if (err != ESP_OK) {
         ESP_LOGE(TAG, "failed to get real working frequency (0x%x)", err);

+ 16 - 0
components/soc/esp32/include/soc/clk_tree_defs.h

@@ -412,6 +412,22 @@ typedef enum {
     LEDC_USE_RTC8M_CLK __attribute__((deprecated("please use 'LEDC_USE_RC_FAST_CLK' instead"))) = LEDC_USE_RC_FAST_CLK,   /*!< Alias of 'LEDC_USE_RC_FAST_CLK' */
 } soc_periph_ledc_clk_src_legacy_t;
 
+
+//////////////////////////////////////////////////SDMMC///////////////////////////////////////////////////////////////
+
+/**
+ * @brief Array initializer for all supported clock sources of SDMMC
+ */
+#define SOC_SDMMC_CLKS {SOC_MOD_CLK_PLL_F160M}
+
+/**
+ * @brief Type of SDMMC clock source
+ */
+typedef enum {
+    SDMMC_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the default choice */
+    SDMMC_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the source clock */
+} soc_periph_sdmmc_clk_src_t;
+
 #ifdef __cplusplus
 }
 #endif

+ 4 - 0
components/soc/esp32s3/include/soc/Kconfig.soc_caps.in

@@ -1223,6 +1223,10 @@ config SOC_SDMMC_SUPPORT_XTAL_CLOCK
     bool
     default y
 
+config SOC_SDMMC_DELAY_PHASE_NUM
+    int
+    default 4
+
 config SOC_TEMPERATURE_SENSOR_SUPPORT_FAST_RC
     bool
     default y

+ 17 - 0
components/soc/esp32s3/include/soc/clk_tree_defs.h

@@ -414,6 +414,23 @@ typedef enum {
     LEDC_USE_RTC8M_CLK __attribute__((deprecated("please use 'LEDC_USE_RC_FAST_CLK' instead"))) = LEDC_USE_RC_FAST_CLK,   /*!< Alias of 'LEDC_USE_RC_FAST_CLK' */
 } soc_periph_ledc_clk_src_legacy_t;
 
+//////////////////////////////////////////////////SDMMC///////////////////////////////////////////////////////////////
+
+/**
+ * @brief Array initializer for all supported clock sources of SDMMC
+ */
+#define SOC_SDMMC_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL}
+
+/**
+ * @brief Type of SDMMC clock source
+ */
+typedef enum {
+    SDMMC_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the default choice */
+    SDMMC_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the source clock */
+    SDMMC_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL,          /*!< Select XTAL as the source clock */
+} soc_periph_sdmmc_clk_src_t;
+
+
 #ifdef __cplusplus
 }
 #endif

+ 2 - 0
components/soc/esp32s3/include/soc/soc_caps.h

@@ -495,6 +495,8 @@
 #define SOC_SDMMC_NUM_SLOTS        2
 /* Indicates that there is an option to use XTAL clock instead of PLL for SDMMC */
 #define SOC_SDMMC_SUPPORT_XTAL_CLOCK    1
+/* Supported host clock delay phase number */
+#define SOC_SDMMC_DELAY_PHASE_NUM    4
 
 /*-------------------------- Temperature Sensor CAPS -------------------------------------*/
 #define SOC_TEMPERATURE_SENSOR_SUPPORT_FAST_RC                (1)