From df70223f6c7fdc48bcbadf6607ce3ea27cf03e5e Mon Sep 17 00:00:00 2001 From: Maksims Matjakubovs Date: Tue, 31 May 2022 18:53:26 +0300 Subject: [PATCH] New SPI Master transaction flag to not use DMA on DMA enabled host. Flag: SPI_TRANS_DONT_DMA Prevend DMA usage for transaction = process it without DMA. --- components/driver/include/driver/spi_master.h | 1 + components/driver/spi_master.c | 13 ++++++++----- components/hal/include/hal/spi_hal.h | 1 + components/hal/spi_hal_iram.c | 8 ++++---- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/components/driver/include/driver/spi_master.h b/components/driver/include/driver/spi_master.h index 3c599ae9e011..3c5065fd3f4a 100644 --- a/components/driver/include/driver/spi_master.h +++ b/components/driver/include/driver/spi_master.h @@ -108,6 +108,7 @@ typedef struct { #define SPI_TRANS_MULTILINE_CMD (1<<9) ///< The data lines used at command phase is the same as data phase (otherwise, only one data line is used at command phase) #define SPI_TRANS_MODE_OCT (1<<10) ///< Transmit/receive data in 8-bit mode #define SPI_TRANS_MULTILINE_ADDR SPI_TRANS_MODE_DIOQIO_ADDR ///< The data lines used at address phase is the same as data phase (otherwise, only one data line is used at address phase) +#define SPI_TRANS_DONT_DMA (1<<11) ///< Do Not use DMA (even if it is configured) /** * This structure describes one SPI transaction. The descriptor should not be modified until the transaction finishes. diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index 30d5a2fb5c32..de828a24c608 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -531,6 +531,7 @@ static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_ hal_trans.cmd = trans->cmd; hal_trans.addr = trans->addr; hal_trans.cs_keep_active = (trans->flags & SPI_TRANS_CS_KEEP_ACTIVE) ? 1 : 0; + hal_trans.dont_dma = (trans->flags & SPI_TRANS_DONT_DMA) ? 1 : 0; //Set up OIO/QIO/DIO if needed hal_trans.line_mode.data_lines = (trans->flags & SPI_TRANS_MODE_DIO) ? 2 : @@ -605,7 +606,7 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) //Okay, transaction is done. const int cs = host->cur_cs; //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset. - if (bus_attr->dma_enabled) { + if (bus_attr->dma_enabled && (host->cur_trans_buf.trans->flags & SPI_TRANS_DONT_DMA) == 0) { //This workaround is only for esp32, where tx_dma_chan and rx_dma_chan are always same spicommon_dmaworkaround_idle(bus_attr->tx_dma_chan); } @@ -660,7 +661,7 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) if (trans_found) { spi_trans_priv_t *const cur_trans_buf = &host->cur_trans_buf; - if (bus_attr->dma_enabled && (cur_trans_buf->buffer_to_rcv || cur_trans_buf->buffer_to_send)) { + if (bus_attr->dma_enabled && (cur_trans_buf->buffer_to_rcv || cur_trans_buf->buffer_to_send) && (cur_trans_buf->trans->flags & SPI_TRANS_DONT_DMA) == 0) { //mark channel as active, so that the DMA will not be reset by the slave //This workaround is only for esp32, where tx_dma_chan and rx_dma_chan are always same spicommon_dmaworkaround_transfer_active(bus_attr->tx_dma_chan); @@ -689,6 +690,8 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl //check transmission length SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_RXDATA)==0 || trans_desc->rxlength <= 32, "SPI_TRANS_USE_RXDATA only available for rxdata transfer <= 32 bits", ESP_ERR_INVALID_ARG); SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_TXDATA)==0 || trans_desc->length <= 32, "SPI_TRANS_USE_TXDATA only available for txdata transfer <= 32 bits", ESP_ERR_INVALID_ARG); + SPI_CHECK((trans_desc->flags & SPI_TRANS_DONT_DMA)==0 || trans_desc->length <= SOC_SPI_MAXIMUM_BUFFER_SIZE*8, "txdata transfer > host maximum without DMA", ESP_ERR_INVALID_ARG); + SPI_CHECK((trans_desc->flags & SPI_TRANS_DONT_DMA)==0 || trans_desc->rxlength <= SOC_SPI_MAXIMUM_BUFFER_SIZE*8, "rxdata transfer > host maximum without DMA", ESP_ERR_INVALID_ARG); SPI_CHECK(trans_desc->length <= bus_attr->max_transfer_sz*8, "txdata transfer > host maximum", ESP_ERR_INVALID_ARG); SPI_CHECK(trans_desc->rxlength <= bus_attr->max_transfer_sz*8, "rxdata transfer > host maximum", ESP_ERR_INVALID_ARG); SPI_CHECK(is_half_duplex || trans_desc->rxlength <= trans_desc->length, "rx length > tx length in full duplex mode", ESP_ERR_INVALID_ARG); @@ -701,7 +704,7 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "Incompatible when setting to both multi-line mode and 3-wire-mode", ESP_ERR_INVALID_ARG); SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && !is_half_duplex), "Incompatible when setting to both multi-line mode and half duplex mode", ESP_ERR_INVALID_ARG); #ifdef CONFIG_IDF_TARGET_ESP32 - SPI_CHECK(!is_half_duplex || !bus_attr->dma_enabled || !rx_enabled || !tx_enabled, "SPI half duplex mode does not support using DMA with both MOSI and MISO phases.", ESP_ERR_INVALID_ARG ); + SPI_CHECK(!is_half_duplex || !(bus_attr->dma_enabled && (trans_desc->flags & SPI_TRANS_DONT_DMA)==0) || !rx_enabled || !tx_enabled, "SPI half duplex mode does not support using DMA with both MOSI and MISO phases.", ESP_ERR_INVALID_ARG ); #endif #if !SOC_SPI_HD_BOTH_INOUT_SUPPORTED SPI_CHECK(!is_half_duplex || !tx_enabled || !rx_enabled, "SPI half duplex mode is not supported when both MOSI and MISO phases are enabled.", ESP_ERR_INVALID_ARG); @@ -804,7 +807,7 @@ esp_err_t SPI_MASTER_ATTR spi_device_queue_trans(spi_device_handle_t handle, spi } spi_trans_priv_t trans_buf; - ret = setup_priv_desc(trans_desc, &trans_buf, (host->bus_attr->dma_enabled)); + ret = setup_priv_desc(trans_desc, &trans_buf, ((host->bus_attr->dma_enabled) && (trans_desc->flags & SPI_TRANS_DONT_DMA) == 0)); if (ret != ESP_OK) return ret; #ifdef CONFIG_PM_ENABLE @@ -953,7 +956,7 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_start(spi_device_handle_t handl } if (ret != ESP_OK) return ret; - ret = setup_priv_desc(trans_desc, &host->cur_trans_buf, (host->bus_attr->dma_enabled)); + ret = setup_priv_desc(trans_desc, &host->cur_trans_buf, ((host->bus_attr->dma_enabled) && (trans_desc->flags & SPI_TRANS_DONT_DMA) == 0)); if (ret!=ESP_OK) return ret; //Polling, no interrupt is used. diff --git a/components/hal/include/hal/spi_hal.h b/components/hal/include/hal/spi_hal.h index 8950e26dac5e..4268c020db5b 100644 --- a/components/hal/include/hal/spi_hal.h +++ b/components/hal/include/hal/spi_hal.h @@ -97,6 +97,7 @@ typedef struct { uint8_t *rcv_buffer; ///< Buffer to hold the receive data. spi_line_mode_t line_mode; ///< SPI line mode of this transaction int cs_keep_active; ///< Keep CS active after transaction + int dont_dma; ///< Keep CS active after transaction } spi_hal_trans_config_t; /** diff --git a/components/hal/spi_hal_iram.c b/components/hal/spi_hal_iram.c index a8432402264b..8bef86fe1943 100644 --- a/components/hal/spi_hal_iram.c +++ b/components/hal/spi_hal_iram.c @@ -136,7 +136,7 @@ void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *de //Fill DMA descriptors if (trans->rcv_buffer) { - if (!hal->dma_enabled) { + if (!hal->dma_enabled || trans->dont_dma) { //No need to setup anything; we'll copy the result out of the work registers directly later. } else { lldesc_setup_link(hal->dmadesc_rx, trans->rcv_buffer, ((trans->rx_bitlen + 7) / 8), true); @@ -152,7 +152,7 @@ void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *de #if CONFIG_IDF_TARGET_ESP32 else { //DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon - if (hal->dma_enabled && !dev->half_duplex) { + if (hal->dma_enabled && !dev->half_duplex && !trans->dont_dma) { spi_ll_dma_rx_enable(hal->hw, 1); spi_dma_ll_rx_start(hal->dma_in, hal->rx_dma_chan, 0); } @@ -160,7 +160,7 @@ void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *de #endif if (trans->send_buffer) { - if (!hal->dma_enabled) { + if (!hal->dma_enabled || trans->dont_dma) { //Need to copy data to registers manually spi_ll_write_buffer(hw, trans->send_buffer, trans->tx_bitlen); } else { @@ -197,7 +197,7 @@ void spi_hal_fetch_result(const spi_hal_context_t *hal) { const spi_hal_trans_config_t *trans = &hal->trans_config; - if (trans->rcv_buffer && !hal->dma_enabled) { + if (trans->rcv_buffer && (!hal->dma_enabled || trans->dont_dma)) { //Need to copy from SPI regs to result buffer. spi_ll_read_buffer(hal->hw, trans->rcv_buffer, trans->rx_bitlen); }