mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
Merge branch 'feat/spi_master_high_freq_no_tuning' into 'master'
feat(driver_spi): master driver support higher speed limit if 'input_delay_ns' not use See merge request espressif/esp-idf!51871
This commit is contained in:
@@ -411,15 +411,24 @@ int spi_get_freq_limit(bool gpio_is_used, int input_delay_ns)
|
||||
}
|
||||
|
||||
#if SPI_LL_SRC_PRE_DIV_MAX
|
||||
static uint32_t s_spi_find_clock_src_pre_div(uint32_t src_freq, uint32_t target_freq)
|
||||
static uint32_t s_spi_find_clock_src_pre_div(uint32_t src_freq, uint32_t target_freq, bool need_timing_tune)
|
||||
{
|
||||
// pre division must be even and at least 2
|
||||
uint32_t min_div = ((src_freq / SPI_PERIPH_SRC_FREQ_MAX) + 1) & (~0x01UL);
|
||||
min_div = min_div < 2 ? 2 : min_div;
|
||||
uint32_t min_div;
|
||||
uint32_t step;
|
||||
if (need_timing_tune) {
|
||||
// mst_div == 2: total pre_div must be even and at least 2
|
||||
min_div = ((src_freq / SPI_PERIPH_SRC_FREQ_MAX) + 1) & (~0x01UL);
|
||||
min_div = min_div < 2 ? 2 : min_div;
|
||||
step = 2;
|
||||
} else {
|
||||
// mst_div == 1: only guarantee peripheral input <= SPI_PERIPH_SRC_FREQ_MAX
|
||||
min_div = (src_freq + SPI_PERIPH_SRC_FREQ_MAX - 1) / SPI_PERIPH_SRC_FREQ_MAX;
|
||||
min_div = min_div < 1 ? 1 : min_div;
|
||||
step = 1;
|
||||
}
|
||||
|
||||
uint32_t total_div = src_freq / target_freq;
|
||||
// Loop the `div` to find a divisible value of `total_div`
|
||||
for (uint32_t pre_div = min_div; pre_div <= MIN(total_div, SPI_LL_SRC_PRE_DIV_MAX); pre_div += 2) {
|
||||
for (uint32_t pre_div = min_div; pre_div <= MIN(total_div, SPI_LL_SRC_PRE_DIV_MAX); pre_div += step) {
|
||||
if ((total_div % pre_div) || (total_div / pre_div) > SPI_LL_PERIPH_CLK_DIV_MAX) {
|
||||
continue;
|
||||
}
|
||||
@@ -457,7 +466,7 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
|
||||
SPI_CHECK(esp_clk_tree_enable_src(clk_src, true) == ESP_OK, "clock source enable failed", ESP_ERR_INVALID_STATE);
|
||||
esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clock_source_hz);
|
||||
#if SPI_LL_SRC_PRE_DIV_MAX
|
||||
clock_source_div = s_spi_find_clock_src_pre_div(clock_source_hz, dev_config->clock_speed_hz);
|
||||
clock_source_div = s_spi_find_clock_src_pre_div(clock_source_hz, dev_config->clock_speed_hz, dev_config->input_delay_ns > 0);
|
||||
clock_source_hz /= clock_source_div; //actual freq enter to SPI peripheral
|
||||
#endif
|
||||
SPI_CHECK(dev_config->clock_speed_hz <= clock_source_hz, "invalid sclk speed", ESP_ERR_INVALID_ARG);
|
||||
@@ -707,10 +716,13 @@ static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev, spi_trans_pr
|
||||
spi_hal_setup_device(hal, hal_dev);
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
#if SPI_LL_SRC_PRE_DIV_MAX
|
||||
//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)
|
||||
assert(hal_dev->timing_conf.source_pre_div >= 2); // source_pre_div must be even and at least 2
|
||||
spi_ll_clk_source_pre_div(hal->hw, hal_dev->timing_conf.source_pre_div / 2, 2);
|
||||
// input_delay_ns > 0: mst_div = 2 for timing tuning; otherwise mst_div = 1
|
||||
uint32_t pre_div = hal_dev->timing_conf.source_pre_div;
|
||||
if (dev->cfg.input_delay_ns > 0) {
|
||||
spi_ll_clk_source_pre_div(hal->hw, pre_div / 2, 2);
|
||||
} else {
|
||||
spi_ll_clk_source_pre_div(hal->hw, pre_div, 1);
|
||||
}
|
||||
#endif
|
||||
spi_ll_set_clk_source(hal->hw, hal_dev->timing_conf.clock_source);
|
||||
}
|
||||
|
||||
+4
-4
@@ -56,7 +56,7 @@
|
||||
#endif
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#define IDF_TARGET_MAX_SPI_CLK_FREQ 26666*1000
|
||||
#define IDF_TARGET_MAX_SPI_CLK_FREQ 40*1000*1000
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_INTR_DMA 38 //TODO: IDF-9551, check perform
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_POLL_DMA 22
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_INTR_CPU 32
|
||||
@@ -70,7 +70,7 @@
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_INTR_CPU 54
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#define IDF_TARGET_MAX_SPI_CLK_FREQ 40*1000*1000
|
||||
#define IDF_TARGET_MAX_SPI_CLK_FREQ 60*1000*1000
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_INTR_DMA 44
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_POLL_DMA 28
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_INTR_CPU 26
|
||||
@@ -98,14 +98,14 @@
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_POLL_CPU 26
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32H4
|
||||
#define IDF_TARGET_MAX_SPI_CLK_FREQ 24*1000*1000
|
||||
#define IDF_TARGET_MAX_SPI_CLK_FREQ 24*1000*1000 // gpio matrix @48M and AHB@32M which limit slave's speed
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_INTR_DMA 70
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_POLL_DMA 35
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_INTR_CPU 60
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_POLL_CPU 25
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32S31
|
||||
#define IDF_TARGET_MAX_SPI_CLK_FREQ 40*1000*1000
|
||||
#define IDF_TARGET_MAX_SPI_CLK_FREQ 60*1000*1000
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_POLL_DMA 22
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_POLL_CPU 17
|
||||
#define IDF_TARGET_MAX_TRANS_TIME_INTR_DMA 32
|
||||
|
||||
@@ -97,15 +97,21 @@ static void check_spi_pre_n_for(int clk, int pre, int n)
|
||||
* Only test on SPI_CLK_SRC_DEFAULT here
|
||||
*/
|
||||
#define TEST_CLK_TIMES 8
|
||||
uint32_t clk_param_80m[TEST_CLK_TIMES][3] = {{1, SPI_LL_MAX_PRE_DIV_NUM, 64}, {100000, 16, 50}, {333333, 4, 60}, {800000, 2, 50}, {900000, 2, 44}, {8000000, 1, 10}, {20000000, 1, 4}, {26000000, 1, 3} };
|
||||
uint32_t clk_param_160m[TEST_CLK_TIMES][3] = {{1, SPI_LL_MAX_PRE_DIV_NUM, 64}, {100000, 16, 50}, {333333, 4, 60}, {800000, 2, 50}, {900000, 2, 44}, {8000000, 1, 10}, {20000000, 1, 4}, {26000000, 1, 3} };
|
||||
#if SPI_LL_SRC_PRE_DIV_MAX
|
||||
uint32_t clk_param_40m[TEST_CLK_TIMES][3] = {{1, SPI_LL_MAX_PRE_DIV_NUM, 64}, {100000, 4, 50}, {333333, 1, 60}, {800000, 1, 25}, {2000000, 1, 10}, {5000000, 1, 4}, {12000000, 1, 2}, {18000000, 1, 1} };
|
||||
uint32_t clk_param_48m[TEST_CLK_TIMES][3] = {{1, SPI_LL_MAX_PRE_DIV_NUM, 64}, {100000, 4, 60}, {333333, 2, 36}, {800000, 1, 30}, {5000000, 1, 5}, {12000000, 1, 2}, {18000000, 1, 2}, {24000000, 1, 1} };
|
||||
#else
|
||||
uint32_t clk_param_40m[TEST_CLK_TIMES][3] = {{1, SPI_LL_MAX_PRE_DIV_NUM, 64}, {100000, 8, 50}, {333333, 2, 60}, {800000, 1, 50}, {2000000, 1, 20}, {5000000, 1, 8}, {12000000, 1, 3}, {18000000, 1, 2} };
|
||||
uint32_t clk_param_48m[TEST_CLK_TIMES][3] = {{1, SPI_LL_MAX_PRE_DIV_NUM, 64}, {100000, 8, 60}, {333333, 3, 48}, {800000, 1, 60}, {5000000, 1, 10}, {12000000, 1, 4}, {18000000, 1, 3}, {26000000, 1, 2} };
|
||||
#endif
|
||||
uint32_t clk_param_80m[TEST_CLK_TIMES][3] = {{1, SPI_LL_MAX_PRE_DIV_NUM, 64}, {100000, 16, 50}, {333333, 4, 60}, {800000, 2, 50}, {900000, 2, 44}, {8000000, 1, 10}, {20000000, 1, 4}, {26000000, 1, 3} };
|
||||
uint32_t clk_param_160m[TEST_CLK_TIMES][3] = {{1, SPI_LL_MAX_PRE_DIV_NUM, 64}, {100000, 16, 50}, {333333, 4, 60}, {800000, 2, 50}, {900000, 1, 59}, {8000000, 1, 10}, {20000000, 1, 4}, {26000000, 1, 3} };
|
||||
uint32_t clk_param_480m[TEST_CLK_TIMES][3] = {{1, SPI_LL_MAX_PRE_DIV_NUM, 64}, {100000, 16, 50}, {333333, 4, 60}, {800000, 2, 50}, {900000, 1, 41}, {8000000, 1, 10}, {20000000, 1, 4}, {26000000, 1, 3} };
|
||||
static struct {
|
||||
uint32_t clock_source_hz;
|
||||
uint32_t (*clk_param)[3];
|
||||
} clk_param_map[] = {
|
||||
{40 * 1000 * 1000, clk_param_40m},
|
||||
{48 * 1000 * 1000, clk_param_48m},
|
||||
{80 * 1000 * 1000, clk_param_80m},
|
||||
{160 * 1000 * 1000, clk_param_160m},
|
||||
{480 * 1000 * 1000, clk_param_480m},
|
||||
};
|
||||
|
||||
TEST_CASE("SPI Master clockdiv calculation routines", "[spi]")
|
||||
{
|
||||
@@ -114,27 +120,19 @@ TEST_CASE("SPI Master clockdiv calculation routines", "[spi]")
|
||||
uint32_t clock_source_hz;
|
||||
|
||||
esp_clk_tree_src_get_freq_hz(SPI_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &clock_source_hz);
|
||||
printf("\nTest clock source SPI_CLK_SRC_DEFAULT = %ld\n", clock_source_hz);
|
||||
if ((160 * 1000 * 1000) == clock_source_hz) {
|
||||
for (int i = 0; i < TEST_CLK_TIMES; i++) {
|
||||
check_spi_pre_n_for(clk_param_160m[i][0], clk_param_160m[i][1], clk_param_160m[i][2]);
|
||||
printf("\nTest clock source SPI_CLK_SRC_DEFAULT = %ld Hz\n", clock_source_hz);
|
||||
int i = 0;
|
||||
for (; i < sizeof(clk_param_map) / sizeof(clk_param_map[0]); i++) {
|
||||
if (clk_param_map[i].clock_source_hz == clock_source_hz) {
|
||||
for (int j = 0; j < TEST_CLK_TIMES; j++) {
|
||||
check_spi_pre_n_for(clk_param_map[i].clk_param[j][0], clk_param_map[i].clk_param[j][1], clk_param_map[i].clk_param[j][2]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if ((80 * 1000 * 1000) == clock_source_hz) {
|
||||
for (int i = 0; i < TEST_CLK_TIMES; i++) {
|
||||
check_spi_pre_n_for(clk_param_80m[i][0], clk_param_80m[i][1], clk_param_80m[i][2]);
|
||||
}
|
||||
} else if ((48 * 1000 * 1000) == clock_source_hz) {
|
||||
for (int i = 0; i < TEST_CLK_TIMES; i++) {
|
||||
check_spi_pre_n_for(clk_param_48m[i][0], clk_param_48m[i][1], clk_param_48m[i][2]);
|
||||
}
|
||||
} else if ((40 * 1000 * 1000) == clock_source_hz) {
|
||||
for (int i = 0; i < TEST_CLK_TIMES; i++) {
|
||||
check_spi_pre_n_for(clk_param_40m[i][0], clk_param_40m[i][1], clk_param_40m[i][2]);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
if (i == sizeof(clk_param_map) / sizeof(clk_param_map[0])) {
|
||||
ESP_LOGW(TAG, "Don't find any routing param!!");
|
||||
}
|
||||
|
||||
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
|
||||
}
|
||||
|
||||
@@ -159,12 +157,10 @@ TEST_CASE("SPI Master clk_source and divider accuracy", "[spi]")
|
||||
for (uint8_t sour_idx = 0; sour_idx < sizeof(spi_clk_sour); sour_idx++) {
|
||||
esp_clk_tree_src_get_freq_hz(spi_clk_sour[sour_idx], ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &clock_source_hz);
|
||||
printf("\nTesting unknown clock source @%ld Hz\n", clock_source_hz);
|
||||
#if SPI_LL_SRC_PRE_DIV_MAX
|
||||
clock_source_hz /= 2; //targets support pre-div will divide clock by 2 before SPI peripheral
|
||||
#endif
|
||||
for (uint8_t test_time = 0; test_time < 8; test_time ++) {
|
||||
spi_device_handle_t handle;
|
||||
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
|
||||
devcfg.input_delay_ns = 0;
|
||||
devcfg.clock_source = spi_clk_sour[sour_idx];
|
||||
devcfg.clock_speed_hz = MIN(IDF_TARGET_MAX_SPI_CLK_FREQ, clock_source_hz) >> test_time;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
@@ -217,6 +213,8 @@ TEST_CASE("test_device_dynamic_freq_update", "[spi]")
|
||||
|
||||
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
|
||||
devcfg.input_delay_ns = 0;
|
||||
devcfg.clock_speed_hz = IDF_TARGET_MAX_SPI_CLK_FREQ;
|
||||
devcfg.flags |= SPI_DEVICE_HALFDUPLEX;
|
||||
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0));
|
||||
|
||||
@@ -1512,8 +1512,11 @@ static void test_master_hd_dma(void)
|
||||
.dummy_bits = 8,
|
||||
.queue_size = 10,
|
||||
};
|
||||
if (is_gpio && devcfg.clock_speed_hz > 40 * 1000 * 1000) {
|
||||
devcfg.clock_speed_hz = 40 * 1000 * 1000; // using gpio matrix, clk freq <= 40MHz
|
||||
}
|
||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0));
|
||||
printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (is_gpio) ? "GPIO_Matrix" : "IOMUX", mode, s_spi_bus_freq[speed_level] / 1000000.f);
|
||||
printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (is_gpio) ? "GPIO_Matrix" : "IOMUX", mode, devcfg.clock_speed_hz / 1000000.f);
|
||||
|
||||
unity_send_signal("Master ready");
|
||||
for (int i = 0; i < TEST_STEP; i++) {
|
||||
|
||||
@@ -187,9 +187,10 @@ static inline void spi_ll_set_clk_source(spi_dev_t *hw, spi_clock_source_t clk_s
|
||||
__attribute__((always_inline))
|
||||
static inline void spi_ll_clk_source_pre_div(spi_dev_t *hw, uint8_t hs_div, uint8_t mst_div)
|
||||
{
|
||||
// In IDF master driver 'mst_div' will be const 2 and 'hs_div' is actually pre_div temporally
|
||||
(void) hs_div;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.spi2_clkm_conf, spi2_clkm_div_num, mst_div - 1);
|
||||
(void) hw;
|
||||
// Single-stage PCR divider: program total_div = hs_div * mst_div
|
||||
uint16_t total_div = (uint16_t)hs_div * mst_div;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.spi2_clkm_conf, spi2_clkm_div_num, total_div - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -188,9 +188,10 @@ static inline void spi_ll_set_clk_source(spi_dev_t *hw, spi_clock_source_t clk_s
|
||||
__attribute__((always_inline))
|
||||
static inline void spi_ll_clk_source_pre_div(spi_dev_t *hw, uint8_t hs_div, uint8_t mst_div)
|
||||
{
|
||||
// In IDF master driver 'mst_div' will be const 2 and 'hs_div' is actually pre_div temporally
|
||||
(void) hs_div;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.spi2_clkm_conf, spi2_clkm_div_num, mst_div - 1);
|
||||
(void) hw;
|
||||
// Single-stage PCR divider: program total_div = hs_div * mst_div
|
||||
uint16_t total_div = (uint16_t)hs_div * mst_div;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.spi2_clkm_conf, spi2_clkm_div_num, total_div - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -204,12 +204,12 @@ static inline void spi_ll_set_clk_source(spi_dev_t *hw, spi_clock_source_t clk_s
|
||||
__attribute__((always_inline))
|
||||
static inline void spi_ll_clk_source_pre_div(spi_dev_t *hw, uint8_t hs_div, uint8_t mst_div)
|
||||
{
|
||||
// In IDF master driver 'mst_div' will be const 2 and 'hs_div' is actually pre_div temporally
|
||||
(void) hs_div;
|
||||
// Single-stage PCR divider: program total_div = hs_div * mst_div
|
||||
uint16_t total_div = (uint16_t)hs_div * mst_div;
|
||||
if (hw == &GPSPI2) {
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.spi2_clkm_conf, spi2_clkm_div_num, mst_div - 1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.spi2_clkm_conf, spi2_clkm_div_num, total_div - 1);
|
||||
} else if (hw == &GPSPI3) {
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.spi3_clkm_conf, spi3_clkm_div_num, mst_div - 1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.spi3_clkm_conf, spi3_clkm_div_num, total_div - 1);
|
||||
} else {
|
||||
HAL_ASSERT(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user