mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
238 lines
9.0 KiB
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
|
|
}
|