esp-idf/components/esp_hw_support/mspi_timing_by_dqs.c

238 lines
9.0 KiB
C

/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief
*
* This file contains configuration APIs doing MSPI timing tuning by MSPI dqs
* This file will only be built, when `SOC_MEMSPI_TIMING_TUNING_BY_DQS == 1`
*/
#include <stdint.h>
#include <string.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "mspi_timing_by_dqs.h"
#include "mspi_timing_tuning_configs.h"
#include "esp_private/mspi_timing_config.h"
#include "esp_private/rtc_clk.h"
#include "esp_private/periph_ctrl.h"
#include "hal/psram_ctrlr_ll.h"
#include "hal/mspi_timing_tuning_ll.h"
#define AP_HEX_PSRAM_SYNC_READ 0x0000
#define AP_HEX_PSRAM_SYNC_WRITE 0x8080
#define AP_HEX_PSRAM_RD_CMD_BITLEN 16
#define AP_HEX_PSRAM_WR_CMD_BITLEN 16
#define AP_HEX_PSRAM_ADDR_BITLEN 32
#if CONFIG_SPIRAM_SPEED_250M
#define AP_HEX_PSRAM_RD_DUMMY_BITLEN (2*(18-1))
#define AP_HEX_PSRAM_WR_DUMMY_BITLEN (2*(9-1))
#elif CONFIG_SPIRAM_SPEED_200M
#define AP_HEX_PSRAM_RD_DUMMY_BITLEN (2*(14-1))
#define AP_HEX_PSRAM_WR_DUMMY_BITLEN (2*(7-1))
#else
#define AP_HEX_PSRAM_RD_DUMMY_BITLEN (2*(10-1))
#define AP_HEX_PSRAM_WR_DUMMY_BITLEN (2*(5-1))
#endif
#define WRONG_DELAYLINE 16
const static char *TAG = "MSPI DQS";
const static uint32_t s_test_data[MSPI_TIMING_TEST_DATA_LEN] = {0x7f786655, 0xa5ff005a, 0x3f3c33aa, 0xa5ff5a00, 0x1f1e9955, 0xa5005aff, 0x0f0fccaa, 0xa55a00ff,
0x07876655, 0xffa55a00, 0x03c333aa, 0xff00a55a, 0x01e19955, 0xff005aa5, 0x00f0ccaa, 0xff5a00a5,
0x80786655, 0x00a5ff5a, 0xc03c33aa, 0x00a55aff, 0xe01e9355, 0x00ff5aa5, 0xf00fccaa, 0x005affa5,
0xf8876655, 0x5aa5ff00, 0xfcc333aa, 0x5affa500, 0xfee19955, 0x5a00a5ff, 0x11f0ccaa, 0x5a00ffa5};
const static mspi_timing_config_t s_test_delayline_config = {
.delayline_table = {{15, 0}, {14, 0}, {13, 0}, {12, 0}, {11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0},
{0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}, {0, 8}, {0, 9}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}},
.available_config_num = 32,
};
static mspi_ll_dqs_phase_t s_psram_best_phase = MSPI_LL_DQS_PHASE_MAX;
static delayline_config_t s_psram_best_delayline = {WRONG_DELAYLINE, WRONG_DELAYLINE};
void mspi_timing_psram_init(uint32_t psram_freq_mhz)
{
psram_ctrlr_ll_enable_variable_dummy(PSRAM_CTRLR_LL_MSPI_ID_3, false);
mspi_timing_config_set_psram_clock(psram_freq_mhz, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, true);
}
void mspi_timing_config_psram_prepare_reference_data(uint8_t *buf, uint32_t len)
{
assert(len == MSPI_TIMING_TEST_DATA_LEN);
memcpy(buf, &s_test_data, len);
}
void mspi_timing_config_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
uint8_t *w_ptr = buf;
while (len) {
int len_to_send = MIN(len, PSRAM_CTRLR_LL_FIFO_MAX_BYTES);
psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3,
AP_HEX_PSRAM_SYNC_WRITE, AP_HEX_PSRAM_WR_CMD_BITLEN,
addr, AP_HEX_PSRAM_ADDR_BITLEN,
AP_HEX_PSRAM_WR_DUMMY_BITLEN,
w_ptr, len_to_send * 8,
NULL, 0,
false);
w_ptr += len_to_send;
addr += len_to_send;
len -= len_to_send;
}
}
void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
uint8_t *r_ptr = buf;
while (len) {
int len_to_recv = MIN(len, PSRAM_CTRLR_LL_FIFO_MAX_BYTES);
psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3,
AP_HEX_PSRAM_SYNC_READ, AP_HEX_PSRAM_RD_CMD_BITLEN,
addr, AP_HEX_PSRAM_ADDR_BITLEN,
AP_HEX_PSRAM_RD_DUMMY_BITLEN,
NULL, 0,
r_ptr, len_to_recv * 8,
false);
r_ptr += len_to_recv;
addr += len_to_recv;
len -= len_to_recv;
}
}
void mspi_timing_get_psram_tuning_phases(mspi_timing_config_t *configs)
{
*configs = (mspi_timing_config_t) {
.phase = {MSPI_LL_DQS_PHASE_67_5, MSPI_LL_DQS_PHASE_78_75, MSPI_LL_DQS_PHASE_90, MSPI_LL_DQS_PHASE_101_25},
.available_phase_num = 4,
};
}
void mspi_timing_config_psram_set_tuning_phase(const void *configs, uint8_t id)
{
mspi_ll_dqs_phase_t phase = ((mspi_timing_config_t *)configs)->phase[id];
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, phase);
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, phase);
ESP_EARLY_LOGD(TAG, "set to phase: %d", phase);
}
uint32_t mspi_timing_psram_select_best_tuning_phase(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr)
{
assert(consecutive_length < 5);
uint32_t best_phase_id = 0;
bool success = true;
if (consecutive_length == 0) {
best_phase_id = 0;
success = false;
} else if (consecutive_length == 1) {
best_phase_id = end;
} else if (consecutive_length == 2 || consecutive_length == 3){
best_phase_id = end - 1;
} else {
best_phase_id = end - 2;
}
if (success) {
ESP_EARLY_LOGI(TAG, "tuning success, best phase id is %"PRIu32, best_phase_id);
} else {
ESP_EARLY_LOGW(TAG, "tuning fail, best phase id is fallen back to index %"PRIu32"", best_phase_id);
}
return best_phase_id;
}
void mspi_timing_psram_set_best_tuning_phase(const void *configs, uint8_t best_id)
{
s_psram_best_phase = ((const mspi_timing_config_t *)configs)->phase[best_id];
}
void mspi_timing_get_psram_tuning_delaylines(mspi_timing_config_t *configs)
{
ESP_EARLY_LOGD(TAG, "sizeof(delayline_config_t): %d, sizeof(test_config): %d", sizeof(delayline_config_t), sizeof(s_test_delayline_config));
memcpy(configs, &s_test_delayline_config, sizeof(s_test_delayline_config));
}
void mspi_timing_config_psram_set_tuning_delayline(const void *configs, uint8_t id)
{
assert(s_psram_best_phase != MSPI_LL_DQS_PHASE_MAX);
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, s_psram_best_phase);
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, s_psram_best_phase);
ESP_EARLY_LOGD(TAG, "set to best phase: %d", s_psram_best_phase);
const delayline_config_t *delayline_config = &((mspi_timing_config_t *)configs)->delayline_table[id];
for (int i = 0; i < MSPI_LL_PIN_MAX; i++) {
if (i == MSPI_LL_PIN_DQS0 || i == MSPI_LL_PIN_DQS1) {
mspi_timing_ll_set_delayline(i, delayline_config->dqs_delayline);
} else {
mspi_timing_ll_set_delayline(i, delayline_config->data_delayline);
}
}
}
uint32_t mspi_timing_psram_select_best_tuning_delayline(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr)
{
assert(consecutive_length <= 32);
uint32_t bset_delayline_id = 0;
if (consecutive_length <= 1) {
bset_delayline_id = 0;
ESP_EARLY_LOGW(TAG, "tuning fail, best delayline id is fallen back to index %"PRIu32"", bset_delayline_id);
} else {
bset_delayline_id = end - consecutive_length / 2;
ESP_EARLY_LOGI(TAG, "tuning success, best delayline id is %"PRIu32, bset_delayline_id);
}
return bset_delayline_id;
}
void mspi_timing_psram_set_best_tuning_delayline(const void *configs, uint8_t best_id)
{
s_psram_best_delayline = ((mspi_timing_config_t *)configs)->delayline_table[best_id];
}
void mspi_timing_psram_config_clear_tuning_regs(bool control_both_mspi)
{
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, 0);
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, 0);
for (int i = 0; i < MSPI_LL_PIN_MAX; i++) {
mspi_timing_ll_set_delayline(i, 0);
}
}
void mspi_timing_psram_config_set_tuning_regs(bool control_both_mspi)
{
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, s_psram_best_phase);
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, s_psram_best_phase);
for (int i = 0; i < MSPI_LL_PIN_MAX; i++) {
if (i == MSPI_LL_PIN_DQS0 || i == MSPI_LL_PIN_DQS1) {
mspi_timing_ll_set_delayline(i, s_psram_best_delayline.dqs_delayline);
} else {
mspi_timing_ll_set_delayline(i, s_psram_best_delayline.data_delayline);
}
}
}
void mspi_timing_flash_config_set_tuning_regs(bool control_both_mspi)
{
//no need for now, may need set drvs
//keep for compatibility
}
void mspi_timing_flash_config_clear_tuning_regs(bool control_both_mspi)
{
//no need for now, may need clear drvs
//keep for compatibility
}