blob: 68c63c4f09a6f43ea4908a98d85d2079a0aff187 [file] [log] [blame]
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_spi_timing.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <debug.h>
#include <sys/param.h>
#include "xtensa.h"
#include "esp32s3_gpio.h"
#include "esp32s3_psram.h"
#include "esp32s3_spi_timing.h"
#include "hardware/esp32s3_spi_mem_reg.h"
#include "hardware/esp32s3_iomux.h"
#include "hardware/esp32s3_gpio.h"
#include "hardware/esp32s3_gpio_sigmap.h"
#include "rom/esp32s3_spiflash.h"
#include "rom/esp32s3_opi_flash.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define CORE_CLK_REG_SEL_80M 0
#define CORE_CLK_REG_SEL_120M 1
#define CORE_CLK_REG_SEL_160M 2
#define CORE_CLK_REG_SEL_240M 3
#if defined(CONFIG_ESP32S3_FLASH_SAMPLE_MODE_DTR) || \
defined(CONFIG_ESP32S3_SPIRAM_MODE_OCT)
# define ESP32S3_SPI_TIMING_CORE_CLOCK_DIV 2
#else
# define ESP32S3_SPI_TIMING_CORE_CLOCK_DIV 1
#endif
#if defined(CONFIG_ESP32S3_FLASH_SAMPLE_MODE_DTR)
# if defined(CONFIG_ESP32S3_FLASH_FREQ_80M)
# define ESP32S3_SPI_TIMING_FLASH_CORE_CLK 160
# elif defined(CONFIG_ESP32S3_FLASH_FREQ_120M)
# define ESP32S3_SPI_TIMING_FLASH_CORE_CLK 240
# endif
#elif defined(CONFIG_ESP32S3_FLASH_SAMPLE_MODE_STR)
# if defined(CONFIG_ESP32S3_FLASH_FREQ_120M)
# if ESP32S3_SPI_TIMING_CORE_CLOCK_DIV == 1
# define ESP32S3_SPI_TIMING_FLASH_CORE_CLK 120
# elif ESP32S3_SPI_TIMING_CORE_CLOCK_DIV == 2
# define ESP32S3_SPI_TIMING_FLASH_CORE_CLK 240
# endif
# endif
#endif
#if defined(CONFIG_ESP32S3_SPIRAM_MODE_OCT)
# if defined(CONFIG_ESP32S3_SPIRAM_SPEED_80M)
# define ESP32S3_SPI_TIMING_PSRAM_CORE_CLK 160
# endif
#elif defined(ESP32S3_SPI_TIMING_PSRAM_STR_MODE)
# if defined(CONFIG_ESP32S3_SPIRAM_SPEED_120M)
# if ESP32S3_SPI_TIMING_CORE_CLOCK_DIV == 1
# define ESP32S3_SPI_TIMING_PSRAM_CORE_CLK 120
# elif ESP32S3_SPI_TIMING_CORE_CLOCK_DIV == 2
# define ESP32S3_SPI_TIMING_PSRAM_CORE_CLK 240
# endif
# endif
#endif
#if ESP32S3_SPI_TIMING_FLASH_TUNING
# if ESP32S3_SPI_TIMING_PSRAM_TUNING
# if ESP32S3_SPI_TIMING_FLASH_CORE_CLK != ESP32S3_SPI_TIMING_PSRAM_CORE_CLK
# error "FLASH and PSRAM Mode configuration are not supported"
# endif
# define ESP32S3_SPI_TIMING_CORE_CLK ESP32S3_SPI_TIMING_FLASH_CORE_CLK
# else
# if ESP32S3_SPI_TIMING_FLASH_CORE_CLK % ESP32S3_SPI_TIMING_PSRAM_CLOCK != 0
# error "FLASH and PSRAM Mode configuration are not supported"
# endif
# define ESP32S3_SPI_TIMING_CORE_CLK ESP32S3_SPI_TIMING_FLASH_CORE_CLK
# endif
#else
# if ESP32S3_SPI_TIMING_PSRAM_TUNING
# if ESP32S3_SPI_TIMING_PSRAM_CORE_CLK % ESP32S3_SPI_TIMING_FLASH_CLOCK != 0
# error "FLASH and PSRAM Mode configuration are not supported"
# endif
# define ESP32S3_SPI_TIMING_CORE_CLK ESP32S3_SPI_TIMING_PSRAM_CORE_CLK
# else
# define ESP32S3_SPI_TIMING_CORE_CLK 80
# endif
#endif
#define ESP32S3_CHECK_POWER_OF_2(n) ((((n) & ((~(n)) + 1))) == (n))
#ifdef CONFIG_ESP32S3_FLASH_SAMPLE_MODE_DTR
# if ESP32S3_CHECK_POWER_OF_2(ESP32S3_SPI_TIMING_CORE_CLK / ESP32S3_SPI_TIMING_FLASH_CLOCK) == 0
# error "FLASH and PSRAM Mode configuration are not supported"
# endif
#endif
#ifdef CONFIG_ESP32S3_SPIRAM_MODE_OCT
# if ESP32S3_CHECK_POWER_OF_2(ESP32S3_SPI_TIMING_CORE_CLK / ESP32S3_SPI_TIMING_PSRAM_CLOCK) == 0
# error "FLASH and PSRAM Mode configuration are not supported"
# endif
#endif
#if ESP32S3_SPI_TIMING_CORE_CLK == 80
# define DEFAULT_CORE_CLOCK CORE_CLOCK_80M
# define DEFAULT_CORE_CLK_REG CORE_CLK_REG_SEL_80M
#elif ESP32S3_SPI_TIMING_CORE_CLK == 120
# define DEFAULT_CORE_CLOCK CORE_CLOCK_120M
# define DEFAULT_CORE_CLK_REG CORE_CLK_REG_SEL_120M
#elif ESP32S3_SPI_TIMING_CORE_CLK == 160
# define DEFAULT_CORE_CLOCK CORE_CLOCK_160M
# define DEFAULT_CORE_CLK_REG CORE_CLK_REG_SEL_160M
#elif ESP32S3_SPI_TIMING_CORE_CLK == 240
# define DEFAULT_CORE_CLOCK CORE_CLOCK_240M
# define DEFAULT_CORE_CLK_REG CORE_CLK_REG_SEL_240M
#else
# error "SPI timing core clock is invalid"
#endif
#if defined(CONFIG_ESP32S3_FLASH_FREQ_20M)
# define FLASH_CLOCK_DIVIDER (ESP32S3_SPI_TIMING_CORE_CLK / 20)
#elif defined(CONFIG_ESP32S3_FLASH_FREQ_40M)
# define FLASH_CLOCK_DIVIDER (ESP32S3_SPI_TIMING_CORE_CLK / 40)
#elif defined(CONFIG_ESP32S3_FLASH_FREQ_80M)
# define FLASH_CLOCK_DIVIDER (ESP32S3_SPI_TIMING_CORE_CLK / 80)
#elif defined(CONFIG_ESP32S3_FLASH_FREQ_120M)
# define FLASH_CLOCK_DIVIDER (ESP32S3_SPI_TIMING_CORE_CLK / 120)
#else
# error "SPI timing flash clock is invalid"
#endif
#if defined(CONFIG_ESP32S3_SPIRAM_SPEED_40M)
# define PSRAM_CLOCK_DIVIDER (ESP32S3_SPI_TIMING_CORE_CLK / 40)
#elif defined(CONFIG_ESP32S3_SPIRAM_SPEED_80M)
# define PSRAM_CLOCK_DIVIDER (ESP32S3_SPI_TIMING_CORE_CLK / 80)
#elif defined(CONFIG_ESP32S3_SPIRAM_SPEED_120M)
# define PSRAM_CLOCK_DIVIDER (ESP32S3_SPI_TIMING_CORE_CLK / 120)
#else
# define PSRAM_CLOCK_DIVIDER 0
#endif
#define MSPI_TIMING_LL_FLASH_OCT_MASK (SPI_MEM_FCMD_OCT | SPI_MEM_FADDR_OCT | SPI_MEM_FDIN_OCT | SPI_MEM_FDOUT_OCT)
#define MSPI_TIMING_LL_FLASH_QUAD_MASK (SPI_MEM_FASTRD_MODE | SPI_MEM_FREAD_DUAL | SPI_MEM_FREAD_DIO | SPI_MEM_FREAD_QUAD | SPI_MEM_FREAD_QIO)
#define MSPI_TIMING_LL_FLASH_QIO_MODE_MASK (SPI_MEM_FREAD_QIO | SPI_MEM_FASTRD_MODE)
#define MSPI_TIMING_LL_FLASH_QUAD_MODE_MASK (SPI_MEM_FREAD_QUAD | SPI_MEM_FASTRD_MODE)
#define MSPI_TIMING_LL_FLASH_DIO_MODE_MASK (SPI_MEM_FREAD_DIO | SPI_MEM_FASTRD_MODE)
#define MSPI_TIMING_LL_FLASH_DUAL_MODE_MASK (SPI_MEM_FREAD_DUAL | SPI_MEM_FASTRD_MODE)
#define MSPI_TIMING_LL_FLASH_FAST_MODE_MASK (SPI_MEM_FASTRD_MODE)
#define MSPI_TIMING_PSRAM_DTR_MODE CONFIG_ESP32S3_SPIRAM_MODE_OCT
#define MSPI_TIMING_PSRAM_STR_MODE !CONFIG_ESP32S3_SPIRAM_MODE_OCT
#define MSPI_TIMING_FLASH_DTR_MODE CONFIG_ESP32S3_FLASH_SAMPLE_MODE_DTR
#define MSPI_TIMING_FLASH_STR_MODE CONFIG_ESP32S3_FLASH_SAMPLE_MODE_STR
#define MSPI_TIMING_TEST_DATA_LEN 1024
#define MSPI_TIMING_PSRAM_TEST_DATA_ADDR 0
#define MSPI_TIMING_FLASH_TEST_DATA_ADDR 0
#define OPI_PSRAM_SYNC_READ 0x0000
#define OPI_PSRAM_SYNC_WRITE 0x8080
#define OCT_PSRAM_RD_DUMMY_NUM (2*(10-1))
#define OCT_PSRAM_WR_DUMMY_NUM (2*(5-1))
#define QPI_PSRAM_FAST_READ 0XEB
#define QPI_PSRAM_WRITE 0X38
#define QPI_PSRAM_FAST_READ_DUMMY 6
#define NOT_INIT_INT 127
#define g_spiflash_dummy_len_plus (rom_spiflash_legacy_data->dummy_len_plus)
/****************************************************************************
* Private Types
****************************************************************************/
enum core_clock_e
{
CORE_CLOCK_80M = 0,
CORE_CLOCK_120M = 1,
CORE_CLOCK_160M = 2,
CORE_CLOCK_240M = 3
};
/****************************************************************************
* Private Functions Prototypes
****************************************************************************/
static void IRAM_ATTR set_psram_clock(uint8_t spi_num, uint32_t freqdiv);
static void IRAM_ATTR set_flash_clock(uint8_t spi_num, uint32_t freqdiv);
#if ESP32S3_SPI_TIMING_FLASH_TUNING || ESP32S3_SPI_TIMING_PSRAM_TUNING
static void init_spi1_for_tuning(bool is_flash);
static uint32_t get_dummy(void);
static void set_flash_extra_dummy(uint8_t spi_num, uint8_t extra_dummy);
static void set_psram_extra_dummy(uint8_t spi_num, uint8_t extra_dummy);
static void set_flash_din_mode_num(uint8_t spi_num, uint8_t din_mode,
uint8_t din_num);
static void set_psram_din_mode_num(uint8_t spi_num, uint8_t din_mode,
uint8_t din_num);
#if ESP32S3_SPI_TIMING_FLASH_TUNING
static void config_flash_read_data(uint8_t *buf, uint32_t addr,
uint32_t len);
static void config_flash_set_tuning_regs(const tuning_param_s *params);
static void get_flash_tuning_configs(tuning_config_s *config);
#endif
#if ESP32S3_SPI_TIMING_PSRAM_TUNING
static void psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len);
static void psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len);
static void config_psram_read_write_data(uint8_t *buf, uint32_t addr,
uint32_t len, bool is_read);
static void get_psram_tuning_configs(tuning_config_s *config);
static void config_psram_set_tuning_regs(const tuning_param_s *params);
#endif
static void sweep_for_success_sample_points(const uint8_t *reference_data,
const tuning_config_s *config, bool is_flash, uint8_t *out_array);
static void find_max_consecutive_success_points(uint8_t *array,
uint32_t size, uint32_t *out_length, uint32_t *out_end_index);
#if MSPI_TIMING_FLASH_STR_MODE || MSPI_TIMING_PSRAM_STR_MODE
static uint32_t select_best_tuning_config_str(tuning_config_s *config,
uint32_t consecutive_length, uint32_t end);
#endif
#if MSPI_TIMING_FLASH_DTR_MODE || MSPI_TIMING_PSRAM_DTR_MODE
static uint32_t select_best_tuning_config_dtr(tuning_config_s *config,
uint32_t consecutive_length, uint32_t end);
#endif
static void select_best_tuning_config(tuning_config_s *config,
uint32_t consecutive_length, uint32_t end,
const uint8_t *reference_data, bool is_flash);
static void set_timing_tuning_regs(bool control_spi1);
static void clear_timing_tuning_regs(bool control_spi1);
static void do_tuning(const uint8_t *reference_data,
tuning_config_s *timing_config, bool is_flash);
#endif
/****************************************************************************
* Extern Functions declaration
****************************************************************************/
#if CONFIG_ESP32S3_SPIRAM_MODE_QUAD
extern void psram_exec_cmd(int spi_num, int mode,
uint32_t cmd, int cmd_bit_len,
uint32_t addr, int addr_bit_len,
int dummy_bits, uint8_t *mosi_data,
int mosi_bit_len, uint8_t *miso_data,
int miso_bit_len, uint32_t cs_mask,
bool is_write_erase_operation);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
#if ESP32S3_SPI_TIMING_FLASH_TUNING || ESP32S3_SPI_TIMING_PSRAM_TUNING
#if CONFIG_ESP32S3_SPIRAM_MODE_QUAD
static uint8_t g_psram_extra_dummy;
#endif
static uint8_t g_flash_extra_dummy[2] =
{
NOT_INIT_INT,
NOT_INIT_INT
};
static tuning_param_s g_flash_timing_tuning_config;
static tuning_param_s g_psram_timing_tuning_config;
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: set_psram_clock
*
* Description:
* Set PSRAM clock.
*
* Input Parameters:
* spi_num - SPI port
* freqdiv - SPI clock divideor
*
* Returned Value:
* None.
*
****************************************************************************/
static void IRAM_ATTR set_psram_clock(uint8_t spi_num, uint32_t freqdiv)
{
if (freqdiv == 1)
{
WRITE_PERI_REG(SPI_MEM_SRAM_CLK_REG(spi_num), SPI_MEM_SCLK_EQU_SYSCLK);
}
else
{
uint32_t freqbits = ((freqdiv - 1) << SPI_MEM_SCLKCNT_N_S) |
((freqdiv / 2 - 1) << SPI_MEM_SCLKCNT_H_S) |
((freqdiv - 1) << SPI_MEM_SCLKCNT_L_S);
WRITE_PERI_REG(SPI_MEM_SRAM_CLK_REG(spi_num), freqbits);
}
}
/****************************************************************************
* Name: set_flash_clock
*
* Description:
* Set flash clock.
*
* Input Parameters:
* spi_num - SPI port
* freqdiv - SPI clock divideor
*
* Returned Value:
* None.
*
****************************************************************************/
static void IRAM_ATTR set_flash_clock(uint8_t spi_num, uint32_t freqdiv)
{
DEBUGASSERT(freqdiv > 0);
if (freqdiv == 1)
{
WRITE_PERI_REG(SPI_MEM_CLOCK_REG(spi_num), SPI_MEM_CLK_EQU_SYSCLK);
}
else
{
uint32_t freqbits = ((freqdiv - 1) << SPI_MEM_CLKCNT_N_S) |
((freqdiv / 2 - 1) << SPI_MEM_CLKCNT_H_S) |
((freqdiv - 1) << SPI_MEM_CLKCNT_L_S);
WRITE_PERI_REG(SPI_MEM_CLOCK_REG(spi_num), freqbits);
}
}
#if ESP32S3_SPI_TIMING_FLASH_TUNING || ESP32S3_SPI_TIMING_PSRAM_TUNING
/****************************************************************************
* Name: init_spi1_for_tuning
*
* Description:
* Initialize SPI1 for timing tuning.
*
* Input Parameters:
* is_flash - is flash or not
*
* Returned Value:
* None.
*
****************************************************************************/
static void init_spi1_for_tuning(bool is_flash)
{
/* Set SPI1 core clock. SPI0 and SPI1 share the register for core clock.
* So we only set SPI0 here.
*/
REG_SET_FIELD(SPI_MEM_CORE_CLK_SEL_REG(0),
SPI_MEM_CORE_CLK_SEL,
DEFAULT_CORE_CLK_REG);
/* Set SPI1 module clock as required */
if (is_flash)
{
set_flash_clock(1, FLASH_CLOCK_DIVIDER);
/* Enable Flash HCLK */
REG_SET_BIT(SPI_MEM_TIMING_CALI_REG(0), SPI_MEM_TIMING_CLK_ENA);
}
else
{
/* We use SPI1 Flash to tune PSRAM, PSRAM timing related regs
* do nothing on SPI1
*/
set_flash_clock(1, PSRAM_CLOCK_DIVIDER);
/* Enable PSRAM HCLK */
REG_SET_BIT(SPI_MEM_SPI_SMEM_TIMING_CALI_REG(0),
SPI_MEM_SPI_SMEM_TIMING_CLK_ENA);
}
}
/****************************************************************************
* Name: get_dummy
*
* Description:
* Get dummy cycle length
*
* Input Parameters:
* None
*
* Returned Value:
* Dummy cycle length.
*
****************************************************************************/
static uint32_t get_dummy(void)
{
uint32_t ctrl_reg = READ_PERI_REG(SPI_MEM_CTRL_REG(0));
if (ctrl_reg & MSPI_TIMING_LL_FLASH_OCT_MASK)
{
DEBUGPANIC();
return 0;
}
switch (ctrl_reg & MSPI_TIMING_LL_FLASH_QUAD_MASK)
{
case MSPI_TIMING_LL_FLASH_QIO_MODE_MASK:
return SPI1_R_QIO_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_DIO_MODE_MASK:
return SPI1_R_DIO_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_QUAD_MODE_MASK:
case MSPI_TIMING_LL_FLASH_DUAL_MODE_MASK:
case MSPI_TIMING_LL_FLASH_FAST_MODE_MASK:
return SPI1_R_FAST_DUMMY_CYCLELEN;
default:
DEBUGPANIC();
return 0;
}
}
/****************************************************************************
* Name: set_flash_extra_dummy
*
* Description:
* Set MSPI Flash extra dummy
*
* Input Parameters:
* spi_num - SPI0 / 1
* extra_dummy - extra dummy
*
* Returned Value:
* None.
*
****************************************************************************/
static void set_flash_extra_dummy(uint8_t spi_num, uint8_t extra_dummy)
{
#ifdef CONFIG_ESP32S3_FLASH_MODE_OCT
if (extra_dummy > 0)
{
SET_PERI_REG_MASK(SPI_MEM_TIMING_CALI_REG(spi_num),
SPI_MEM_TIMING_CALI_M);
SET_PERI_REG_BITS(SPI_MEM_TIMING_CALI_REG(spi_num),
SPI_MEM_EXTRA_DUMMY_CYCLELEN_V,
extra_dummy,
SPI_MEM_EXTRA_DUMMY_CYCLELEN_S);
}
else
{
CLEAR_PERI_REG_MASK(SPI_MEM_TIMING_CALI_REG(spi_num),
SPI_MEM_TIMING_CALI_M);
SET_PERI_REG_BITS(SPI_MEM_TIMING_CALI_REG(spi_num),
SPI_MEM_EXTRA_DUMMY_CYCLELEN_V, 0,
SPI_MEM_EXTRA_DUMMY_CYCLELEN_S);
}
return;
#endif
if (g_flash_extra_dummy[spi_num] == NOT_INIT_INT)
{
g_flash_extra_dummy[spi_num] = g_spiflash_dummy_len_plus[spi_num];
}
g_spiflash_dummy_len_plus[spi_num] = g_flash_extra_dummy[(int)spi_num] +
extra_dummy;
/* Only Quad Flash will run into this branch. */
uint32_t dummy = get_dummy();
/* Set MSPI Quad Flash dummy */
SET_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_USR_DUMMY);
SET_PERI_REG_BITS(SPI_MEM_USER1_REG(spi_num), SPI_MEM_USR_DUMMY_CYCLELEN_V,
dummy + g_spiflash_dummy_len_plus[spi_num],
SPI_MEM_USR_DUMMY_CYCLELEN_S);
}
/****************************************************************************
* Name: set_psram_extra_dummy
*
* Description:
* Set MSPI PSRAM extra dummy
*
* Input Parameters:
* spi_num - SPI0 / 1
* extra_dummy - extra dummy
*
* Returned Value:
* None.
*
****************************************************************************/
static void set_psram_extra_dummy(uint8_t spi_num, uint8_t extra_dummy)
{
#if CONFIG_ESP32S3_SPIRAM_MODE_OCT
/* Set MSPI Octal PSRAM extra dummy */
if (extra_dummy > 0)
{
SET_PERI_REG_MASK(SPI_MEM_SPI_SMEM_TIMING_CALI_REG(spi_num),
SPI_MEM_SPI_SMEM_TIMING_CALI_M);
SET_PERI_REG_BITS(SPI_MEM_SPI_SMEM_TIMING_CALI_REG(spi_num),
SPI_MEM_SPI_SMEM_EXTRA_DUMMY_CYCLELEN_V,
extra_dummy,
SPI_MEM_SPI_SMEM_EXTRA_DUMMY_CYCLELEN_S);
}
else
{
CLEAR_PERI_REG_MASK(SPI_MEM_SPI_SMEM_TIMING_CALI_REG(spi_num),
SPI_MEM_SPI_SMEM_TIMING_CALI_M);
SET_PERI_REG_BITS(SPI_MEM_SPI_SMEM_TIMING_CALI_REG(spi_num),
SPI_MEM_SPI_SMEM_EXTRA_DUMMY_CYCLELEN_V, 0,
SPI_MEM_SPI_SMEM_EXTRA_DUMMY_CYCLELEN_S);
}
#elif CONFIG_ESP32S3_SPIRAM_MODE_QUAD
/* Set MSPI QUAD PSRAM dummy.
* HW workaround: Use normal dummy register to set extra dummy, the
* calibration dedicated extra dummy register doesn't work for quad mode
*/
SET_PERI_REG_MASK(SPI_MEM_CACHE_SCTRL_REG(spi_num),
SPI_MEM_USR_RD_SRAM_DUMMY_M);
SET_PERI_REG_BITS(SPI_MEM_CACHE_SCTRL_REG(spi_num),
SPI_MEM_SRAM_RDUMMY_CYCLELEN_V,
(QPI_PSRAM_FAST_READ_DUMMY + extra_dummy - 1),
SPI_MEM_SRAM_RDUMMY_CYCLELEN_S);
#endif
}
/****************************************************************************
* Name: set_flash_din_mode_num
*
* Description:
* Set MSPI Flash Din Mode and Din Num
*
* Input Parameters:
* spi_num - SPI0 / 1
* din_mode - Din mode
* din_num - Din num
*
* Returned Value:
* None.
*
****************************************************************************/
static void set_flash_din_mode_num(uint8_t spi_num, uint8_t din_mode,
uint8_t din_num)
{
/* Set MSPI Flash din mode */
uint32_t reg_val = (getreg32(SPI_MEM_DIN_MODE_REG(spi_num)) &
(~(SPI_MEM_DIN0_MODE_M | SPI_MEM_DIN1_MODE_M | SPI_MEM_DIN2_MODE_M |
SPI_MEM_DIN3_MODE_M | SPI_MEM_DIN4_MODE_M | SPI_MEM_DIN5_MODE_M |
SPI_MEM_DIN6_MODE_M | SPI_MEM_DIN7_MODE_M | SPI_MEM_DINS_MODE_M))) |
(din_mode << SPI_MEM_DIN0_MODE_S) | (din_mode << SPI_MEM_DIN1_MODE_S) |
(din_mode << SPI_MEM_DIN2_MODE_S) | (din_mode << SPI_MEM_DIN3_MODE_S) |
(din_mode << SPI_MEM_DIN4_MODE_S) | (din_mode << SPI_MEM_DIN5_MODE_S) |
(din_mode << SPI_MEM_DIN6_MODE_S) | (din_mode << SPI_MEM_DIN7_MODE_S) |
(din_mode << SPI_MEM_DINS_MODE_S);
putreg32(reg_val, SPI_MEM_DIN_MODE_REG(spi_num));
/* Set MSPI Flash din num */
reg_val = (getreg32(SPI_MEM_DIN_NUM_REG(spi_num)) &
(~(SPI_MEM_DIN0_NUM_M | SPI_MEM_DIN1_NUM_M | SPI_MEM_DIN2_NUM_M |
SPI_MEM_DIN3_NUM_M | SPI_MEM_DIN4_NUM_M | SPI_MEM_DIN5_NUM_M |
SPI_MEM_DIN6_NUM_M | SPI_MEM_DIN7_NUM_M | SPI_MEM_DINS_NUM_M))) |
(din_num << SPI_MEM_DIN0_NUM_S) | (din_num << SPI_MEM_DIN1_NUM_S) |
(din_num << SPI_MEM_DIN2_NUM_S) | (din_num << SPI_MEM_DIN3_NUM_S) |
(din_num << SPI_MEM_DIN4_NUM_S) | (din_num << SPI_MEM_DIN5_NUM_S) |
(din_num << SPI_MEM_DIN6_NUM_S) | (din_num << SPI_MEM_DIN7_NUM_S) |
(din_num << SPI_MEM_DINS_NUM_S);
putreg32(reg_val, SPI_MEM_DIN_NUM_REG(spi_num));
}
/****************************************************************************
* Name: set_psram_din_mode_num
*
* Description:
* Set MSPI PSRAM Din Mode and Din Num
*
* Input Parameters:
* spi_num - SPI0 / 1
* din_mode - Din mode
* din_num - Din num
*
* Returned Value:
* None.
*
****************************************************************************/
static void set_psram_din_mode_num(uint8_t spi_num, uint8_t din_mode,
uint8_t din_num)
{
/* Set MSPI PSRAM din mode */
uint32_t reg_val = (getreg32(SPI_MEM_SPI_SMEM_DIN_MODE_REG(spi_num)) &
(~(SPI_MEM_SPI_SMEM_DIN0_MODE_M | SPI_MEM_SPI_SMEM_DIN1_MODE_M |
SPI_MEM_SPI_SMEM_DIN2_MODE_M | SPI_MEM_SPI_SMEM_DIN3_MODE_M |
SPI_MEM_SPI_SMEM_DIN4_MODE_M | SPI_MEM_SPI_SMEM_DIN5_MODE_M |
SPI_MEM_SPI_SMEM_DIN6_MODE_M | SPI_MEM_SPI_SMEM_DIN7_MODE_M |
SPI_MEM_SPI_SMEM_DINS_MODE_M))) |
(din_mode << SPI_MEM_SPI_SMEM_DIN0_MODE_S) |
(din_mode << SPI_MEM_SPI_SMEM_DIN1_MODE_S) |
(din_mode << SPI_MEM_SPI_SMEM_DIN2_MODE_S) |
(din_mode << SPI_MEM_SPI_SMEM_DIN3_MODE_S) |
(din_mode << SPI_MEM_SPI_SMEM_DIN4_MODE_S) |
(din_mode << SPI_MEM_SPI_SMEM_DIN5_MODE_S) |
(din_mode << SPI_MEM_SPI_SMEM_DIN6_MODE_S) |
(din_mode << SPI_MEM_SPI_SMEM_DIN7_MODE_S) |
(din_mode << SPI_MEM_SPI_SMEM_DINS_MODE_S);
putreg32(reg_val, SPI_MEM_SPI_SMEM_DIN_MODE_REG(spi_num));
/* Set MSPI PSRAM din num */
reg_val = (getreg32(SPI_MEM_SPI_SMEM_DIN_NUM_REG(spi_num)) &
(~(SPI_MEM_SPI_SMEM_DIN0_NUM_M | SPI_MEM_SPI_SMEM_DIN1_NUM_M |
SPI_MEM_SPI_SMEM_DIN2_NUM_M | SPI_MEM_SPI_SMEM_DIN3_NUM_M |
SPI_MEM_SPI_SMEM_DIN4_NUM_M | SPI_MEM_SPI_SMEM_DIN5_NUM_M |
SPI_MEM_SPI_SMEM_DIN6_NUM_M | SPI_MEM_SPI_SMEM_DIN7_NUM_M |
SPI_MEM_SPI_SMEM_DINS_NUM_M))) |
(din_num << SPI_MEM_SPI_SMEM_DIN0_NUM_S) |
(din_num << SPI_MEM_SPI_SMEM_DIN1_NUM_S) |
(din_num << SPI_MEM_SPI_SMEM_DIN2_NUM_S) |
(din_num << SPI_MEM_SPI_SMEM_DIN3_NUM_S) |
(din_num << SPI_MEM_SPI_SMEM_DIN4_NUM_S) |
(din_num << SPI_MEM_SPI_SMEM_DIN5_NUM_S) |
(din_num << SPI_MEM_SPI_SMEM_DIN6_NUM_S) |
(din_num << SPI_MEM_SPI_SMEM_DIN7_NUM_S) |
(din_num << SPI_MEM_SPI_SMEM_DINS_NUM_S);
putreg32(reg_val, SPI_MEM_SPI_SMEM_DIN_NUM_REG(spi_num));
}
#if ESP32S3_SPI_TIMING_FLASH_TUNING
/****************************************************************************
* Name: config_flash_read_data
*
* Description:
* Configure Flash to read data via SPI1h
*
* Input Parameters:
* buf - data buffer pointer
* addr - target address value
* len - data length
*
* Returned Value:
* None.
*
****************************************************************************/
static void config_flash_read_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
#ifdef CONFIG_ESP32S3_FLASH_MODE_OCT
/* Clear MSPI hw fifo */
for (int i = 0; i < 16; i++)
{
putreg32(0, SPI_MEM_W0_REG(1) + i * 4);
}
esp_rom_opiflash_read_raw(addr, buf, len);
#else
esp_rom_spiflash_read(addr, (uint32_t *)buf, len);
#endif
}
/****************************************************************************
* Name: config_flash_set_tuning_regs
*
* Description:
* Tune Flash timing registers for SPI1 accessing Flash
*
* Input Parameters:
* params - tuning timing parameters pointer
*
* Returned Value:
* None.
*
****************************************************************************/
static void config_flash_set_tuning_regs(const tuning_param_s *params)
{
/* SPI_MEM_DINx_MODE(1), SPI_MEM_DINx_NUM(1) are meaningless SPI0 and SPI1
* share SPI_MEM_DINx_MODE(0), SPI_MEM_DINx_NUM(0) for FLASH timing tuning
* We use SPI1 to get the best Flash timing tuning (mode and num) config
*/
set_flash_din_mode_num(0, params->spi_din_mode, params->spi_din_num);
set_flash_extra_dummy(1, params->extra_dummy_len);
}
/****************************************************************************
* Name: get_flash_tuning_configs
*
* Description:
* Get FLASH tuning configuration.
*
* Input Parameters:
* config - tuning timing configuration pointer
*
* Returned Value:
* None.
*
****************************************************************************/
static void get_flash_tuning_configs(tuning_config_s *config)
{
#if MSPI_TIMING_FLASH_DTR_MODE
# define FLASH_MODE DTR_MODE
#else
# define FLASH_MODE STR_MODE
#endif
#if CONFIG_ESP32S3_FLASH_FREQ_20M
*config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(
ESP32S3_SPI_TIMING_CORE_CLK, 20, FLASH_MODE);
#elif CONFIG_ESP32S3_FLASH_FREQ_40M
*config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(
ESP32S3_SPI_TIMING_CORE_CLK, 40, FLASH_MODE);
#elif CONFIG_ESP32S3_FLASH_FREQ_80M
*config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(
ESP32S3_SPI_TIMING_CORE_CLK, 80, FLASH_MODE);
#elif CONFIG_ESP32S3_FLASH_FREQ_120M
*config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(
ESP32S3_SPI_TIMING_CORE_CLK, 120, FLASH_MODE);
#endif
#undef FLASH_MODE
}
#endif
#if ESP32S3_SPI_TIMING_PSRAM_TUNING
/****************************************************************************
* Name: psram_read_data
*
* Description:
* Configure PSRAM to read data via SPI1.
*
* Input Parameters:
* buf - data buffer pointer
* addr - target address value
* len - data length
*
* Returned Value:
* None.
*
****************************************************************************/
static void psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
#if CONFIG_ESP32S3_SPIRAM_MODE_OCT
/* Clear MSPI hw fifo */
for (int i = 0; i < 16; i++)
{
putreg32(0, SPI_MEM_W0_REG(1) + i * 4);
}
esp_rom_opiflash_exec_cmd(1, ESP_ROM_SPIFLASH_OPI_DTR_MODE,
OPI_PSRAM_SYNC_READ, 16,
addr, 32,
OCT_PSRAM_RD_DUMMY_NUM,
NULL, 0,
buf, len * 8,
BIT(1),
false);
#elif CONFIG_ESP32S3_SPIRAM_MODE_QUAD
psram_exec_cmd(1, 0,
QPI_PSRAM_FAST_READ, 8,
addr, 24,
QPI_PSRAM_FAST_READ_DUMMY + g_psram_extra_dummy,
NULL, 0,
buf, len * 8,
SPI_MEM_CS1_DIS_M,
false);
#endif
}
/****************************************************************************
* Name: psram_write_data
*
* Description:
* Configure PSRAM to write data via SPI1.
*
* Input Parameters:
* buf - data buffer pointer
* addr - target address value
* len - data length
*
* Returned Value:
* None.
*
****************************************************************************/
static void psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
#if CONFIG_ESP32S3_SPIRAM_MODE_OCT
esp_rom_opiflash_exec_cmd(1, ESP_ROM_SPIFLASH_OPI_DTR_MODE,
OPI_PSRAM_SYNC_WRITE, 16,
addr, 32,
OCT_PSRAM_WR_DUMMY_NUM,
buf, len * 8,
NULL, 0,
BIT(1),
false);
#elif CONFIG_ESP32S3_SPIRAM_MODE_QUAD
psram_exec_cmd(1, 0,
QPI_PSRAM_WRITE, 8, /* command and command bit len */
addr, 24, /* address and address bit len */
0, /* dummy bit len */
buf, len * 8, /* tx data and tx bit len */
NULL, 0, /* rx data and rx bit len */
SPI_MEM_CS1_DIS_M, /* cs bit mask */
false); /* whether is program/erase operation */
#endif
}
/****************************************************************************
* Name: config_psram_read_write_data
*
* Description:
* Configure PSRAM to read or write data via SPI1.
*
* Input Parameters:
* buf - data buffer pointer
* addr - target address value
* len - data length
* is_read - is read or write
*
* Returned Value:
* None.
*
****************************************************************************/
static void config_psram_read_write_data(uint8_t *buf, uint32_t addr,
uint32_t len, bool is_read)
{
while (len > 0)
{
uint32_t length = MIN(len, 32);
if (is_read)
{
psram_read_data(buf, addr, length);
}
else
{
psram_write_data(buf, addr, length);
}
addr += length;
buf += length;
len -= length;
}
}
/****************************************************************************
* Name: get_psram_tuning_configs
*
* Description:
* Get PSRAM tuning configuration.
*
* Input Parameters:
* config - tuning timing configuration pointer
*
* Returned Value:
* None.
*
****************************************************************************/
static void get_psram_tuning_configs(tuning_config_s *config)
{
#if MSPI_TIMING_PSRAM_DTR_MODE
# define PSRAM_MODE DTR_MODE
#else
# define PSRAM_MODE STR_MODE
#endif
#if CONFIG_ESP32S3_SPIRAM_SPEED_40M
*config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(
ESP32S3_SPI_TIMING_CORE_CLK, 40, PSRAM_MODE);
#elif CONFIG_ESP32S3_SPIRAM_SPEED_80M
*config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(
ESP32S3_SPI_TIMING_CORE_CLK, 80, PSRAM_MODE);
#elif CONFIG_ESP32S3_SPIRAM_SPEED_120M
*config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(
ESP32S3_SPI_TIMING_CORE_CLK, 120, PSRAM_MODE);
#endif
#undef PSRAM_MODE
}
/****************************************************************************
* Name: config_psram_set_tuning_regs
*
* Description:
* Tune PSRAM timing registers for SPI1 accessing PSRAM
*
* Input Parameters:
* params - tuning timing parameters pointer
*
* Returned Value:
* None.
*
****************************************************************************/
static void config_psram_set_tuning_regs(const tuning_param_s *params)
{
/* 1. SPI_MEM_SPI_SMEM_DINx_MODE(1), SPI_MEM_SPI_SMEM_DINx_NUM(1) are
* meaningless SPI0 and SPI1 share the SPI_MEM_SPI_SMEM_DINx_MODE(0),
* SPI_MEM_SPI_SMEM_DINx_NUM(0) for PSRAM timing tuning
* 2. We use SPI1 to get the best PSRAM timing tuning (mode and num) config
*/
set_psram_din_mode_num(0, params->spi_din_mode, params->spi_din_num);
#if CONFIG_ESP32S3_SPIRAM_MODE_OCT
/* On 728, for SPI1, flash and psram share the extra dummy register */
set_flash_extra_dummy(1, params->extra_dummy_len);
#elif CONFIG_ESP32S3_SPIRAM_MODE_QUAD
/* Update this `g_psram_extra_dummy`, the `psram_read_data` will
* set dummy according to this `g_psram_extra_dummy`
*/
g_psram_extra_dummy = params->extra_dummy_len;
/* Set MSPI Quad Flash dummy */
SET_PERI_REG_MASK(SPI_MEM_USER_REG(1), SPI_MEM_USR_DUMMY);
SET_PERI_REG_BITS(SPI_MEM_USER1_REG(1), SPI_MEM_USR_DUMMY_CYCLELEN_V,
g_psram_extra_dummy - 1, SPI_MEM_USR_DUMMY_CYCLELEN_S);
#endif
}
#endif
/****************************************************************************
* Name: sweep_for_success_sample_points
*
* Description:
* Use different SPI1 timing tuning config to read data to see if current
* MSPI sampling is successful.The sampling result will be stored in an
* array. In this array, successful item will be 1, failed item will be 0.
*
* Input Parameters:
* reference_data - reference data pointer
* config - tuning timing configuration pointer
* is_flash - is flash or not
* out_array - last success point pointer
*
* Returned Value:
* None.
*
****************************************************************************/
static void sweep_for_success_sample_points(const uint8_t *reference_data,
const tuning_config_s *config, bool is_flash, uint8_t *out_array)
{
uint32_t config_idx = 0;
uint8_t read_data[MSPI_TIMING_TEST_DATA_LEN];
for (config_idx = 0; config_idx < config->config_num; config_idx++)
{
memset(read_data, 0, MSPI_TIMING_TEST_DATA_LEN);
#if ESP32S3_SPI_TIMING_FLASH_TUNING
if (is_flash)
{
config_flash_set_tuning_regs(&(config->config_table[config_idx]));
config_flash_read_data(read_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR,
sizeof(read_data));
}
#endif
#if ESP32S3_SPI_TIMING_PSRAM_TUNING
if (!is_flash)
{
config_psram_set_tuning_regs(&(config->config_table[config_idx]));
config_psram_read_write_data(read_data,
MSPI_TIMING_PSRAM_TEST_DATA_ADDR,
MSPI_TIMING_TEST_DATA_LEN, true);
}
#endif
if (memcmp(reference_data, read_data, sizeof(read_data)) == 0)
{
out_array[config_idx] = 1;
minfo("%d, good\n", config_idx);
}
else
{
minfo("%d, bad\n", config_idx);
}
}
}
/****************************************************************************
* Name: find_max_consecutive_success_points
*
* Description:
* Find max consecutive success points.
*
* Input Parameters:
* array - sampling results pointer
* size - sampling results length
* out_length - consecutive length pointer
* out_end_index - last success point pointer
*
* Returned Value:
* None.
*
****************************************************************************/
static void find_max_consecutive_success_points(uint8_t *array,
uint32_t size, uint32_t *out_length, uint32_t *out_end_index)
{
uint32_t max = 0;
uint32_t match_num = 0;
uint32_t i = 0;
uint32_t end = 0;
while (i < size)
{
if (array[i])
{
match_num++;
}
else
{
if (match_num > max)
{
max = match_num;
end = i - 1;
}
match_num = 0;
}
i++;
}
*out_length = match_num > max ? match_num : max;
*out_end_index = match_num == size ? size : end;
}
/****************************************************************************
* Name: select_best_tuning_config_str
*
* Description:
* Select the STR best point
*
* Input Parameters:
* config - tuning timing configuration pointer
* consecutive_length - consecutive length
* end - last success point
*
* Returned Value:
* Returns the STR best point.
*
****************************************************************************/
#if MSPI_TIMING_FLASH_STR_MODE || MSPI_TIMING_PSRAM_STR_MODE
static uint32_t select_best_tuning_config_str(tuning_config_s *config,
uint32_t consecutive_length, uint32_t end)
{
#if (ESP32S3_SPI_TIMING_CORE_CLK == 120 || ESP32S3_SPI_TIMING_CORE_CLK == 240)
minfo("DO NOT USE FOR MASS PRODUCTION! Timing parameters may be updated");
/* STR best point scheme */
uint32_t best_point;
if (consecutive_length <= 2 || consecutive_length >= 5)
{
/* tuning is FAIL, select default point, and generate a warning */
best_point = config->default_config_id;
minfo("tuning fail, best point is fallen back to index %d\n",
best_point);
}
else
{
/* consecutive length : 3 or 4 */
best_point = end - consecutive_length / 2;
minfo("tuning success, best point is index %d\n", best_point);
}
return best_point;
#else
/* won't reach here */
abort();
#endif
}
#endif
/****************************************************************************
* Name: select_best_tuning_config_dtr
*
* Description:
* Select the DTR best point
*
* Input Parameters:
* config - tuning timing configuration pointer
* consecutive_length - consecutive length
* end - last success point
*
* Returned Value:
* Returns the DTR best point.
*
****************************************************************************/
#if MSPI_TIMING_FLASH_DTR_MODE || MSPI_TIMING_PSRAM_DTR_MODE
static uint32_t select_best_tuning_config_dtr(tuning_config_s *config,
uint32_t consecutive_length, uint32_t end)
{
#if (ESP32S3_SPI_TIMING_CORE_CLK == 160)
/* Core clock 160M DTR best point scheme */
uint32_t best_point;
/* These numbers will probably be same on other chips,
* if this version of algorithm is utilised
*/
if (consecutive_length <= 2 || consecutive_length >= 6)
{
/* tuning is FAIL, select default point, and generate a warning */
best_point = config->default_config_id;
minfo("tuning fail, best point is fallen back to index %d\n",
best_point);
}
else if (consecutive_length <= 4)
{
/* consecutive length : 3 or 4 */
best_point = end - 1;
minfo("tuning success, best point is index %d\n", best_point);
}
else
{
/* consecutive point list length equals 5 */
best_point = end - 2;
minfo("tuning success, best point is index %d\n", best_point);
}
return best_point;
#else
/* won't reach here */
abort();
#endif
}
#endif
/****************************************************************************
* Name: select_best_tuning_config
*
* Description:
* Select the best timing tuning configuration
*
* Input Parameters:
* config - tuning timing configuration pointer
* consecutive_length - consecutive length
* end - last success point
* reference_data - reference data pointer
* is_flash - is flash or not
*
* Returned Value:
* None.
*
****************************************************************************/
static void select_best_tuning_config(tuning_config_s *config,
uint32_t consecutive_length, uint32_t end,
const uint8_t *reference_data, bool is_flash)
{
uint32_t best_point = 0;
if (is_flash)
{
#if MSPI_TIMING_FLASH_DTR_MODE
best_point = select_best_tuning_config_dtr(config, consecutive_length,
end);
#elif MSPI_TIMING_FLASH_STR_MODE
best_point = select_best_tuning_config_str(config, consecutive_length,
end);
#endif
g_flash_timing_tuning_config = config->config_table[best_point];
minfo("Flash timing tuning index: %d\n", best_point);
}
else
{
#if MSPI_TIMING_PSRAM_DTR_MODE
best_point = select_best_tuning_config_dtr(config, consecutive_length,
end);
#elif MSPI_TIMING_PSRAM_STR_MODE
best_point = select_best_tuning_config_str(config, consecutive_length,
end);
#endif
g_psram_timing_tuning_config = config->config_table[best_point];
minfo("PSRAM timing tuning index: %d\n", best_point);
}
}
/****************************************************************************
* Name: set_timing_tuning_regs
*
* Description:
* Set timing tuning registers
*
* Input Parameters:
* spi1 - Select whether to control SPI1
*
* Returned Value:
* None.
*
****************************************************************************/
static void set_timing_tuning_regs(bool control_spi1)
{
/* SPI0 and SPI1 share the registers for flash din mode and num setting,
* so we only set SPI0's reg
*/
set_flash_din_mode_num(0, g_flash_timing_tuning_config.spi_din_mode,
g_flash_timing_tuning_config.spi_din_num);
set_flash_extra_dummy(0, g_flash_timing_tuning_config.extra_dummy_len);
if (control_spi1)
{
set_flash_extra_dummy(1, g_flash_timing_tuning_config.extra_dummy_len);
}
set_psram_din_mode_num(0, g_psram_timing_tuning_config.spi_din_mode,
g_psram_timing_tuning_config.spi_din_num);
set_psram_extra_dummy(0, g_psram_timing_tuning_config.extra_dummy_len);
}
/****************************************************************************
* Name: clear_timing_tuning_regs
*
* Description:
* Clear timing tuning registers
*
* Input Parameters:
* spi1 - Select whether to control SPI1
*
* Returned Value:
* None.
*
****************************************************************************/
static void clear_timing_tuning_regs(bool control_spi1)
{
/* SPI0 and SPI1 share the registers for flash din mode and num setting,
* so we only set SPI0's reg
*/
set_flash_din_mode_num(0, 0, 0);
set_flash_extra_dummy(0, 0);
if (control_spi1)
{
set_flash_extra_dummy(1, 0);
}
set_psram_din_mode_num(0, 0, 0);
set_psram_extra_dummy(0, 0);
}
/****************************************************************************
* Name: do_tuning
*
* Description:
* Start to tune the timing:
*
* Input Parameters:
* reference_data - reference data pointer
* timing_config - tuning timing configuration pointer
* is_flash - is flash or not
*
* Returned Value:
* None.
*
****************************************************************************/
static void do_tuning(const uint8_t *reference_data,
tuning_config_s *timing_config, bool is_flash)
{
/* We use SPI1 to tune the timing:
* 1. Get all SPI1 sampling results.
* 2. Find the longest consecutive successful sampling points.
* 3. The middle one will be the best sampling point.
*/
uint32_t consecutive_length = 0;
uint32_t last_success_point = 0;
uint8_t sample_result[MSPI_TIMING_CONFIG_NUM_DEFAULT] =
{
0
};
init_spi1_for_tuning(is_flash);
sweep_for_success_sample_points(reference_data, timing_config, is_flash,
sample_result);
find_max_consecutive_success_points(sample_result,
MSPI_TIMING_CONFIG_NUM_DEFAULT, &consecutive_length, &last_success_point);
select_best_tuning_config(timing_config, consecutive_length,
last_success_point, reference_data, is_flash);
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp32s3_spi_timing_set_pin_drive_strength
*
* Description:
* Make SPI all GPIO strength to be 3 under default clock.
*
* Input Parameters:
* None
*
* Returned Value:
* None.
*
****************************************************************************/
void IRAM_ATTR esp32s3_spi_timing_set_pin_drive_strength(void)
{
const uint32_t regs[] =
{
IO_MUX_GPIO27_REG,
IO_MUX_GPIO28_REG,
IO_MUX_GPIO31_REG,
IO_MUX_GPIO32_REG,
IO_MUX_GPIO33_REG,
IO_MUX_GPIO34_REG,
IO_MUX_GPIO35_REG,
IO_MUX_GPIO36_REG,
IO_MUX_GPIO37_REG
};
/* Set default clock */
SET_PERI_REG_MASK(SPI_MEM_DATE_REG(0), SPI_MEM_SPICLK_PAD_DRV_CTL_EN);
REG_SET_FIELD(SPI_MEM_DATE_REG(0), SPI_MEM_SPI_SMEM_SPICLK_FUN_DRV, 3);
REG_SET_FIELD(SPI_MEM_DATE_REG(0), SPI_MEM_SPI_FMEM_SPICLK_FUN_DRV, 3);
/* Set default mspi d0 ~ d7, dqs pin drive strength */
for (int i = 0; i < nitems(regs); i++)
{
PIN_SET_DRV(regs[i], 3);
}
}
/****************************************************************************
* Name: esp32s3_spi_timing_set_mspi_high_speed
*
* Description:
* Make MSPI work under the frequency as users set, may add certain
* delays to MSPI RX direction to meet timing requirements.
*
* Input Parameters:
* spi1 - Select whether to control SPI1
*
* Returned Value:
* None.
*
****************************************************************************/
void IRAM_ATTR esp32s3_spi_timing_set_mspi_high_speed(bool spi1)
{
uint32_t flash_div = FLASH_CLOCK_DIVIDER;
uint32_t psram_div = PSRAM_CLOCK_DIVIDER;
/* Set SPI0 & 1 core clock */
REG_SET_FIELD(SPI_MEM_CORE_CLK_SEL_REG(0),
SPI_MEM_CORE_CLK_SEL,
DEFAULT_CORE_CLK_REG);
set_flash_clock(0, flash_div);
if (spi1)
{
set_flash_clock(1, flash_div);
}
set_psram_clock(0, psram_div);
#if ESP32S3_SPI_TIMING_FLASH_TUNING || ESP32S3_SPI_TIMING_PSRAM_TUNING
set_timing_tuning_regs(true);
#endif
}
/****************************************************************************
* Name: esp32s3_spi_timing_set_mspi_low_speed
*
* Description:
* Make MSPI work under 20MHz and remove the timing tuning required delays.
*
* Input Parameters:
* spi1 - Select whether to control SPI1
*
* Returned Value:
* None.
*
****************************************************************************/
void IRAM_ATTR esp32s3_spi_timing_set_mspi_low_speed(bool spi1)
{
/* Here we are going to set the SPI1 frequency to be 20MHz,
* so we need to set SPI1 din_num and din_mode regs.
*
* Because SPI0 and SPI1 share the din_num and din_mode regs,
* but if we clear SPI1 din_num and din_mode to 0 and SPI0 flash
* module clock is still in high freq, it may not work correctly.
*
* Therefore, we need to set both the SPI0 and SPI1 and related
* timing tuning regs to be 20MHz.
*/
/* Set SPIMEM core clock as 80MHz, and set SPI1 and SPI0 clock
* to be 20MHz by setting clock division as 4
*/
REG_SET_FIELD(SPI_MEM_CORE_CLK_SEL_REG(0),
SPI_MEM_CORE_CLK_SEL,
CORE_CLK_REG_SEL_80M);
set_flash_clock(0, 4);
if (spi1)
{
/* After tuning, won't touch SPI1 again */
set_flash_clock(1, 4);
}
set_psram_clock(0, 4);
#if ESP32S3_SPI_TIMING_FLASH_TUNING || ESP32S3_SPI_TIMING_PSRAM_TUNING
clear_timing_tuning_regs(spi1);
#endif
}
/****************************************************************************
* Name: esp32s3_spi_timing_set_mspi_psram_tuning
*
* Description:
* Tune MSPI psram timing to make it work under high frequency
*
* Input Parameters:
* None
*
* Returned Value:
* None.
*
****************************************************************************/
void IRAM_ATTR esp32s3_spi_timing_set_mspi_psram_tuning(void)
{
#if ESP32S3_SPI_TIMING_PSRAM_TUNING
tuning_config_s timing_configs =
{
0
};
/* set SPI01 related regs to 20mhz configuration, to write reference data
* to PSRAM.
*/
esp32s3_spi_timing_set_mspi_low_speed(true);
/* write data into psram, used to do timing tuning test. */
uint8_t reference_data[MSPI_TIMING_TEST_DATA_LEN];
for (int i = 0; i < MSPI_TIMING_TEST_DATA_LEN / 4; i++)
{
((uint32_t *)reference_data)[i] = 0xa5ff005a;
}
config_psram_read_write_data(reference_data,
MSPI_TIMING_PSRAM_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN, false);
get_psram_tuning_configs(&timing_configs);
/* Disable the variable dummy mode when doing timing tuning */
REG_SET_FIELD(SPI_MEM_DDR_REG(1), SPI_MEM_SPI_FMEM_VAR_DUMMY, false);
/* Get required config, and set them to PSRAM related registers */
do_tuning(reference_data, &timing_configs, false);
esp32s3_spi_timing_set_mspi_high_speed(true);
#endif
}
/****************************************************************************
* Name: esp32s3_spi_timing_set_mspi_flash_tuning
*
* Description:
* Tune MSPI flash timing to make it work under high frequency
*
* Input Parameters:
* None
*
* Returned Value:
* None.
*
****************************************************************************/
void IRAM_ATTR esp32s3_spi_timing_set_mspi_flash_tuning(void)
{
#if ESP32S3_SPI_TIMING_FLASH_TUNING
tuning_config_s timing_configs =
{
0
};
/* set SPI01 related regs to 20mhz configuration, to get reference data
* from FLASH.
*/
esp32s3_spi_timing_set_mspi_low_speed(true);
/* Disable the variable dummy mode when doing timing tuning. */
REG_SET_FIELD(SPI_MEM_DDR_REG(1), SPI_MEM_SPI_FMEM_VAR_DUMMY, false);
uint8_t reference_data[MSPI_TIMING_TEST_DATA_LEN] =
{
0
};
config_flash_read_data(reference_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR,
sizeof(reference_data));
get_flash_tuning_configs(&timing_configs);
do_tuning(reference_data, &timing_configs, true);
esp32s3_spi_timing_set_mspi_high_speed(true);
#endif
}