fix(spi_master): Fix p4 spi clock source support other than XTAL

This commit is contained in:
wanlei 2024-01-17 17:01:23 +08:00
parent 1903c51d07
commit 4a46d70e9a
8 changed files with 90 additions and 32 deletions

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -126,6 +126,7 @@ We have two bits to control the interrupt:
#include "driver/gpio.h"
#include "hal/spi_hal.h"
#include "hal/spi_ll.h"
#include "hal/hal_utils.h"
#include "esp_heap_caps.h"
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#include "esp_cache.h"
@ -163,7 +164,6 @@ typedef struct {
struct spi_device_t {
int id;
int real_clk_freq_hz;
QueueHandle_t trans_queue;
QueueHandle_t ret_queue;
spi_device_interface_config_t cfg;
@ -368,11 +368,30 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
#endif
spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT;
uint32_t clock_source_hz = 0;
uint32_t clock_source_div = 1;
if (dev_config->clock_source) {
clk_src = dev_config->clock_source;
}
esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &clock_source_hz);
#if SPI_LL_SUPPORT_CLK_SRC_PRE_DIV
SPI_CHECK((dev_config->clock_speed_hz > 0) && (dev_config->clock_speed_hz <= MIN(clock_source_hz / 2, (80 * 1000000))), "invalid sclk speed", ESP_ERR_INVALID_ARG);
if (clock_source_hz / 2 > (80 * 1000000)) { //clock_source_hz beyond peripheral HW limitation, calc pre-divider
hal_utils_clk_info_t clk_cfg = {
.src_freq_hz = clock_source_hz,
.exp_freq_hz = dev_config->clock_speed_hz * 2, //we have (hs_clk = 2*mst_clk), calc hs_clk first
.round_opt = HAL_DIV_ROUND,
};
hal_utils_calc_clk_div_integer(&clk_cfg, &clock_source_div);
}
clock_source_div *= 2; //convert to mst_clk function divider
if (clock_source_div > SPI_LL_CLK_SRC_PRE_DIV_MAX) {
clock_source_div = SPI_LL_CLK_SRC_PRE_DIV_MAX;
}
clock_source_hz /= clock_source_div; //actual freq enter to SPI peripheral
#else
SPI_CHECK((dev_config->clock_speed_hz > 0) && (dev_config->clock_speed_hz <= clock_source_hz), "invalid sclk speed", ESP_ERR_INVALID_ARG);
#endif
#ifdef CONFIG_IDF_TARGET_ESP32
//The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full
//duplex mode does absolutely nothing on the ESP32.
@ -416,10 +435,10 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
//output values of timing configuration
spi_hal_timing_conf_t temp_timing_conf;
int freq;
esp_err_t ret = spi_hal_cal_clock_conf(&timing_param, &freq, &temp_timing_conf);
temp_timing_conf.clock_source = clk_src;
esp_err_t ret = spi_hal_cal_clock_conf(&timing_param, &temp_timing_conf);
SPI_CHECK(ret == ESP_OK, "assigned clock speed not supported", ret);
temp_timing_conf.clock_source = clk_src;
temp_timing_conf.source_pre_div = clock_source_div;
//Allocate memory for device
dev = malloc(sizeof(spi_device_t));
@ -447,7 +466,6 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
//We want to save a copy of the dev config in the dev struct.
memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t));
dev->cfg.duty_cycle_pos = duty_cycle;
dev->real_clk_freq_hz = freq;
// TODO: if we have to change the apb clock among transactions, re-calculate this each time the apb clock lock is locked.
//Set CS pin, CS options
@ -483,7 +501,7 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
hal_dev->positive_cs = dev_config->flags & SPI_DEVICE_POSITIVE_CS ? 1 : 0;
*handle = dev;
ESP_LOGD(SPI_TAG, "SPI%d: New device added to CS%d, effective clock: %dkHz", host_id + 1, freecs, freq / 1000);
ESP_LOGD(SPI_TAG, "SPI%d: New device added to CS%d, effective clock: %d Hz", host_id + 1, freecs, temp_timing_conf.real_freq);
return ESP_OK;
@ -545,7 +563,7 @@ esp_err_t spi_device_get_actual_freq(spi_device_handle_t handle, int* freq_khz)
return ESP_ERR_INVALID_ARG;
}
*freq_khz = handle->real_clk_freq_hz / 1000;
*freq_khz = handle->hal_dev.timing_conf.real_freq / 1000;
return ESP_OK;
}
@ -567,6 +585,11 @@ static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev)
/* Configuration has not been applied yet. */
spi_hal_setup_device(hal, hal_dev);
SPI_MASTER_PERI_CLOCK_ATOMIC() {
#if SPI_LL_SUPPORT_CLK_SRC_PRE_DIV
//we set mst_div as const 2, then (hs_clk = 2*mst_clk) to ensure timing turning work as past
//and sure (hs_div * mst_div = source_pre_div)
spi_ll_clk_source_pre_div(hal->hw, hal_dev->timing_conf.source_pre_div / 2, 2);
#endif
spi_ll_set_clk_source(hal->hw, hal_dev->timing_conf.clock_source);
}
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -40,6 +40,8 @@ extern "C" {
#define SPI_LL_DMA_MAX_BIT_LEN (1 << 18) //reg len: 18 bits
#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words
#define SPI_LL_SUPPORT_CLK_SRC_PRE_DIV 1 //clock source have divider before peripheral
#define SPI_LL_CLK_SRC_PRE_DIV_MAX 512//div1(8bit) * div2(8bit but set const 2)
/**
* The data structure holding calculated clock configuration. Since the
@ -175,6 +177,12 @@ static inline void spi_ll_set_clk_source(spi_dev_t *hw, spi_clock_source_t clk_s
{
uint32_t clk_id = 0;
switch (clk_source) {
case SPI_CLK_SRC_SPLL:
clk_id = 4;
break;
case SPI_CLK_SRC_RC_FAST:
clk_id = 1;
break;
case SPI_CLK_SRC_XTAL:
clk_id = 0;
break;
@ -193,6 +201,32 @@ static inline void spi_ll_set_clk_source(spi_dev_t *hw, spi_clock_source_t clk_s
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define spi_ll_set_clk_source(...) (void)__DECLARE_RCC_ATOMIC_ENV; spi_ll_set_clk_source(__VA_ARGS__)
/**
* Config clock source integrate pre_div before it enter GPSPI peripheral
*
* @note 1. For timing turning(e.g. input_delay) feature available, should be (mst_div >= 2)
* 2. From peripheral limitation: (sour_freq/hs_div <= 160M) and (sour_freq/hs_div/mst_div <= 80M)
*
* @param hw Beginning address of the peripheral registers.
* @param hs_div Timing turning clock divider: (hs_clk_o = sour_freq/hs_div)
* @param mst_div Functional output clock divider: (mst_clk_o = sour_freq/hs_div/mst_div)
*/
__attribute__((always_inline))
static inline void spi_ll_clk_source_pre_div(spi_dev_t *hw, uint8_t hs_div, uint8_t mst_div)
{
if (hw == &GPSPI2) {
HP_SYS_CLKRST.peri_clk_ctrl116.reg_gpspi2_hs_clk_div_num = hs_div - 1;
HP_SYS_CLKRST.peri_clk_ctrl116.reg_gpspi2_mst_clk_div_num = mst_div - 1;
} else if (hw == &GPSPI3) {
HP_SYS_CLKRST.peri_clk_ctrl117.reg_gpspi3_hs_clk_div_num = hs_div - 1;
HP_SYS_CLKRST.peri_clk_ctrl117.reg_gpspi3_mst_clk_div_num = mst_div - 1;
}
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define spi_ll_clk_sour_pre_div(...) (void)__DECLARE_RCC_ATOMIC_ENV; spi_ll_clk_sour_pre_div(__VA_ARGS__)
/**
* Initialize SPI peripheral (master).
*

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -70,6 +70,8 @@ typedef struct {
typedef struct {
spi_ll_clock_val_t clock_reg; ///< Register value used by the LL layer
spi_clock_source_t clock_source; ///< Clock source of each device used by LL layer
uint32_t source_pre_div; ///< Pre divider befor enter SPI peripheral
int real_freq; ///< Output of the actual frequency
int timing_dummy; ///< Extra dummy needed to compensate the timing
int timing_miso_delay; ///< Extra miso delay clocks to compensate the timing
} spi_hal_timing_conf_t;
@ -222,12 +224,11 @@ void spi_hal_fetch_result(const spi_hal_context_t *hal);
* It is highly suggested to do this at initialization, since it takes long time.
*
* @param timing_param Input parameters to calculate timing configuration
* @param out_freq Output of the actual frequency, left NULL if not required.
* @param timing_conf Output of the timing configuration.
*
* @return ESP_OK if desired is available, otherwise fail.
*/
esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, int *out_freq, spi_hal_timing_conf_t *timing_conf);
esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, spi_hal_timing_conf_t *timing_conf);
/**
* Get the frequency actual used.

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -52,7 +52,7 @@ void spi_hal_deinit(spi_hal_context_t *hal)
}
}
esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, int *out_freq, spi_hal_timing_conf_t *timing_conf)
esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, spi_hal_timing_conf_t *timing_conf)
{
spi_hal_timing_conf_t temp_conf = {};
@ -73,12 +73,10 @@ Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output dat
ESP_ERR_NOT_SUPPORTED, freq_limit / 1000. / 1000 );
#endif
temp_conf.real_freq = eff_clk_n;
if (timing_conf) {
*timing_conf = temp_conf;
}
if (out_freq) {
*out_freq = eff_clk_n;
}
return ESP_OK;
}

View File

@ -987,6 +987,14 @@ config SOC_SPI_SUPPORT_CLK_XTAL
bool
default y
config SOC_SPI_SUPPORT_CLK_RC_FAST
bool
default y
config SOC_SPI_SUPPORT_CLK_SPLL
bool
default y
config SOC_MEMSPI_IS_INDEPENDENT
bool
default y

View File

@ -404,13 +404,9 @@ typedef enum {
*/
typedef enum {
SPI_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as SPI source clock */
#if SOC_CLK_TREE_SUPPORTED
SPI_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST,
SPI_CLK_SRC_SPLL_480 = SOC_MOD_CLK_SPLL,
SPI_CLK_SRC_DEFAULT = SPI_CLK_SRC_SPLL_480, /*!< Select SPLL_480M as SPI source clock */
#else
SPI_CLK_SRC_DEFAULT = SOC_MOD_CLK_XTAL, /*!< Select XTAL as SPI source clock */
#endif
SPI_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST_20M as SPI source clock */
SPI_CLK_SRC_SPLL = SOC_MOD_CLK_SPLL, /*!< Select SPLL as SPI source clock */
SPI_CLK_SRC_DEFAULT = SOC_MOD_CLK_SPLL, /*!< Select SPLL as SPI source clock */
} soc_periph_spi_clk_src_t;
/////////////////////////////////////////////////PSRAM////////////////////////////////////////////////////////////////////

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -425,10 +425,8 @@
#define SOC_SPI_SUPPORT_CD_SIG 1
#define SOC_SPI_SUPPORT_OCT 1
#define SOC_SPI_SUPPORT_CLK_XTAL 1
// #define SOC_SPI_SUPPORT_CLK_RC_FAST 1 //bellow clks are waiting for clock tree
// #define SOC_SPI_SUPPORT_CLK_SPLL_F480M 1 //super pll
// #define SOC_SPI_SUPPORT_CLK_SDIO 1 //sdio pll
// #define SOC_SPI_SUPPORT_CLK_APLL 1 //audio pll
#define SOC_SPI_SUPPORT_CLK_RC_FAST 1
#define SOC_SPI_SUPPORT_CLK_SPLL 1
// Peripheral supports DIO, DOUT, QIO, or QOUT
// host_id = 0 -> SPI0/SPI1, host_id = 1 -> SPI2,

View File

@ -1,2 +1,2 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |