sdmmc: support tuning input delay on esp32s3

This commit is contained in:
Armando 2023-04-18 19:50:01 +08:00 committed by Armando (Dou Yiwen)
parent e7fcfa43e9
commit 01a3d79b6f
7 changed files with 102 additions and 2 deletions

View File

@ -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

View File

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

View File

@ -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;

View File

@ -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 \
}
/**

View File

@ -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);

View File

@ -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

View File

@ -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)