mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
sdmmc: support tuning input delay on esp32s3
This commit is contained in:
parent
e7fcfa43e9
commit
01a3d79b6f
@ -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
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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 \
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user