/* * 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 #include #include #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"; 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}; 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 = ((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); } } ESP_EARLY_LOGD(TAG, "set to delayline: {%d, %d}", delayline_config->data_delayline, delayline_config->dqs_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 }