mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-18 17:38:09 +00:00
feat(driver_spi): slave hd driver sleep retention support
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user