From e7fcfa43e96027caa3688986dee152ee8c8eeef4 Mon Sep 17 00:00:00 2001 From: Armando Date: Tue, 18 Apr 2023 19:45:27 +0800 Subject: [PATCH 1/2] sdmmc: use clk_tree api inside --- components/driver/sdmmc/sdmmc_host.c | 20 +++++++++++++------ .../soc/esp32/include/soc/clk_tree_defs.h | 16 +++++++++++++++ .../soc/esp32s3/include/soc/clk_tree_defs.h | 17 ++++++++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/components/driver/sdmmc/sdmmc_host.c b/components/driver/sdmmc/sdmmc_host.c index 16e33cbf92..296d041372 100644 --- a/components/driver/sdmmc/sdmmc_host.c +++ b/components/driver/sdmmc/sdmmc_host.c @@ -22,6 +22,7 @@ #include "sdmmc_private.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" +#include "esp_clk_tree.h" #include "soc/sdmmc_periph.h" #include "hal/gpio_hal.h" @@ -148,7 +149,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 +224,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 +244,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 +259,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) diff --git a/components/soc/esp32/include/soc/clk_tree_defs.h b/components/soc/esp32/include/soc/clk_tree_defs.h index 83ef092220..bac8df3e99 100644 --- a/components/soc/esp32/include/soc/clk_tree_defs.h +++ b/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 diff --git a/components/soc/esp32s3/include/soc/clk_tree_defs.h b/components/soc/esp32s3/include/soc/clk_tree_defs.h index 0e5911a893..1ef1c17a06 100644 --- a/components/soc/esp32s3/include/soc/clk_tree_defs.h +++ b/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 From 01a3d79b6f420cbac54661983a00e13a2bcc050b Mon Sep 17 00:00:00 2001 From: Armando Date: Tue, 18 Apr 2023 19:50:01 +0800 Subject: [PATCH 2/2] sdmmc: support tuning input delay on esp32s3 --- .../driver/sdmmc/include/driver/sdmmc_host.h | 23 +++++++++- .../driver/sdmmc/include/driver/sdmmc_types.h | 15 +++++++ components/driver/sdmmc/sdmmc_host.c | 43 +++++++++++++++++++ .../driver/spi/include/driver/sdspi_host.h | 4 +- components/sdmmc/sdmmc_common.c | 13 ++++++ .../esp32s3/include/soc/Kconfig.soc_caps.in | 4 ++ components/soc/esp32s3/include/soc/soc_caps.h | 2 + 7 files changed, 102 insertions(+), 2 deletions(-) diff --git a/components/driver/sdmmc/include/driver/sdmmc_host.h b/components/driver/sdmmc/include/driver/sdmmc_host.h index 46b6f6af36..9005d4eaf4 100644 --- a/components/driver/sdmmc/include/driver/sdmmc_host.h +++ b/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 diff --git a/components/driver/sdmmc/include/driver/sdmmc_types.h b/components/driver/sdmmc/include/driver/sdmmc_types.h index bc74a38c1d..bd0a25e001 100644 --- a/components/driver/sdmmc/include/driver/sdmmc_types.h +++ b/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; /** diff --git a/components/driver/sdmmc/sdmmc_host.c b/components/driver/sdmmc/sdmmc_host.c index 296d041372..0596d863ce 100644 --- a/components/driver/sdmmc/sdmmc_host.c +++ b/components/driver/sdmmc/sdmmc_host.c @@ -24,6 +24,7 @@ #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 @@ -345,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; diff --git a/components/driver/spi/include/driver/sdspi_host.h b/components/driver/spi/include/driver/sdspi_host.h index 146cff69cd..5f8189fa6b 100644 --- a/components/driver/spi/include/driver/sdspi_host.h +++ b/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 \ } /** diff --git a/components/sdmmc/sdmmc_common.c b/components/sdmmc/sdmmc_common.c index 407c74c79c..f55a48f6ba 100644 --- a/components/sdmmc/sdmmc_common.c +++ b/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); diff --git a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in index 8352676154..eec3270d24 100644 --- a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in +++ b/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 diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 2486847850..dbfeea2d8e 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/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)