diff --git a/components/driver/test_apps/components/test_driver_utils/include/test_spi_utils.h b/components/driver/test_apps/components/test_driver_utils/include/test_spi_utils.h index 6bcac2a72d..ee06c71980 100644 --- a/components/driver/test_apps/components/test_driver_utils/include/test_spi_utils.h +++ b/components/driver/test_apps/components/test_driver_utils/include/test_spi_utils.h @@ -157,9 +157,10 @@ .flags=0,\ } -//default device config for slave hd devices +//default device config for slave hd devices, DMA is always required for slave hd #define SPI_SLOT_TEST_DEFAULT_CONFIG() {\ .spics_io_num = PIN_NUM_CS, \ + .dma_chan = SPI_DMA_CH_AUTO, \ .flags = 0, \ .mode = 0, \ .command_bits = 8,\ @@ -283,4 +284,9 @@ void spitest_gpio_input_sel(uint32_t gpio_num, int func, uint32_t signal_idx); //then the cs_num of the 1st and 2nd devices are 0 and 1 respectively. void same_pin_func_sel(spi_bus_config_t bus, spi_device_interface_config_t dev, uint8_t cs_num); +// Soft simulated spi master host for slave testing +// TODO: `speed_hz` is not implemented yet, temp to max 500Hz +// TODO: mode 0 only +void spi_master_trans_impl_gpio(spi_bus_config_t bus, uint8_t cs_pin, uint8_t speed_hz, void *tx, void *rx, uint32_t len); + #endif //_TEST_COMMON_SPI_H_ diff --git a/components/driver/test_apps/components/test_driver_utils/test_spi_utils.c b/components/driver/test_apps/components/test_driver_utils/test_spi_utils.c index 8e23021d84..bdc7c1edc6 100644 --- a/components/driver/test_apps/components/test_driver_utils/test_spi_utils.c +++ b/components/driver/test_apps/components/test_driver_utils/test_spi_utils.c @@ -111,7 +111,7 @@ void slave_pull_up(const spi_bus_config_t* cfg, int spics_io_num) } /********************************************************************************** - * functions for slave task + * functions for master task *********************************************************************************/ static int test_len[] = {1, 3, 5, 7, 9, 11, 33, 64}; @@ -232,7 +232,6 @@ void spitest_gpio_input_sel(uint32_t gpio_num, int func, uint32_t signal_idx) esp_rom_gpio_connect_in_signal(gpio_num, signal_idx, 0); } -#if (TEST_SPI_PERIPH_NUM >= 2) //Note this cs_dev_id is the ID of the connected devices' ID, e.g. if 2 devices are connected to the bus, //then the cs_dev_id of the 1st and 2nd devices are 0 and 1 respectively. void same_pin_func_sel(spi_bus_config_t bus, spi_device_interface_config_t dev, uint8_t cs_dev_id) @@ -249,4 +248,32 @@ void same_pin_func_sel(spi_bus_config_t bus, spi_device_interface_config_t dev, spitest_gpio_output_sel(bus.sclk_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spiclk_out); spitest_gpio_input_sel(bus.sclk_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spiclk_in); } -#endif //(TEST_SPI_PERIPH_NUM >= 2) + +void spi_master_trans_impl_gpio(spi_bus_config_t bus, uint8_t cs_pin, uint8_t speed_hz, void *tx, void *rx, uint32_t len) +{ + uint8_t *u8_tx = tx, *u8_rx = rx; + esp_rom_gpio_connect_out_signal(cs_pin, SIG_GPIO_OUT_IDX, 0, 0); + esp_rom_gpio_connect_out_signal(bus.sclk_io_num, SIG_GPIO_OUT_IDX, 0, 0); + esp_rom_gpio_connect_out_signal(bus.mosi_io_num, SIG_GPIO_OUT_IDX, 0, 0); + esp_rom_gpio_connect_in_signal(bus.miso_io_num, SIG_GPIO_OUT_IDX, 0); + + gpio_set_level(cs_pin, 0); + vTaskDelay(1); // cs_ena_pre_trans + for (uint32_t index = 0; index < len; index ++) { + uint8_t rx_data = 0; + for (uint8_t bit = 0x80; bit > 0; bit >>= 1) { + // mode 0, output data first + gpio_set_level(bus.mosi_io_num, (u8_tx) ? (u8_tx[index] & bit) : 0); + vTaskDelay(1); + gpio_set_level(bus.sclk_io_num, 1); + rx_data <<= 1; + rx_data |= gpio_get_level(bus.miso_io_num); + vTaskDelay(1); + gpio_set_level(bus.sclk_io_num, 0); + } + if (u8_rx) { + u8_rx[index] = rx_data; + } + } + gpio_set_level(cs_pin, 1); +} diff --git a/components/esp_driver_spi/src/gpspi/spi_slave.c b/components/esp_driver_spi/src/gpspi/spi_slave.c index 65b011675d..58d8b36486 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave.c @@ -28,6 +28,7 @@ #include "driver/spi_slave.h" #include "hal/gpio_hal.h" #include "hal/spi_slave_hal.h" +#include "esp_private/sleep_retention.h" #include "esp_private/spi_slave_internal.h" #include "esp_private/spi_common_internal.h" #include "esp_private/esp_cache_private.h" @@ -129,6 +130,17 @@ static void ipc_isr_reg_to_core(void *args) } #endif +#if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +static esp_err_t s_spi_create_sleep_retention_cb(void *arg) +{ + spi_slave_t *context = arg; + return sleep_retention_entries_create(spi_reg_retention_info[context->id - 1].entry_array, + spi_reg_retention_info[context->id - 1].array_size, + REGDMA_LINK_PRI_GPSPI, + spi_reg_retention_info[context->id - 1].module_id); +} +#endif // SOC_SPI_SUPPORT_SLEEP_RETENTION + esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *bus_config, const spi_slave_interface_config_t *slave_config, spi_dma_chan_t dma_chan) { bool spi_chan_claimed; @@ -222,6 +234,32 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b esp_pm_lock_acquire(spihost[host]->pm_lock); #endif //CONFIG_PM_ENABLE +#if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + sleep_retention_module_init_param_t init_param = { + .cbs = { + .create = { + .handle = s_spi_create_sleep_retention_cb, + .arg = spihost[host], + }, + }, + .depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM), + }; + + if (ESP_OK == sleep_retention_module_init(spi_reg_retention_info[host - 1].module_id, &init_param)) { + if ((bus_config->flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) && (sleep_retention_module_allocate(spi_reg_retention_info[host - 1].module_id) != ESP_OK)) { + // even though the sleep retention create failed, SPI driver should still work, so just warning here + ESP_LOGW(SPI_TAG, "Alloc sleep recover failed, spi may hold power on"); + } + } else { + // even the sleep retention init failed, SPI driver should still work, so just warning here + ESP_LOGW(SPI_TAG, "Init sleep recover failed, spi may offline after sleep"); + } +#else + if (bus_config->flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) { + ESP_LOGE(SPI_TAG, "power down peripheral in sleep is not enabled or not supported on your target"); + } +#endif // SOC_SPI_SUPPORT_SLEEP_RETENTION + //Create queues spihost[host]->trans_queue = xQueueCreate(slave_config->queue_size, sizeof(spi_slave_trans_priv_t)); if (!spihost[host]->trans_queue) { @@ -290,6 +328,17 @@ esp_err_t spi_slave_free(spi_host_device_t host) } spicommon_bus_free_io_cfg(&spihost[host]->bus_config); esp_intr_free(spihost[host]->intr); + +#if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + const periph_retention_module_t retention_id = spi_reg_retention_info[spihost[host]->id - 1].module_id; + if (sleep_retention_is_module_created(retention_id)) { + assert(sleep_retention_is_module_inited(retention_id)); + sleep_retention_module_free(retention_id); + } + if (sleep_retention_is_module_inited(retention_id)) { + sleep_retention_module_deinit(retention_id); + } +#endif #ifdef CONFIG_PM_ENABLE if (spihost[host]->pm_lock) { esp_pm_lock_release(spihost[host]->pm_lock); diff --git a/components/esp_driver_spi/test_apps/param/main/test_spi_param.c b/components/esp_driver_spi/test_apps/param/main/test_spi_param.c index 7f507a549b..2d097c34e5 100644 --- a/components/esp_driver_spi/test_apps/param/main/test_spi_param.c +++ b/components/esp_driver_spi/test_apps/param/main/test_spi_param.c @@ -1527,10 +1527,16 @@ static void test_master_hd_dma(void) TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { - spi_device_interface_config_t devcfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); - devcfg.mode = mode; - devcfg.flags = SPI_DEVICE_HALFDUPLEX; - devcfg.clock_speed_hz = s_spi_bus_freq[speed_level]; + spi_device_interface_config_t devcfg = { + .spics_io_num = PIN_NUM_CS, + .clock_speed_hz = s_spi_bus_freq[speed_level], + .mode = mode, + .flags = SPI_DEVICE_HALFDUPLEX, + .command_bits = 8, + .address_bits = 8, + .dummy_bits = 8, + .queue_size = 10, + }; 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); @@ -1629,10 +1635,16 @@ static void test_master_hd_no_dma(void) TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_DISABLED)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { - spi_device_interface_config_t devcfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); - devcfg.mode = mode; - devcfg.flags = SPI_DEVICE_HALFDUPLEX; - devcfg.clock_speed_hz = s_spi_bus_freq[speed_level]; + spi_device_interface_config_t devcfg = { + .spics_io_num = PIN_NUM_CS, + .clock_speed_hz = s_spi_bus_freq[speed_level], + .mode = mode, + .flags = SPI_DEVICE_HALFDUPLEX, + .command_bits = 8, + .address_bits = 8, + .dummy_bits = 8, + .queue_size = 10, + }; 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); diff --git a/components/esp_driver_spi/test_apps/slave/main/test_spi_slave.c b/components/esp_driver_spi/test_apps/slave/main/test_spi_slave.c index f5634dbba9..1279828fcb 100644 --- a/components/esp_driver_spi/test_apps/slave/main/test_spi_slave.c +++ b/components/esp_driver_spi/test_apps/slave/main/test_spi_slave.c @@ -18,6 +18,9 @@ #include "driver/gpio.h" #include "esp_private/cache_utils.h" #include "esp_private/spi_slave_internal.h" +#include "esp_private/sleep_cpu.h" +#include "esp_private/esp_sleep_internal.h" +#include "esp_private/esp_pmu.h" #include "esp_log.h" #include "esp_rom_gpio.h" @@ -778,3 +781,62 @@ TEST_CASE("test_slave_isr_pin_to_core", "[spi]") TEST_ASSERT_EQUAL_UINT32(TEST_ISR_CNT, slave_expect); } #endif + +TEST_CASE("test spi slave 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 + sleep_cpu_configure(true); +#endif + esp_sleep_context_t sleep_ctx; + esp_sleep_set_sleep_context(&sleep_ctx); + + uint8_t slv_send[14] = "I'm slave x\n", slv_rexcv[14]; + uint8_t mst_send[14] = "I'm master x\n", mst_rexcv[14]; + spi_slave_transaction_t *ret_trans, trans_cfg = { + .tx_buffer = slv_send, + .rx_buffer = slv_rexcv, + .length = sizeof(slv_send) * 8, + }; + + 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; + spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); + TEST_ESP_OK(spi_slave_initialize(TEST_SPI_HOST, &buscfg, &slvcfg, SPI_DMA_DISABLED)); + gpio_pullup_en(slvcfg.spics_io_num); + vTaskDelay(1); + + for (uint8_t cnt = 0; cnt < 3; cnt ++) { + printf("Going into sleep with power %s ...\n", (buscfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? "down" : "hold"); + 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 + // 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 + slv_send[11] = cnt + '0'; + mst_send[11] = cnt + 'A'; + memset(mst_rexcv, 0, sizeof(mst_rexcv)); + memset(slv_rexcv, 0, sizeof(slv_rexcv)); + TEST_ESP_OK(spi_slave_queue_trans(TEST_SPI_HOST, &trans_cfg, portMAX_DELAY)); + spi_master_trans_impl_gpio(buscfg, slvcfg.spics_io_num, 0, mst_send, mst_rexcv, sizeof(mst_send)); + TEST_ESP_OK(spi_slave_get_trans_result(TEST_SPI_HOST, &ret_trans, portMAX_DELAY)); + + spitest_cmp_or_dump(slv_send, mst_rexcv, sizeof(mst_rexcv)); + spitest_cmp_or_dump(mst_send, slv_rexcv, sizeof(slv_rexcv)); + } + + TEST_ESP_OK(spi_slave_free(TEST_SPI_HOST)); + } + + esp_sleep_set_sleep_context(NULL); +#if ESP_SLEEP_POWER_DOWN_CPU + TEST_ESP_OK(sleep_cpu_configure(false)); +#endif +} diff --git a/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.defaults b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.defaults deleted file mode 100644 index 250a29bc45..0000000000 --- a/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.defaults +++ /dev/null @@ -1,2 +0,0 @@ -# don't delete. -# used for CI to compile a default config when 'sdkconfig.ci.xxxx' is exist diff --git a/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release new file mode 100644 index 0000000000..17aaee1e8e --- /dev/null +++ b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release @@ -0,0 +1,6 @@ +CONFIG_PM_ENABLE=y +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +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 diff --git a/components/esp_driver_spi/test_apps/slave/sdkconfig.defaults b/components/esp_driver_spi/test_apps/slave/sdkconfig.defaults index b308cb2ddd..fff8c78591 100644 --- a/components/esp_driver_spi/test_apps/slave/sdkconfig.defaults +++ b/components/esp_driver_spi/test_apps/slave/sdkconfig.defaults @@ -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 diff --git a/components/soc/esp32c5/spi_periph.c b/components/soc/esp32c5/spi_periph.c index 67fa9fdcb9..c2b0c6f091 100644 --- a/components/soc/esp32c5/spi_periph.c +++ b/components/soc/esp32c5/spi_periph.c @@ -40,7 +40,7 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = { }; /** - * Backup registers in Light sleep: (total cnt 12) + * Backup registers in Light sleep: (total cnt 29) * * cmd * addr @@ -53,10 +53,12 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = { * misc * dma_conf * dma_int_ena + * data_buf[0-15] // slave driver only * slave + * slave1 */ -#define SPI_RETENTION_REGS_CNT 12 -static const uint32_t spi_regs_map[4] = {0x31ff, 0x1000000, 0x0, 0x0}; +#define SPI_RETENTION_REGS_CNT 29 +static const uint32_t spi_regs_map[4] = {0x31ff, 0x33fffc0, 0x0, 0x0}; #define SPI_REG_RETENTION_ENTRIES(num) { \ [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_GPSPI_LINK(0), \ REG_SPI_BASE(num), REG_SPI_BASE(num), \ diff --git a/components/soc/esp32c6/spi_periph.c b/components/soc/esp32c6/spi_periph.c index 1a3696a27f..f7f16db5fe 100644 --- a/components/soc/esp32c6/spi_periph.c +++ b/components/soc/esp32c6/spi_periph.c @@ -41,7 +41,7 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = { /** - * Backup registers in Light sleep: (total cnt 12) + * Backup registers in Light sleep: (total cnt 29) * * cmd * addr @@ -54,10 +54,12 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = { * misc * dma_conf * dma_int_ena + * data_buf[0-15] // slave driver only * slave + * slave1 */ -#define SPI_RETENTION_REGS_CNT 12 -static const uint32_t spi_regs_map[4] = {0x31ff, 0x1000000, 0x0, 0x0}; +#define SPI_RETENTION_REGS_CNT 29 +static const uint32_t spi_regs_map[4] = {0x31ff, 0x33fffc0, 0x0, 0x0}; #define SPI_REG_RETENTION_ENTRIES(num) { \ [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_GPSPI_LINK(0), \ REG_SPI_BASE(num), REG_SPI_BASE(num), \ diff --git a/components/soc/esp32c61/spi_periph.c b/components/soc/esp32c61/spi_periph.c index 67fa9fdcb9..c2b0c6f091 100644 --- a/components/soc/esp32c61/spi_periph.c +++ b/components/soc/esp32c61/spi_periph.c @@ -40,7 +40,7 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = { }; /** - * Backup registers in Light sleep: (total cnt 12) + * Backup registers in Light sleep: (total cnt 29) * * cmd * addr @@ -53,10 +53,12 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = { * misc * dma_conf * dma_int_ena + * data_buf[0-15] // slave driver only * slave + * slave1 */ -#define SPI_RETENTION_REGS_CNT 12 -static const uint32_t spi_regs_map[4] = {0x31ff, 0x1000000, 0x0, 0x0}; +#define SPI_RETENTION_REGS_CNT 29 +static const uint32_t spi_regs_map[4] = {0x31ff, 0x33fffc0, 0x0, 0x0}; #define SPI_REG_RETENTION_ENTRIES(num) { \ [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_GPSPI_LINK(0), \ REG_SPI_BASE(num), REG_SPI_BASE(num), \ diff --git a/components/soc/esp32h2/spi_periph.c b/components/soc/esp32h2/spi_periph.c index 1ac5f7e40c..fd048b69db 100644 --- a/components/soc/esp32h2/spi_periph.c +++ b/components/soc/esp32h2/spi_periph.c @@ -40,7 +40,7 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = { }; /** - * Backup registers in Light sleep: (total cnt 12) + * Backup registers in Light sleep: (total cnt 29) * * cmd * addr @@ -53,10 +53,12 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = { * misc * dma_conf * dma_int_ena + * data_buf[0-15] // slave driver only * slave + * slave1 */ -#define SPI_RETENTION_REGS_CNT 12 -static const uint32_t spi_regs_map[4] = {0x31ff, 0x1000000, 0x0, 0x0}; +#define SPI_RETENTION_REGS_CNT 29 +static const uint32_t spi_regs_map[4] = {0x31ff, 0x33fffc0, 0x0, 0x0}; #define SPI_REG_RETENTION_ENTRIES(num) { \ [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_GPSPI_LINK(0), \ REG_SPI_BASE(num), REG_SPI_BASE(num), \ diff --git a/components/soc/esp32p4/spi_periph.c b/components/soc/esp32p4/spi_periph.c index db30a6bf68..527c767f19 100644 --- a/components/soc/esp32p4/spi_periph.c +++ b/components/soc/esp32p4/spi_periph.c @@ -72,7 +72,7 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = { }; /** - * Backup registers in Light sleep: (total cnt 12) + * Backup registers in Light sleep: (total cnt 29) * * cmd * addr @@ -85,10 +85,12 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = { * misc * dma_conf * dma_int_ena + * data_buf[0-15] // slave driver only * slave + * slave1 */ -#define SPI_RETENTION_REGS_CNT 12 -static const uint32_t spi_regs_map[4] = {0x31ff, 0x1000000, 0x0, 0x0}; +#define SPI_RETENTION_REGS_CNT 29 +static const uint32_t spi_regs_map[4] = {0x31ff, 0x33fffc0, 0x0, 0x0}; #define SPI_REG_RETENTION_ENTRIES(num) { \ [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_GPSPI_LINK(0), \ REG_SPI_BASE(num), REG_SPI_BASE(num), \ diff --git a/docs/en/api-reference/peripherals/spi_slave.rst b/docs/en/api-reference/peripherals/spi_slave.rst index 57af784bdd..f237c27b99 100644 --- a/docs/en/api-reference/peripherals/spi_slave.rst +++ b/docs/en/api-reference/peripherals/spi_slave.rst @@ -55,6 +55,17 @@ The SPI slave driver allows using the SPI peripherals as full-duplex Devices. Th The SPI slave driver supports registering the SPI ISR to a certain CPU core. If multiple tasks try to access the same SPI Device simultaneously, it is recommended that your application be refactored so that each SPI peripheral is only accessed by a single task at a time. Please also use :cpp:member:`spi_bus_config_t::isr_cpu_id` to register the SPI ISR to the same core as SPI peripheral related tasks to ensure thread safety. +.. only:: SOC_SPI_SUPPORT_SLEEP_RETENTION + + Sleep Retention + ^^^^^^^^^^^^^^^ + + {IDF_TARGET_NAME} supports to retain the SPI register context before entering **light sleep** and restore them after waking up. This means you don't have to re-init the SPI driver after the light sleep. + + This feature can be enabled by setting the flag :c:macro:`SPICOMMON_BUSFLAG_SLP_ALLOW_PD`. It will allow the system to power down the SPI in light sleep, meanwhile save the register context. It can help to save more power consumption with some extra cost of the memory. + + Notice that when GPSPI is working as a slave, it is **not** support to enter sleep when any transaction (including TX and RX) is not finished. + SPI Transactions ---------------- diff --git a/docs/zh_CN/api-reference/peripherals/spi_slave.rst b/docs/zh_CN/api-reference/peripherals/spi_slave.rst index bf21efe7b3..b32f8c7e37 100644 --- a/docs/zh_CN/api-reference/peripherals/spi_slave.rst +++ b/docs/zh_CN/api-reference/peripherals/spi_slave.rst @@ -55,6 +55,17 @@ SPI 从机驱动程序允许将 SPI 外设作为全双工设备使用。驱动 SPI 从机驱动程序支持将 SPI ISR 注册至指定 CPU 内核。如果多个任务同时尝试访问一个 SPI 设备,建议重构应用程序,以使每个 SPI 外设一次只由一个任务访问。此外,请使用 :cpp:member:`spi_bus_config_t::isr_cpu_id` 将 SPI ISR 注册至与 SPI 外设相关任务相同的内核,确保线程安全。 +.. only:: SOC_SPI_SUPPORT_SLEEP_RETENTION + + 睡眠保留 + ^^^^^^^^ + + {IDF_TARGET_NAME} 支持在进入 **Light Sleep** 之前保留 SPI 寄存器中的内容,并在唤醒后恢复。即程序不需要在 **Light Sleep** 唤醒后重新配置 SPI。 + + 该特性可以通过置位配置中的 :c:macro:`SPICOMMON_BUSFLAG_SLP_ALLOW_PD` 标志位启用。启用后驱动允许系统在 Light Sleep 时对 SPI 掉电,同时保存寄存器配置。它可以帮助降低轻度睡眠时的功耗,但需要花费一些额外的存储来保存寄存器的配置。 + + 注意在 Slave 角色下,不支持在所有传输(发送和接收)未完成时进入睡眠,否则将会出错。 + SPI 传输事务 ----------------