feat(driver_spi): slave hd driver sleep retention support

This commit is contained in:
wanckl
2024-09-29 20:24:58 +08:00
parent 3100fb6f1b
commit ff78ea91cd
11 changed files with 306 additions and 85 deletions

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;