Merge branch 'feat/spi_slave_sleep_retention' into 'master'

feat(driver_spi): spi slave driver sleep retention

Closes IDF-9746 and IDF-8467

See merge request espressif/esp-idf!33858
This commit is contained in:
Wan Lei
2024-11-26 14:44:54 +08:00
23 changed files with 547 additions and 131 deletions

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -13,6 +13,7 @@
#include "freertos/queue.h"
#include "freertos/ringbuf.h"
#include "driver/gpio.h"
#include "esp_private/sleep_retention.h"
#include "esp_private/spi_common_internal.h"
#include "esp_private/spi_share_hw_ctrl.h"
#include "esp_private/esp_cache_private.h"
@@ -36,7 +37,7 @@ typedef struct {
} spi_slave_hd_trans_priv_t;
typedef struct {
bool dma_enabled;
spi_host_device_t host_id;
spi_dma_ctx_t *dma_ctx;
uint16_t internal_mem_align_size;
int max_transfer_sz;
@@ -79,6 +80,17 @@ static void s_spi_slave_hd_append_legacy_isr(void *arg);
static void s_spi_slave_hd_segment_isr(void *arg);
#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_hd_slot_t *host = arg;
return sleep_retention_entries_create(spi_reg_retention_info[host->host_id - 1].entry_array,
spi_reg_retention_info[host->host_id - 1].array_size,
REGDMA_LINK_PRI_GPSPI,
spi_reg_retention_info[host->host_id - 1].module_id);
}
#endif // SOC_SPI_SUPPORT_SLEEP_RETENTION
esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *bus_config, const spi_slave_hd_slot_config_t *config)
{
bool spi_chan_claimed;
@@ -86,10 +98,11 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b
esp_err_t ret = ESP_OK;
SPIHD_CHECK(VALID_HOST(host_id), "invalid host", ESP_ERR_INVALID_ARG);
//DMA is mandatory required for slave HD driver, check here for better experience to `transmit` api
#if CONFIG_IDF_TARGET_ESP32S2
SPIHD_CHECK(config->dma_chan == SPI_DMA_DISABLED || config->dma_chan == (int)host_id || config->dma_chan == SPI_DMA_CH_AUTO, "invalid dma channel", ESP_ERR_INVALID_ARG);
SPIHD_CHECK(config->dma_chan == (int)host_id || config->dma_chan == SPI_DMA_CH_AUTO, "dma is required or invalid channel", ESP_ERR_INVALID_ARG);
#elif SOC_GDMA_SUPPORTED
SPIHD_CHECK(config->dma_chan == SPI_DMA_DISABLED || config->dma_chan == SPI_DMA_CH_AUTO, "invalid dma channel, chip only support spi dma channel auto-alloc", ESP_ERR_INVALID_ARG);
SPIHD_CHECK(config->dma_chan == SPI_DMA_CH_AUTO, "dma is required or invalid channel, only support SPI_DMA_CH_AUTO", ESP_ERR_INVALID_ARG);
#endif
spi_chan_claimed = spicommon_periph_claim(host_id, "slave_hd");
@@ -101,54 +114,49 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b
goto cleanup;
}
spihost[host_id] = host;
host->host_id = host_id;
host->int_spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
host->dma_enabled = (config->dma_chan != SPI_DMA_DISABLED);
host->append_mode = append_mode;
if (host->dma_enabled) {
ret = spicommon_dma_chan_alloc(host_id, config->dma_chan, &host->dma_ctx);
if (ret != ESP_OK) {
goto cleanup;
}
ret = spicommon_dma_chan_alloc(host_id, config->dma_chan, &host->dma_ctx);
if (ret != ESP_OK) {
goto cleanup;
}
#if SOC_GDMA_SUPPORTED
gdma_strategy_config_t dma_strategy = {
.auto_update_desc = true,
.eof_till_data_popped = true,
};
gdma_apply_strategy(host->dma_ctx->tx_dma_chan, &dma_strategy);
gdma_strategy_config_t dma_strategy = {
.auto_update_desc = true,
.eof_till_data_popped = true,
};
gdma_apply_strategy(host->dma_ctx->tx_dma_chan, &dma_strategy);
#else
spi_dma_ll_enable_out_auto_wrback(SPI_LL_GET_HW(host->dma_ctx->tx_dma_chan.host_id), host->dma_ctx->tx_dma_chan.chan_id, 1);
spi_dma_ll_set_out_eof_generation(SPI_LL_GET_HW(host->dma_ctx->tx_dma_chan.host_id), host->dma_ctx->tx_dma_chan.chan_id, 1);
spi_dma_ll_enable_out_auto_wrback(SPI_LL_GET_HW(host->dma_ctx->tx_dma_chan.host_id), host->dma_ctx->tx_dma_chan.chan_id, 1);
spi_dma_ll_set_out_eof_generation(SPI_LL_GET_HW(host->dma_ctx->tx_dma_chan.host_id), host->dma_ctx->tx_dma_chan.chan_id, 1);
#endif
ret = spicommon_dma_desc_alloc(host->dma_ctx, bus_config->max_transfer_sz, &host->max_transfer_sz);
if (ret != ESP_OK) {
goto cleanup;
}
ret = spicommon_dma_desc_alloc(host->dma_ctx, bus_config->max_transfer_sz, &host->max_transfer_sz);
if (ret != ESP_OK) {
goto cleanup;
}
host->hal.dma_desc_num = host->dma_ctx->dma_desc_num;
host->hal.dmadesc_tx = heap_caps_malloc(sizeof(spi_slave_hd_hal_desc_append_t) * host->hal.dma_desc_num, MALLOC_CAP_DEFAULT);
host->hal.dmadesc_rx = heap_caps_malloc(sizeof(spi_slave_hd_hal_desc_append_t) * host->hal.dma_desc_num, MALLOC_CAP_DEFAULT);
if (!(host->hal.dmadesc_tx && host->hal.dmadesc_rx)) {
ret = ESP_ERR_NO_MEM;
goto cleanup;
}
//Pair each desc to each possible trans
for (int i = 0; i < host->hal.dma_desc_num; i ++) {
host->hal.dmadesc_tx[i].desc = &host->dma_ctx->dmadesc_tx[i];
host->hal.dmadesc_rx[i].desc = &host->dma_ctx->dmadesc_rx[i];
}
host->hal.dma_desc_num = host->dma_ctx->dma_desc_num;
host->hal.dmadesc_tx = heap_caps_malloc(sizeof(spi_slave_hd_hal_desc_append_t) * host->hal.dma_desc_num, MALLOC_CAP_DEFAULT);
host->hal.dmadesc_rx = heap_caps_malloc(sizeof(spi_slave_hd_hal_desc_append_t) * host->hal.dma_desc_num, MALLOC_CAP_DEFAULT);
if (!(host->hal.dmadesc_tx && host->hal.dmadesc_rx)) {
ret = ESP_ERR_NO_MEM;
goto cleanup;
}
//Pair each desc to each possible trans
for (int i = 0; i < host->hal.dma_desc_num; i ++) {
host->hal.dmadesc_tx[i].desc = &host->dma_ctx->dmadesc_tx[i];
host->hal.dmadesc_rx[i].desc = &host->dma_ctx->dmadesc_rx[i];
}
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
size_t alignment;
esp_cache_get_alignment(MALLOC_CAP_DMA, &alignment);
host->internal_mem_align_size = alignment;
size_t alignment;
esp_cache_get_alignment(MALLOC_CAP_DMA, &alignment);
host->internal_mem_align_size = alignment;
#else
host->internal_mem_align_size = 4;
host->internal_mem_align_size = 4;
#endif
} else {
//We're limited to non-DMA transfers: the SPI work registers can hold (72 for S2, 64 for others) bytes at most.
host->max_transfer_sz = 0;
}
ret = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, &host->flags);
if (ret != ESP_OK) {
@@ -159,7 +167,7 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b
spi_slave_hd_hal_config_t hal_config = {
.host_id = host_id,
.dma_enabled = host->dma_enabled,
.dma_enabled = true,
.append_mode = append_mode,
.mode = config->mode,
.tx_lsbfirst = (config->flags & SPI_SLAVE_HD_RXBIT_LSBFIRST),
@@ -178,6 +186,32 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b
esp_pm_lock_acquire(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 = host,
},
},
.depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM),
};
if (ESP_OK == sleep_retention_module_init(spi_reg_retention_info[host_id - 1].module_id, &init_param)) {
if ((bus_config->flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) && (sleep_retention_module_allocate(spi_reg_retention_info[host_id - 1].module_id) != ESP_OK)) {
// even though the sleep retention create failed, SPI driver should still work, so just warning here
ESP_LOGW(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(TAG, "Init sleep recover failed, spi may offline after sleep");
}
#else
if (bus_config->flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) {
ESP_LOGE(TAG, "power down peripheral in sleep is not enabled or not supported on your target");
}
#endif // SOC_SPI_SUPPORT_SLEEP_RETENTION
//Create Queues and Semaphores
host->tx_ret_queue = xQueueCreate(config->queue_size, sizeof(spi_slave_hd_trans_priv_t));
host->rx_ret_queue = xQueueCreate(config->queue_size, sizeof(spi_slave_hd_trans_priv_t));
@@ -279,6 +313,17 @@ esp_err_t spi_slave_hd_deinit(spi_host_device_t host_id)
}
esp_intr_free(host->intr);
esp_intr_free(host->intr_dma);
#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[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 (host->pm_lock) {
esp_pm_lock_release(host->pm_lock);
@@ -287,13 +332,12 @@ esp_err_t spi_slave_hd_deinit(spi_host_device_t host_id)
#endif
spicommon_periph_free(host_id);
if (host->dma_enabled) {
free(host->dma_ctx->dmadesc_tx);
free(host->dma_ctx->dmadesc_rx);
free(host->hal.dmadesc_tx);
free(host->hal.dmadesc_rx);
spicommon_dma_chan_free(host->dma_ctx);
}
free(host->dma_ctx->dmadesc_tx);
free(host->dma_ctx->dmadesc_rx);
free(host->hal.dmadesc_tx);
free(host->hal.dmadesc_rx);
spicommon_dma_chan_free(host->dma_ctx);
free(host);
spihost[host_id] = NULL;
return ESP_OK;
@@ -459,6 +503,9 @@ static IRAM_ATTR void spi_slave_hd_append_tx_isr(void *arg)
if (!trans_finish) {
break;
}
portENTER_CRITICAL_ISR(&host->int_spinlock);
hal->tx_used_desc_cnt--;
portEXIT_CRITICAL_ISR(&host->int_spinlock);
bool ret_queue = true;
if (callback->cb_sent) {
@@ -500,7 +547,11 @@ static IRAM_ATTR void spi_slave_hd_append_rx_isr(void *arg)
if (!trans_finish) {
break;
}
portENTER_CRITICAL_ISR(&host->int_spinlock);
hal->rx_used_desc_cnt--;
portEXIT_CRITICAL_ISR(&host->int_spinlock);
ret_priv_trans.trans->trans_len = trans_len;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE //invalidate here to let user access rx data in post_cb if possible
uint16_t alignment = host->internal_mem_align_size;
uint32_t buff_len = (ret_priv_trans.trans->len + alignment - 1) & (~(alignment - 1));
@@ -646,8 +697,7 @@ esp_err_t s_spi_slave_hd_append_txdma(spi_slave_hd_slot_t *host, uint8_t *data,
//Check if there are enough available DMA descriptors for software to use
int num_required = (len + LLDESC_MAX_NUM_PER_DESC - 1) / LLDESC_MAX_NUM_PER_DESC;
int not_recycled_desc_num = hal->tx_used_desc_cnt - hal->tx_recycled_desc_cnt;
int available_desc_num = hal->dma_desc_num - not_recycled_desc_num;
int available_desc_num = hal->dma_desc_num - hal->tx_used_desc_cnt;
if (num_required > available_desc_num) {
return ESP_ERR_INVALID_STATE;
}
@@ -655,8 +705,7 @@ esp_err_t s_spi_slave_hd_append_txdma(spi_slave_hd_slot_t *host, uint8_t *data,
spicommon_dma_desc_setup_link(hal->tx_cur_desc->desc, data, len, false);
hal->tx_cur_desc->arg = arg;
if (!hal->tx_dma_started) {
hal->tx_dma_started = true;
if (!hal->tx_used_desc_cnt) {
//start a link
hal->tx_dma_tail = hal->tx_cur_desc;
spi_dma_reset(host->dma_ctx->tx_dma_chan);
@@ -670,8 +719,10 @@ esp_err_t s_spi_slave_hd_append_txdma(spi_slave_hd_slot_t *host, uint8_t *data,
}
//Move the current descriptor pointer according to the number of the linked descriptors
portENTER_CRITICAL(&host->int_spinlock);
hal->tx_used_desc_cnt += num_required;
portEXIT_CRITICAL(&host->int_spinlock);
for (int i = 0; i < num_required; i++) {
hal->tx_used_desc_cnt++;
hal->tx_cur_desc++;
if (hal->tx_cur_desc == hal->dmadesc_tx + hal->dma_desc_num) {
hal->tx_cur_desc = hal->dmadesc_tx;
@@ -687,8 +738,7 @@ esp_err_t s_spi_slave_hd_append_rxdma(spi_slave_hd_slot_t *host, uint8_t *data,
//Check if there are enough available dma descriptors for software to use
int num_required = (len + LLDESC_MAX_NUM_PER_DESC - 1) / LLDESC_MAX_NUM_PER_DESC;
int not_recycled_desc_num = hal->rx_used_desc_cnt - hal->rx_recycled_desc_cnt;
int available_desc_num = hal->dma_desc_num - not_recycled_desc_num;
int available_desc_num = hal->dma_desc_num - hal->rx_used_desc_cnt;
if (num_required > available_desc_num) {
return ESP_ERR_INVALID_STATE;
}
@@ -696,8 +746,7 @@ esp_err_t s_spi_slave_hd_append_rxdma(spi_slave_hd_slot_t *host, uint8_t *data,
spicommon_dma_desc_setup_link(hal->rx_cur_desc->desc, data, len, false);
hal->rx_cur_desc->arg = arg;
if (!hal->rx_dma_started) {
hal->rx_dma_started = true;
if (!hal->rx_used_desc_cnt) {
//start a link
hal->rx_dma_tail = hal->rx_cur_desc;
spi_dma_reset(host->dma_ctx->rx_dma_chan);
@@ -711,8 +760,10 @@ esp_err_t s_spi_slave_hd_append_rxdma(spi_slave_hd_slot_t *host, uint8_t *data,
}
//Move the current descriptor pointer according to the number of the linked descriptors
portENTER_CRITICAL(&host->int_spinlock);
hal->rx_used_desc_cnt += num_required;
portEXIT_CRITICAL(&host->int_spinlock);
for (int i = 0; i < num_required; i++) {
hal->rx_used_desc_cnt++;
hal->rx_cur_desc++;
if (hal->rx_cur_desc == hal->dmadesc_rx + hal->dma_desc_num) {
hal->rx_cur_desc = hal->dmadesc_rx;

View File

@@ -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);

View File

@@ -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
}

View File

@@ -1,2 +0,0 @@
# don't delete.
# used for CI to compile a default config when 'sdkconfig.ci.xxxx' is exist

View File

@@ -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

View File

@@ -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

View File

@@ -7,18 +7,20 @@
Tests for the spi slave hd mode
*/
#include "string.h"
#include "esp_log.h"
#include "test_utils.h"
#include "test_spi_utils.h"
#include "soc/spi_periph.h"
#include "esp_serial_slave_link/essl_spi.h"
#include "test_dualboard_utils.h"
#if SOC_SPI_SUPPORT_SLAVE_HD_VER2
#include "hal/spi_ll.h"
#include "driver/gpio.h"
#include "driver/spi_slave_hd.h"
#include "esp_serial_slave_link/essl_spi.h"
#include "esp_private/sleep_cpu.h"
#include "esp_private/esp_sleep_internal.h"
#include "esp_private/esp_pmu.h"
#if (TEST_SPI_PERIPH_NUM >= 2) //These will be only enabled on chips with 2 or more SPI peripherals
#include "esp_rom_gpio.h"
#define TEST_BUFFER_SIZE 256 ///< buffer size of each wrdma buffer in fifo mode
@@ -110,7 +112,6 @@ static void init_slave_hd(int mode, bool append_mode, const spi_slave_hd_callbac
#endif
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
slave_hd_cfg.mode = mode;
slave_hd_cfg.dma_chan = SPI_DMA_CH_AUTO;
if (append_mode) {
slave_hd_cfg.flags |= SPI_SLAVE_HD_APPEND_MODE;
}
@@ -680,16 +681,7 @@ static void hd_slave(void)
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
bus_cfg.max_transfer_sz = 14000 * 30;
spi_slave_hd_slot_config_t slave_hd_cfg = {
.spics_io_num = PIN_NUM_CS,
.dma_chan = SPI_DMA_CH_AUTO,
.flags = 0,
.mode = 0,
.command_bits = 8,
.address_bits = 8,
.dummy_bits = 8,
.queue_size = 10,
};
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
unity_wait_for_signal("master ready");
@@ -831,16 +823,7 @@ static void hd_slave_quad(void)
.max_transfer_sz = 14000 * 30
};
spi_slave_hd_slot_config_t slave_hd_cfg = {
.spics_io_num = PIN_NUM_CS,
.dma_chan = SPI_DMA_CH_AUTO,
.flags = 0,
.mode = 0,
.command_bits = 8,
.address_bits = 8,
.dummy_bits = 8,
.queue_size = 10,
};
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
WORD_ALIGNED_ATTR uint8_t *slave_send_buf = heap_caps_malloc(BUF_SIZE, MALLOC_CAP_DMA);
@@ -928,7 +911,6 @@ void slave_run_append(void)
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
slave_hd_cfg.flags |= SPI_SLAVE_HD_APPEND_MODE;
slave_hd_cfg.dma_chan = SPI_DMA_CH_AUTO;
TEST_ESP_OK(spi_slave_hd_init(TEST_SPI_HOST, &bus_cfg, &slave_hd_cfg));
unity_wait_for_signal("Master ready");
@@ -1032,9 +1014,12 @@ void master_run_essl(void)
bus_cfg.max_transfer_sz = 50000;
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO));
spi_device_interface_config_t dev_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
dev_cfg.clock_speed_hz = 1 * 1000 * 1000;
spi_device_interface_config_t dev_cfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
dev_cfg.flags = SPI_DEVICE_HALFDUPLEX;
dev_cfg.clock_speed_hz = 1 * 1000 * 1000;
dev_cfg.command_bits = 8;
dev_cfg.address_bits = 8;
dev_cfg.dummy_bits = 8;
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &dev_cfg, &devhd));
printf("\n================Master Tx==================\n");
@@ -1076,6 +1061,170 @@ void master_run_essl(void)
TEST_ESP_OK(spi_bus_remove_device(devhd));
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
}
TEST_CASE_MULTIPLE_DEVICES("SPI Slave HD: Append mode", "[spi_ms]", master_run_essl, slave_run_append);
#endif //SOC_SPI_SUPPORT_SLAVE_HD_VER2
#define TEST_SLP_BUF_ID 12
#define TEST_SLP_BUF_VAL 0xDEADBEEF
TEST_CASE("test_spi_slave_hd_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);
uint32_t slave_hd_cmd[2][2] = {{SPI_LL_BASE_CMD_HD_WRDMA, SPI_LL_BASE_CMD_HD_WR_END}, {SPI_LL_BASE_CMD_HD_RDDMA, SPI_LL_BASE_CMD_HD_INT0}};
uint32_t slave_share_sig = TEST_SLP_BUF_VAL;
uint8_t slv_send[14] = "I'm slave x\n", slv_rexcv[14];
uint8_t mst_txbuff[17] = " I'm master x\n", mst_rxbuff[17]; //more than 3 byte to hold cmd,addrs,dummy
uint8_t share_sig_buff[7] = {0}; // cmd + addr + dummy + uint32_t
uint8_t *mst_send = &mst_txbuff[3], *mst_rexcv = &mst_rxbuff[3];
spi_slave_hd_data_t *ret_trans, tx_data = {
.data = slv_send,
.len = sizeof(slv_send),
}, rx_data = {
.data = slv_rexcv,
.len = sizeof(slv_rexcv),
};
for (uint8_t allow_pd = 0; allow_pd < 2; allow_pd ++) {
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
bus_cfg.flags = (allow_pd) ? SPICOMMON_BUSFLAG_SLP_ALLOW_PD : 0;
bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
gpio_pullup_en(slave_hd_cfg.spics_io_num);
vTaskDelay(1);
for (uint8_t cnt = 0; cnt < 3; cnt ++) {
printf("Going into sleep with power %s ...\n", (bus_cfg.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((bus_cfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP);
#endif
// test slave hd segment transactions
slv_send[11] = cnt + '0';
mst_send[11] = cnt + 'A';
memset(mst_rexcv, 0, sizeof(slv_send));
memset(slv_rexcv, 0, sizeof(slv_rexcv));
TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SLAVE_HOST, SPI_SLAVE_CHAN_TX, &tx_data, portMAX_DELAY));
TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SLAVE_HOST, SPI_SLAVE_CHAN_RX, &rx_data, portMAX_DELAY));
// tx rx transaction
mst_txbuff[0] = slave_hd_cmd[0][0];
spi_master_trans_impl_gpio(bus_cfg, slave_hd_cfg.spics_io_num, 0, mst_txbuff, NULL, sizeof(mst_txbuff));
spi_master_trans_impl_gpio(bus_cfg, slave_hd_cfg.spics_io_num, 0, &slave_hd_cmd[0][1], NULL, 3);
mst_rxbuff[0] = slave_hd_cmd[1][0];
spi_master_trans_impl_gpio(bus_cfg, slave_hd_cfg.spics_io_num, 0, mst_rxbuff, mst_rxbuff, sizeof(mst_rxbuff));
spi_master_trans_impl_gpio(bus_cfg, slave_hd_cfg.spics_io_num, 0, &slave_hd_cmd[1][1], NULL, 3);
// check trans result
TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SLAVE_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY));
printf("master rx %s", mst_rexcv);
printf("slave rx %s", slv_rexcv);
spitest_cmp_or_dump(slv_send, mst_rexcv, sizeof(slv_send));
spitest_cmp_or_dump(mst_send, slv_rexcv, sizeof(slv_rexcv));
// test slave hd share registers
slave_share_sig += cnt;
spi_slave_hd_write_buffer(TEST_SLAVE_HOST, TEST_SLP_BUF_ID, (uint8_t *)&slave_share_sig, sizeof(uint32_t));
memset(share_sig_buff, 0, sizeof(share_sig_buff));
share_sig_buff[0] = SPI_LL_BASE_CMD_HD_RDBUF; // cmd
share_sig_buff[1] = TEST_SLP_BUF_ID; // addr
spi_master_trans_impl_gpio(bus_cfg, slave_hd_cfg.spics_io_num, 0, share_sig_buff, share_sig_buff, sizeof(share_sig_buff));
printf("slave reg %lX\n", *((uint32_t *)&share_sig_buff[3]));
TEST_ASSERT_EQUAL_UINT32(slave_share_sig, *((uint32_t *)&share_sig_buff[3]));
}
spi_slave_hd_deinit(TEST_SLAVE_HOST);
}
esp_sleep_set_sleep_context(NULL);
#if ESP_SLEEP_POWER_DOWN_CPU
TEST_ESP_OK(sleep_cpu_configure(false));
#endif
}
#define TEST_SLP_TRANS_NUM 3
TEST_CASE("test_spi_slave_hd_append_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);
uint32_t slave_hd_cmd[2][2] = {{SPI_LL_BASE_CMD_HD_WRDMA, SPI_LL_BASE_CMD_HD_WR_END}, {SPI_LL_BASE_CMD_HD_RDDMA, SPI_LL_BASE_CMD_HD_INT0}};
uint8_t slv_rexcv[14], slv_send[TEST_SLP_TRANS_NUM][14] = {{"I'm append x\n"}, {"I'm append x\n"}, {"I'm append x\n"}};
uint8_t mst_txbuff[17] = " I'm master x\n", mst_rxbuff[17]; //more than 3 byte to hold cmd,addrs,dummy
uint8_t *mst_send = &mst_txbuff[3], *mst_rexcv = &mst_rxbuff[3];
spi_slave_hd_data_t *ret_trans, tx_data[TEST_SLP_TRANS_NUM], rx_data = {
.data = slv_rexcv,
.len = sizeof(slv_rexcv),
};
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
bus_cfg.max_transfer_sz = 4092 * 4; // append mode require at least 2 for tx and 2 for rx dma descs
bus_cfg.flags = SPICOMMON_BUSFLAG_SLP_ALLOW_PD;
bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
slave_hd_cfg.flags |= SPI_SLAVE_HD_APPEND_MODE;
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
gpio_pullup_en(slave_hd_cfg.spics_io_num);
vTaskDelay(1);
for (uint8_t i = 0; i < 2; i++) {
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
// check if the power domain also is powered down
TEST_ASSERT_EQUAL((bus_cfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP);
#endif
// append transaction first
for (uint8_t cnt = 0; cnt < TEST_SLP_TRANS_NUM; cnt ++) {
slv_send[cnt][11] = cnt + i + '0';
tx_data[cnt].data = slv_send[cnt];
tx_data[cnt].len = sizeof(slv_send[0]);
TEST_ESP_OK(spi_slave_hd_append_trans(TEST_SLAVE_HOST, SPI_SLAVE_CHAN_TX, &tx_data[cnt], portMAX_DELAY));
TEST_ESP_OK(spi_slave_hd_append_trans(TEST_SLAVE_HOST, SPI_SLAVE_CHAN_RX, &rx_data, portMAX_DELAY));
}
// test slave hd append transactions
for (uint8_t cnt = 0; cnt < TEST_SLP_TRANS_NUM; cnt ++) {
mst_send[11] = cnt + i + 'A';
memset(mst_rexcv, 0, sizeof(slv_send[0]));
memset(slv_rexcv, 0, sizeof(slv_rexcv));
// tx rx append transaction
mst_txbuff[0] = slave_hd_cmd[0][0];
spi_master_trans_impl_gpio(bus_cfg, slave_hd_cfg.spics_io_num, 0, mst_txbuff, NULL, sizeof(mst_txbuff));
spi_master_trans_impl_gpio(bus_cfg, slave_hd_cfg.spics_io_num, 0, &slave_hd_cmd[0][1], NULL, 3);
mst_rxbuff[0] = slave_hd_cmd[1][0];
spi_master_trans_impl_gpio(bus_cfg, slave_hd_cfg.spics_io_num, 0, mst_rxbuff, mst_rxbuff, sizeof(mst_rxbuff));
spi_master_trans_impl_gpio(bus_cfg, slave_hd_cfg.spics_io_num, 0, &slave_hd_cmd[1][1], NULL, 3);
// check append trans result
TEST_ESP_OK(spi_slave_hd_get_append_trans_res(TEST_SLAVE_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY));
printf("master rx %s", mst_rexcv);
printf("slave rx %s", slv_rexcv);
spitest_cmp_or_dump(slv_send[cnt], mst_rexcv, sizeof(slv_send[0]));
spitest_cmp_or_dump(mst_send, slv_rexcv, sizeof(slv_rexcv));
}
}
spi_slave_hd_deinit(TEST_SLAVE_HOST);
esp_sleep_set_sleep_context(NULL);
#if ESP_SLEEP_POWER_DOWN_CPU
TEST_ESP_OK(sleep_cpu_configure(false));
#endif
}

View File

@@ -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

View File

@@ -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

View File

@@ -114,17 +114,13 @@ typedef struct {
spi_slave_hd_hal_desc_append_t *tx_dma_head; ///< Head of the linked TX DMA descriptors which are not used by hardware
spi_slave_hd_hal_desc_append_t *tx_dma_tail; ///< Tail of the linked TX DMA descriptors which are not used by hardware
uint32_t tx_used_desc_cnt; ///< Number of the TX descriptors that have been setup
uint32_t tx_recycled_desc_cnt; ///< Number of the TX descriptors that could be recycled
spi_slave_hd_hal_desc_append_t *rx_cur_desc; ///< Current RX DMA descriptor that could be linked (set up).
spi_slave_hd_hal_desc_append_t *rx_dma_head; ///< Head of the linked RX DMA descriptors which are not used by hardware
spi_slave_hd_hal_desc_append_t *rx_dma_tail; ///< Tail of the linked RX DMA descriptors which are not used by hardware
uint32_t rx_used_desc_cnt; ///< Number of the RX descriptors that have been setup
uint32_t rx_recycled_desc_cnt; ///< Number of the RX descriptors that could be recycled
/* Internal status used by the HAL implementation, initialized as 0. */
uint32_t intr_not_triggered;
bool tx_dma_started;
bool rx_dma_started;
} spi_slave_hd_hal_context_t;
/**

View File

@@ -235,7 +235,6 @@ bool spi_slave_hd_hal_get_tx_finished_trans(spi_slave_hd_hal_context_t *hal, voi
}
*out_trans = hal->tx_dma_head->arg;
s_desc_get_received_len_addr(hal->tx_dma_head->desc, NULL, real_buff_addr);
hal->tx_recycled_desc_cnt++;
return true;
}
@@ -252,6 +251,5 @@ bool spi_slave_hd_hal_get_rx_finished_trans(spi_slave_hd_hal_context_t *hal, voi
}
*out_trans = hal->rx_dma_head->arg;
*out_len = s_desc_get_received_len_addr(hal->rx_dma_head->desc, NULL, real_buff_addr);
hal->rx_recycled_desc_cnt++;
return true;
}

View File

@@ -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), \
@@ -67,7 +69,7 @@ static const uint32_t spi_regs_map[4] = {0x31ff, 0x1000000, 0x0, 0x0};
/* Additional interrupt setting is required by idf SPI drivers after register recovered */ \
[1] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_GPSPI_LINK(1), \
SPI_DMA_INT_SET_REG(num), \
SPI_TRANS_DONE_INT_SET | SPI_DMA_SEG_TRANS_DONE_INT_SET , \
SPI_TRANS_DONE_INT_SET | SPI_DMA_SEG_TRANS_DONE_INT_SET | SPI_SLV_CMD7_INT_SET | SPI_SLV_CMD8_INT_SET , \
UINT32_MAX, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
}

View File

@@ -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), \
@@ -67,7 +69,7 @@ static const uint32_t spi_regs_map[4] = {0x31ff, 0x1000000, 0x0, 0x0};
/* Additional interrupt setting is required by idf SPI drivers after register recovered */ \
[1] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_GPSPI_LINK(1), \
SPI_DMA_INT_SET_REG(num), \
SPI_TRANS_DONE_INT_SET | SPI_DMA_SEG_TRANS_DONE_INT_SET , \
SPI_TRANS_DONE_INT_SET | SPI_DMA_SEG_TRANS_DONE_INT_SET | SPI_SLV_CMD7_INT_SET | SPI_SLV_CMD8_INT_SET , \
UINT32_MAX, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
}

View File

@@ -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), \
@@ -67,7 +69,7 @@ static const uint32_t spi_regs_map[4] = {0x31ff, 0x1000000, 0x0, 0x0};
/* Additional interrupt setting is required by idf SPI drivers after register recovered */ \
[1] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_GPSPI_LINK(1), \
SPI_DMA_INT_SET_REG(num), \
SPI_TRANS_DONE_INT_SET | SPI_DMA_SEG_TRANS_DONE_INT_SET , \
SPI_TRANS_DONE_INT_SET | SPI_DMA_SEG_TRANS_DONE_INT_SET | SPI_SLV_CMD7_INT_SET | SPI_SLV_CMD8_INT_SET , \
UINT32_MAX, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
}

View File

@@ -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), \
@@ -67,7 +69,7 @@ static const uint32_t spi_regs_map[4] = {0x31ff, 0x1000000, 0x0, 0x0};
/* Additional interrupt setting is required by idf SPI drivers after register recovered */ \
[1] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_GPSPI_LINK(1), \
SPI_DMA_INT_SET_REG(num), \
SPI_TRANS_DONE_INT_SET | SPI_DMA_SEG_TRANS_DONE_INT_SET , \
SPI_TRANS_DONE_INT_SET | SPI_DMA_SEG_TRANS_DONE_INT_SET | SPI_SLV_CMD7_INT_SET | SPI_SLV_CMD8_INT_SET , \
UINT32_MAX, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
}

View File

@@ -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), \
@@ -99,7 +101,7 @@ static const uint32_t spi_regs_map[4] = {0x31ff, 0x1000000, 0x0, 0x0};
/* Additional interrupt setting is required by idf SPI drivers after register recovered */ \
[1] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_GPSPI_LINK(1), \
SPI_DMA_INT_SET_REG(num), \
SPI_TRANS_DONE_INT_SET | SPI_DMA_SEG_TRANS_DONE_INT_SET , \
SPI_TRANS_DONE_INT_SET | SPI_DMA_SEG_TRANS_DONE_INT_SET | SPI_SLV_CMD7_INT_SET | SPI_SLV_CMD8_INT_SET , \
UINT32_MAX, 1, 0), \
.owner = ENTRY(0) }, \
}

View File

@@ -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
----------------

View File

@@ -121,6 +121,16 @@ Receiving General Purpose Interrupts from the Master
When the master sends ``CMD8``, ``CMD9`` or ``CMDA``, the slave corresponding is triggered. Currently the ``CMD8`` is permanently used to indicate the termination of ``Rd_DMA`` segments. To receive general-purpose interrupts, register callbacks for ``CMD9`` and ``CMDA`` when the slave is initialized, see :ref:`spi_slave_hd_callbacks`.
.. 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.
.. only:: not esp32

View File

@@ -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 传输事务
----------------

View File

@@ -121,6 +121,16 @@ SPI 从机半双工模式
当主机发送 ``CMD8````CMD9````CMDA`` 时,从机会触发相应的动作。目前,``CMD8`` 固定用于指示 ``Rd_DMA`` 段的终止。要接收通用中断,可以在从机初始化时为 ``CMD9````CMDA`` 注册回调函数,详情请参阅 :ref:`spi_slave_hd_callbacks`
.. 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 角色下,不支持在所有传输(发送和接收)未完成时进入睡眠,否则将会出错。
.. only:: not esp32