diff --git a/components/esp_driver_spi/include/driver/spi_common.h b/components/esp_driver_spi/include/driver/spi_common.h index 9ea2926e7f..859740127e 100644 --- a/components/esp_driver_spi/include/driver/spi_common.h +++ b/components/esp_driver_spi/include/driver/spi_common.h @@ -177,14 +177,15 @@ esp_err_t spi_bus_free(spi_host_device_t host_id); /** * @brief Helper function for malloc DMA capable memory for SPI driver * - * @note This API will take care of the cache and hardware alignment internally. - * To free/release memory allocated by this helper function, simply calling `free()` + * @note Using this API AFTER spi_bus_initialize() is called, this API will take care of the cache and hardware + * alignment internally. To free/release memory allocated by this helper function, simply calling `free()`. * * @param[in] host_id SPI peripheral who will using the memory * @param[in] size Size in bytes, the amount of memory to allocate * @param[in] extra_heap_caps Extra heap caps based on MALLOC_CAP_DMA * * @return Pointer to the memory if allocated successfully + * - NULL If allocation failed or bus not initialized */ void *spi_bus_dma_memory_alloc(spi_host_device_t host_id, size_t size, uint32_t extra_heap_caps); diff --git a/components/esp_driver_spi/include/esp_private/spi_common_internal.h b/components/esp_driver_spi/include/esp_private/spi_common_internal.h index 22222551d2..a10c75a682 100644 --- a/components/esp_driver_spi/include/esp_private/spi_common_internal.h +++ b/components/esp_driver_spi/include/esp_private/spi_common_internal.h @@ -54,7 +54,7 @@ typedef enum { typedef struct { spi_bus_config_t bus_cfg; ///< Config used to initialize the bus uint64_t gpio_reserve; ///< reserved output gpio bit mask - uint32_t flags; ///< Flags (attributes) of the bus + uint32_t flags; ///< Flags (SPICOMMON_BUSFLAG_* flag combination of bus abilities) of the bus int max_transfer_sz; ///< Maximum length of bytes available to send bool dma_enabled; ///< To enable DMA or not size_t cache_align_int; ///< Internal memory align byte requirement @@ -179,12 +179,11 @@ esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx); * - ``SPICOMMON_BUSFLAG_QUAD``: Combination of ``SPICOMMON_BUSFLAG_DUAL`` and ``SPICOMMON_BUSFLAG_WPHD``. * - ``SPICOMMON_BUSFLAG_IO4_IO7``: The bus has spi data4 ~ spi data7 connected. * - ``SPICOMMON_BUSFLAG_OCTAL``: Combination of ``SPICOMMON_BUSFLAG_QUAL`` and ``SPICOMMON_BUSFLAG_IO4_IO7``. - * @param[out] io_reserved Output the reserved gpio map * @return * - ESP_ERR_INVALID_ARG if parameter is invalid * - ESP_OK on success */ -esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t *flags_o, uint64_t *io_reserved); +esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t *flags_o, uint64_t *io_reserved); /** * @brief Free the IO used by a SPI peripheral @@ -206,7 +205,6 @@ esp_err_t spicommon_bus_free_io_cfg(const spi_bus_config_t *bus_cfg, uint64_t *i * @param cs_id Hardware CS id to route * @param force_gpio_matrix If true, CS will always be routed through the GPIO matrix. If false, * if the GPIO number allows it, the routing will happen through the IO_mux. - * @param[out] io_reserved Output the reserved gpio map */ void spicommon_cs_initialize(spi_host_device_t host, int cs_io_num, int cs_id, int force_gpio_matrix, uint64_t *io_reserved); diff --git a/components/esp_driver_spi/src/gpspi/spi_common.c b/components/esp_driver_spi/src/gpspi/spi_common.c index 61f4373063..b19f9a8e83 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -728,12 +728,14 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf #endif //SOC_SPI_SUPPORT_OCT } + if (bus_ctx[host]) { + bus_ctx[host]->bus_attr.bus_cfg = *bus_config; + bus_ctx[host]->bus_attr.flags = temp_flag; + bus_ctx[host]->bus_attr.gpio_reserve = gpio_reserv; + } if (flags_o) { *flags_o = temp_flag; } - if (io_reserved) { - *io_reserved |= gpio_reserv; - } return ESP_OK; } @@ -777,9 +779,7 @@ void spicommon_cs_initialize(spi_host_device_t host, int cs_io_num, int cs_id, i } gpio_func_sel(cs_io_num, PIN_FUNC_GPIO); } - if (io_reserved) { - *io_reserved |= out_mask; - } + bus_ctx[host]->bus_attr.gpio_reserve |= out_mask; } void spicommon_cs_free_io(int cs_gpio_num, uint64_t *io_reserved) @@ -828,10 +828,8 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t * #endif ESP_RETURN_ON_ERROR(spicommon_bus_alloc(host_id, "spi master"), SPI_TAG, "alloc host failed"); - spi_bus_attr_t *bus_attr = (spi_bus_attr_t *)spi_bus_get_attr(host_id); - spicommon_bus_context_t *ctx = __containerof(bus_attr, spicommon_bus_context_t, bus_attr); - assert(bus_attr && ctx); //coverity check - bus_attr->bus_cfg = *bus_config; + spicommon_bus_context_t *ctx = bus_ctx[host_id]; + spi_bus_attr_t *bus_attr = &ctx->bus_attr; bus_attr->dma_enabled = (dma_chan != SPI_DMA_DISABLED); bus_attr->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE; @@ -872,7 +870,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t * _lock_acquire(&ctx->mutex); if (sleep_retention_module_init(spi_reg_retention_info[host_id - 1].module_id, &init_param) == ESP_OK) { - if ((bus_attr->bus_cfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) && (sleep_retention_module_allocate(spi_reg_retention_info[host_id - 1].module_id) != ESP_OK)) { + 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(SPI_TAG, "alloc sleep recover failed, peripherals may hold power on"); } @@ -882,7 +880,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t * } _lock_release(&ctx->mutex); #else - if (bus_attr->bus_cfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) { + 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 @@ -901,7 +899,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t * } #endif //CONFIG_PM_ENABLE - err = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_MASTER | bus_config->flags, &bus_attr->flags, &bus_attr->gpio_reserve); + err = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_MASTER | bus_config->flags, NULL, NULL); if (err != ESP_OK) { goto cleanup; } @@ -929,10 +927,16 @@ cleanup: void *spi_bus_dma_memory_alloc(spi_host_device_t host_id, size_t size, uint32_t extra_heap_caps) { - // As don't know the buffer will used for TX or RX, so use the max alignment requirement - size_t alignment = (extra_heap_caps & MALLOC_CAP_SPIRAM) ? \ - MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_ext, bus_ctx[host_id]->dma_ctx->dma_align_rx_ext) : \ - MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_int, bus_ctx[host_id]->dma_ctx->dma_align_rx_int); + SPI_CHECK(bus_ctx[host_id], "SPI %d not initialized", NULL, host_id + 1); + + size_t alignment = 16; + // detailed alignment requirement is not available for slave bus, so use 16 bytes as default + if (bus_ctx[host_id]->bus_attr.flags & SPICOMMON_BUSFLAG_MASTER) { + // As don't know the buffer will used for TX or RX, so use the max alignment requirement + alignment = (extra_heap_caps & MALLOC_CAP_SPIRAM) ? \ + MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_ext, bus_ctx[host_id]->dma_ctx->dma_align_rx_ext) : \ + MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_int, bus_ctx[host_id]->dma_ctx->dma_align_rx_int); + } return heap_caps_aligned_calloc(alignment, 1, size, extra_heap_caps | MALLOC_CAP_DMA); } diff --git a/components/esp_driver_spi/src/gpspi/spi_master.c b/components/esp_driver_spi/src/gpspi/spi_master.c index 64894d1b8c..9136ad5f76 100644 --- a/components/esp_driver_spi/src/gpspi/spi_master.c +++ b/components/esp_driver_spi/src/gpspi/spi_master.c @@ -550,7 +550,7 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa //Set CS pin, CS options if (dev_config->spics_io_num >= 0) { - spicommon_cs_initialize(host_id, dev_config->spics_io_num, freecs, use_gpio, (uint64_t *)&bus_attr->gpio_reserve); + spicommon_cs_initialize(host_id, dev_config->spics_io_num, freecs, use_gpio, NULL); } //save a pointer to device in spi_host_t diff --git a/components/esp_driver_spi/src/gpspi/spi_slave.c b/components/esp_driver_spi/src/gpspi/spi_slave.c index 2b8b58b8e9..6b04e3524e 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave.c @@ -61,8 +61,7 @@ typedef struct { typedef struct { int id; _Atomic spi_bus_fsm_t fsm; - uint64_t gpio_reserve; - spi_bus_config_t bus_config; + spi_bus_attr_t* bus_attr; spi_dma_ctx_t *dma_ctx; spi_slave_interface_config_t cfg; intr_handle_t intr; @@ -181,8 +180,8 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b goto cleanup; } memcpy(&spihost[host]->cfg, slave_config, sizeof(spi_slave_interface_config_t)); - memcpy(&spihost[host]->bus_config, bus_config, sizeof(spi_bus_config_t)); spihost[host]->id = host; + spihost[host]->bus_attr = (spi_bus_attr_t *)spi_bus_get_attr(host); atomic_store(&spihost[host]->fsm, SPI_BUS_FSM_ENABLED); spi_slave_hal_context_t *hal = &spihost[host]->hal; @@ -213,17 +212,18 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b spihost[host]->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE; } - err = spicommon_bus_initialize_io(host, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, &spihost[host]->flags, &spihost[host]->gpio_reserve); + err = spicommon_bus_initialize_io(host, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL, NULL); if (err != ESP_OK) { ret = err; goto cleanup; } if (slave_config->spics_io_num >= 0) { - spicommon_cs_initialize(host, slave_config->spics_io_num, 0, !bus_is_iomux(spihost[host]), &spihost[host]->gpio_reserve); + spicommon_cs_initialize(host, slave_config->spics_io_num, 0, !bus_is_iomux(spihost[host]), NULL); // check and save where cs line really route through spihost[host]->cs_iomux = (slave_config->spics_io_num == spi_periph_signal[host].spics0_iomux_pin) && bus_is_iomux(spihost[host]); spihost[host]->cs_in_signal = spi_periph_signal[host].spics_in; } + spihost[host]->flags = spihost[host]->bus_attr->flags; // This flag MUST be set after spicommon_bus_initialize_io is called // The slave DMA suffers from unexpected transactions. Forbid reading if DMA is enabled by disabling the CS line. if (spihost[host]->dma_enabled) { @@ -337,9 +337,9 @@ esp_err_t spi_slave_free(spi_host_device_t host) free(spihost[host]->dma_ctx->dmadesc_rx); spicommon_dma_chan_free(spihost[host]->dma_ctx); } - spicommon_bus_free_io_cfg(&spihost[host]->bus_config, &spihost[host]->gpio_reserve); + spicommon_bus_free_io_cfg(&spihost[host]->bus_attr->bus_cfg, &spihost[host]->bus_attr->gpio_reserve); if (spihost[host]->cfg.spics_io_num >= 0) { - spicommon_cs_free_io(spihost[host]->cfg.spics_io_num, &spihost[host]->gpio_reserve); + spicommon_cs_free_io(spihost[host]->cfg.spics_io_num, &spihost[host]->bus_attr->gpio_reserve); } esp_intr_free(spihost[host]->intr); diff --git a/components/esp_driver_spi/src/gpspi/spi_slave_hd.c b/components/esp_driver_spi/src/gpspi/spi_slave_hd.c index 58ed06661d..a779f3b63b 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave_hd.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave_hd.c @@ -44,9 +44,8 @@ typedef struct { typedef struct { spi_host_device_t host_id; - spi_bus_config_t bus_config; int cs_io_num; - uint64_t gpio_reserve; + spi_bus_attr_t* bus_attr; _Atomic spi_bus_fsm_t fsm; spi_dma_ctx_t *dma_ctx; uint16_t internal_mem_align_size; @@ -129,7 +128,7 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b host->int_spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; host->append_mode = append_mode; atomic_store(&host->fsm, SPI_BUS_FSM_ENABLED); - memcpy(&host->bus_config, bus_config, sizeof(spi_bus_config_t)); + host->bus_attr = (spi_bus_attr_t *)spi_bus_get_attr(host_id); host->cs_io_num = config->spics_io_num; ret = spicommon_dma_chan_alloc(host_id, config->dma_chan, &host->dma_ctx); @@ -172,11 +171,12 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b host->internal_mem_align_size = 4; #endif - ret = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, &host->flags, &host->gpio_reserve); + ret = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL, NULL); if (ret != ESP_OK) { goto cleanup; } - spicommon_cs_initialize(host_id, config->spics_io_num, 0, !(bus_config->flags & SPICOMMON_BUSFLAG_NATIVE_PINS), &host->gpio_reserve); + spicommon_cs_initialize(host_id, config->spics_io_num, 0, !(bus_config->flags & SPICOMMON_BUSFLAG_NATIVE_PINS), NULL); + host->flags = host->bus_attr->flags; // This flag MUST be set after spicommon_bus_initialize_io is called spi_slave_hd_hal_config_t hal_config = { .host_id = host_id, @@ -349,8 +349,8 @@ esp_err_t spi_slave_hd_deinit(spi_host_device_t host_id) } #endif - spicommon_bus_free_io_cfg(&host->bus_config, &host->gpio_reserve); - spicommon_cs_free_io(host->cs_io_num, &host->gpio_reserve); + spicommon_bus_free_io_cfg(&host->bus_attr->bus_cfg, &host->bus_attr->gpio_reserve); + spicommon_cs_free_io(host->cs_io_num, &host->bus_attr->gpio_reserve); spicommon_bus_free(host_id); free(host->dma_ctx->dmadesc_tx); free(host->dma_ctx->dmadesc_rx); 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 238849da0e..1af008387a 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 @@ -130,6 +130,53 @@ TEST_CASE("test fullduplex slave with only RX direction", "[spi]") ESP_LOGI(SLAVE_TAG, "test passed."); } +TEST_CASE("test fullduplex slave with only TX direction", "[spi]") +{ + custom_setup(); + + memcpy(slave_txbuf, slave_send, sizeof(slave_send)); + + for (int i = 0; i < 4; i ++) { + //slave send + spi_slave_transaction_t slave_t; + spi_slave_transaction_t *out; + memset(&slave_t, 0, sizeof(spi_slave_transaction_t)); + slave_t.length = 8 * 32; + slave_t.tx_buffer = slave_txbuf; + slave_t.rx_buffer = NULL; + slave_t.flags |= SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO; + + // Colorize RX buffer with known pattern + memset(master_rxbuf, 0x66, sizeof(master_rxbuf)); + + TEST_ESP_OK(spi_slave_queue_trans(TEST_SLAVE_HOST, &slave_t, portMAX_DELAY)); + + //send + spi_transaction_t t = {}; + t.length = 32 * (i + 1); + if (t.length != 0) { + t.tx_buffer = NULL; + t.rx_buffer = master_rxbuf; + } + spi_device_transmit(spi, &t); + + //wait for end + TEST_ESP_OK(spi_slave_get_trans_result(TEST_SLAVE_HOST, &out, portMAX_DELAY)); + + //show result + ESP_LOGI(SLAVE_TAG, "trans_len: %d", slave_t.trans_len); + ESP_LOG_BUFFER_HEX("master rx", t.rx_buffer, t.length / 8); + ESP_LOG_BUFFER_HEX("slave tx", slave_t.tx_buffer, (slave_t.trans_len + 7) / 8); + + TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_t.tx_buffer, t.rx_buffer, t.length / 8); + TEST_ASSERT_EQUAL(t.length, slave_t.trans_len); + } + + custom_teardown(); + + ESP_LOGI(SLAVE_TAG, "test passed."); +} + #define TEST_SLV_RX_BUF_LEN 15 TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi]") { @@ -202,53 +249,6 @@ TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi] TEST_ESP_OK(spi_bus_remove_device(spidev0)); TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); } - -TEST_CASE("test fullduplex slave with only TX direction", "[spi]") -{ - custom_setup(); - - memcpy(slave_txbuf, slave_send, sizeof(slave_send)); - - for (int i = 0; i < 4; i ++) { - //slave send - spi_slave_transaction_t slave_t; - spi_slave_transaction_t *out; - memset(&slave_t, 0, sizeof(spi_slave_transaction_t)); - slave_t.length = 8 * 32; - slave_t.tx_buffer = slave_txbuf; - slave_t.rx_buffer = NULL; - slave_t.flags |= SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO; - - // Colorize RX buffer with known pattern - memset(master_rxbuf, 0x66, sizeof(master_rxbuf)); - - TEST_ESP_OK(spi_slave_queue_trans(TEST_SLAVE_HOST, &slave_t, portMAX_DELAY)); - - //send - spi_transaction_t t = {}; - t.length = 32 * (i + 1); - if (t.length != 0) { - t.tx_buffer = NULL; - t.rx_buffer = master_rxbuf; - } - spi_device_transmit(spi, &t); - - //wait for end - TEST_ESP_OK(spi_slave_get_trans_result(TEST_SLAVE_HOST, &out, portMAX_DELAY)); - - //show result - ESP_LOGI(SLAVE_TAG, "trans_len: %d", slave_t.trans_len); - ESP_LOG_BUFFER_HEX("master rx", t.rx_buffer, t.length / 8); - ESP_LOG_BUFFER_HEX("slave tx", slave_t.tx_buffer, (slave_t.trans_len + 7) / 8); - - TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_t.tx_buffer, t.rx_buffer, t.length / 8); - TEST_ASSERT_EQUAL(t.length, slave_t.trans_len); - } - - custom_teardown(); - - ESP_LOGI(SLAVE_TAG, "test passed."); -} #endif // !CONFIG_SPIRAM #endif // #if (TEST_SPI_PERIPH_NUM >= 2)