feat(driver_spi): spi master support sleep retention(recovery)

This commit is contained in:
wanckl
2024-09-23 20:40:22 +08:00
parent 92d335548f
commit ef7406257a
41 changed files with 698 additions and 408 deletions
@@ -8,7 +8,7 @@ set(srcs
# sct test using slave hd APIs, need slave hd support
# tmp skip sct test under iram_safe, both sct and slave hd are not cleaned
if(CONFIG_SOC_SPI_SUPPORT_SLAVE_HD_VER2 AND NOT CONFIG_COMPILER_DUMP_RTL_FILES)
if(CONFIG_SOC_SPI_SUPPORT_SLAVE_HD_VER2 AND CONFIG_SOC_SPI_SCT_SUPPORTED AND NOT CONFIG_COMPILER_DUMP_RTL_FILES)
list(APPEND srcs "test_spi_master_sct.c")
endif()
@@ -19,6 +19,9 @@
#include "esp_private/cache_utils.h"
#include "esp_private/spi_common_internal.h"
#include "esp_private/esp_clk.h"
#include "esp_private/sleep_cpu.h"
#include "esp_private/esp_sleep_internal.h"
#include "esp_private/esp_pmu.h"
#include "esp_heap_caps.h"
#include "esp_clk_tree.h"
#include "esp_timer.h"
@@ -1788,3 +1791,135 @@ TEST_CASE("test_bus_free_safty_to_remain_devices", "[spi]")
TEST_ESP_OK(spi_bus_remove_device(dev1));
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
}
TEST_CASE("test_spi_master_sleep_retention", "[spi]")
{
// Prepare a TOP PD sleep
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000));
#if ESP_SLEEP_POWER_DOWN_CPU
TEST_ESP_OK(sleep_cpu_configure(true));
#endif
esp_sleep_context_t sleep_ctx;
esp_sleep_set_sleep_context(&sleep_ctx);
spi_device_handle_t dev_handle;
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
buscfg.flags |= SPICOMMON_BUSFLAG_SLP_ALLOW_PD;
uint8_t send[16] = "hello spi x\n";
uint8_t recv[16];
spi_transaction_t trans_cfg = {
.length = 8 * sizeof(send),
.tx_buffer = send,
.rx_buffer = recv,
};
for (int periph = SPI2_HOST; periph < SPI_HOST_MAX; periph ++) {
for (int test_dma = 0; test_dma <= 1; test_dma ++) {
int use_dma = SPI_DMA_DISABLED;
#if SOC_GDMA_SUPPORT_SLEEP_RETENTION // TODO: IDF-11317 test dma on esp32 and s2
use_dma = test_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED;
#endif
printf("Retention on GPSPI%d with dma: %d\n", periph + 1, use_dma);
TEST_ESP_OK(spi_bus_initialize(periph, &buscfg, use_dma));
// set spi "self-loop" after bus initialized
spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, spi_periph_signal[periph].spid_out);
TEST_ESP_OK(spi_bus_add_device(periph, &devcfg, &dev_handle));
for (uint8_t cnt = 0; cnt < 3; cnt ++) {
printf("Going into sleep...\n");
TEST_ESP_OK(esp_light_sleep_start());
printf("Waked up!\n");
// check if the sleep happened as expected
TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result);
#if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
// check if the power domain also is powered down
TEST_ASSERT_EQUAL((buscfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP);
#endif
memset(recv, 0, sizeof(recv));
send[10] = cnt + 'A';
TEST_ESP_OK(spi_device_transmit(dev_handle, &trans_cfg));
printf("%s", recv);
spitest_cmp_or_dump(trans_cfg.tx_buffer, trans_cfg.rx_buffer, sizeof(send));
}
TEST_ESP_OK(spi_bus_remove_device(dev_handle));
TEST_ESP_OK(spi_bus_free(periph));
}
}
esp_sleep_set_sleep_context(NULL);
#if ESP_SLEEP_POWER_DOWN_CPU
TEST_ESP_OK(sleep_cpu_configure(false));
#endif
}
#if 0 /* Temp disable, TODO: IDFCI-2455*/
#if CONFIG_PM_ENABLE
TEST_CASE("test_spi_master_auto_sleep_retention", "[spi]")
{
// Configure dynamic frequency scaling:
// maximum and minimum frequencies are set in sdkconfig,
// automatic light sleep is enabled if tickless idle support is enabled.
uint32_t xtal_hz = 0;
esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_XTAL, ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT, &xtal_hz);
esp_pm_config_t pm_config = {
.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ,
.min_freq_mhz = xtal_hz / 1000000,
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
.light_sleep_enable = true,
#endif
};
TEST_ESP_OK(esp_pm_configure(&pm_config));
esp_sleep_context_t sleep_ctx;
esp_sleep_set_sleep_context(&sleep_ctx);
for (uint8_t allow_pd = 0; allow_pd < 2; allow_pd ++) {
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
buscfg.flags = (allow_pd) ? SPICOMMON_BUSFLAG_SLP_ALLOW_PD : 0;
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_DISABLED));
// set spi "self-loop" after bus initialized
spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spid_out);
spi_device_handle_t dev_handle;
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev_handle));
uint8_t send[13] = "hello spi 0\n";
uint8_t recv[13];
spi_transaction_t trans_cfg = {
.length = 8 * sizeof(send),
.tx_buffer = send,
.rx_buffer = recv,
};
for (uint8_t cnt = 0; cnt < 3; cnt ++) {
printf("Going into Auto sleep with power %s ...\n", (buscfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? "down" : "hold");
vTaskDelay(1000); //auto light sleep here
printf("Waked up!\n");
// check if the sleep happened as expected
TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result);
#if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
// check if the power domain also is powered down
TEST_ASSERT_EQUAL((buscfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP);
#endif
memset(recv, 0, sizeof(recv));
send[10] = cnt + '0';
TEST_ESP_OK(spi_device_polling_transmit(dev_handle, &trans_cfg));
printf("%s", recv);
spitest_cmp_or_dump(trans_cfg.tx_buffer, trans_cfg.rx_buffer, sizeof(send));
}
TEST_ESP_OK(spi_bus_remove_device(dev_handle));
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
}
esp_sleep_set_sleep_context(NULL);
pm_config.light_sleep_enable = false;
TEST_ESP_OK(esp_pm_configure(&pm_config));
}
#endif //CONFIG_PM_ENABLE
#endif // 0
@@ -15,6 +15,9 @@
#include "esp_heap_caps.h"
#include "driver/spi_master.h"
#include "esp_private/spi_master_internal.h"
#include "esp_private/sleep_cpu.h"
#include "esp_private/esp_sleep_internal.h"
#include "esp_private/esp_pmu.h"
#include "driver/spi_slave_hd.h"
#include "driver/spi_slave.h"
#include "soc/spi_pins.h"
@@ -22,7 +25,6 @@
__attribute__((unused)) static const char *TAG = "SCT";
#if (SOC_SPI_SUPPORT_SLAVE_HD_VER2 && SOC_SPI_SCT_SUPPORTED)
/*-----------------------------------------------------------
* HD SCT Functional Test
*-----------------------------------------------------------*/
@@ -230,7 +232,6 @@ TEST_CASE_MULTIPLE_DEVICES("SPI_Master_SCT_HD_Functional", "[spi_ms]", hd_master
TEST_CASE("spi_master: test_sct_dma_desc_oob_on_tail", "[spi]")
{
spi_device_handle_t handle;
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
buscfg.max_transfer_sz = 4092 * 8;
@@ -291,4 +292,127 @@ TEST_CASE("spi_master: test_sct_dma_desc_oob_on_tail", "[spi]")
TEST_ESP_OK(spi_bus_free(SPI2_HOST));
}
#endif //#if (SOC_SPI_SUPPORT_SLAVE_HD_VER2 && SOC_SPI_SCT_SUPPORTED)
/*-----------------------------------------------------------
* Sleep Retention Test
*-----------------------------------------------------------*/
#define TEST_SLP_DATA_LEN 64
//Master write, slave read, wrt slave reg
#define TEST_SLP_BUF_ID 12
#define TEST_SLP_BUF_VAL 0x11223344
static void sleep_master(void)
{
// Prepare a TOP PD sleep
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000));
#if ESP_SLEEP_POWER_DOWN_CPU
sleep_cpu_configure(true);
#endif
esp_sleep_context_t sleep_ctx;
esp_sleep_set_sleep_context(&sleep_ctx);
spi_device_handle_t handle;
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
buscfg.max_transfer_sz = 4092 * 10;
#if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
buscfg.flags |= SPICOMMON_BUSFLAG_SLP_ALLOW_PD;
#endif
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
devcfg.command_bits = 8;
devcfg.address_bits = 8;
devcfg.dummy_bits = 8;
devcfg.flags = SPI_DEVICE_HALFDUPLEX;
TEST_ESP_OK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
TEST_ESP_OK(spi_bus_add_device(SPI2_HOST, &devcfg, &handle));
TEST_ESP_OK(spi_bus_multi_trans_mode_enable(handle, true));
unity_send_signal("Master ready");
//Test data preparation
uint32_t master_tx_val = TEST_SLP_BUF_VAL;
uint8_t *master_tx_buf = heap_caps_calloc(1, TEST_SLP_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
test_fill_random_to_buffers_dualboard(199, master_tx_buf, master_tx_buf, TEST_SLP_DATA_LEN);
//---------------------Master TX---------------------------//
spi_multi_transaction_t *ret_seg_trans = NULL;
spi_multi_transaction_t tx_seg_trans[3] = {
{
.base = {
.cmd = 0x1,
.addr = TEST_SLP_BUF_ID,
.length = 4 * 8,
.tx_buffer = (uint8_t *) &master_tx_val,
},
},
{
.base = {
.cmd = 0x3,
.addr = 0xf2,
.length = TEST_SLP_DATA_LEN * 8,
.tx_buffer = master_tx_buf,
},
.dummy_bits = 8,
},
{
.base = {
.cmd = 0x7,
},
},
};
unity_wait_for_signal("Slave ready");
printf("Going into sleep with power down ...\n");
TEST_ESP_OK(esp_light_sleep_start());
printf("Waked up!\n");
// check if the sleep happened as expected
TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result);
#if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
// check if the power domain also is powered down
TEST_ASSERT_EQUAL((buscfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP);
#endif
TEST_ESP_OK(spi_device_queue_multi_trans(handle, tx_seg_trans, 3, portMAX_DELAY));
TEST_ESP_OK(spi_device_get_multi_trans_result(handle, &ret_seg_trans, portMAX_DELAY));
TEST_ASSERT(ret_seg_trans == tx_seg_trans);
ESP_LOG_BUFFER_HEX("Master tx", master_tx_buf, TEST_SLP_DATA_LEN);
free(master_tx_buf);
TEST_ESP_OK(spi_bus_multi_trans_mode_enable(handle, false));
TEST_ESP_OK(spi_bus_remove_device(handle));
TEST_ESP_OK(spi_bus_free(SPI2_HOST));
esp_sleep_set_sleep_context(NULL);
#if ESP_SLEEP_POWER_DOWN_CPU
TEST_ESP_OK(sleep_cpu_configure(false));
#endif
}
static void sleep_slave(void)
{
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
slave_hd_cfg.dma_chan = SPI_DMA_CH_AUTO,
TEST_ESP_OK(spi_slave_hd_init(SPI2_HOST, &buscfg, &slave_hd_cfg));
//Test data preparation
uint32_t slave_rx_val = 0;
uint8_t *slave_rx_buf = heap_caps_calloc(1, TEST_SLP_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
uint8_t *master_tx_buf = heap_caps_calloc(1, TEST_SLP_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
test_fill_random_to_buffers_dualboard(199, master_tx_buf, master_tx_buf, TEST_SLP_DATA_LEN);
//---------------------Slave RX---------------------------//
spi_slave_hd_data_t *ret_trans = NULL;
spi_slave_hd_data_t slave_rx_trans = {
.data = slave_rx_buf,
.len = TEST_SLP_DATA_LEN,
};
unity_wait_for_signal("Master ready");
TEST_ESP_OK(spi_slave_hd_queue_trans(SPI2_HOST, SPI_SLAVE_CHAN_RX, &slave_rx_trans, portMAX_DELAY));
unity_send_signal("Slave ready");
TEST_ESP_OK(spi_slave_hd_get_trans_res(SPI2_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY));
TEST_ASSERT(ret_trans == &slave_rx_trans);
spitest_cmp_or_dump(master_tx_buf, slave_rx_buf, TEST_SLP_DATA_LEN);
spi_slave_hd_read_buffer(SPI2_HOST, TEST_SLP_BUF_ID, (uint8_t *)&slave_rx_val, 4);
ESP_LOGI("Slave", "Slave Reg[%d] value is: 0x%" PRIx32, TEST_SLP_BUF_ID, slave_rx_val);
TEST_ASSERT(slave_rx_val == TEST_SLP_BUF_VAL);
free(master_tx_buf);
free(slave_rx_buf);
TEST_ESP_OK(spi_slave_hd_deinit(SPI2_HOST));
}
TEST_CASE_MULTIPLE_DEVICES("test_spi_master_sct_sleep_retention", "[spi_ms]", sleep_master, sleep_slave);
@@ -1,2 +0,0 @@
# don't delete.
# used for CI to compile a default config when 'sdkconfig.ci.xxxx' is exist
@@ -1 +1,3 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE=y
@@ -1,3 +1,4 @@
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
@@ -1,2 +1,4 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_INIT=n
# primitives for checking sleep internal state
CONFIG_ESP_SLEEP_DEBUG=y